Fetch an indication of whether the interface supports capturing in
[obnox/wireshark/wip.git] / capture_sync.c
index ba435e0bf9be6e904889afc48c942807966c6393..f412c4e51af9ab727a49023a1033184ca2305577 100644 (file)
 #include <unistd.h>
 #endif
 
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
 #include <signal.h>
 
 #ifdef _WIN32
-#include <fcntl.h>
-#include "epan/strutil.h"
+#include <wsutil/unicode-utils.h>
 #endif
 
 #ifdef HAVE_SYS_WAIT_H
 #include "globals.h"
 #include "file.h"
 #include <epan/filesystem.h>
+#include <epan/report_err.h>
 
 #include "capture.h"
 #include "capture_sync.h"
-#include "simple_dialog.h"
 
 #include "sync_pipe.h"
 
@@ -95,7 +98,7 @@
 #include "capture-wpcap.h"
 #endif
 #include "ui_util.h"
-#include "file_util.h"
+#include <wsutil/file_util.h>
 #include "log.h"
 
 #ifdef _WIN32
@@ -208,14 +211,39 @@ protect_arg (const gchar *argv)
 }
 #endif
 
+/* Initialize an argument list and add dumpcap to it. */
+static const char **
+init_pipe_args(int *argc) {
+    const char **argv;
+    const char *progfile_dir;
+    char *exename;
 
+    progfile_dir = get_progfile_dir();
+    if (progfile_dir == NULL) {
+      return NULL;
+    }
 
-#define ARGV_NUMBER_LEN 24
+    /* Allocate the string pointer array with enough space for the
+       terminating NULL pointer. */
+    *argc = 0;
+    argv = g_malloc(sizeof (char *));
+    *argv = NULL;
+
+    /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */
+    exename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "dumpcap", progfile_dir);
 
