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