diff --git a/capture-pcap-util.h b/capture-pcap-util.h index 72b1f6742c..6de58441a9 100644 --- a/capture-pcap-util.h +++ b/capture-pcap-util.h @@ -72,7 +72,7 @@ GList *get_interface_list(int *err, char **err_str); /* Error values from "get_interface_list()/capture_interface_list()". */ #define CANT_GET_INTERFACE_LIST 1 /* error getting list */ #define NO_INTERFACES_FOUND 2 /* list is empty */ -#define CANT_RUN_DUMPCAP 3 /* problem running 'dumpcap -I l' */ +#define CANT_RUN_DUMPCAP 3 /* problem running dumpcap */ void free_interface_list(GList *if_list); diff --git a/capture.c b/capture.c index a09daed2a8..6b0efc982a 100644 --- a/capture.c +++ b/capture.c @@ -44,14 +44,18 @@ #include #endif -#ifdef HAVE_SYS_SOCKET_H -#include -#endif - #ifdef HAVE_NETINET_IN_H #include #endif +#ifdef HAVE_NETDB_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + #ifdef HAVE_SYS_SOCKET_H #include /* needed to define AF_ values on UNIX */ #endif @@ -92,7 +96,16 @@ #include "file_util.h" #include "log.h" +typedef struct if_stat_cache_item_s { + char *name; + struct pcap_stat ps; +} if_stat_cache_item_t; +struct if_stat_cache_s { + int stat_fd; + int fork_child; + GList *cache_list; /* List of if_stat_chache_entry_t */ +}; /** * Start a capture. @@ -675,7 +688,7 @@ capture_interface_list(int *err, char **err_str) /* Check to see if we built a list */ if (if_list == NULL) { - if (*err_str) + if (err_str && *err_str) *err_str = g_strdup("No interfaces found"); *err = NO_INTERFACES_FOUND; } @@ -722,7 +735,7 @@ capture_pcap_linktype_list(gchar *ifname, char **err_str) data_link_info = g_malloc(sizeof (data_link_info_t)); data_link_info->dlt = (int) strtol(lt_parts[0], NULL, 10); data_link_info->name = g_strdup(lt_parts[1]); - if (strcmp(lt_parts[2], "(not supported)") != NULL) + if (strcmp(lt_parts[2], "(not supported)") != 0) data_link_info->description = g_strdup(lt_parts[2]); else data_link_info->description = NULL; @@ -739,5 +752,119 @@ capture_pcap_linktype_list(gchar *ifname, char **err_str) return linktype_list; } +if_stat_cache_t * +capture_stat_start(GList *if_list) { + int stat_fd, fork_child; + gchar *msg; + if_stat_cache_t *sc = NULL; + GList *if_entry; + if_info_t *if_info; + if_stat_cache_item_t *sc_item; + + /* Fire up dumpcap. */ + /* + * XXX - on systems with BPF, the number of BPF devices limits the + * number of devices on which you can capture simultaneously. + * + * This means that + * + * 1) this might fail if you run out of BPF devices + * + * and + * + * 2) opening every interface could leave too few BPF devices + * for *other* programs. + * + * It also means the system could end up getting a lot of traffic + * that it has to pass through the networking stack and capture + * mechanism, so opening all the devices and presenting packet + * counts might not always be a good idea. + */ + if (sync_interface_stats_open(&stat_fd, &fork_child, &msg) == 0) { + sc = g_malloc(sizeof(if_stat_cache_t)); + sc->stat_fd = stat_fd; + sc->fork_child = fork_child; + sc->cache_list = NULL; + + /* Initialize the cache */ + for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) { + if_info = if_entry->data; + sc_item = g_malloc0(sizeof(if_stat_cache_item_t)); + sc_item->name = g_strdup(if_info->name); + sc->cache_list = g_list_append(sc->cache_list, sc_item); + } + } + return sc; +} + +#define MAX_STAT_LINE_LEN 500 + +static void +capture_stat_cache_update(if_stat_cache_t *sc) { + gchar stat_line[MAX_STAT_LINE_LEN]; + gchar **stat_parts; + GList *sc_entry; + if_stat_cache_item_t *sc_item; + + if (!sc) + return; + + while (sync_pipe_gets_nonblock(sc->stat_fd, stat_line, MAX_STAT_LINE_LEN) > 0) { + g_strstrip(stat_line); + stat_parts = g_strsplit(stat_line, "\t", 3); + if (stat_parts[0] == NULL || stat_parts[1] == NULL || + stat_parts[2] == NULL) { + g_strfreev(stat_parts); + continue; + } + for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) { + sc_item = sc_entry->data; + if (strcmp(sc_item->name, stat_parts[0]) == 0) { + sc_item->ps.ps_recv = (u_int) strtoul(stat_parts[1], NULL, 10); + sc_item->ps.ps_drop = (u_int) strtoul(stat_parts[2], NULL, 10); + } + } + g_strfreev(stat_parts); + } +} + +gboolean +capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps) { + GList *sc_entry; + if_stat_cache_item_t *sc_item; + + if (!sc || !ifname || !ps) { + return FALSE; + } + + capture_stat_cache_update(sc); + for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) { + sc_item = sc_entry->data; + if (strcmp(sc_item->name, ifname) == 0) { + memcpy(ps, &sc_item->ps, sizeof(struct pcap_stat)); + return TRUE; + } + } + return FALSE; +} + +void +capture_stat_stop(if_stat_cache_t *sc) { + GList *sc_entry; + if_stat_cache_item_t *sc_item; + gchar *msg; + + if (!sc) + return; + + sync_interface_stats_close(&sc->stat_fd, &sc->fork_child, &msg); + + for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) { + sc_item = sc_entry->data; + g_free(sc_item->name); + g_free(sc_item); + } + g_free(sc); +} #endif /* HAVE_LIBPCAP */ diff --git a/capture.h b/capture.h index 9ced0542a8..25bbb9ab88 100644 --- a/capture.h +++ b/capture.h @@ -81,6 +81,7 @@ extern void capture_input_cfilter_error_message(capture_options *capture_opts, c */ extern void capture_input_closed(capture_options *capture_opts); +#ifdef HAVE_LIBPCAP /** * Fetch the interface list from a child process. */ @@ -92,4 +93,26 @@ extern GList *capture_interface_list(int *err, char **err_str); extern GList *capture_pcap_linktype_list(char *devname, char **err_str); +struct if_stat_cache_s; +typedef struct if_stat_cache_s if_stat_cache_t; + +/** + * Start gathering capture statistics for the interfaces specified. + * @param A GList of if_info_t items + * @return A pointer to the statistics state data. + */ +extern if_stat_cache_t * capture_stat_start(GList *if_list); + +/** + * Fetch capture statistics, similar to pcap_stats(). + */ +struct pcap_stat; /* Stub in case we don't or haven't yet included pcap.h */ +extern gboolean capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps); + +/** + * Stop gathering capture statistics. + */ +void capture_stat_stop(if_stat_cache_t *sc); +#endif /* HAVE_LIBPCAP */ + #endif /* capture.h */ diff --git a/capture_opts.c b/capture_opts.c index 15ed92bb4a..c3ecd6a20a 100644 --- a/capture_opts.c +++ b/capture_opts.c @@ -31,6 +31,10 @@ #include #include +#ifdef HAVE_UNISTD_H +#include +#endif + #ifdef HAVE_SYS_TYPES_H # include #endif @@ -68,6 +72,10 @@ #include "capture-pcap-util.h" #include +typedef struct { + char *name; + pcap_t *pch; +} if_stat_t; static gboolean capture_opts_output_to_pipe(const char *save_file, gboolean *is_pipe); @@ -558,6 +566,90 @@ capture_opts_list_interfaces(gboolean machine_readable) return 0; } +/* Print the number of packets captured for each interface until we're killed. */ +int +capture_opts_print_statistics(gboolean machine_readable) +{ + GList *if_list, *if_entry, *stat_list = NULL, *stat_entry; + if_info_t *if_info; + if_stat_t *if_stat; + int err; + gchar *err_str; + pcap_t *pch; + char errbuf[PCAP_ERRBUF_SIZE]; + struct pcap_stat ps; + + if_list = get_interface_list(&err, &err_str); + if (if_list == NULL) { + switch (err) { + case CANT_GET_INTERFACE_LIST: + cmdarg_err("%s", err_str); + g_free(err_str); + break; + + case NO_INTERFACES_FOUND: + cmdarg_err("There are no interfaces on which a capture can be done"); + break; + } + return err; + } + + for (if_entry = g_list_first(if_list); if_entry != NULL; if_entry = g_list_next(if_entry)) { + if_info = if_entry->data; + pch = pcap_open_live(if_info->name, MIN_PACKET_SIZE, 0, 0, errbuf); + + if (pch) { + if_stat = g_malloc(sizeof(if_stat_t)); + if_stat->name = g_strdup(if_info->name); + if_stat->pch = pch; + stat_list = g_list_append(stat_list, if_stat); + } + } + + if (!stat_list) { + cmdarg_err("There are no interfaces on which a capture can be done"); + return 2; + } + + if (!machine_readable) { + printf("%-15s %10s %10s\n", "Interface", "Received", + "Dropped"); + } + + while (1) { /* XXX - Add signal handling? */ + for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) { + if_stat = stat_entry->data; + pcap_stats(if_stat->pch, &ps); + + if (!machine_readable) { + printf("%-15s %10d %10d\n", if_stat->name, + ps.ps_recv, ps.ps_drop); + } else { + printf("%s\t%d\t%d\n", if_stat->name, + ps.ps_recv, ps.ps_drop); + fflush(stdout); + } + } +#ifdef _WIN32 + Sleep(1 * 1000); +#else + sleep(1); +#endif + } + + /* XXX - Not reached. Should we look for 'q' in stdin? */ + for (stat_entry = g_list_first(stat_list); stat_entry != NULL; stat_entry = g_list_next(stat_entry)) { + if_stat = stat_entry->data; + pcap_close(if_stat->pch); + g_free(if_stat->name); + g_free(if_stat); + } + g_list_free(stat_list); + free_interface_list(if_list); + + return 0; +} + void capture_opts_trim_snaplen(capture_options *capture_opts, int snaplen_min) { diff --git a/capture_opts.h b/capture_opts.h index df5a6ef9cb..8d1d5600ea 100644 --- a/capture_opts.h +++ b/capture_opts.h @@ -120,6 +120,10 @@ capture_opts_list_link_layer_types(capture_options *capture_opts, gboolean machi extern int capture_opts_list_interfaces(gboolean machine_readable); +/* print interface statistics */ +extern int +capture_opts_print_statistics(gboolean machine_readable); + /* trim the snaplen entry */ extern void capture_opts_trim_snaplen(capture_options *capture_opts, int snaplen_min); diff --git a/capture_sync.c b/capture_sync.c index baced40235..b7bbb23a4c 100644 --- a/capture_sync.c +++ b/capture_sync.c @@ -37,10 +37,13 @@ #include #endif +#ifdef HAVE_FCNTL_H +#include +#endif + #include #ifdef _WIN32 -#include #include "epan/unicode-utils.h" #endif @@ -523,15 +526,17 @@ sync_pipe_start(capture_options *capture_opts) { } /* - * Run dumpcap with the supplied arguments. On success, msg points to - * a buffer containing the dumpcap output and returns 0. On failure, msg - * points to the error message returned by dumpcap, and returns dumpcap's - * exit value. In either case, msg must be freed with g_free(). + * Open dumpcap with the supplied arguments. On success, msg points to + * a buffer containing the dumpcap output and returns 0. read_fd and + * fork_child point to the pipe's file descriptor and child PID/handle, + * respectively. On failure, msg points to the error message returned by + * dumpcap, and returns dumpcap's exit value. In either case, msg must be + * freed with g_free(). */ /* XXX - This duplicates a lot of code in sync_pipe_start() */ #define PIPE_BUF_SIZE 5120 static int -sync_pipe_run_command(const char** argv, gchar **msg) { +sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar **msg) { #ifdef _WIN32 HANDLE sync_pipe_read; /* pipe used to send messages from child to parent */ HANDLE sync_pipe_write; /* pipe used to send messages from parent to child */ @@ -545,12 +550,9 @@ sync_pipe_run_command(const char** argv, gchar **msg) { int sync_pipe[2]; /* pipe used to send messages from child to parent */ enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ #endif - int fork_child = -1, fork_child_status; - int sync_pipe_read_fd = -1; - GString *msg_buf = NULL; - gchar buf[PIPE_BUF_SIZE+1]; - int count; + *fork_child = -1; + *read_fd = -1; g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_run_command"); if (!msg) { @@ -613,12 +615,12 @@ sync_pipe_run_command(const char** argv, gchar **msg) { g_free( (gpointer) argv); return CANT_RUN_DUMPCAP; } - fork_child = (int) pi.hProcess; + *fork_child = (int) pi.hProcess; g_string_free(args, TRUE); /* associate the operating system filehandle to a C run-time file handle */ /* (good file handle infos at: http://www.flounder.com/handles.htm) */ - sync_pipe_read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY); + *read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY); #else /* _WIN32 */ if (pipe(sync_pipe) < 0) { @@ -629,7 +631,7 @@ sync_pipe_run_command(const char** argv, gchar **msg) { return CANT_RUN_DUMPCAP; } - if ((fork_child = fork()) == 0) { + if ((*fork_child = fork()) == 0) { /* * Child process - run dumpcap with the right arguments to make * it just capture with the specified capture parameters @@ -643,7 +645,7 @@ sync_pipe_run_command(const char** argv, gchar **msg) { return CANT_RUN_DUMPCAP; } - sync_pipe_read_fd = sync_pipe[PIPE_READ]; + *read_fd = sync_pipe[PIPE_READ]; #endif g_free( (gpointer) argv[0]); /* exename */ @@ -662,31 +664,34 @@ sync_pipe_run_command(const char** argv, gchar **msg) { eth_close(sync_pipe[PIPE_WRITE]); #endif - if (fork_child == -1) { + if (*fork_child == -1) { /* We couldn't even create the child process. */ *msg = g_strdup_printf("Couldn't create child process: %s", strerror(errno)); - eth_close(sync_pipe_read_fd); + eth_close(*read_fd); return CANT_RUN_DUMPCAP; } /* we might wait for a moment till child is ready, so update screen now */ main_window_update(); + return 0; +} - /* We were able to set up to read dumpcap's output. Do so and - return its exit value. */ - msg_buf = g_string_new(""); - while ((count = eth_read(sync_pipe_read_fd, buf, PIPE_BUF_SIZE)) > 0) { - buf[count] = '\0'; - g_string_append(msg_buf, buf); - } +static int +#ifdef _WIN32 +sync_pipe_close_command(int *read_fd, int *fork_child, gchar **msg) { +#else +sync_pipe_close_command(int *read_fd, gchar **msg) { +#endif + int fork_child_status; - eth_close(sync_pipe_read_fd); + eth_close(*read_fd); g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open: wait till child closed"); #ifdef _WIN32 - if (_cwait(&fork_child_status, fork_child, _WAIT_CHILD) == -1) { - g_string_free(msg_buf, TRUE); + /* XXX - Should we signal the child somehow? */ + sync_pipe_kill(*fork_child); + if (_cwait(&fork_child_status, *fork_child, _WAIT_CHILD) == -1) { *msg = g_strdup_printf("Child capture process stopped unexpectedly " "(errno:%u)", errno); return CANT_RUN_DUMPCAP; @@ -697,7 +702,6 @@ sync_pipe_run_command(const char** argv, gchar **msg) { /* The child exited. */ fork_child_status = WEXITSTATUS(fork_child_status); } else { - g_string_free(msg_buf, TRUE); if (WIFSTOPPED(fork_child_status)) { /* It stopped, rather than exiting. "Should not happen." */ *msg = g_strdup_printf("Child capture process stopped: %s", @@ -716,16 +720,56 @@ sync_pipe_run_command(const char** argv, gchar **msg) { return CANT_RUN_DUMPCAP; } } else { - g_string_free(msg_buf, TRUE); *msg = g_strdup_printf("Child capture process stopped unexpectedly " "(errno:%u)", errno); return CANT_RUN_DUMPCAP; } #endif + return 0; +} + +/* + * Run dumpcap with the supplied arguments. On success, msg points to + * a buffer containing the dumpcap output and returns 0. On failure, msg + * points to the error message returned by dumpcap, and returns dumpcap's + * exit value. In either case, msg must be freed with g_free(). + */ +/* XXX - This duplicates a lot of code in sync_pipe_start() */ +#define PIPE_BUF_SIZE 5120 +static int +sync_pipe_run_command(const char** argv, gchar **msg) { + int sync_pipe_read_fd, fork_child, ret; + gchar buf[PIPE_BUF_SIZE+1]; + GString *msg_buf = NULL; + int count; + + ret = sync_pipe_open_command(argv, &sync_pipe_read_fd, &fork_child, msg); + + if (ret) + return ret; + + /* We were able to set up to read dumpcap's output. Do so and + return its exit value. */ + msg_buf = g_string_new(""); + while ((count = eth_read(sync_pipe_read_fd, buf, PIPE_BUF_SIZE)) > 0) { + buf[count] = '\0'; + g_string_append(msg_buf, buf); + } + +#ifdef _WIN32 + ret = sync_pipe_close_command(&sync_pipe_read_fd, &fork_child, msg); +#else + ret = sync_pipe_close_command(&sync_pipe_read_fd, msg); +#endif + + if (ret) { + g_string_free(msg_buf, TRUE); + return ret; + } *msg = msg_buf->str; g_string_free(msg_buf, FALSE); - return fork_child_status; + return 0; } /* @@ -804,6 +848,52 @@ sync_linktype_list_open(gchar *ifname, gchar **msg) { return sync_pipe_run_command(argv, msg); } +/* + * Start getting interface statistics using dumpcap. On success, read_fd + * contains the file descriptor for the pipe's stdout, msg is unchanged, + * and zero is returned. On failure, msg will point to an error message + * that must be g_free()d and a nonzero error value will be returned. + */ +int +sync_interface_stats_open(int *read_fd, int *fork_child, gchar **msg) { + int argc; + const char **argv; + + if (!msg) { + /* We can't return anything */ + return -1; + } + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_linktype_list_open"); + + argv = init_pipe_args(&argc); + + if (!argv) { + *msg = g_strdup_printf("We don't know where to find dumpcap."); + return CANT_RUN_DUMPCAP; + } + + /* Ask for the linktype list */ + argv = sync_pipe_add_arg(argv, &argc, "-S"); + argv = sync_pipe_add_arg(argv, &argc, "-M"); + + /* dumpcap should be running in capture child mode (hidden feature) */ +#ifndef DEBUG_CHILD + argv = sync_pipe_add_arg(argv, &argc, "-Z"); +#endif + + return sync_pipe_open_command(argv, read_fd, fork_child, msg); +} + +/* Close down the stats process */ +int +sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg) { +#ifdef _WIN32 + return sync_pipe_close_command(read_fd, fork_child, msg); +#else + return sync_pipe_close_command(read_fd, msg); +#endif +} /* read a number of bytes from a pipe */ /* (blocks until enough bytes read or an error occurs) */ @@ -812,7 +902,6 @@ pipe_read_bytes(int pipe, char *bytes, int required) { int newly; int offset = 0; - while(required) { newly = read(pipe, &bytes[offset], required); if (newly == 0) { @@ -835,6 +924,67 @@ pipe_read_bytes(int pipe, char *bytes, int required) { return offset; } +static gboolean pipe_data_available(int pipe) { +#ifdef _WIN32 /* PeekNamedPipe */ + HANDLE hPipe = (HANDLE) _get_osfhandle(pipe); + DWORD bytes_avail; + + if (hPipe == INVALID_HANDLE_VALUE) + return FALSE; + + if (! PeekNamedPipe(hPipe, NULL, 0, NULL, &bytes_avail, NULL)) + return FALSE; + + if (bytes_avail > 0) + return TRUE; + return FALSE; +#else /* select */ + fd_set rfds; + struct timeval timeout; + + FD_ZERO(&rfds); + FD_SET(pipe, &rfds); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + if (select(pipe+1, &rfds, NULL, NULL, &timeout) > 0) + return TRUE; + + return FALSE; +#endif +} + +/* Read a line from a pipe, similar to fgets */ +int +sync_pipe_gets_nonblock(int pipe, char *bytes, int max) { + int newly; + int offset = -1; + + while(offset < max - 1) { + offset++; + if (! pipe_data_available(pipe)) + break; + newly = read(pipe, &bytes[offset], 1); + if (newly == 0) { + /* EOF - not necessarily an error */ + break; + } else if (newly < 0) { + /* error */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read from pipe %d: error(%u): %s", pipe, errno, strerror(errno)); + return newly; + } else if (bytes[offset] == '\n') { + break; + } + } + + if (offset >= 0) + bytes[offset] = '\0'; + + return offset; +} + + /* convert header values (indicator and 4-byte length) */ static void pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) { diff --git a/capture_sync.h b/capture_sync.h index e82027efc7..a0a9f55185 100644 --- a/capture_sync.h +++ b/capture_sync.h @@ -71,4 +71,17 @@ sync_interface_list_open(gchar **msg); extern int sync_linktype_list_open(gchar *ifname, gchar **msg); +/** Start getting interface statistics using dumpcap. */ +extern int +sync_interface_stats_open(int *read_fd, int *fork_child, gchar **msg); + +/** Stop gathering statistics. */ +extern int +sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg); + +/** Read a line from a pipe, similar to fgets. Non-blocking. */ +extern int +sync_pipe_gets_nonblock(int pipe, char *bytes, int max); + + #endif /* capture_sync.h */ diff --git a/doc/README.packaging b/doc/README.packaging index 342207db82..53d22a19ad 100644 --- a/doc/README.packaging +++ b/doc/README.packaging @@ -1,37 +1,66 @@ -Here's a brief list of information that might be useful to anyone -distributing a software package containing Wireshark: +The following guidelines should be followed by anyone distributing a software +package containing Wireshark: -1. The canonical location for every Wireshark source release is +1. URLs. + +1.1. Wireshark web site. + +The Wireshark web site URL is http://www.wireshark.org/ . + +1.2. Wireshark releases. + +The canonical location for every Wireshark source release is http://www.wireshark.org/download/src/all-versions/, e.g. http://www.wireshark.org/download/src/all-versions/wireshark-0.99.55.tar.bz2 - If your packaging system downloads a copy of the Wireshark sources, - use this location. Don't use http://www.wireshark.org/download/src. +If your packaging system downloads a copy of the Wireshark sources, use +this location. Don't use http://www.wireshark.org/download/src. -2. The Wireshark web site URL is http://www.wireshark.org/ . +1.3. Artwork. -3. Wireshark is released under the GNU General Public License. Make sure - your package complies with this license, or we send in the marmots. +Logo and icon artwork can be found in the "image" directory in the +distribution. This is available online at -4. Wireshark and the "fin" logo are registered trademarks of Gerald - Combs. + http://anonsvn.wireshark.org/wireshark/trunk/image/ -5. Custom version information can be added by creating a file called - "version.conf". See make-version.pl for details. If your package - contains significant changes we recommend that you use this to - differentiate it from official Wireshark releases. +2. Licensing. -6. The SVN version corresponding to each release is in svnversion.h. - It's defined as a string. If you need a numeric definition, let - us know. +Wireshark is released under the GNU General Public License. Make sure +your package complies with this license, or we send in the marmots. -7. Wireshark icons, logos, and other artwork can be found in the - "image" directory of the Wireshark sources. +3. Privileges. + +In versions up to and including 0.99.6, it was necessary to run +Wireshark with elevated privileges in order to be able to capture +traffic. With version 0.99.7, all function calls that require elevated +privliges have been moved out of the GUI. + +WIRESHARK CONTAINS OVER ONE POINT FIVE MILLION LINES OF SOURCE CODE. DO +NOT RUN THEM AS ROOT. + +4. Customization. + +Custom version information can be added by creating a file called +"version.conf". See make-version.pl for details. If your package +contains significant changes we recommend that you use this to +differentiate it from official Wireshark releases. + +4.1. Source-level version detection. + +The SVN version corresponding to each release is in svnversion.h. It's +defined as a string. If you need a numeric definition, let us know. + +5. Trademarks. + +Wireshark and the "fin" logo are registered trademarks of Gerald Combs. + +6. Spelling. + +Wireshark is spelled with a capital "W", and with everything else lower +case. E.g., "WireShark" is incorrect. -8. Wireshark is spelled with a capital "W", and with everything else - lower case. E.g., "WireShark" is incorrect. If you have a question not addressed here, send it to wireshark-dev@wireshark.org. diff --git a/dumpcap.c b/dumpcap.c index 2c054c492a..37673c7637 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -115,7 +115,8 @@ print_usage(gboolean print_ver) { fprintf(output, " -y link layer type (def: first appropriate)\n"); fprintf(output, " -D print list of interfaces and exit\n"); fprintf(output, " -L print list of link-layer types of iface and exit\n"); - fprintf(output, " -M for -D and -L, produce machine-readable output\n"); + fprintf(output, " -S print statistics for each interface once every second\n"); + fprintf(output, " -M for -D, -L, and -S produce machine-readable output\n"); fprintf(output, "\n"); fprintf(output, "Stop conditions:\n"); fprintf(output, " -c stop after n packets (def: infinite)\n"); @@ -249,9 +250,10 @@ main(int argc, char *argv[]) gboolean list_interfaces = FALSE; gboolean list_link_layer_types = FALSE; gboolean machine_readable = FALSE; - int status; + gboolean print_statistics = FALSE; + int status, run_once_args = 0; -#define OPTSTRING_INIT "a:b:c:Df:hi:LMps:vw:y:Z" +#define OPTSTRING_INIT "a:b:c:Df:hi:LMpSs:vw:y:Z" #ifdef _WIN32 #define OPTSTRING_WIN32 "B:" @@ -369,9 +371,15 @@ main(int argc, char *argv[]) /*** all non capture option specific ***/ case 'D': /* Print a list of capture devices and exit */ list_interfaces = TRUE; + run_once_args++; break; case 'L': /* Print list of link-layer types and exit */ list_link_layer_types = TRUE; + run_once_args++; + break; + case 'S': /* Print interface statistics once a second */ + print_statistics = TRUE; + run_once_args++; break; case 'M': /* For -D and -L, print machine-readable output */ machine_readable = TRUE; @@ -406,8 +414,8 @@ main(int argc, char *argv[]) exit_main(1); } - if (list_interfaces && list_link_layer_types) { - cmdarg_err("Only one of -D or -L may be supplied."); + if (run_once_args > 1) { + cmdarg_err("Only one of -D, -L, or -S may be supplied."); exit_main(1); } else if (list_link_layer_types) { /* We're supposed to list the link-layer types for an interface; @@ -452,6 +460,9 @@ main(int argc, char *argv[]) } else if (list_link_layer_types) { status = capture_opts_list_link_layer_types(capture_opts, machine_readable); exit_main(status); + } else if (print_statistics) { + status = capture_opts_print_statistics(machine_readable); + exit_main(status); } capture_opts_trim_snaplen(capture_opts, MIN_PACKET_SIZE); diff --git a/gtk/capture_if_dlg.c b/gtk/capture_if_dlg.c index b2114b481c..959d4d2615 100644 --- a/gtk/capture_if_dlg.c +++ b/gtk/capture_if_dlg.c @@ -107,7 +107,6 @@ GList *if_list; /* the "runtime" data of one interface */ typedef struct if_dlg_data_s { - pcap_t *pch; GtkWidget *device_lb; GtkWidget *descr_lb; GtkWidget *ip_lb; @@ -123,8 +122,6 @@ typedef struct if_dlg_data_s { if_info_t if_info; } if_dlg_data_t; -void update_if(if_dlg_data_t *if_dlg_data); - /* start capture button was pressed */ static void @@ -185,48 +182,9 @@ capture_details_cb(GtkWidget *details_bt _U_, gpointer if_data) } #endif - -/* open a single interface */ -static void -open_if(gchar *name, if_dlg_data_t *if_dlg_data) -{ - gchar open_err_str[PCAP_ERRBUF_SIZE]; - - /* - * XXX - on systems with BPF, the number of BPF devices limits the - * number of devices on which you can capture simultaneously. - * - * This means that - * - * 1) this might fail if you run out of BPF devices - * - * and - * - * 2) opening every interface could leave too few BPF devices - * for *other* programs. - * - * It also means the system could end up getting a lot of traffic - * that it has to pass through the networking stack and capture - * mechanism, so opening all the devices and presenting packet - * counts might not always be a good idea. - */ - if_dlg_data->pch = pcap_open_live(name, - MIN_PACKET_SIZE, - capture_opts->promisc_mode, CAP_READ_TIMEOUT, - open_err_str); - - if (if_dlg_data->pch != NULL) { - update_if(if_dlg_data); - } else { - printf("open_if: %s\n", open_err_str); - gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), "error"); - gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), "error"); - } -} - /* update a single interface */ void -update_if(if_dlg_data_t *if_dlg_data) +update_if(if_dlg_data_t *if_dlg_data, if_stat_cache_t *sc) { struct pcap_stat stats; gchar *str; @@ -243,8 +201,8 @@ update_if(if_dlg_data_t *if_dlg_data) * (Note also that some versions of libpcap, on some versions of UN*X, * have the same bug.) */ - if (if_dlg_data->pch) { - if(pcap_stats(if_dlg_data->pch, &stats) >= 0) { + if (sc) { + if(capture_stats(sc, if_dlg_data->device, &stats)) { #ifdef _WIN32 diff = stats.ps_recv - if_dlg_data->last_packets; if_dlg_data->last_packets = stats.ps_recv; @@ -269,31 +227,20 @@ update_if(if_dlg_data_t *if_dlg_data) } } - -/* close a single interface */ -static void -close_if(if_dlg_data_t *if_dlg_data) -{ - if(if_dlg_data->pch) - pcap_close(if_dlg_data->pch); -} - - - /* update all interfaces */ static gboolean update_all(gpointer data) { GList *curr; int ifs; - + if_stat_cache_t *sc = data; if(!cap_if_w) { return FALSE; } - for(ifs = 0; (curr = g_list_nth(data, ifs)); ifs++) { - update_if(curr->data); + for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) { + update_if(curr->data, sc); } return TRUE; @@ -322,17 +269,15 @@ set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress) /* the window was closed, cleanup things */ static void -capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_) +capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data) { GList *curr; int ifs; + if_stat_cache_t *sc = user_data; gtk_timeout_remove(timer_id); for(ifs = 0; (curr = g_list_nth(if_data, ifs)); ifs++) { - if_dlg_data_t *if_dlg_data = curr->data; - - close_if(if_dlg_data); g_free(curr->data); } @@ -343,8 +288,10 @@ capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_) /* Note that we no longer have a "Capture Options" dialog box. */ cap_if_w = NULL; + capture_stat_stop(sc); + #ifdef HAVE_AIRPCAP - airpcap_set_toolbar_stop_capture(airpcap_if_active); + airpcap_set_toolbar_stop_capture(airpcap_if_active); #endif } @@ -438,6 +385,7 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_) if_addr_t *ip_addr; GString *if_tool_str = g_string_new(""); gchar *tmp_str; + if_stat_cache_t *sc; if (cap_if_w != NULL) { /* There's already a "Capture Interfaces" dialog box; reactivate it. */ @@ -567,6 +515,10 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_) gtk_widget_size_request(stop_bt, &requisition); height += requisition.height + 15; + /* Start gathering statistics (using dumpcap) */ + sc = capture_stat_start(if_list); + + /* List the interfaces */ for(ifs = 0; (curr = g_list_nth(if_list, ifs)); ifs++) { g_string_assign(if_tool_str, ""); if_info = curr->data; @@ -674,8 +626,6 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_) gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->details_bt, 8, 9, row, row+1); #endif - open_if(if_info->name, if_dlg_data); - if_data = g_list_append(if_data, if_dlg_data); row++; @@ -713,7 +663,7 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_) gtk_widget_grab_default(close_bt); SIGNAL_CONNECT(cap_if_w, "delete_event", window_delete_event_cb, NULL); - SIGNAL_CONNECT(cap_if_w, "destroy", capture_if_destroy_cb, NULL); + SIGNAL_CONNECT(cap_if_w, "destroy", capture_if_destroy_cb, sc); gtk_widget_show_all(cap_if_w); window_present(cap_if_w); @@ -721,7 +671,7 @@ capture_if_cb(GtkWidget *w _U_, gpointer d _U_) set_capture_if_dialog_for_capture_in_progress(is_capture_in_progress()); /* update the interface list every 1000ms */ - timer_id = gtk_timeout_add(1000, update_all, if_data); + timer_id = gtk_timeout_add(1000, update_all, sc); }