+    /* Make that the first argument in the argument list (argv[0]). */
+    argv = sync_pipe_add_arg(argv, argc, exename);
+
+    return argv;
+}
+
+#define ARGV_NUMBER_LEN 24
 /* a new capture run: start a new dumpcap task and hand over parameters through command line */
 gboolean
 sync_pipe_start(capture_options *capture_opts) {
     char ssnap[ARGV_NUMBER_LEN];
+    char sdlt[ARGV_NUMBER_LEN];
     char scount[ARGV_NUMBER_LEN];
     char sfilesize[ARGV_NUMBER_LEN];
     char sfile_duration[ARGV_NUMBER_LEN];
@@ -223,26 +251,33 @@ sync_pipe_start(capture_options *capture_opts) {
     char sautostop_files[ARGV_NUMBER_LEN];
     char sautostop_filesize[ARGV_NUMBER_LEN];
     char sautostop_duration[ARGV_NUMBER_LEN];
-#ifdef _WIN32
+#ifdef HAVE_PCAP_REMOTE
+    char sauth[256];
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+    char ssampling[ARGV_NUMBER_LEN];
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
     char buffer_size[ARGV_NUMBER_LEN];
+#endif
+#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 child to parent */
-    HANDLE signal_pipe_read;                /* pipe used to send messages from parent to child (currently only stop) */
-    HANDLE signal_pipe_write;               /* pipe used to send messages from parent to child (currently only stop) */
+    HANDLE signal_pipe;                     /* named pipe used to send messages from parent to child (currently only stop) */
     GString *args = g_string_sized_new(200);
     gchar *quoted_arg;
     SECURITY_ATTRIBUTES sa;
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
     int i;
+    char control_id[ARGV_NUMBER_LEN];
+    gchar *signal_pipe_name;
 #else
     char errmsg[1024+1];
     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 sync_pipe_read_fd;
-    const char *progfile_dir;
-    char *exename;
     int argc;
     const char **argv;
 
@@ -252,24 +287,14 @@ sync_pipe_start(capture_options *capture_opts) {
 
     capture_opts->fork_child = -1;
 
-    progfile_dir = get_progfile_dir();
-    if (progfile_dir == NULL) {
-      /* We don't know where to find dumpcap. */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "We don't know where to find dumpcap.");
-      return FALSE;
+    argv = init_pipe_args(&argc);
+    if (!argv) {
+        /* We don't know where to find dumpcap. */
+        report_failure("We don't know where to find dumpcap.");
+        return FALSE;
     }
 
-    /* Allocate the string pointer array with enough space for the
-       terminating NULL pointer. */
-    argc = 0;
-    argv = g_malloc(sizeof (char *));
-    *argv = NULL;
-
-    /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */
-    exename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "dumpcap", progfile_dir);
-
-    /* Make that the first argument in the argument list (argv[0]). */
-    argv = sync_pipe_add_arg(argv, &argc, exename);
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[0]: %s", argv[0]);
 
     argv = sync_pipe_add_arg(argv, &argc, "-i");
     argv = sync_pipe_add_arg(argv, &argc, capture_opts->iface);
@@ -282,13 +307,8 @@ sync_pipe_start(capture_options *capture_opts) {
 
     if (capture_opts->linktype != -1) {
       argv = sync_pipe_add_arg(argv, &argc, "-y");
-#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME
-      g_snprintf(ssnap, ARGV_NUMBER_LEN, "%s",linktype_val_to_name(capture_opts->linktype));
-#else
-      /* we can't get the type name, just treat it as a number */
-      g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d",capture_opts->linktype);
-#endif
-      argv = sync_pipe_add_arg(argv, &argc, ssnap);
+      g_snprintf(sdlt, ARGV_NUMBER_LEN, "%s",linktype_val_to_name(capture_opts->linktype));
+      argv = sync_pipe_add_arg(argv, &argc, sdlt);
     }
 
     if(capture_opts->multi_files_on) {
@@ -337,14 +357,59 @@ sync_pipe_start(capture_options *capture_opts) {
 
     if (!capture_opts->promisc_mode)
       argv = sync_pipe_add_arg(argv, &argc, "-p");
+#ifdef HAVE_PCAP_CREATE
+    if (capture_opts->monitor_mode)
+      argv = sync_pipe_add_arg(argv, &argc, "-I");
+#endif
+    if (capture_opts->use_pcapng)
+      argv = sync_pipe_add_arg(argv, &argc, "-n");
+#ifdef HAVE_PCAP_REMOTE
+    if (capture_opts->datatx_udp)
+      argv = sync_pipe_add_arg(argv, &argc, "-u");
+
+    if (!capture_opts->nocap_rpcap)
+      argv = sync_pipe_add_arg(argv, &argc, "-r");
+
+    if (capture_opts->auth_type == CAPTURE_AUTH_PWD)
+    {
+        argv = sync_pipe_add_arg(argv, &argc, "-A");
+        g_snprintf(sauth, sizeof(sauth), "%s:%s", capture_opts->auth_username,
+                   capture_opts->auth_password);
+        argv = sync_pipe_add_arg(argv, &argc, sauth);
+    }
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+    if (capture_opts->sampling_method != CAPTURE_SAMP_NONE)
+    {
+        argv = sync_pipe_add_arg(argv, &argc, "-m");
+        g_snprintf(ssampling, ARGV_NUMBER_LEN, "%s:%d",
+             capture_opts->sampling_method == CAPTURE_SAMP_BY_COUNT ? "count" :
+             capture_opts->sampling_method == CAPTURE_SAMP_BY_TIMER ? "timer" :
+             "undef",
+             capture_opts->sampling_param);
+        argv = sync_pipe_add_arg(argv, &argc, ssampling);
+    }
+#endif
 
     /* dumpcap should be running in capture child mode (hidden feature) */
 #ifndef DEBUG_CHILD
     argv = sync_pipe_add_arg(argv, &argc, "-Z");
+#ifdef _WIN32
+    g_snprintf(control_id, ARGV_NUMBER_LEN, "%d", GetCurrentProcessId());
+    argv = sync_pipe_add_arg(argv, &argc, control_id);
+#else
+    argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
+#endif
 #endif
 
-#ifdef _WIN32
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
     argv = sync_pipe_add_arg(argv, &argc, "-B");
+#ifdef HAVE_PCAP_REMOTE
+    if (capture_opts->src_type == CAPTURE_IFREMOTE)
+      /* No buffer size when using remote interfaces */
+      g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", 1);
+    else
+#endif
     g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d",capture_opts->buffer_size);
     argv = sync_pipe_add_arg(argv, &argc, buffer_size);
 #endif
@@ -366,22 +431,25 @@ sync_pipe_start(capture_options *capture_opts) {
     sa.lpSecurityDescriptor = NULL;
 
     /* Create a pipe for the child process */
-    /* (inrease this value if you have trouble while fast capture file switches) */
+    /* (increase this value if you have trouble while fast capture file switches) */
     if (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) {
       /* Couldn't create the pipe between parent and child. */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create sync pipe: %s",
-                        strerror(errno));
+      report_failure("Couldn't create sync pipe: %s", strerror(errno));
+      g_free( (gpointer) argv[0]);
       g_free( (gpointer) argv);
       return FALSE;
     }
 
-    /* Create a pipe for the parent process */
-    if (! CreatePipe(&signal_pipe_read, &signal_pipe_write, &sa, 512)) {
+    /* Create the signal pipe */
+    signal_pipe_name = g_strdup_printf(SIGNAL_PIPE_FORMAT, control_id);
+    signal_pipe = CreateNamedPipe(utf_8to16(signal_pipe_name),
+      PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL);
+    g_free(signal_pipe_name);
+
+    if (signal_pipe == INVALID_HANDLE_VALUE) {
       /* Couldn't create the signal pipe between parent and child. */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create signal pipe: %s",
-                        strerror(errno));
-      CloseHandle(sync_pipe_read);
-      CloseHandle(sync_pipe_write);
+      report_failure("Couldn't create signal pipe: %s", strerror(errno));
+      g_free( (gpointer) argv[0]);
       g_free( (gpointer) argv);
       return FALSE;
     }
@@ -395,8 +463,8 @@ sync_pipe_start(capture_options *capture_opts) {
 #else
     si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
     si.wShowWindow  = SW_HIDE;  /* this hides the console window */
-    si.hStdInput = signal_pipe_read;
-    si.hStdOutput = sync_pipe_write;
+    si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+    si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
     si.hStdError = sync_pipe_write;
     /*si.hStdError = (HANDLE) _get_osfhandle(2);*/
 #endif
@@ -414,11 +482,11 @@ sync_pipe_start(capture_options *capture_opts) {
     /* call dumpcap */
     if(!CreateProcess(NULL, utf_8to16(args->str), NULL, NULL, TRUE,
                       CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-                    "Couldn't run %s in child process: error %u",
-                    args->str, GetLastError());
+      report_failure("Couldn't run %s in child process: error %u",
+                     args->str, GetLastError());
       CloseHandle(sync_pipe_read);
       CloseHandle(sync_pipe_write);
+      g_free( (gpointer) argv[0]);
       g_free( (gpointer) argv);
       return FALSE;
     }
@@ -430,44 +498,43 @@ sync_pipe_start(capture_options *capture_opts) {
     sync_pipe_read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
 
     /* associate the operating system filehandle to a C run-time file handle */
-    capture_opts->signal_pipe_write_fd = _open_osfhandle( (long) signal_pipe_write, _O_BINARY);
+    capture_opts->signal_pipe_write_fd = _open_osfhandle( (long) signal_pipe, _O_BINARY);
 
-    /* child own's the read side now, close our handle */
-    CloseHandle(signal_pipe_read);
 #else /* _WIN32 */
     if (pipe(sync_pipe) < 0) {
       /* Couldn't create the pipe between parent and child. */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't create sync pipe: %s",
-                       strerror(errno));
+      report_failure("Couldn't create sync pipe: %s", strerror(errno));
+      g_free( (gpointer) argv[0]);
       g_free(argv);
       return FALSE;
     }
 
     if ((capture_opts->fork_child = fork()) == 0) {
       /*
-       * Child process - run Wireshark with the right arguments to make
-       * it just pop up the live capture dialog box and capture with
-       * the specified capture parameters, writing to the specified file.
+       * Child process - run dumpcap with the right arguments to make
+       * it just capture with the specified capture parameters
        */
-      eth_close(1);
-      dup(sync_pipe[PIPE_WRITE]);
-      eth_close(sync_pipe[PIPE_READ]);
-      execv(exename, argv);
+      dup2(sync_pipe[PIPE_WRITE], 2);
+      ws_close(sync_pipe[PIPE_READ]);
+      execv(argv[0], (gpointer)argv);
       g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
-               exename, strerror(errno));
-      sync_pipe_errmsg_to_parent(errmsg, "");
+               argv[0], strerror(errno));
+      sync_pipe_errmsg_to_parent(2, errmsg, "");
 
       /* Exit with "_exit()", so that we don't close the connection
          to the X server (and cause stuff buffered up by our parent but
         not yet sent to be sent, as that stuff should only be sent by
-        our parent). */
-      _exit(2);
+        our parent).  We've sent an error message to the parent, so
+        we exit with an exit status of 1 (any exit status other than
+        0 or 1 will cause an additional message to report that exit
+        status, over and above the error message we sent to the parent). */
+      _exit(1);
     }
 
     sync_pipe_read_fd = sync_pipe[PIPE_READ];
 #endif
 
-    g_free(exename);
+    g_free( (gpointer) argv[0]);  /* exename */
 
     /* Parent process - read messages from the child process over the
        sync pipe. */
@@ -480,16 +547,15 @@ sync_pipe_start(capture_options *capture_opts) {
 #ifdef _WIN32
     CloseHandle(sync_pipe_write);
 #else
-    eth_close(sync_pipe[PIPE_WRITE]);
+    ws_close(sync_pipe[PIPE_WRITE]);
 #endif
 
     if (capture_opts->fork_child == -1) {
       /* We couldn't even create the child process. */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-                       "Couldn't create child process: %s", strerror(errno));
-      eth_close(sync_pipe_read_fd);
+      report_failure("Couldn't create child process: %s", strerror(errno));
+      ws_close(sync_pipe_read_fd);
 #ifdef _WIN32
-      eth_close(capture_opts->signal_pipe_write_fd);
+      ws_close(capture_opts->signal_pipe_write_fd);
 #endif
       return FALSE;
     }
@@ -509,28 +575,456 @@ sync_pipe_start(capture_options *capture_opts) {
     return TRUE;
 }
 
+/*
+ * Open a pipe to dumpcap with the supplied arguments.  On success, *msg
+ * is unchanged and 0 is returned; read_fd and fork_child point to the
+ * pipe's file descriptor and child PID/handle, respectively.  On failure,
+ * *msg points to an error message for the failure, and -1 is returned.
+ * In the latter 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_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 */
+    GString *args = g_string_sized_new(200);
+    gchar *quoted_arg;
+    SECURITY_ATTRIBUTES sa;
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    int i;
+#else
+    char errmsg[1024+1];
+    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
+
+    *fork_child = -1;
+    *read_fd = -1;
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_run_command");
+
+    if (!msg) {
+        /* We can't return anything */
+#ifdef _WIN32
+        g_string_free(args, TRUE);
+#endif
+        return -1;
+    }
+
+#ifdef _WIN32
+    /* init SECURITY_ATTRIBUTES */
+    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sa.bInheritHandle = TRUE;
+    sa.lpSecurityDescriptor = NULL;
+
+    /* Create a pipe for the child process */
+    /* (increase this value if you have trouble while fast capture file switches) */
+    if (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) {
+        /* Couldn't create the pipe between parent and child. */
+        *msg = g_strdup_printf("Couldn't create sync pipe: %s", strerror(errno));
+        g_free( (gpointer) argv[0]);
+        g_free( (gpointer) argv);
+        return -1;
+    }
+
+    /* init STARTUPINFO */
+    memset(&si, 0, sizeof(si));
+    si.cb           = sizeof(si);
+#ifdef DEBUG_CHILD
+    si.dwFlags = STARTF_USESHOWWINDOW;
+    si.wShowWindow  = SW_SHOW;
+#else
+    si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+    si.wShowWindow  = SW_HIDE;  /* this hides the console window */
+    si.hStdInput = NULL;
+    si.hStdOutput = sync_pipe_write;
+    si.hStdError = sync_pipe_write;
+    /*si.hStdError = (HANDLE) _get_osfhandle(2);*/
+#endif
+
+    /* convert args array into a single string */
+    /* XXX - could change sync_pipe_add_arg() instead */
+    /* there is a drawback here: the length is internally limited to 1024 bytes */
+    for(i=0; argv[i] != 0; i++) {
+        if(i != 0) g_string_append_c(args, ' ');    /* don't prepend a space before the path!!! */
+        quoted_arg = protect_arg(argv[i]);
+        g_string_append(args, quoted_arg);
+        g_free(quoted_arg);
+    }
 
+    /* call dumpcap */
+    if(!CreateProcess(NULL, utf_8to16(args->str), NULL, NULL, TRUE,
+                      CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
+        *msg = g_strdup_printf("Couldn't run %s in child process: error %u",
+                        args->str, GetLastError());
+        CloseHandle(sync_pipe_read);
+        CloseHandle(sync_pipe_write);
+        g_free( (gpointer) argv[0]);
+        g_free( (gpointer) argv);
+        return -1;
+    }
+    *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) */
+    *read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
+
+#else /* _WIN32 */
+    if (pipe(sync_pipe) < 0) {
+        /* Couldn't create the pipe between parent and child. */
+        *msg = g_strdup_printf("Couldn't create sync pipe: %s", strerror(errno));
+        g_free( (gpointer) argv[0]);
+        g_free(argv);
+        return -1;
+    }
+
+    if ((*fork_child = fork()) == 0) {
+        /*
+         * Child process - run dumpcap with the right arguments to make
+         * it just capture with the specified capture parameters
+         */
+        dup2(sync_pipe[PIPE_WRITE], 1);
+        ws_close(sync_pipe[PIPE_READ]);
+        execv(argv[0], (gpointer)argv);
+        g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
+                  argv[0], strerror(errno));
+        sync_pipe_errmsg_to_parent(1, errmsg, "");
+
+        /* Exit with "_exit()", so that we don't close the connection
+           to the X server (and cause stuff buffered up by our parent but
+           not yet sent to be sent, as that stuff should only be sent by
+           our parent).  We've sent an error message to the parent, so
+           we exit with an exit status of 1 (any exit status other than
+           0 or 1 will cause an additional message to report that exit
+           status, over and above the error message we sent to the parent). */
+        _exit(1);
+    }
+
+    *read_fd = sync_pipe[PIPE_READ];
+#endif
+
+    g_free( (gpointer) argv[0]);  /* exename */
+
+    /* Parent process - read messages from the child process over the
+       sync pipe. */
+    g_free( (gpointer) argv);  /* free up arg array */
+
+    /* Close the write side of the pipe, so that only the child has it
+       open, and thus it completely closes, and thus returns to us
+       an EOF indication, if the child closes it (either deliberately
+       or by exiting abnormally). */
+#ifdef _WIN32
+    CloseHandle(sync_pipe_write);
+#else
+    ws_close(sync_pipe[PIPE_WRITE]);
+#endif
+
+    if (*fork_child == -1) {
+        /* We couldn't even create the child process. */
+        *msg = g_strdup_printf("Couldn't create child process: %s", strerror(errno));
+        ws_close(*read_fd);
+        return -1;
+    }
+
+    /* we might wait for a moment till child is ready, so update screen now */
+    main_window_update();
+    return 0;
+}
+
+/*
+ * Wait for dumpcap to finish.  On success, *msg is unchanged, and 0 is
+ * returned.  On failure, *msg points to an error message for the
+ * failure, and -1 is returned.  In the latter case, *msg must be
+ * freed with g_free().
+ */
+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;
+
+    ws_close(*read_fd);
+
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_close_command: wait till child closed");
+
+#ifdef _WIN32
+    /* 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 -1;
+    }
+#else
+    if (wait(&fork_child_status) != -1) {
+        if (WIFEXITED(fork_child_status)) {
+            /* The child exited. */
+            fork_child_status = WEXITSTATUS(fork_child_status);
+        } else {
+            if (WIFSTOPPED(fork_child_status)) {
+                /* It stopped, rather than exiting.  "Should not happen." */
+                *msg = g_strdup_printf("Child capture process stopped: %s",
+                    sync_pipe_signame(WSTOPSIG(fork_child_status)));
+            } else if (WIFSIGNALED(fork_child_status)) {
+                /* It died with a signal. */
+                *msg = g_strdup_printf("Child capture process died: %s%s",
+                   sync_pipe_signame(WTERMSIG(fork_child_status)),
+                   WCOREDUMP(fork_child_status) ? " - core dumped" : "");
+            } else {
+                /* What?  It had to either have exited, or stopped, or died with
+                   a signal; what happened here? */
+                *msg = g_strdup_printf("Child capture process died: wait status %#o",
+                    fork_child_status);
+            }
+            return -1;
+        }
+    } else {
+      *msg = g_strdup_printf("Child capture process stopped unexpectedly "
+        "(errno:%u)", errno);
+      return -1;
+    }
+#endif
+    return 0;
+}
+
+/*
+ * Run dumpcap with the supplied arguments.  On success, *msg points to
+ * a buffer containing the dumpcap output, and 0 is returned.  On failure,
+ * *msg points to an error message, and -1 is returned.  In either case,
+ * *msg must be freed with g_free().
+ *
+ * XXX - this doesn't check the exit status of dumpcap if it can be
+ * started and its return status could be fetched.
+ */
+/* 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. */
+    msg_buf = g_string_new("");
+    while ((count = ws_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 0;
+}
+
+/*
+ * Get an interface list using dumpcap.  On success, *msg points to
+ * a buffer containing the dumpcap output, and 0 is returned.  On failure,
+ * *msg points to an error message, and -1 is returned.  In either case,
+ * msg must be freed with g_free().
+ */
+int
+sync_interface_list_open(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_interface_list_open");
+
+    argv = init_pipe_args(&argc);
+
+    if (!argv) {
+        *msg = g_strdup_printf("We don't know where to find dumpcap.");
+        return -1;
+    }
+
+    /* Ask for the interface list */
+    argv = sync_pipe_add_arg(argv, &argc, "-D");
+    argv = sync_pipe_add_arg(argv, &argc, "-M");
+
+#if 0
+    /* dumpcap should be running in capture child mode (hidden feature)                   */
+    /* XXX: Actually: don't run dumpcap in capture_child_mode.                            */
+    /*     Instead run dumpcap in 'normal' mode so that dumpcap err msgs are sent to      */
+    /*     stderr in normal format and are then sent to whereever our stderr goes.        */
+    /*     Note: Using 'dumpcap -D -M -Z' (capture_child mode) changes only the format of */
+    /*           dumpcap err msgs. That is: dumpcap in capture_child mode outputs err     */
+    /*           msgs to stderr in a special type/len/string format which would then      */
+    /*           currently be sent as is to stderr resulting in garbled output.           */
+    /*     ToDo: Revise this code to be similar to sync_pipe_start so that 'dumpcap -Z'   */
+    /*     special format error messages to stderr are captured and returned to caller    */
+    /*     (eg: so can be processed and displayed in a pop-up box).                       */
+#ifndef DEBUG_CHILD
+    argv = sync_pipe_add_arg(argv, &argc, "-Z");
+    argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
+#endif
+#endif
+
+    return sync_pipe_run_command(argv, msg);
+}
+
+/*
+ * Get interface capabilities using dumpcap.  On success, *msg points to
+ * a buffer containing the dumpcap output, and 0 is returned.  On failure,
+ * *msg points to an error message, and -1 is returned.  In either case,
+ * *msg must be freed with g_free().
+ */
+int
+sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode,
+                          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 -1;
+    }
+
+    /* Ask for the interface capabilities */
+    argv = sync_pipe_add_arg(argv, &argc, "-i");
+    argv = sync_pipe_add_arg(argv, &argc, ifname);
+    argv = sync_pipe_add_arg(argv, &argc, "-L");
+    if (monitor_mode)
+        argv = sync_pipe_add_arg(argv, &argc, "-I");
+    argv = sync_pipe_add_arg(argv, &argc, "-M");
+
+#if 0
+    /* dumpcap should be running in capture child mode (hidden feature)                   */
+    /* XXX: Actually: don't run dumpcap in capture_child_mode.                            */
+    /*     Instead run dumpcap in 'normal' mode so that dumpcap err msgs are sent to      */
+    /*     stderr in normal format and are then sent to whereever our stderr goes.        */
+    /*     Note: Using 'dumpcap -L -M -Z' (capture_child mode) changes only the format of */
+    /*           dumpcap err msgs. That is: dumpcap in capture_child mode outputs err     */
+    /*           msgs to stderr in a special type/len/string format which would then      */
+    /*           currently be sent as is to stderr resulting in garbled output.           */
+    /*     ToDo: Revise this code to be similar to sync_pipe_start so that 'dumpcap -Z'   */
+    /*     special format error messages to stderr are captured and returned to caller    */
+    /*     (eg: so can be processed and displayed in a pop-up box).                       */
+#ifndef DEBUG_CHILD
+    argv = sync_pipe_add_arg(argv, &argc, "-Z");
+    argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
+#endif
+#endif
+    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 -1 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_interface_stats_open");
+
+    argv = init_pipe_args(&argc);
+
+    if (!argv) {
+        *msg = g_strdup_printf("We don't know where to find dumpcap.");
+        return -1;
+    }
+
+    /* Ask for the interface statistics */
+    argv = sync_pipe_add_arg(argv, &argc, "-S");
+    argv = sync_pipe_add_arg(argv, &argc, "-M");
+
+#if 0
+    /* dumpcap should be running in capture child mode (hidden feature)                   */
+    /* XXX: Actually: don't run dumpcap in capture_child_mode.                            */
+    /*     Instead run dumpcap in 'normal' mode so that dumpcap err msgs are sent to      */
+    /*     stderr in normal format and are then sent to whereever our stderr goes.        */
+    /*     Note: Using 'dumpcap -S -M -Z' (capture_child mode) changes only the format of */
+    /*           dumpcap err msgs. That is: dumpcap in capture_child mode outputs err     */
+    /*           msgs to stderr in a special type/len/string format which would then      */
+    /*           currently be sent as is to stderr resulting in garbled output.           */
+    /*     ToDo: Revise this code to be similar to sync_pipe_start so that 'dumpcap -Z'   */
+    /*     special format error messages to stderr are captured and returned to caller    */
+    /*     (eg: so can be processed and displayed in a pop-up box).                       */
+#ifndef DEBUG_CHILD
+    argv = sync_pipe_add_arg(argv, &argc, "-Z");
+    argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
+#endif
+#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
+#ifndef _WIN32
+_U_
+#endif
+, 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) */
 static int
