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