Allow taps to have menu item registration routines; the menu item
[obnox/wireshark/wip.git] / gtk / smb_stat.c
1 /* smb_stat.c
2  * smb_stat   2003 Ronnie Sahlberg
3  *
4  * $Id: smb_stat.c,v 1.3 2003/04/23 03:51:03 guy 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 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #include <gtk/gtk.h>
36 #include <string.h>
37 #include "../epan/packet_info.h"
38 #include "../tap.h"
39 #include "../epan/value_string.h"
40 #include "../smb.h"
41 #include "../register.h"
42 #include "smb_stat.h"
43 #include "compat_macros.h"
44 #include "../simple_dialog.h"
45 #include "../file.h"
46 #include "../globals.h"
47
48 typedef struct _smb_procedure_t {
49         int num;
50         nstime_t min;
51         nstime_t max;
52         nstime_t tot;
53 } smb_procedure_t;
54
55 /* used to keep track of the statistics for an entire program interface */
56 typedef struct _smbstat_t {
57         GtkWidget *win;
58         GtkWidget *vbox;
59         char *filter;
60         GtkWidget *table;
61         int table_height;
62         GtkWidget *table_widgets[768];
63         smb_procedure_t proc[256];
64         smb_procedure_t trans2[256];
65         smb_procedure_t nt_trans[256];
66 } smbstat_t;
67
68
69
70 static void
71 add_table_entry(smbstat_t *ss, char *str, int x, int y)
72 {
73         GtkWidget *tmp;
74
75         if(y>=ss->table_height){
76                 ss->table_height=y+1;
77                 gtk_table_resize(GTK_TABLE(ss->table), ss->table_height, 5);
78         }
79         tmp=gtk_label_new(str);
80         gtk_table_attach_defaults(GTK_TABLE(ss->table), tmp, x, x+1, y, y+1);
81         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
82         gtk_widget_show(tmp);
83 }
84
85
86 static void
87 smbstat_reset(void *pss)
88 {
89         smbstat_t *ss=(smbstat_t *)pss;
90         guint32 i;
91
92         for(i=0;i<256;i++){
93                 ss->proc[i].num=0;      
94                 ss->proc[i].min.secs=0;
95                 ss->proc[i].min.nsecs=0;
96                 ss->proc[i].max.secs=0;
97                 ss->proc[i].max.nsecs=0;
98                 ss->proc[i].tot.secs=0;
99                 ss->proc[i].tot.nsecs=0;
100                 
101                 ss->trans2[i].num=0;    
102                 ss->trans2[i].min.secs=0;
103                 ss->trans2[i].min.nsecs=0;
104                 ss->trans2[i].max.secs=0;
105                 ss->trans2[i].max.nsecs=0;
106                 ss->trans2[i].tot.secs=0;
107                 ss->trans2[i].tot.nsecs=0;
108
109                 ss->nt_trans[i].num=0;  
110                 ss->nt_trans[i].min.secs=0;
111                 ss->nt_trans[i].min.nsecs=0;
112                 ss->nt_trans[i].max.secs=0;
113                 ss->nt_trans[i].max.nsecs=0;
114                 ss->nt_trans[i].tot.secs=0;
115                 ss->nt_trans[i].tot.nsecs=0;
116         }
117 }
118
119 static int
120 smbstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, void *psi)
121 {
122         smbstat_t *ss=(smbstat_t *)pss;
123         smb_info_t *si=psi;
124         nstime_t delta;
125         smb_procedure_t *sp;
126
127         /* we are only interested in reply packets */
128         if(si->request){
129                 return 0;
130         }
131         /* if we havnt seen the request, just ignore it */
132         if(!si->sip){
133                 return 0;
134         }
135
136         if(si->cmd==0xA0){
137                 smb_nt_transact_info_t *sti=(smb_nt_transact_info_t *)si->sip->extra_info;
138
139                 /*nt transaction*/
140                 sp=&(ss->nt_trans[sti->subcmd]);
141         } else if(si->cmd==0x32){
142                 smb_transact2_info_t *st2i=(smb_transact2_info_t *)si->sip->extra_info;
143
144                 /*transaction2*/
145                 sp=&(ss->trans2[st2i->subcmd]);
146         } else {
147                 sp=&(ss->proc[si->cmd]);
148         }
149
150         /* calculate time delta between request and reply */
151         delta.secs=pinfo->fd->abs_secs-si->sip->req_time.secs;
152         delta.nsecs=pinfo->fd->abs_usecs*1000-si->sip->req_time.nsecs;
153         if(delta.nsecs<0){
154                 delta.nsecs+=1000000000;
155                 delta.secs--;
156         }
157
158         if((sp->max.secs==0)
159         && (sp->max.nsecs==0) ){
160                 sp->max.secs=delta.secs;
161                 sp->max.nsecs=delta.nsecs;
162         }
163
164         if((sp->min.secs==0)
165         && (sp->min.nsecs==0) ){
166                 sp->min.secs=delta.secs;
167                 sp->min.nsecs=delta.nsecs;
168         }
169
170         if( (delta.secs<sp->min.secs)
171         ||( (delta.secs==sp->min.secs)
172           &&(delta.nsecs<sp->min.nsecs) ) ){
173                 sp->min.secs=delta.secs;
174                 sp->min.nsecs=delta.nsecs;
175         }
176
177         if( (delta.secs>sp->max.secs)
178         ||( (delta.secs==sp->max.secs)
179           &&(delta.nsecs>sp->max.nsecs) ) ){
180                 sp->max.secs=delta.secs;
181                 sp->max.nsecs=delta.nsecs;
182         }
183         
184         sp->tot.secs += delta.secs;
185         sp->tot.nsecs += delta.nsecs;
186         if(sp->tot.nsecs>1000000000){
187                 sp->tot.nsecs-=1000000000;
188                 sp->tot.secs++;
189         }
190         sp->num++;
191
192         return 1;
193 }
194
195 static void
196 smbstat_draw(void *pss)
197 {
198         smbstat_t *ss=(smbstat_t *)pss;
199         guint32 i;
200         int pos;
201         char str[256];
202 #ifdef G_HAVE_UINT64
203         guint64 td;
204 #else
205         guint32 td;
206 #endif
207
208         gtk_widget_destroy(ss->table);
209         ss->table_height=5;
210         ss->table=gtk_table_new(ss->table_height, 5, TRUE);
211         gtk_container_add(GTK_CONTAINER(ss->vbox), ss->table);
212
213         pos=0;
214         add_table_entry(ss, "Command", 0, pos);
215         add_table_entry(ss, "Calls", 1, pos);
216         add_table_entry(ss, "Min RTT", 2, pos);
217         add_table_entry(ss, "Max RTT", 3, pos);
218         add_table_entry(ss, "Avg RTT", 4, pos);
219         pos++;
220
221         for(i=0;i<256;i++){
222                 /* nothing seen, nothing to do */
223                 if(ss->proc[i].num==0){
224                         continue;
225                 }
226
227                 /* we deal with transaction2 later */
228                 if(i==0x32){
229                         continue;
230                 }
231
232                 /* we deal with nt transaction later */
233                 if(i==0xA0){
234                         continue;
235                 }
236
237                 /* scale it to units of 10us.*/
238                 /* for long captures with a large tot time, this can overflow on 32bit */
239                 td=(int)ss->proc[i].tot.secs;
240                 td=td*100000+(int)ss->proc[i].tot.nsecs/10000;
241                 if(ss->proc[i].num){
242                         td/=ss->proc[i].num;
243                 } else {
244                         td=0;
245                 }
246
247                 sprintf(str, "%s", val_to_str(i, smb_cmd_vals, "Unknown (0x%02x)"));
248                 add_table_entry(ss, str, 0, pos);
249                 sprintf(str, "%d", ss->proc[i].num);
250                 add_table_entry(ss, str, 1, pos);
251                 sprintf(str, "%3d.%05d", (int)ss->proc[i].min.secs,ss->proc[i].min.nsecs/10000);
252                 add_table_entry(ss, str, 2, pos);
253                 sprintf(str, "%3d.%05d", (int)ss->proc[i].max.secs,ss->proc[i].max.nsecs/10000);
254                 add_table_entry(ss, str, 3, pos);
255                 sprintf(str, "%3d.%05d", td/100000, td%100000);
256                 add_table_entry(ss, str, 4, pos);
257                 pos++;
258         }
259
260
261         add_table_entry(ss, "", 0, pos);
262         add_table_entry(ss, "", 1, pos);
263         add_table_entry(ss, "", 2, pos);
264         add_table_entry(ss, "", 3, pos);
265         add_table_entry(ss, "", 4, pos);
266         pos++;
267
268         add_table_entry(ss, "Transaction2 Command", 0, pos);
269         add_table_entry(ss, "Calls", 1, pos);
270         add_table_entry(ss, "Min RTT", 2, pos);
271         add_table_entry(ss, "Max RTT", 3, pos);
272         add_table_entry(ss, "Avg RTT", 4, pos);
273         pos++;
274
275         for(i=0;i<256;i++){
276                 /* nothing seen, nothing to do */
277                 if(ss->trans2[i].num==0){
278                         continue;
279                 }
280
281                 /* scale it to units of 10us.*/
282                 /* for long captures with a large tot time, this can overflow on 32bit */
283                 td=(int)ss->trans2[i].tot.secs;
284                 td=td*100000+(int)ss->trans2[i].tot.nsecs/10000;
285                 if(ss->trans2[i].num){
286                         td/=ss->trans2[i].num;
287                 } else {
288                         td=0;
289                 }
290
291                 sprintf(str, "%s", val_to_str(i, trans2_cmd_vals, "Unknown (0x%02x)"));
292                 add_table_entry(ss, str, 0, pos);
293                 sprintf(str, "%d", ss->trans2[i].num);
294                 add_table_entry(ss, str, 1, pos);
295                 sprintf(str, "%3d.%05d", (int)ss->trans2[i].min.secs,ss->trans2[i].min.nsecs/10000);
296                 add_table_entry(ss, str, 2, pos);
297                 sprintf(str, "%3d.%05d", (int)ss->trans2[i].max.secs,ss->trans2[i].max.nsecs/10000);
298                 add_table_entry(ss, str, 3, pos);
299                 sprintf(str, "%3d.%05d", td/100000, td%100000);
300                 add_table_entry(ss, str, 4, pos);
301                 pos++;
302         }
303
304         add_table_entry(ss, "", 0, pos);
305         add_table_entry(ss, "", 1, pos);
306         add_table_entry(ss, "", 2, pos);
307         add_table_entry(ss, "", 3, pos);
308         add_table_entry(ss, "", 4, pos);
309         pos++;
310
311         add_table_entry(ss, "NT Transaction Command", 0, pos);
312         add_table_entry(ss, "Calls", 1, pos);
313         add_table_entry(ss, "Min RTT", 2, pos);
314         add_table_entry(ss, "Max RTT", 3, pos);
315         add_table_entry(ss, "Avg RTT", 4, pos);
316         pos++;
317
318         for(i=0;i<256;i++){
319                 /* nothing seen, nothing to do */
320                 if(ss->nt_trans[i].num==0){
321                         continue;
322                 }
323
324                 /* scale it to units of 10us.*/
325                 /* for long captures with a large tot time, this can overflow on 32bit */
326                 td=(int)ss->nt_trans[i].tot.secs;
327                 td=td*100000+(int)ss->nt_trans[i].tot.nsecs/10000;
328                 if(ss->nt_trans[i].num){
329                         td/=ss->nt_trans[i].num;
330                 } else {
331                         td=0;
332                 }
333
334                 sprintf(str, "%s", val_to_str(i, nt_cmd_vals, "Unknown (0x%02x)"));
335                 add_table_entry(ss, str, 0, pos);
336                 sprintf(str, "%d", ss->nt_trans[i].num);
337                 add_table_entry(ss, str, 1, pos);
338                 sprintf(str, "%3d.%05d", (int)ss->nt_trans[i].min.secs,ss->nt_trans[i].min.nsecs/10000);
339                 add_table_entry(ss, str, 2, pos);
340                 sprintf(str, "%3d.%05d", (int)ss->nt_trans[i].max.secs,ss->nt_trans[i].max.nsecs/10000);
341                 add_table_entry(ss, str, 3, pos);
342                 sprintf(str, "%3d.%05d", td/100000, td%100000);
343                 add_table_entry(ss, str, 4, pos);
344                 pos++;
345         }
346         gtk_widget_show(ss->table);
347 }
348
349
350 void protect_thread_critical_region(void);
351 void unprotect_thread_critical_region(void);
352 static void
353 win_destroy_cb(GtkWindow *win _U_, gpointer data)
354 {
355         smbstat_t *ss=(smbstat_t *)data;
356
357         protect_thread_critical_region();
358         remove_tap_listener(ss);
359         unprotect_thread_critical_region();
360
361         if(ss->filter){
362                 g_free(ss->filter);
363                 ss->filter=NULL;
364         }
365         g_free(ss);
366 }
367
368
369 static void
370 gtk_smbstat_init(char *optarg)
371 {
372         smbstat_t *ss;
373         guint32 i;
374         char *filter=NULL;
375         GtkWidget *stat_label;
376         GtkWidget *filter_label;
377         char filter_string[256];
378
379         if(!strncmp(optarg,"smb,rtt,",8)){
380                 filter=optarg+8;
381         } else {
382                 filter=NULL;
383         }
384
385         ss=g_malloc(sizeof(smbstat_t));
386         if(filter){
387                 ss->filter=g_malloc(strlen(filter)+1);
388                 strcpy(ss->filter, filter);
389         } else {
390                 ss->filter=NULL;
391         }
392
393         for(i=0;i<256;i++){
394                 ss->proc[i].num=0;      
395                 ss->proc[i].min.secs=0;
396                 ss->proc[i].min.nsecs=0;
397                 ss->proc[i].max.secs=0;
398                 ss->proc[i].max.nsecs=0;
399                 ss->proc[i].tot.secs=0;
400                 ss->proc[i].tot.nsecs=0;
401                 
402                 ss->trans2[i].num=0;    
403                 ss->trans2[i].min.secs=0;
404                 ss->trans2[i].min.nsecs=0;
405                 ss->trans2[i].max.secs=0;
406                 ss->trans2[i].max.nsecs=0;
407                 ss->trans2[i].tot.secs=0;
408                 ss->trans2[i].tot.nsecs=0;
409
410                 ss->nt_trans[i].num=0;  
411                 ss->nt_trans[i].min.secs=0;
412                 ss->nt_trans[i].min.nsecs=0;
413                 ss->nt_trans[i].max.secs=0;
414                 ss->nt_trans[i].max.nsecs=0;
415                 ss->nt_trans[i].tot.secs=0;
416                 ss->nt_trans[i].tot.nsecs=0;
417         }
418
419         ss->win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
420         gtk_window_set_title(GTK_WINDOW(ss->win), "SMB RTT Statistics");
421         SIGNAL_CONNECT(ss->win, "destroy", win_destroy_cb, ss);
422
423         ss->vbox=gtk_vbox_new(FALSE, 0);
424         gtk_container_add(GTK_CONTAINER(ss->win), ss->vbox);
425         gtk_container_set_border_width(GTK_CONTAINER(ss->vbox), 10);
426         gtk_widget_show(ss->vbox);
427
428         stat_label=gtk_label_new("SMB RTT Statistics");
429         gtk_box_pack_start(GTK_BOX(ss->vbox), stat_label, FALSE, FALSE, 0);
430         gtk_widget_show(stat_label);
431
432         snprintf(filter_string,255,"Filter:%s",filter?filter:"");
433         filter_label=gtk_label_new(filter_string);
434         gtk_box_pack_start(GTK_BOX(ss->vbox), filter_label, FALSE, FALSE, 0);
435         gtk_widget_show(filter_label);
436
437
438         ss->table_height=5;
439         ss->table=gtk_table_new(ss->table_height, 5, TRUE);
440         gtk_container_add(GTK_CONTAINER(ss->vbox), ss->table);
441
442         add_table_entry(ss, "Command", 0, 0);
443         add_table_entry(ss, "Calls", 1, 0);
444         add_table_entry(ss, "Min RTT", 2, 0);
445         add_table_entry(ss, "Max RTT", 3, 0);
446         add_table_entry(ss, "Avg RTT", 4, 0);
447
448         add_table_entry(ss, "", 0, 1);
449         add_table_entry(ss, "", 1, 1);
450         add_table_entry(ss, "", 2, 1);
451         add_table_entry(ss, "", 3, 1);
452         add_table_entry(ss, "", 4, 1);
453
454         add_table_entry(ss, "Transaction2 Commands", 0, 2);
455         add_table_entry(ss, "Calls", 1, 2);
456         add_table_entry(ss, "Min RTT", 2, 2);
457         add_table_entry(ss, "Max RTT", 3, 2);
458         add_table_entry(ss, "Avg RTT", 4, 2);
459
460         add_table_entry(ss, "", 0, 3);
461         add_table_entry(ss, "", 1, 3);
462         add_table_entry(ss, "", 2, 3);
463         add_table_entry(ss, "", 3, 3);
464         add_table_entry(ss, "", 4, 3);
465
466         add_table_entry(ss, "NT Transaction Commands", 0, 4);
467         add_table_entry(ss, "Calls", 1, 4);
468         add_table_entry(ss, "Min RTT", 2, 4);
469         add_table_entry(ss, "Max RTT", 3, 4);
470         add_table_entry(ss, "Avg RTT", 4, 4);
471
472         gtk_widget_show(ss->table);
473
474         if(register_tap_listener("smb", ss, filter, smbstat_reset, smbstat_packet, smbstat_draw)){
475                 char str[256];
476                 /* error, we failed to attach to the tap. clean up */
477                 snprintf(str,255,"Could not attach to tap using filter:%s\nMaybe the filter string is invalid?",filter?filter:"");
478                 simple_dialog(ESD_TYPE_WARN, NULL, str);
479                 g_free(ss->filter);
480                 g_free(ss);
481                 return;
482         }
483
484         gtk_widget_show_all(ss->win);
485         redissect_packets(&cfile);
486 }
487
488
489 void
490 register_tap_listener_gtksmbstat(void)
491 {
492         register_ethereal_tap("smb,rtt", gtk_smbstat_init);
493 }
494
495
496 static GtkWidget *dlg=NULL, *dlg_box;
497 static GtkWidget *filter_box;
498 static GtkWidget *filter_label, *filter_entry;
499 static GtkWidget *start_button;
500
501 static void
502 dlg_destroy_cb(void)
503 {
504         dlg=NULL;
505 }
506
507 static void
508 smbstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
509 {
510         char *filter;
511         char str[256];
512
513         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
514         if(filter[0]==0){
515                 gtk_smbstat_init("smb,rtt");
516         } else {
517                 sprintf(str,"smb,rtt,%s", filter);
518                 gtk_smbstat_init(str);
519         }
520 }
521
522 void
523 gtk_smbstat_cb(GtkWidget *w _U_, gpointer d _U_)
524 {
525         /* if the window is already open, bring it to front */
526         if(dlg){
527                 gdk_window_raise(dlg->window);
528                 return;
529         }
530
531         dlg=gtk_window_new(GTK_WINDOW_TOPLEVEL);
532         gtk_window_set_title(GTK_WINDOW(dlg), "SMB RTT Statistics");
533         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
534         dlg_box=gtk_vbox_new(FALSE, 0);
535         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
536         gtk_widget_show(dlg_box);
537
538
539         /* filter box */
540         filter_box=gtk_hbox_new(FALSE, 10);
541         /* Filter label */
542         gtk_container_set_border_width(GTK_CONTAINER(filter_box), 10);
543         filter_label=gtk_label_new("Filter:");
544         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
545         gtk_widget_show(filter_label);
546
547         filter_entry=gtk_entry_new_with_max_length(250);
548         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, FALSE, FALSE, 0);
549         gtk_widget_show(filter_entry);
550         
551         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
552         gtk_widget_show(filter_box);
553
554
555         /* the start button */
556         start_button=gtk_button_new_with_label("Create Stat");
557         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
558                               smbstat_start_button_clicked, NULL);
559         gtk_box_pack_start(GTK_BOX(dlg_box), start_button, TRUE, TRUE, 0);
560         gtk_widget_show(start_button);
561
562         gtk_widget_show_all(dlg);
563 }
564