-pipe_read_bytes(int pipe, char *bytes, int required) {
+pipe_read_bytes(int pipe_fd, char *bytes, int required) {
     int newly;
     int offset = 0;
 
-
     while(required) {
-        newly = read(pipe, &bytes[offset], required);
+        newly = read(pipe_fd, &bytes[offset], required);
         if (newly == 0) {
             /* EOF */
             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-                  "read from pipe %d: EOF (capture closed?)", pipe);
+                  "read from pipe %d: EOF (capture closed?)", pipe_fd);
             return offset;
         }
         if (newly < 0) {
             /* error */
             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-                  "read from pipe %d: error(%u): %s", pipe, errno, strerror(errno));
+                  "read from pipe %d: error(%u): %s", pipe_fd, errno, strerror(errno));
             return newly;
         }
 
@@ -541,9 +1035,70 @@ pipe_read_bytes(int pipe, char *bytes, int required) {
     return offset;
 }
 
+static gboolean pipe_data_available(int pipe_fd) {
+#ifdef _WIN32 /* PeekNamedPipe */
+    HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
+    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_fd, &rfds);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 0;
+
+    if (select(pipe_fd+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_fd, char *bytes, int max) {
+    int newly;
+    int offset = -1;
+
+    while(offset < max - 1) {
+        offset++;
+        if (! pipe_data_available(pipe_fd))
+            break;
+        newly = read(pipe_fd, &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_fd, 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) {    
+pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) {
 
     g_assert(header_len == 4);
 
@@ -556,17 +1111,17 @@ pipe_convert_header(const guchar *header, int header_len, char *indicator, int *
    (1-byte message indicator, 3-byte message length (excluding length
    and indicator field), and the rest is the message) */
 static int
-pipe_read_block(int pipe, char *indicator, int len, char *msg) {
+pipe_read_block(int pipe_fd, char *indicator, int len, char *msg) {
     int required;
     int newly;
     guchar header[4];
 
 
     /* read header (indicator and 3-byte length) */
-    newly = pipe_read_bytes(pipe, header, 4);
+    newly = pipe_read_bytes(pipe_fd, header, 4);
     if(newly != 4) {
         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-              "read %d failed to read header: %u", pipe, newly);
+              "read %d failed to read header: %u", pipe_fd, newly);
         return -1;
     }
 
@@ -576,7 +1131,7 @@ pipe_read_block(int pipe, char *indicator, int len, char *msg) {
     /* only indicator with no value? */
     if(required == 0) {
         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-              "read %d indicator: %c empty value", pipe, *indicator);
+              "read %d indicator: %c empty value", pipe_fd, *indicator);
         return 4;
     }
 
@@ -584,25 +1139,26 @@ pipe_read_block(int pipe, char *indicator, int len, char *msg) {
     if(required > len) {
         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
               "read %d length error, required %d > len %d, indicator: %u",
-              pipe, required, len, *indicator);
+              pipe_fd, required, len, *indicator);
 
         /* we have a problem here, try to read some more bytes from the pipe to debug where the problem really is */
         memcpy(msg, header, sizeof(header));
-        newly = read(pipe, &msg[sizeof(header)], len-sizeof(header));
+        newly = read(pipe_fd, &msg[sizeof(header)], len-sizeof(header));
         g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
         return -1;
     }
     len = required;
 
     /* read the actual block data */
-    newly = pipe_read_bytes(pipe, msg, required);
+    newly = pipe_read_bytes(pipe_fd, msg, required);
     if(newly != required) {
         g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
         return -1;
     }
 
+    /* XXX If message is "2part", the msg probably won't be sent to debug log correctly */
     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-          "read %d ok indicator: %c len: %u msg: %s", pipe, *indicator,
+          "read %d ok indicator: %c len: %u msg: %s", pipe_fd, *indicator,
           len, msg);
     return newly + 4;
 }
@@ -637,15 +1193,15 @@ sync_pipe_input_cb(gint source, gpointer user_data)
        capturing any more packets.  Pick up its exit status, and
        complain if it did anything other than exit with status 0.
 
-       We don't have to worry about killing the child, if the sync pipe 
-       returned an error. Usually this error is caused as the child killed itself 
-       while going down. Even in the rare cases that this isn't the case, 
-       the child will get an error when writing to the broken pipe the next time, 
+       We don't have to worry about killing the child, if the sync pipe
+       returned an error. Usually this error is caused as the child killed itself
+       while going down. Even in the rare cases that this isn't the case,
+       the child will get an error when writing to the broken pipe the next time,
        cleaning itself up then. */
     sync_pipe_wait_for_child(capture_opts);
 
 #ifdef _WIN32
-    eth_close(capture_opts->signal_pipe_write_fd);
+    ws_close(capture_opts->signal_pipe_write_fd);
 #endif
     capture_input_closed(capture_opts);
     return FALSE;
@@ -659,7 +1215,7 @@ sync_pipe_input_cb(gint source, gpointer user_data)
 
       /* We weren't able to open the new capture file; user has been
          alerted. Close the sync pipe. */
-      eth_close(source);
+      ws_close(source);
 
       /* the child has send us a filename which we couldn't open.
          this probably means, the child is creating files faster than we can handle it.
@@ -691,7 +1247,7 @@ sync_pipe_input_cb(gint source, gpointer user_data)
     /* the capture child will close the sync_pipe, nothing to do for now */
     break;
   case SP_DROPS:
-    capture_input_drops(capture_opts, atoi(buffer));
+    capture_input_drops(capture_opts, (guint32)strtoul(buffer, NULL, 10));
     break;
   default:
     g_assert_not_reached();
@@ -714,8 +1270,8 @@ sync_pipe_wait_for_child(capture_options *capture_opts)
 
 #ifdef _WIN32
   if (_cwait(&wstatus, capture_opts->fork_child, _WAIT_CHILD) == -1) {
-    simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-               "Child capture process stopped unexpectedly (errno:%u)", errno);
+    report_failure("Child capture process stopped unexpectedly (errno:%u)",
+                   errno);
   }
 #else
   if (wait(&wstatus) != -1) {
@@ -727,26 +1283,22 @@ sync_pipe_wait_for_child(capture_options *capture_opts)
       /* If there are situations where the child won't send us such an error message, */
       /* this should be fixed in the child and not here! */
       if (WEXITSTATUS(wstatus) != 0 && WEXITSTATUS(wstatus) != 1) {
-        simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-                     "Child capture process exited: exit status %d",
-                     WEXITSTATUS(wstatus));
+        report_failure("Child capture process exited: exit status %d",
+                      WEXITSTATUS(wstatus));
       }
     } else if (WIFSTOPPED(wstatus)) {
       /* It stopped, rather than exiting.  "Should not happen." */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-                   "Child capture process stopped: %s",
-                   sync_pipe_signame(WSTOPSIG(wstatus)));
+      report_failure("Child capture process stopped: %s",
+                    sync_pipe_signame(WSTOPSIG(wstatus)));
     } else if (WIFSIGNALED(wstatus)) {
       /* It died with a signal. */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-                   "Child capture process died: %s%s",
-                   sync_pipe_signame(WTERMSIG(wstatus)),
-                   WCOREDUMP(wstatus) ? " - core dumped" : "");
+      report_failure("Child capture process died: %s%s",
+                    sync_pipe_signame(WTERMSIG(wstatus)),
+                    WCOREDUMP(wstatus) ? " - core dumped" : "");
     } else {
       /* What?  It had to either have exited, or stopped, or died with
          a signal; what happened here? */
-      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-                   "Child capture process died: wait status %#o", wstatus);
+      report_failure("Child capture process died: wait status %#o", wstatus);
     }
   }
 #endif
