Set the svn:eol-style property on all text files to "native", so that
[obnox/wireshark/wip.git] / gtk / capture_if_dlg.c
1 /* capture_if_dlg.c
2  * Routines for the capture interface dialog
3  *
4  * $Id$
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 /* With MSVC and a libethereal.dll this file needs to import some variables 
26    in a special way. Therefore _NEED_VAR_IMPORT_ is defined. */
27 /*#define _NEED_VAR_IMPORT_*/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_LIBPCAP
34
35 #ifdef HAVE_SYS_STAT_H
36 # include <sys/stat.h>
37 #endif
38
39 #ifdef HAVE_SYS_WAIT_H
40 # include <sys/wait.h>
41 #endif
42
43
44 #include <pcap.h>
45
46 #include <gtk/gtk.h>
47
48 #include "globals.h"
49 #include "pcap-util.h"
50
51 #ifdef _WIN32
52 #include "capture-wpcap.h"
53 #endif
54
55 #include "compat_macros.h"
56 #include "simple_dialog.h"
57 #include "capture_dlg.h"
58
59 #include "ui_util.h"
60 #include "dlg_utils.h"
61
62 #include "wtap.h"
63 #include "capture.h"
64
65
66 extern char *iptos(u_long in);
67
68 extern gboolean is_capture_in_progress(void);
69
70 /*
71  * Keep a static pointer to the current "Capture Interfaces" window, if
72  * any, so that if somebody tries to do "Capture:Start" while there's
73  * already a "Capture Interfaces" window up, we just pop up the existing
74  * one, rather than creating a new one.
75  */
76 static GtkWidget *cap_if_w;
77
78 GList           *if_data = NULL;
79
80 guint           timer_id;
81
82 GtkWidget       *stop_bt;
83
84 GList           *if_list;
85
86 /*
87  * Timeout, in milliseconds, for reads from the stream of captured packets.
88  */
89 #define CAP_READ_TIMEOUT        250
90
91
92 /* the "runtime" data of one interface */
93 typedef struct if_dlg_data_s {
94     pcap_t      *pch;
95     GtkWidget   *device_lb;
96     GtkWidget   *descr_lb;
97     GtkWidget   *ip_lb;
98     GtkWidget   *curr_lb;
99     GtkWidget   *last_lb;
100     GtkWidget   *capture_bt;
101     GtkWidget   *prepare_bt;
102     guint32     last_packets;
103     gchar       *device;
104 } if_dlg_data_t;
105
106 void update_if(if_dlg_data_t *if_dlg_data);
107
108
109 /* start capture button was pressed */
110 static void
111 capture_do_cb(GtkWidget *capture_bt _U_, gpointer if_data)
112 {
113   if_dlg_data_t *if_dlg_data = if_data;
114
115   if (cfile.iface)
116     g_free(cfile.iface);
117
118   cfile.iface = g_strdup(if_dlg_data->device);
119
120   do_capture(NULL /* save_file */);
121 }
122
123
124 /* prepare capture button was pressed */
125 static void
126 capture_prepare_cb(GtkWidget *prepare_bt _U_, gpointer if_data)
127 {
128   if_dlg_data_t *if_dlg_data = if_data;
129
130   if (cfile.iface)
131     g_free(cfile.iface);
132
133   cfile.iface = g_strdup(if_dlg_data->device);
134
135   capture_prep_cb(NULL, NULL);
136 }
137
138
139 /* open a single interface */
140 void
141 open_if(gchar *name, if_dlg_data_t *if_dlg_data)
142 {
143   gchar       open_err_str[PCAP_ERRBUF_SIZE];
144
145   if_dlg_data->pch = pcap_open_live(name,
146                        WTAP_MAX_PACKET_SIZE,
147                        capture_opts.promisc_mode, CAP_READ_TIMEOUT,
148                        open_err_str);
149
150   if (if_dlg_data->pch != NULL) {
151       update_if(if_dlg_data);
152   } else {
153     printf("open_if: %s\n", open_err_str);
154     gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), "error");
155     gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), "error");
156   }
157 }
158
159 /* update a single interface */
160 void
161 update_if(if_dlg_data_t *if_dlg_data)
162 {
163   struct pcap_stat stats;
164   gchar *str;
165   guint diff;
166
167
168   /* pcap_stats() stats values differ on libpcap and winpcap!
169    * libpcap: returns the number of packets since pcap_open_live
170    * winpcap: returns the number of packets since the last pcap_stats call
171    */
172   if (if_dlg_data->pch) {
173     if(pcap_stats(if_dlg_data->pch, &stats) >= 0) {
174 #if WIN32
175     diff = stats.ps_recv - if_dlg_data->last_packets;
176     if_dlg_data->last_packets = stats.ps_recv;
177 #else
178     diff = stats.ps_recv;
179     if_dlg_data->last_packets = stats.ps_recv + if_dlg_data->last_packets;
180 #endif    
181
182     str = g_strdup_printf("%u", if_dlg_data->last_packets);
183     gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), str);
184     g_free(str);
185     str = g_strdup_printf("%u", diff);
186     gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), str);
187     g_free(str);
188
189     gtk_widget_set_sensitive(if_dlg_data->curr_lb, diff);
190     gtk_widget_set_sensitive(if_dlg_data->last_lb, diff);
191   } else {
192     gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), "error");
193     gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), "error");
194   }
195   }
196 }
197
198
199 /* close a single interface */
200 void
201 close_if(if_dlg_data_t *if_dlg_data)
202 {
203     if(if_dlg_data->pch)
204         pcap_close(if_dlg_data->pch);
205 }
206
207
208
209 /* update all interfaces */
210 gboolean
211 update_all(gpointer data)
212 {
213     GList *curr;
214     int ifs;
215
216
217     if(!cap_if_w) {
218         return FALSE;
219     }
220
221     for(ifs = 0; (curr = g_list_nth(data, ifs)); ifs++) {
222         update_if(curr->data);
223     }
224
225     return TRUE;
226 }
227
228
229 /* a live capture has started or stopped */
230 void
231 set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress)
232 {
233     GList *curr;
234     int ifs;
235
236     if(cap_if_w) {
237         gtk_widget_set_sensitive(stop_bt, capture_in_progress);
238         
239         for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) {
240             if_dlg_data_t *if_dlg_data = curr->data;
241
242             gtk_widget_set_sensitive(if_dlg_data->capture_bt, !capture_in_progress);
243             gtk_widget_set_sensitive(if_dlg_data->prepare_bt, !capture_in_progress);
244         }        
245     }
246 }
247
248
249 /* the window was closed, cleanup things */
250 static void
251 capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
252 {
253     GList *curr;
254     int ifs;
255
256     gtk_timeout_remove(timer_id);
257
258     for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) {
259         if_dlg_data_t *if_dlg_data = curr->data;
260
261         close_if(if_dlg_data);
262         g_free(curr->data);
263     }
264
265     if_data = NULL;
266
267     free_interface_list(if_list);
268
269     /* Note that we no longer have a "Capture Options" dialog box. */
270     cap_if_w = NULL;
271 }
272
273
274 /* start getting capture stats from all interfaces */
275 void
276 capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
277 {
278   GtkWidget     *main_vb, *bbox, *close_bt;
279
280   GtkWidget     *if_tb;
281   GtkWidget     *if_lb;
282 #if GTK_MAJOR_VERSION < 2
283   GtkAccelGroup *accel_group;
284 #endif
285   GtkTooltips   *tooltips;
286   int           err;
287   char          err_str[PCAP_ERRBUF_SIZE];
288   gchar         *cant_get_if_list_errstr;
289   int           row;
290   if_dlg_data_t *if_dlg_data;
291   int ifs;
292   GList *curr;
293   if_info_t *if_info;
294   GSList *curr_ip;
295   guint32       ip_addr;
296   GString       *if_tool_str = g_string_new("");
297   gchar         *tmp_str;
298
299
300   if (cap_if_w != NULL) {
301     /* There's already a "Capture Interfaces" dialog box; reactivate it. */
302     reactivate_window(cap_if_w);
303     return;
304   }
305
306 #ifdef _WIN32
307   /* Is WPcap loaded? */
308   if (!has_wpcap) {
309           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
310                   "Unable to load WinPcap (wpcap.dll); Ethereal will not be able\n"
311                   "to capture packets.\n\n"
312                   "In order to capture packets, WinPcap must be installed; see\n"
313                   "\n"
314                   "        http://winpcap.polito.it/\n"
315                   "\n"
316                   "or the mirror at\n"
317                   "\n"
318                   "        http://winpcap.mirror.ethereal.com/\n"
319                   "\n"
320                   "or the mirror at\n"
321                   "\n"
322                   "        http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n"
323                   "\n"
324                   "for a downloadable version of WinPcap and for instructions\n"
325                   "on how to install WinPcap.");
326           return;
327   }
328 #endif
329
330   if_list = get_interface_list(&err, err_str);
331   if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
332     cant_get_if_list_errstr = cant_get_if_list_error_message(err_str);
333     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s",
334                   cant_get_if_list_errstr);
335     g_free(cant_get_if_list_errstr);
336     return;
337   }
338
339   cap_if_w = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: Capture Interfaces");
340
341   tooltips = gtk_tooltips_new();
342
343 #if GTK_MAJOR_VERSION < 2
344   /* Accelerator group for the accelerators (or, as they're called in
345      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
346      Ctrl+<key> is an accelerator). */
347   accel_group = gtk_accel_group_new();
348   gtk_window_add_accel_group(GTK_WINDOW(cap_if_w), accel_group);
349 #endif
350
351   main_vb = gtk_vbox_new(FALSE, 0);
352   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
353   gtk_container_add(GTK_CONTAINER(cap_if_w), main_vb);
354
355
356   if_tb = gtk_table_new(6,1, FALSE);
357   gtk_table_set_row_spacings(GTK_TABLE(if_tb), 3);
358   gtk_table_set_col_spacings(GTK_TABLE(if_tb), 3);
359   gtk_container_add(GTK_CONTAINER(main_vb), if_tb);
360
361   row = 0;
362
363 #ifndef WIN32
364   if_lb = gtk_label_new("Device");
365   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 0, 1, row, row+1);
366 #endif
367
368   if_lb = gtk_label_new("Description");
369   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 1, 2, row, row+1);
370
371   if_lb = gtk_label_new(" IP ");
372   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 2, 3, row, row+1);
373
374   if_lb = gtk_label_new("Packets");
375   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 3, 4, row, row+1);
376
377   if_lb = gtk_label_new(" Packets/s ");
378   gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 4, 5, row, row+1);
379
380   stop_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_STOP);
381   gtk_tooltips_set_tip(tooltips, stop_bt, 
382           "Stop a running capture.", NULL);
383   gtk_table_attach_defaults(GTK_TABLE(if_tb), stop_bt, 5, 7, row, row+1);
384   SIGNAL_CONNECT(stop_bt, "clicked", capture_stop_cb, NULL);
385
386   row++;
387
388   for(ifs = 0; (curr = g_list_nth(if_list, ifs)); ifs++) {
389       g_string_assign(if_tool_str, "");
390       if_info = curr->data;
391       if_dlg_data = g_malloc0(sizeof(if_dlg_data_t));
392
393       /* device name */
394       if_dlg_data->device_lb = gtk_label_new(if_info->name);
395       if_dlg_data->device = if_info->name;
396 #ifndef WIN32
397       gtk_misc_set_alignment(GTK_MISC(if_dlg_data->device_lb), 0.0, 0.5);
398       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->device_lb, 0, 1, row, row+1);
399 #endif
400       g_string_append(if_tool_str, "Device: ");
401       g_string_append(if_tool_str, if_info->name);
402       g_string_append(if_tool_str, "\n");
403
404       /* description */
405       if_dlg_data->descr_lb = gtk_label_new(if_info->description);
406       gtk_misc_set_alignment(GTK_MISC(if_dlg_data->descr_lb), 0.0, 0.5);
407       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->descr_lb, 1, 2, row, row+1);
408
409       g_string_append(if_tool_str, "Description: ");
410       g_string_append(if_tool_str, if_info->description);
411       g_string_append(if_tool_str, "\n");
412
413       /* IP address */
414       /* only one IP address will be shown */
415       g_string_append(if_tool_str, "IP: ");
416       curr_ip = g_slist_nth(if_info->ip_addr, 0);
417       if(curr_ip) {
418         ip_addr = *((guint32 *)curr_ip->data);
419         if_dlg_data->ip_lb = gtk_label_new(iptos(ip_addr));
420         g_string_append(if_tool_str, iptos(ip_addr));
421       } else {
422         ip_addr = 0;
423         if_dlg_data->ip_lb = gtk_label_new("unknown");
424         g_string_append(if_tool_str, "unknown");
425       }
426       gtk_widget_set_sensitive(if_dlg_data->ip_lb, ip_addr);
427       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->ip_lb, 2, 3, row, row+1);
428       g_string_append(if_tool_str, "\n");
429
430       /* packets */
431       if_dlg_data->curr_lb = gtk_label_new("-");
432       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->curr_lb, 3, 4, row, row+1);
433
434       /* packets/s */
435       if_dlg_data->last_lb = gtk_label_new("-");
436       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->last_lb, 4, 5, row, row+1);
437
438       /* capture button */
439       if_dlg_data->capture_bt = gtk_button_new_with_label("Capture");
440       SIGNAL_CONNECT(if_dlg_data->capture_bt, "clicked", capture_do_cb, if_dlg_data);
441       tmp_str = g_strdup_printf("Immediately start a capture from this interface:\n\n%s", if_tool_str->str);
442       gtk_tooltips_set_tip(tooltips, if_dlg_data->capture_bt, 
443           tmp_str, NULL);
444       g_free(tmp_str);
445       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->capture_bt, 5, 6, row, row+1);
446
447       /* prepare button */
448       if_dlg_data->prepare_bt = gtk_button_new_with_label("Prepare");
449       SIGNAL_CONNECT(if_dlg_data->prepare_bt, "clicked", capture_prepare_cb, if_dlg_data);
450       gtk_tooltips_set_tip(tooltips, if_dlg_data->prepare_bt, 
451           "Open the capture options dialog with this interface selected.", NULL);
452       gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->prepare_bt, 6, 7, row, row+1);
453
454       open_if(if_info->name, if_dlg_data);
455
456       if_data = g_list_append(if_data, if_dlg_data);
457
458       row++;
459   }
460
461   g_string_free(if_tool_str, TRUE);
462
463   /* Button row: close button */
464   bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
465   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
466
467   close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
468   window_set_cancel_button(cap_if_w, close_bt, window_cancel_button_cb);
469   gtk_tooltips_set_tip(tooltips, close_bt, "Close this window.", NULL);
470
471   gtk_widget_grab_default(close_bt);
472
473   SIGNAL_CONNECT(cap_if_w, "delete_event", window_delete_event_cb, NULL);
474   SIGNAL_CONNECT(cap_if_w, "destroy", capture_if_destroy_cb, NULL);
475
476   gtk_widget_show_all(cap_if_w);
477   window_present(cap_if_w);
478
479   set_capture_if_dialog_for_capture_in_progress(is_capture_in_progress());
480
481     /* update the interface list every 1000ms */
482   timer_id = gtk_timeout_add(1000, update_all, if_data);
483 }
484
485
486 #endif /* HAVE_LIBPCAP */