aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2007-08-02 21:45:27 +0000
committerGerald Combs <gerald@wireshark.org>2007-08-02 21:45:27 +0000
commit89a2966ced23693ab513131ef37641ed40787e01 (patch)
treecca4caad8e2d8be6877bd0cb6820661817c69dfb
parent98309a6c837fb050aeab6106b619701cbb75af9f (diff)
Add a "-S" flag to dumpcap, which prints out interface statistics. Use
this in the GUI rather than calling pcap_stats() directly. This gets rid of the last pcap_open_live() call in the GUI code. Update README.packaging. svn path=/trunk/; revision=22443
-rw-r--r--capture-pcap-util.h2
-rw-r--r--capture.c139
-rw-r--r--capture.h23
-rw-r--r--capture_opts.c92
-rw-r--r--capture_opts.h4
-rw-r--r--capture_sync.c212
-rw-r--r--capture_sync.h13
-rw-r--r--doc/README.packaging71
-rw-r--r--dumpcap.c21
-rw-r--r--gtk/capture_if_dlg.c86
10 files changed, 531 insertions, 132 deletions
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 <sys/types.h>
#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> /* 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 <string.h>
#include <ctype.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
@@ -68,6 +72,10 @@
#include "capture-pcap-util.h"
#include <wiretap/file_util.h>
+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 <unistd.h>
#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
#include <signal.h>
#ifdef _WIN32
-#include <fcntl.h>
#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.
+
+1.3. Artwork.
+
+Logo and icon artwork can be found in the "image" directory in the
+distribution. This is available online at
+
+ http://anonsvn.wireshark.org/wireshark/trunk/image/
+
+2. Licensing.
+
+Wireshark is released under the GNU General Public License. Make sure
+your package complies with this license, or we send in the marmots.
+
+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.
-2. The Wireshark web site URL is http://www.wireshark.org/ .
+4.1. Source-level version detection.
-3. Wireshark is released under the GNU General Public License. Make sure
- your package complies with this license, or we send in the marmots.
+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.
-4. Wireshark and the "fin" logo are registered trademarks of Gerald
- Combs.
+5. Trademarks.
-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.
+Wireshark and the "fin" logo are registered trademarks of Gerald Combs.
-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.
+6. Spelling.
-7. Wireshark icons, logos, and other artwork can be found in the
- "image" directory of the Wireshark sources.
+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 type> 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 <packet count> 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);
}