@@ -882,14 +1434,44 @@ signal_pipe_capquit_to_child(capture_options *capture_opts)
 void
 sync_pipe_stop(capture_options *capture_opts)
 {
+#ifdef _WIN32
+  int count;
+  DWORD childstatus;
+  gboolean terminate = TRUE;
+#endif
+
   if (capture_opts->fork_child != -1) {
 #ifndef _WIN32
-    /* send the SIGUSR1 signal to close the capture child gracefully. */
-    kill(capture_opts->fork_child, SIGUSR1);
+    /* send the SIGINT signal to close the capture child gracefully. */
+    int sts = kill(capture_opts->fork_child, SIGINT);
+    if (sts != 0) {
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING,
+              "Sending SIGINT to child failed: %s\n", strerror(errno));
+    }
 #else
-    /* Win32 doesn't have the kill() system call, use the special signal pipe
-       instead to close the capture child gracefully. */
+#define STOP_SLEEP_TIME 500 /* ms */
+#define STOP_CHECK_TIME 50
+    /* First, use the special signal pipe to try to close the capture child
+     * gracefully.
+     */
     signal_pipe_capquit_to_child(capture_opts);
+
+    /* Next, wait for the process to exit on its own */
+    for (count = 0; count < STOP_SLEEP_TIME / STOP_CHECK_TIME; count++) {
+      if (GetExitCodeProcess((HANDLE) capture_opts->fork_child, &childstatus) &&
+              childstatus != STILL_ACTIVE) {
+        terminate = FALSE;
+        break;
+      }
+      Sleep(STOP_CHECK_TIME);
+    }
+
+    /* Force the issue. */
+    if (terminate) {
+      g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING,
+            "sync_pipe_stop: forcing child to exit");
+      sync_pipe_kill(capture_opts->fork_child);
+    }
 #endif
   }
 }
@@ -897,11 +1479,15 @@ sync_pipe_stop(capture_options *capture_opts)
 
 /* Wireshark has to exit, force the capture child to close */
 void
-sync_pipe_kill(capture_options *capture_opts)
+sync_pipe_kill(int fork_child)
 {
-  if (capture_opts->fork_child != -1) {
+    if (fork_child != -1) {
 #ifndef _WIN32
-      kill(capture_opts->fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
+        int sts = kill(fork_child, SIGTERM);   /* SIGTERM so it can clean up if necessary */
+        if (sts != 0) {
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING,
+                  "Sending SIGTERM to child failed: %s\n", strerror(errno));
+        }
 #else
       /* Remark: This is not the preferred method of closing a process!
        * the clean way would be getting the process id of the child process,
@@ -921,9 +1507,9 @@ sync_pipe_kill(capture_options *capture_opts)
        * us, as we might not be running in a console.
        * And this also will require to have the process id.
        */
-      TerminateProcess((HANDLE) (capture_opts->fork_child), 0);
+        TerminateProcess((HANDLE) (fork_child), 0);
 #endif
-  }
+    }
 }
 
 #endif /* HAVE_LIBPCAP */