Don't destroy something if it doesn't exist.
[obnox/wireshark/wip.git] / gtk / scsi_stat.c
1 /* scsi_stat.c
2  * scsi_stat   2006 Ronnie Sahlberg
3  *
4  * $Id: scsi_stat.c 17252 2006-02-11 13:05:24Z lego $
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /* This module provides rpc call/reply SRT (Server Response Time) statistics 
26  * to Wireshark.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <gtk/gtk.h>
34
35 #include <epan/packet_info.h>
36 #include <epan/epan.h>
37
38 #include <epan/stat_cmd_args.h>
39 #include "stat_menu.h"
40 #include "gui_stat_menu.h"
41 #include "simple_dialog.h"
42 #include "gui_utils.h"
43 #include "dlg_utils.h"
44 #include <epan/tap.h>
45 #include "register.h"
46 #include "main.h"
47 #include "globals.h"
48 #include "filter_dlg.h"
49 #include "compat_macros.h"
50 #include "service_response_time_table.h"
51 #include "gtkglobals.h"
52 #include <epan/conversation.h>
53 #include <epan/dissectors/packet-scsi.h>
54 #include <epan/dissectors/packet-fc.h>
55
56 static GtkWidget *dlg=NULL;
57
58 /* used to keep track of the statistics for an entire scsi command set */
59 typedef struct _scsistat_t {
60         GtkWidget *win;
61         srt_stat_table srt_table;
62         guint8 cmdset;
63         const value_string *cdbnames;
64         const char *prog;
65 } scsistat_t;
66
67 static guint8 scsi_program=0;
68 static GtkWidget *filter_entry;
69
70 static char *
71 scsistat_gen_title(scsistat_t *rs)
72 {
73         char *title;
74
75         title = g_strdup_printf("SCSI Service Response Time statistics for %s: %s",
76             rs->prog, cf_get_display_name(&cfile));
77         return title;
78 }
79
80 static void
81 scsistat_set_title(scsistat_t *rs)
82 {
83         char *title;
84
85         title = scsistat_gen_title(rs);
86         gtk_window_set_title(GTK_WINDOW(rs->win), title);
87         g_free(title);
88 }
89
90 static void
91 scsistat_reset(void *arg)
92 {
93         scsistat_t *rs = arg;
94
95         reset_srt_table_data(&rs->srt_table);
96         scsistat_set_title(rs);
97 }
98
99
100 static void
101 dlg_destroy_cb(void)
102 {
103         dlg=NULL;
104 }
105
106
107 static void
108 scsistat_program_select(GtkWidget *item _U_, gpointer key)
109 {
110         int k=(int)key;
111
112         scsi_program=k;
113 }
114
115 static int
116 scsistat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
117 {
118         scsistat_t *rs = arg;
119         const scsi_task_data_t *ri = arg2;
120
121         /* we are only interested in response packets */
122         if(ri->type!=SCSI_PDU_TYPE_RSP){
123                 return 0;
124         }
125         /* we are only interested in a specific commandset */
126         if( (!ri->itl) || ((ri->itl->cmdset&SCSI_CMDSET_MASK)!=rs->cmdset) ){
127                 return 0;
128         }
129         /* check that the opcode looks sane */
130         if( (!ri->itlq) || (ri->itlq->scsi_opcode>255) ){
131                 return 0;
132         }
133
134         add_srt_table_data(&rs->srt_table, ri->itlq->scsi_opcode, &ri->itlq->fc_time, pinfo);
135
136         return 1;
137 }
138
139 static void
140 scsistat_draw(void *arg)
141 {
142         scsistat_t *rs = arg;
143
144         draw_srt_table_data(&rs->srt_table);
145 }
146
147
148
149 /* since the gtk2 implementation of tap is multithreaded we must protect
150  * remove_tap_listener() from modifying the list while draw_tap_listener()
151  * is running.  the other protected block is in main.c
152  *
153  * there should not be any other critical regions in gtk2
154  */
155 static void
156 win_destroy_cb(GtkWindow *win _U_, gpointer data)
157 {
158         scsistat_t *rs=(scsistat_t *)data;
159
160         protect_thread_critical_region();
161         remove_tap_listener(rs);
162         unprotect_thread_critical_region();
163
164         free_srt_table_data(&rs->srt_table);
165         g_free(rs);
166 }
167
168
169 /* When called, this function will create a new instance of gtk2-scsistat.
170  */
171 static void
172 gtk_scsistat_init(const char *optarg, void* userdata _U_)
173 {
174         scsistat_t *rs;
175         guint32 i;
176         char *title_string;
177         char filter_string[256];
178         GtkWidget *vbox;
179         GtkWidget *stat_label;
180         GtkWidget *filter_label;
181         GtkWidget *bbox;
182         GtkWidget *close_bt;
183         int program, pos;
184         const char *filter=NULL;
185         GString *error_string;
186         char *hf_name=NULL;
187
188         pos=0;
189         if(sscanf(optarg,"scsi,srt,%d,%n",&program,&pos)==1){
190                 if(pos){
191                         filter=optarg+pos;
192                 } else {
193                         filter=NULL;
194                 }
195         } else {
196                 fprintf(stderr, "wireshark: invalid \"-z scsi,srt,<cmdset>[,<filter>]\" argument\n");
197                 exit(1);
198         }
199
200         scsi_program=program;
201         rs=g_malloc(sizeof(scsistat_t));
202         rs->cmdset=program;
203         switch(program){
204         case SCSI_DEV_SBC:
205                 rs->prog="SBC (disk)";
206                 rs->cdbnames=scsi_sbc2_vals;
207                 hf_name="scsi.sbc.opcode";
208                 break;
209         case SCSI_DEV_SSC:
210                 rs->prog="SSC (tape)";
211                 rs->cdbnames=scsi_ssc2_vals;
212                 hf_name="scsi.ssc.opcode";
213                 break;
214         case SCSI_DEV_CDROM:
215                 rs->prog="MMC (cd/dvd)";
216                 rs->cdbnames=scsi_mmc_vals;
217                 hf_name="scsi.mmc.opcode";
218                 break;
219         }
220
221         rs->win=window_new(GTK_WINDOW_TOPLEVEL, "scsi-stat");
222         gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
223         scsistat_set_title(rs);
224
225         vbox=gtk_vbox_new(FALSE, 3);
226         gtk_container_add(GTK_CONTAINER(rs->win), vbox);
227         gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
228         
229         title_string = scsistat_gen_title(rs);
230         stat_label=gtk_label_new(title_string);
231         g_free(title_string);
232         gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
233
234         g_snprintf(filter_string,255,"Filter:%s",filter?filter:"");
235         filter_label=gtk_label_new(filter_string);
236         gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
237
238         /* We must display TOP LEVEL Widget before calling init_srt_table() */
239         gtk_widget_show_all(rs->win);
240
241         init_srt_table(&rs->srt_table, 256, vbox, hf_name);
242
243         for(i=0;i<256;i++){
244                 init_srt_table_row(&rs->srt_table, i, val_to_str(i, rs->cdbnames, "Unknown-0x%02x"));
245         }
246
247
248         error_string=register_tap_listener("scsi", rs, filter, scsistat_reset, scsistat_packet, scsistat_draw);
249         if(error_string){
250                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
251                 g_string_free(error_string, TRUE);
252                 free_srt_table_data(&rs->srt_table);
253                 g_free(rs);
254                 return;
255         }
256
257         /* Button row. */
258         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
259         gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
260
261         close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
262         window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
263
264         SIGNAL_CONNECT(rs->win, "delete_event", window_delete_event_cb, NULL);
265         SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
266
267         gtk_widget_show_all(rs->win);
268         window_present(rs->win);
269
270         cf_retap_packets(&cfile, FALSE);
271 }
272
273
274
275 static void
276 scsistat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
277 {
278         GString *str;
279         const char *filter;
280
281         str = g_string_new("scsi,srt");
282         g_string_sprintfa(str, ",%d", scsi_program);
283         filter=gtk_entry_get_text(GTK_ENTRY(filter_entry));
284         if(filter[0]!=0){
285                 g_string_sprintfa(str, ",%s", filter);
286         }
287
288         gtk_scsistat_init(str->str,NULL);
289         g_string_free(str, TRUE);
290 }
291
292
293 static void
294 gtk_scsistat_cb(GtkWidget *w _U_, gpointer d _U_)
295 {
296         GtkWidget *prog_menu;
297         GtkWidget *dlg_box;
298         GtkWidget *prog_box, *prog_label, *prog_opt;
299         GtkWidget *filter_box, *filter_bt;
300         GtkWidget *menu_item;
301         GtkWidget *bbox, *start_button, *cancel_button;
302         const char *filter;
303         static construct_args_t args = {
304           "Service Response Time Statistics Filter",
305           TRUE,
306           FALSE,
307       FALSE
308         };
309
310         /* if the window is already open, bring it to front */
311         if(dlg){
312                 gdk_window_raise(dlg->window);
313                 return;
314         }
315
316         dlg=dlg_window_new("Wireshark: Compute SCSI SRT statistics");
317         gtk_window_set_default_size(GTK_WINDOW(dlg), 300, -1);
318
319         dlg_box=gtk_vbox_new(FALSE, 10);
320         gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
321         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
322         gtk_widget_show(dlg_box);
323
324         /* Program box */
325         prog_box=gtk_hbox_new(FALSE, 10);
326
327         /* Program label */
328         gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
329         prog_label=gtk_label_new("Commandset:");
330         gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
331         gtk_widget_show(prog_label);
332
333         /* Program menu */
334         prog_opt=gtk_option_menu_new();
335         prog_menu=gtk_menu_new();
336
337         /* SBC */
338         menu_item=gtk_menu_item_new_with_label("SBC (disk)");
339         SIGNAL_CONNECT(menu_item, "activate", scsistat_program_select, SCSI_DEV_SBC);
340         gtk_widget_show(menu_item);
341         gtk_menu_append(GTK_MENU(prog_menu), menu_item);
342
343
344         /* SSC */
345         menu_item=gtk_menu_item_new_with_label("SSC (tape)");
346         SIGNAL_CONNECT(menu_item, "activate", scsistat_program_select, SCSI_DEV_SSC);
347         gtk_widget_show(menu_item);
348         gtk_menu_append(GTK_MENU(prog_menu), menu_item);
349
350         /* MMC */
351         menu_item=gtk_menu_item_new_with_label("MMC (cd/dvd)");
352         SIGNAL_CONNECT(menu_item, "activate", scsistat_program_select, SCSI_DEV_CDROM);
353         gtk_widget_show(menu_item);
354         gtk_menu_append(GTK_MENU(prog_menu), menu_item);
355
356
357         gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
358         gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
359         gtk_widget_show(prog_opt);
360
361         gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
362         gtk_widget_show(prog_box);
363
364         /* Filter box */
365         filter_box=gtk_hbox_new(FALSE, 3);
366
367         /* Filter label */
368         filter_bt=BUTTON_NEW_FROM_STOCK(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
369         SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
370         gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
371         gtk_widget_show(filter_bt);
372
373         /* Filter entry */
374         filter_entry=gtk_entry_new();
375     SIGNAL_CONNECT(filter_entry, "changed", filter_te_syntax_check_cb, NULL);
376
377         /* filter prefs dialog */
378         OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_entry);
379         /* filter prefs dialog */
380
381         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
382         filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
383         if(filter){
384                 gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
385         }
386         gtk_widget_show(filter_entry);
387
388         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
389         gtk_widget_show(filter_box);
390
391         /* button box */
392     bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
393         gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
394     gtk_widget_show(bbox);
395
396     start_button = OBJECT_GET_DATA(bbox, WIRESHARK_STOCK_CREATE_STAT);
397     SIGNAL_CONNECT_OBJECT(start_button, "clicked",
398                               scsistat_start_button_clicked, NULL);
399
400     cancel_button = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
401     window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
402
403         /* Give the initial focus to the "Filter" entry box. */
404         gtk_widget_grab_focus(filter_entry);
405
406     gtk_widget_grab_default(start_button );
407
408     SIGNAL_CONNECT(dlg, "delete_event", window_delete_event_cb, NULL);
409         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
410
411     gtk_widget_show_all(dlg);
412     window_present(dlg);
413 }
414
415
416 void
417 register_tap_listener_gtkscsistat(void)
418 {
419         register_stat_cmd_arg("scsi,srt,", gtk_scsistat_init, NULL);
420
421         register_stat_menu_item("SCSI...", REGISTER_STAT_GROUP_RESPONSE_TIME,
422             gtk_scsistat_cb, NULL, NULL, NULL);
423
424 }
425