Add a "-S" flag to dumpcap, which prints out interface statistics. Use
authorgerald <gerald@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 2 Aug 2007 21:45:27 +0000 (21:45 +0000)
committergerald <gerald@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 2 Aug 2007 21:45:27 +0000 (21:45 +0000)
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.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@22443 f5534014-38df-0310-8fa8-9805f1628bb7

capture-pcap-util.h
capture.c
capture.h
capture_opts.c
capture_opts.h
capture_sync.c
capture_sync.h
doc/README.packaging
dumpcap.c
gtk/capture_if_dlg.c

index 72b1f6742cc8b7a6f66d2694790de34d4622f45f..6de58441a90b6460340230be3c834c0b23fbf05c 100644 (file)
@@ -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);
 
index a09daed2a8f05792ed9a6c45ca61c2c52bb7c5ce..6b0efc982aea91d7a7b3555beaf0d7915e078327 100644 (file)
--- a/capture.c
+++ b/capture.c
 #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
 #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 */
index 9ced0542a8247a54844124124277f8c528c990c7..25bbb9ab88daaa9f742ab3092bc5165f9d5066ec 100644 (file)
--- 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 */
index 15ed92bb4a45c5b6d7a271316d4973dce161aa11..c3ecd6a20a29516180aa22c062c964a1e5bd7e64 100644 (file)
 #include <string.h>
 #include <ctype.h>
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
 #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)
 {
index df5a6ef9cb96d52128689695764ca21cc4222302..8d1d5600eaf02955099849a07443a846839bbed8 100644 (file)
@@ -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);
index baced40235c13b76635d062f35b40b3e8f394db0..b7bbb23a4c76a75f312272234a08511799c42e24 100644 (file)
 #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) {
index e82027efc7c557bdee4c6ed33ed6da7602e7f200..a0a9f551854ee3bd64bd50c38eea2150ac51e17d 100644 (file)
@@ -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 */
index 342207db829665b1f2929cffd8bc63321f35a782..53d22a19adcfb640970900049a9e73684b33ab61 100644 (file)
@@ -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.
index 2c054c492ad59d0eab0639ea04869f0c468ed257..37673c76374fad5df38626c8c44f1ee1790f8e41 100644 (file)
--- 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);
index b2114b481cc54b22157e08a7ee25ae578f4a55e3..959d4d261587a3d5ce352f438834deeb2a611c97 100644 (file)
@@ -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);
 }