added dfilter string syntax check to various places
[obnox/wireshark/wip.git] / gtk / rpc_stat.c
1 /* rpc_stat.c
2  * rpc_stat   2002 Ronnie Sahlberg
3  *
4  * $Id: rpc_stat.c,v 1.42 2004/02/27 19:07:20 ulfl Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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 ethereal.
27  *
28  * It serves as an example on how to use the tap api.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <gtk/gtk.h>
36
37 #include <epan/packet_info.h>
38 #include <epan/epan.h>
39
40 #include "tap_menu.h"
41 #include "simple_dialog.h"
42 #include "ui_util.h"
43 #include "dlg_utils.h"
44 #include "tap.h"
45 #include "../register.h"
46 #include "packet-rpc.h"
47 #include "../globals.h"
48 #include "filter_prefs.h"
49 #include "compat_macros.h"
50 #include "service_response_time_table.h"
51
52 extern GtkWidget   *main_display_filter_widget;
53
54 /* used to keep track of the statistics for an entire program interface */
55 typedef struct _rpcstat_t {
56         GtkWidget *win;
57         srt_stat_table srt_table;
58         char *prog;
59         guint32 program;
60         guint32 version;
61         guint32 num_procedures;
62 } rpcstat_t;
63
64 static char *
65 rpcstat_gen_title(rpcstat_t *rs)
66 {
67         char *title;
68
69         title = g_strdup_printf("ONC-RPC Service Response Time statistics for %s version %d: %s",
70             rs->prog, rs->version, cf_get_display_name(&cfile));
71         return title;
72 }
73
74 static void
75 rpcstat_set_title(rpcstat_t *rs)
76 {
77         char *title;
78
79         title = rpcstat_gen_title(rs);
80         gtk_window_set_title(GTK_WINDOW(rs->win), title);
81         g_free(title);
82 }
83
84 static void
85 rpcstat_reset(rpcstat_t *rs)
86 {
87         reset_srt_table_data(&rs->srt_table);
88         rpcstat_set_title(rs);
89 }
90
91
92 static int
93 rpcstat_packet(rpcstat_t *rs, packet_info *pinfo, epan_dissect_t *edt _U_, rpc_call_info_value *ri)
94 {
95         if(ri->proc>=rs->num_procedures){
96                 /* dont handle this since its outside of known table */
97                 return 0;
98         }
99         /* we are only interested in reply packets */
100         if(ri->request){
101                 return 0;
102         }
103         /* we are only interested in certain program/versions */
104         if( (ri->prog!=rs->program) || (ri->vers!=rs->version) ){
105                 return 0;
106         }
107
108         add_srt_table_data(&rs->srt_table, ri->proc, &ri->req_time, pinfo);
109
110         return 1;
111 }
112
113 static void
114 rpcstat_draw(rpcstat_t *rs)
115 {
116         draw_srt_table_data(&rs->srt_table);
117 }
118
119
120
121 static guint32 rpc_program=0;
122 static guint32 rpc_version=0;
123 static gint32 rpc_min_vers=-1;
124 static gint32 rpc_max_vers=-1;
125 static gint32 rpc_min_proc=-1;
126 static gint32 rpc_max_proc=-1;
127
128 static void *
129 rpcstat_find_procs(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
130 {
131         rpc_proc_info_key *k=(rpc_proc_info_key *)key;
132
133         if(k->prog!=rpc_program){
134                 return NULL;
135         }
136         if(k->vers!=rpc_version){
137                 return NULL;
138         }
139         if(rpc_min_proc==-1){
140                 rpc_min_proc=k->proc;
141                 rpc_max_proc=k->proc;
142         }
143         if((gint32)k->proc<rpc_min_proc){
144                 rpc_min_proc=k->proc;
145         }
146         if((gint32)k->proc>rpc_max_proc){
147                 rpc_max_proc=k->proc;
148         }
149
150         return NULL;
151 }
152
153 static void *
154 rpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
155 {
156         rpc_proc_info_key *k=(rpc_proc_info_key *)key;
157
158         if(k->prog!=rpc_program){
159                 return NULL;
160         }
161         if(rpc_min_vers==-1){
162                 rpc_min_vers=k->vers;
163                 rpc_max_vers=k->vers;
164         }
165         if((gint32)k->vers<rpc_min_vers){
166                 rpc_min_vers=k->vers;
167         }
168         if((gint32)k->vers>rpc_max_vers){
169                 rpc_max_vers=k->vers;
170         }
171
172         return NULL;
173 }
174
175 /* since the gtk2 implementation of tap is multithreaded we must protect
176  * remove_tap_listener() from modifying the list while draw_tap_listener()
177  * is running.  the other protected block is in main.c
178  *
179  * there should not be any other critical regions in gtk2
180  */
181 void protect_thread_critical_region(void);
182 void unprotect_thread_critical_region(void);
183 static void
184 win_destroy_cb(GtkWindow *win _U_, gpointer data)
185 {
186         rpcstat_t *rs=(rpcstat_t *)data;
187
188         protect_thread_critical_region();
189         remove_tap_listener(rs);
190         unprotect_thread_critical_region();
191
192         free_srt_table_data(&rs->srt_table);
193         g_free(rs);
194 }
195
196 /* When called, this function will create a new instance of gtk2-rpcstat.
197  */
198 static void
199 gtk_rpcstat_init(char *optarg)
200 {
201         rpcstat_t *rs;
202         guint32 i;
203         char *title_string;
204         char filter_string[256];
205         GtkWidget *vbox;
206         GtkWidget *stat_label;
207         GtkWidget *filter_label;
208         int program, version, pos;
209         char *filter=NULL;
210         GString *error_string;
211         int hf_index;
212         header_field_info *hfi;
213
214         pos=0;
215         if(sscanf(optarg,"rpc,srt,%d,%d,%n",&program,&version,&pos)==2){
216                 if(pos){
217                         filter=optarg+pos;
218                 } else {
219                         filter=NULL;
220                 }
221         } else {
222                 fprintf(stderr, "ethereal: invalid \"-z rpc,srt,<program>,<version>[,<filter>]\" argument\n");
223                 exit(1);
224         }
225
226         rpc_program=program;
227         rpc_version=version;
228         rs=g_malloc(sizeof(rpcstat_t));
229         rs->prog=rpc_prog_name(rpc_program);
230         rs->program=rpc_program;
231         rs->version=rpc_version;
232         hf_index=rpc_prog_hf(rpc_program, rpc_version);
233         hfi=proto_registrar_get_nth(hf_index);
234
235         rs->win=window_new(GTK_WINDOW_TOPLEVEL, NULL);
236         gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
237         rpcstat_set_title(rs);
238         SIGNAL_CONNECT(rs->win, "destroy", win_destroy_cb, rs);
239
240         vbox=gtk_vbox_new(FALSE, 0);
241         gtk_container_add(GTK_CONTAINER(rs->win), vbox);
242         gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
243         gtk_widget_show(vbox);
244
245         title_string = rpcstat_gen_title(rs);
246         stat_label=gtk_label_new(title_string);
247         g_free(title_string);
248         gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
249         gtk_widget_show(stat_label);
250
251         snprintf(filter_string,255,"Filter:%s",filter?filter:"");
252         filter_label=gtk_label_new(filter_string);
253         gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
254         gtk_widget_show(filter_label);
255
256         rpc_min_proc=-1;
257         rpc_max_proc=-1;
258         g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_procs, NULL);
259         rs->num_procedures=rpc_max_proc+1;
260
261         /* We must display TOP LEVEL Widget before calling init_srt_table() */
262         gtk_widget_show(rs->win);
263
264         init_srt_table(&rs->srt_table, rpc_max_proc+1, vbox, hfi->abbrev);
265
266         for(i=0;i<rs->num_procedures;i++){
267                 init_srt_table_row(&rs->srt_table, i, rpc_proc_name(rpc_program, rpc_version, i));
268         }
269
270
271         error_string=register_tap_listener("rpc", rs, filter, (void*)rpcstat_reset, (void*)rpcstat_packet, (void*)rpcstat_draw);
272         if(error_string){
273                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
274                 g_string_free(error_string, TRUE);
275                 free_srt_table_data(&rs->srt_table);
276                 g_free(rs);
277                 return;
278         }
279
280
281         gtk_widget_show_all(rs->win);
282         retap_packets(&cfile);
283 }
284
285
286
287
288 static GtkWidget *dlg=NULL;
289 static GtkWidget *prog_menu;
290 static GtkWidget *vers_opt, *vers_menu;
291 static GtkWidget *filter_entry;
292
293
294 static void
295 rpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
296 {
297         GString *str;
298         char *filter;
299
300         str = g_string_new("rpc,srt");
301         g_string_sprintfa(str, ",%d,%d", rpc_program, rpc_version);
302         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
303         if(filter[0]!=0){
304                 g_string_sprintfa(str, ",%s", filter);
305         }
306
307         gtk_rpcstat_init(str->str);
308         g_string_free(str, TRUE);
309 }
310
311
312 static void
313 rpcstat_version_select(GtkWidget *item _U_, gpointer key)
314 {
315         int vers=(int)key;
316
317         rpc_version=vers;
318 }
319
320
321
322 static void
323 rpcstat_program_select(GtkWidget *item _U_, gpointer key)
324 {
325         rpc_prog_info_key *k=(rpc_prog_info_key *)key;
326         int i;
327
328         rpc_program=k->prog;
329
330         /* change version menu */
331         rpc_version=0;
332         gtk_object_destroy(GTK_OBJECT(vers_menu));
333         vers_menu=gtk_menu_new();
334         rpc_min_vers=-1;
335         rpc_max_vers=-1;
336         g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_vers, NULL);
337         rpc_version=rpc_min_vers;
338         for(i=rpc_min_vers;i<=rpc_max_vers;i++){
339                 GtkWidget *menu_item;
340                 char vs[5];
341                 sprintf(vs,"%d",i);
342                 menu_item=gtk_menu_item_new_with_label(vs);
343                 SIGNAL_CONNECT(menu_item, "activate", rpcstat_version_select,
344                                i);
345
346                 gtk_widget_show(menu_item);
347                 gtk_menu_append(GTK_MENU(vers_menu), menu_item);
348         }
349         gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
350 }
351
352 static void *
353 rpcstat_list_programs(gpointer *key, gpointer *value, gpointer *user_data _U_)
354 {
355         rpc_prog_info_key *k=(rpc_prog_info_key *)key;
356         rpc_prog_info_value *v=(rpc_prog_info_value *)value;
357         GtkWidget *menu_item;
358
359         menu_item=gtk_menu_item_new_with_label(v->progname);
360         SIGNAL_CONNECT(menu_item, "activate", rpcstat_program_select, k);
361
362         gtk_widget_show(menu_item);
363         gtk_menu_append(GTK_MENU(prog_menu), menu_item);
364
365         if(!rpc_program){
366                 rpc_program=k->prog;
367         }
368
369         return NULL;
370 }
371
372 static void
373 dlg_destroy_cb(void)
374 {
375         dlg=NULL;
376 }
377
378 static void
379 dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
380 {
381         gtk_widget_destroy(GTK_WIDGET(parent_w));
382 }
383
384 static void
385 gtk_rpcstat_cb(GtkWidget *w _U_, gpointer d _U_)
386 {
387         GtkWidget *dlg_box;
388         GtkWidget *prog_box, *prog_label, *prog_opt;
389         GtkWidget *vers_label;
390         GtkWidget *filter_box, *filter_bt;
391         GtkWidget *bbox, *start_button, *cancel_button;
392         int i;
393         const char *filter;
394         static construct_args_t args = {
395           "Service Response Time Statistics Filter",
396           TRUE,
397           FALSE
398         };
399
400         /* if the window is already open, bring it to front */
401         if(dlg){
402                 gdk_window_raise(dlg->window);
403                 return;
404         }
405
406         dlg=dlg_window_new("Ethereal: Compute ONC-RPC SRT statistics");
407         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
408
409         dlg_box=gtk_vbox_new(FALSE, 10);
410         gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
411         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
412         gtk_widget_show(dlg_box);
413
414         /* Program box */
415         prog_box=gtk_hbox_new(FALSE, 10);
416
417         /* Program label */
418         gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
419         prog_label=gtk_label_new("Program:");
420         gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
421         gtk_widget_show(prog_label);
422
423         /* Program menu */
424         prog_opt=gtk_option_menu_new();
425         prog_menu=gtk_menu_new();
426         g_hash_table_foreach(rpc_progs, (GHFunc)rpcstat_list_programs, NULL);
427         gtk_option_menu_set_menu(GTK_OPTION_MENU(prog_opt), prog_menu);
428         gtk_box_pack_start(GTK_BOX(prog_box), prog_opt, TRUE, TRUE, 0);
429         gtk_widget_show(prog_opt);
430
431         /* Version label */
432         gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
433         vers_label=gtk_label_new("Version:");
434         gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
435         gtk_widget_show(vers_label);
436
437         /* Version menu */
438         vers_opt=gtk_option_menu_new();
439         vers_menu=gtk_menu_new();
440         rpc_min_vers=-1;
441         rpc_max_vers=-1;
442         g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_vers, NULL);
443         rpc_version=rpc_min_vers;
444         for(i=rpc_min_vers;i<=rpc_max_vers;i++){
445                 GtkWidget *menu_item;
446                 char vs[5];
447                 sprintf(vs,"%d",i);
448                 menu_item=gtk_menu_item_new_with_label(vs);
449                 SIGNAL_CONNECT(menu_item, "activate", rpcstat_version_select,
450                                i);
451
452                 gtk_widget_show(menu_item);
453                 gtk_menu_append(GTK_MENU(vers_menu), menu_item);
454         }
455         gtk_option_menu_set_menu(GTK_OPTION_MENU(vers_opt), vers_menu);
456         gtk_box_pack_start(GTK_BOX(prog_box), vers_opt, TRUE, TRUE, 0);
457         gtk_widget_show(vers_opt);
458
459         gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
460         gtk_widget_show(prog_box);
461
462         /* Filter box */
463         filter_box=gtk_hbox_new(FALSE, 3);
464
465         /* Filter label */
466         filter_bt=BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_DISPLAY_FILTER_ENTRY);
467         SIGNAL_CONNECT(filter_bt, "clicked", display_filter_construct_cb, &args);
468         gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
469         gtk_widget_show(filter_bt);
470
471         /* Filter entry */
472         filter_entry=gtk_entry_new();
473         WIDGET_SET_SIZE(filter_entry, 300, -1);
474     SIGNAL_CONNECT(filter_entry, "changed", filter_te_syntax_check_cb, NULL);
475
476         /* filter prefs dialog */
477         OBJECT_SET_DATA(filter_bt, E_FILT_TE_PTR_KEY, filter_entry);
478         /* filter prefs dialog */
479
480         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
481         filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
482         if(filter){
483                 gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
484         }
485         gtk_widget_show(filter_entry);
486
487         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
488         gtk_widget_show(filter_box);
489
490         /* button box */
491     bbox = dlg_button_row_new(ETHEREAL_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
492         gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
493     gtk_widget_show(bbox);
494
495     start_button = OBJECT_GET_DATA(bbox, ETHEREAL_STOCK_CREATE_STAT);
496     gtk_widget_grab_default(start_button );
497     SIGNAL_CONNECT_OBJECT(start_button, "clicked",
498                               rpcstat_start_button_clicked, NULL);
499
500     cancel_button = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
501         SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);    
502
503         /* Catch the "activate" signal on the filter text entry, so that
504            if the user types Return there, we act as if the "Create Stat"
505            button had been selected, as happens if Return is typed if some
506            widget that *doesn't* handle the Return key has the input
507            focus. */
508         dlg_set_activate(filter_entry, start_button);
509
510         /* Catch the "key_press_event" signal in the window, so that we can
511            catch the ESC key being pressed and act as if the "Cancel" button
512            had been selected. */
513         dlg_set_cancel(dlg, cancel_button);
514
515         /* Give the initial focus to the "Filter" entry box. */
516         gtk_widget_grab_focus(filter_entry);
517
518         gtk_widget_show_all(dlg);
519 }
520
521
522 void
523 register_tap_listener_gtkrpcstat(void)
524 {
525         register_ethereal_tap("rpc,srt,", gtk_rpcstat_init);
526
527         register_tap_menu_item("ONC-RPC...", REGISTER_TAP_GROUP_RESPONSE_TIME,
528             gtk_rpcstat_cb, NULL, NULL, NULL);
529 }
530