Update some links.
[obnox/wireshark/wip.git] / capture_sync.c
index 28f2b386c8c82c185977157f2a792c1420864abc..68b79f79280acbc1fad8f0245cf2c084b46792c1 100644 (file)
@@ -113,7 +113,10 @@ static const char *sync_pipe_signame(int);
 
 
 static gboolean sync_pipe_input_cb(gint source, gpointer user_data);
-static void sync_pipe_wait_for_child(capture_options *capture_opts);
+static int sync_pipe_wait_for_child(int fork_child, gchar **msgp);
+static void pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len);
+static int pipe_read_block(int pipe_fd, char *indicator, int len, char *msg,
+                           char **err_msg);
 
 
 
@@ -177,7 +180,7 @@ protect_arg (const gchar *argv)
                 pp++;
             if (*pp == '"')
                 len++;
-       }
+        }
         len++;
         p++;
     }
@@ -198,9 +201,9 @@ protect_arg (const gchar *argv)
                 pp++;
             if (*pp == '"')
                 *q++ = '\\';
-       }
-       *q++ = *p;
-       p++;
+        }
+        *q++ = *p;
+        p++;
     }
 
     if (need_dblquotes)
@@ -209,6 +212,80 @@ protect_arg (const gchar *argv)
 
     return new_arg;
 }
+
+/*
+ * Generate a string for a Win32 error.
+ */
+#define ERRBUF_SIZE    1024
+static const char *
+win32strerror(DWORD error)
+{
+    static char errbuf[ERRBUF_SIZE+1];
+    size_t errlen;
+    char *p;
+
+    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errbuf,
+                   ERRBUF_SIZE, NULL);
+
+    /*
+     * "FormatMessage()" "helpfully" sticks CR/LF at the end of the
+     * message.  Get rid of it.
+     */
+    errlen = strlen(errbuf);
+    if (errlen >= 2) {
+        errbuf[errlen - 1] = '\0';
+        errbuf[errlen - 2] = '\0';
+    }
+    p = strchr(errbuf, '\0');
+    g_snprintf(p, (gulong)(sizeof errbuf - (p-errbuf)), " (%lu)", error);
+    return errbuf;
+}
+
+/*
+ * Generate a string for a Win32 exception code.
+ */
+static const char *
+win32strexception(DWORD exception)
+{
+    static char errbuf[ERRBUF_SIZE+1];
+    static const struct exception_msg {
+      int code;
+      char *msg;
+    } exceptions[] = {
+      { EXCEPTION_ACCESS_VIOLATION, "Access violation" },
+      { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded" },
+      { EXCEPTION_BREAKPOINT, "Breakpoint" },
+      { EXCEPTION_DATATYPE_MISALIGNMENT, "Data type misalignment" },
+      { EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal floating-point operand" },
+      { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating-point divide by zero" },
+      { EXCEPTION_FLT_INEXACT_RESULT, "Floating-point inexact result" },
+      { EXCEPTION_FLT_INVALID_OPERATION, "Invalid floating-point operation" },
+      { EXCEPTION_FLT_OVERFLOW, "Floating-point overflow" },
+      { EXCEPTION_FLT_STACK_CHECK, "Floating-point stack check" },
+      { EXCEPTION_FLT_UNDERFLOW, "Floating-point underflow" },
+      { EXCEPTION_GUARD_PAGE, "Guard page violation" },
+      { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" },
+      { EXCEPTION_IN_PAGE_ERROR, "Page-in error" },
+      { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer divide by zero" },
+      { EXCEPTION_INT_OVERFLOW, "Integer overflow" },
+      { EXCEPTION_INVALID_DISPOSITION, "Invalid disposition" },
+      { EXCEPTION_INVALID_HANDLE, "Invalid handle" },
+      { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception" },
+      { EXCEPTION_PRIV_INSTRUCTION, "Privileged instruction" },
+      { EXCEPTION_SINGLE_STEP, "Single-step complete" },
+      { EXCEPTION_STACK_OVERFLOW, "Stack overflow" },
+      { 0, NULL }
+    };
+#define N_EXCEPTIONS   (sizeof exceptions / sizeof exceptions[0])
+    int i;
+
+    for (i = 0; i < N_EXCEPTIONS; i++) {
+       if (exceptions[i].code == exception)
+         return exceptions[i].msg;
+    }
+    g_snprintf(errbuf, (gulong)sizeof errbuf, "Exception 0x%08x", exception);
+    return errbuf;
+}
 #endif
 
 /* Initialize an argument list and add dumpcap to it. */
@@ -257,8 +334,10 @@ sync_pipe_start(capture_options *capture_opts) {
 #ifdef HAVE_PCAP_SETSAMPLING
     char ssampling[ARGV_NUMBER_LEN];
 #endif
-#ifdef _WIN32
+#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;                     /* named pipe used to send messages from parent to child (currently only stop) */
@@ -355,6 +434,10 @@ 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
@@ -396,7 +479,7 @@ sync_pipe_start(capture_options *capture_opts) {
 #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)
@@ -428,7 +511,8 @@ sync_pipe_start(capture_options *capture_opts) {
     /* (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. */
-      report_failure("Couldn't create sync pipe: %s", strerror(errno));
+      report_failure("Couldn't create sync pipe: %s",
+                     win32strerror(GetLastError()));
       g_free( (gpointer) argv[0]);
       g_free( (gpointer) argv);
       return FALSE;
@@ -442,7 +526,8 @@ sync_pipe_start(capture_options *capture_opts) {
 
     if (signal_pipe == INVALID_HANDLE_VALUE) {
       /* Couldn't create the signal pipe between parent and child. */
-      report_failure("Couldn't create signal pipe: %s", strerror(errno));
+      report_failure("Couldn't create signal pipe: %s",
+                     win32strerror(GetLastError()));
       g_free( (gpointer) argv[0]);
       g_free( (gpointer) argv);
       return FALSE;
@@ -476,8 +561,8 @@ 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)) {
-      report_failure("Couldn't run %s in child process: error %u",
-                     args->str, GetLastError());
+      report_failure("Couldn't run %s in child process: %s",
+                     args->str, win32strerror(GetLastError()));
       CloseHandle(sync_pipe_read);
       CloseHandle(sync_pipe_write);
       g_free( (gpointer) argv[0]);
@@ -512,16 +597,16 @@ sync_pipe_start(capture_options *capture_opts) {
       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));
+                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).  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). */
+         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);
     }
 
@@ -532,7 +617,7 @@ sync_pipe_start(capture_options *capture_opts) {
 
     /* Parent process - read messages from the child process over the
        sync pipe. */
-    g_free( (gpointer) argv);  /* free up arg array */
+    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
@@ -554,6 +639,8 @@ sync_pipe_start(capture_options *capture_opts) {
       return FALSE;
     }
 
+    capture_opts->fork_child_status = 0;
+
     /* we might wait for a moment till child is ready, so update screen now */
     main_window_update();
 
@@ -570,20 +657,28 @@ sync_pipe_start(capture_options *capture_opts) {
 }
 
 /*
- * 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().
+ * Open two pipes to dumpcap with the supplied arguments, one for its
+ * standard output and one for its standard error.
+ *
+ * On success, *msg is unchanged and 0 is returned; data_read_fd,
+ * messsage_read_fd, and fork_child point to the standard output pipe's
+ * file descriptor, the standard error pipe's file descriptor, and
+ * the child's PID/handle, respectively.
+ *
+ * On failure, *msg points to an error message for the failure, and -1 is
+ * returned, in which case *msg must be freed with g_free().
  */
 /* XXX - This duplicates a lot of code in sync_pipe_start() */
+/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */
 #define PIPE_BUF_SIZE 5120
 static int
-sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar **msg) {
+sync_pipe_open_command(const char** argv, int *data_read_fd,
+                       int *message_read_fd, int *fork_child, gchar **msg)
+{
+    enum PIPES { PIPE_READ, PIPE_WRITE };   /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */
 #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 */
+    HANDLE sync_pipe[2];                    /* pipe used to send messages from child to parent */
+    HANDLE data_pipe[2];                    /* pipe used to send data from child to parent */
     GString *args = g_string_sized_new(200);
     gchar *quoted_arg;
     SECURITY_ATTRIBUTES sa;
@@ -593,12 +688,13 @@ sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar *
 #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 */
+    int data_pipe[2];                       /* pipe used to send data from child to parent */
 #endif
 
     *fork_child = -1;
-    *read_fd = -1;
-    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_run_command");
+    *data_read_fd = -1;
+    *message_read_fd = -1;
+    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_open_command");
 
     if (!msg) {
         /* We can't return anything */
@@ -614,14 +710,28 @@ sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar *
     sa.bInheritHandle = TRUE;
     sa.lpSecurityDescriptor = NULL;
 
-    /* Create a pipe for the child process */
-    /* (inrease 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));
+    /* Create a pipe for the child process to send us messages */
+    /* (increase this value if you have trouble while fast capture file switches) */
+    if (! CreatePipe(&sync_pipe[PIPE_READ], &sync_pipe[PIPE_WRITE], &sa, 5120)) {
+        /* Couldn't create the message pipe between parent and child. */
+        *msg = g_strdup_printf("Couldn't create sync pipe: %s",
+                               win32strerror(GetLastError()));
         g_free( (gpointer) argv[0]);
         g_free( (gpointer) argv);
-        return CANT_RUN_DUMPCAP;
+        return -1;
+    }
+
+    /* Create a pipe for the child process to send us data */
+    /* (increase this value if you have trouble while fast capture file switches) */
+    if (! CreatePipe(&data_pipe[PIPE_READ], &data_pipe[PIPE_WRITE], &sa, 5120)) {
+        /* Couldn't create the message pipe between parent and child. */
+        *msg = g_strdup_printf("Couldn't create data pipe: %s",
+                               win32strerror(GetLastError()));
+        CloseHandle(sync_pipe[PIPE_READ]);
+        CloseHandle(sync_pipe[PIPE_WRITE]);
+        g_free( (gpointer) argv[0]);
+        g_free( (gpointer) argv);
+        return -1;
     }
 
     /* init STARTUPINFO */
@@ -634,9 +744,8 @@ sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar *
     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);*/
+    si.hStdOutput = data_pipe[PIPE_WRITE];
+    si.hStdError = sync_pipe[PIPE_WRITE];
 #endif
 
     /* convert args array into a single string */
@@ -652,28 +761,42 @@ sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar *
     /* 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);
+        *msg = g_strdup_printf("Couldn't run %s in child process: %s",
+                               args->str, win32strerror(GetLastError()));
+        CloseHandle(data_pipe[PIPE_READ]);
+        CloseHandle(data_pipe[PIPE_WRITE]);
+        CloseHandle(sync_pipe[PIPE_READ]);
+        CloseHandle(sync_pipe[PIPE_WRITE]);
         g_free( (gpointer) argv[0]);
         g_free( (gpointer) argv);
-        return CANT_RUN_DUMPCAP;
+        return -1;
     }
     *fork_child = (int) pi.hProcess;
     g_string_free(args, TRUE);
 
-    /* associate the operating system filehandle to a C run-time file handle */
+    /* associate the operating system filehandles to C run-time file handles */
     /* (good file handle infos at: http://www.flounder.com/handles.htm) */
-    *read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY);
-
+    *data_read_fd = _open_osfhandle( (long) data_pipe[PIPE_READ], _O_BINARY);
+    *message_read_fd = _open_osfhandle( (long) sync_pipe[PIPE_READ], _O_BINARY);
 #else /* _WIN32 */
+    /* Create a pipe for the child process to send us messages */
     if (pipe(sync_pipe) < 0) {
-        /* Couldn't create the pipe between parent and child. */
+        /* Couldn't create the message 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 CANT_RUN_DUMPCAP;
+        return -1;
+    }
+
+    /* Create a pipe for the child process to send us data */
+    if (pipe(data_pipe) < 0) {
+        /* Couldn't create the data pipe between parent and child. */
+        *msg = g_strdup_printf("Couldn't create data pipe: %s", strerror(errno));
+        ws_close(sync_pipe[PIPE_READ]);
+        ws_close(sync_pipe[PIPE_WRITE]);
+        g_free( (gpointer) argv[0]);
+        g_free(argv);
+        return -1;
     }
 
     if ((*fork_child = fork()) == 0) {
@@ -681,12 +804,16 @@ sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar *
          * Child process - run dumpcap with the right arguments to make
          * it just capture with the specified capture parameters
          */
-        dup2(sync_pipe[PIPE_WRITE], 1);
+        dup2(data_pipe[PIPE_WRITE], 1);
+        ws_close(data_pipe[PIPE_READ]);
+        ws_close(data_pipe[PIPE_WRITE]);
+        dup2(sync_pipe[PIPE_WRITE], 2);
         ws_close(sync_pipe[PIPE_READ]);
+        ws_close(sync_pipe[PIPE_WRITE]);
         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, "");
+                   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
@@ -698,30 +825,34 @@ sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar *
         _exit(1);
     }
 
-    *read_fd = sync_pipe[PIPE_READ];
+    *data_read_fd = data_pipe[PIPE_READ];
+    *message_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 */
+    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
+    /* Close the write sides of the pipes, so that only the child has them
+       open, and thus they completely close, and thus return to us
+       an EOF indication, if the child closes them (either deliberately
        or by exiting abnormally). */
 #ifdef _WIN32
-    CloseHandle(sync_pipe_write);
+    CloseHandle(data_pipe[PIPE_WRITE]);
+    CloseHandle(sync_pipe[PIPE_WRITE]);
 #else
+    ws_close(data_pipe[PIPE_WRITE]);
     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 CANT_RUN_DUMPCAP;
+        ws_close(*data_read_fd);
+        ws_close(*message_read_fd);
+        return -1;
     }
 
     /* we might wait for a moment till child is ready, so update screen now */
@@ -729,285 +860,488 @@ sync_pipe_open_command(const char** argv, int *read_fd, int *fork_child, gchar *
     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");
+sync_pipe_close_command(int *data_read_fd, int *message_read_fd,
+                        int *fork_child, gchar **msg)
+{
+    ws_close(*data_read_fd);
+    if (message_read_fd != NULL)
+        ws_close(*message_read_fd);
 
 #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 CANT_RUN_DUMPCAP;
-    }
-#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 CANT_RUN_DUMPCAP;
-        }
-    } else {
-      *msg = g_strdup_printf("Child capture process stopped unexpectedly "
-        "(errno:%u)", errno);
-      return CANT_RUN_DUMPCAP;
-    }
 #endif
-    return 0;
+
+    return sync_pipe_wait_for_child(*fork_child, msg);
 }
 
 /*
- * 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().
+ * Run dumpcap with the supplied arguments.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_message are NULL, and 0 is returned; *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
  */
 /* XXX - This duplicates a lot of code in sync_pipe_start() */
+/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */
 #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 = ws_read(sync_pipe_read_fd, buf, PIPE_BUF_SIZE)) > 0) {
-        buf[count] = '\0';
-        g_string_append(msg_buf, buf);
+sync_pipe_run_command(const char** argv, gchar **data, gchar **primary_msg,
+                      gchar **secondary_msg)
+{
+  gchar *msg;
+  int data_pipe_read_fd, sync_pipe_read_fd, fork_child, ret;
+  char *wait_msg;
+  gchar buffer[PIPE_BUF_SIZE+1];
+  int  nread;
+  char indicator;
+  int  primary_msg_len;
+  char *primary_msg_text;
+  int  secondary_msg_len;
+  char *secondary_msg_text;
+  char *combined_msg;
+  GString *data_buf = NULL;
+  int count;
+
+  ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_fd,
+                               &fork_child, &msg);
+  if (ret == -1) {
+    *primary_msg = msg;
+    *secondary_msg = NULL;
+    *data = NULL;
+    return -1;
+  }
+
+  /*
+   * We were able to set up to read dumpcap's output.  Do so.
+   *
+   * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message.
+   */
+  nread = pipe_read_block(sync_pipe_read_fd, &indicator, SP_MAX_MSG_LEN,
+                          buffer, primary_msg);
+  if(nread <= 0) {
+    /* We got a read error from the sync pipe, or we got no data at
+       all from the sync pipe, so we're not going to be getting any
+       data or error message from the child process.  Pick up its
+       exit status, and complain.
+
+       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. */
+    ret = sync_pipe_wait_for_child(fork_child, &wait_msg);
+    if(nread == 0) {
+      /* We got an EOF from the sync pipe.  That means that it exited
+         before giving us any data to read.  If ret is -1, we report
+         that as a bad exit (e.g., exiting due to a signal); otherwise,
+         we report it as a premature exit. */
+      if (ret == -1)
+        *primary_msg = wait_msg;
+      else
+        *primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely");
+    } else {
+      /* We got an error from the sync pipe.  If ret is -1, report
+         both the sync pipe I/O error and the wait error. */
+      if (ret == -1) {
+        combined_msg = g_strdup_printf("%s\n\n%s", *primary_msg, wait_msg);
+        g_free(*primary_msg);
+        g_free(wait_msg);
+        *primary_msg = combined_msg;
+      }
     }
+    *secondary_msg = NULL;
 
-#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
+    return -1;
+  }
+
+  /* we got a valid message block from the child, process it */
+  switch(indicator) {
+
+  case SP_ERROR_MSG:
+    /*
+     * Error from dumpcap; there will be a primary message and a
+     * secondary message.
+     */
+
+    /* convert primary message */
+    pipe_convert_header(buffer, 4, &indicator, &primary_msg_len);
+    primary_msg_text = buffer+4;
+    /* convert secondary message */
+    pipe_convert_header(primary_msg_text + primary_msg_len, 4, &indicator,
+                        &secondary_msg_len);
+    secondary_msg_text = primary_msg_text + primary_msg_len + 4;
+    /* the capture child will close the sync_pipe, nothing to do */
 
-    if (ret) {
-       g_string_free(msg_buf, TRUE);
-       return ret;
+    /*
+     * Pick up the child status.
+     */
+    ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+                                  &fork_child, &msg);
+    if (ret == -1) {
+      /*
+       * Child process failed unexpectedly, or wait failed; msg is the
+       * error message.
+       */
+      *primary_msg = msg;
+      *secondary_msg = NULL;
+    } else {
+      /*
+       * Child process failed, but returned the expected exit status.
+       * Return the messages it gave us, and indicate failure.
+       */
+      *primary_msg = g_strdup(primary_msg_text);
+      *secondary_msg = g_strdup(secondary_msg_text);
+      ret = -1;
     }
+    *data = NULL;
+    break;
 
-    *msg = msg_buf->str;
-    g_string_free(msg_buf, FALSE);
-    return 0;
+  case SP_SUCCESS:
+    /* read the output from the command */
+    data_buf = g_string_new("");
+    while ((count = ws_read(data_pipe_read_fd, buffer, PIPE_BUF_SIZE)) > 0) {
+      buffer[count] = '\0';
+      g_string_append(data_buf, buffer);
+    }
+
+    /*
+     * Pick up the child status.
+     */
+    ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+                                  &fork_child, &msg);
+    if (ret == -1) {
+      /*
+       * Child process failed unexpectedly, or wait failed; msg is the
+       * error message.
+       */
+      *primary_msg = msg;
+      *secondary_msg = NULL;
+      g_string_free(data_buf, TRUE);
+      *data = NULL;
+    } else {
+      /*
+       * Child process succeeded.
+       */
+      *primary_msg = NULL;
+      *secondary_msg = NULL;
+      *data = data_buf->str;
+      g_string_free(data_buf, FALSE);
+    }
+    break;
+
+  default:
+    /*
+     * Pick up the child status.
+     */
+    ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+                                  &fork_child, &msg);
+    if (ret == -1) {
+      /*
+       * Child process failed unexpectedly, or wait failed; msg is the
+       * error message.
+       */
+      *primary_msg = msg;
+      *secondary_msg = NULL;
+    } else {
+      /*
+       * Child process returned an unknown status.
+       */
+      *primary_msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x",
+                                     indicator);
+      *secondary_msg = NULL;
+      ret = -1;
+    }
+    *data = NULL;
+    break;
+  }
+  return ret;
 }
 
 /*
- * Get an interface list using dumpcap.  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().
+ * Get the list of interfaces using dumpcap.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_msg are NULL, and 0 is returned.  *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
  */
 int
-sync_interface_list_open(gchar **msg) {
+sync_interface_list_open(gchar **data, gchar **primary_msg,
+                         gchar **secondary_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 CANT_RUN_DUMPCAP;
+        *primary_msg = g_strdup("We don't know where to find dumpcap.");
+        *secondary_msg = NULL;
+        *data = NULL;
+        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
+    /* Run dumpcap in capture child mode */
     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);
+    return sync_pipe_run_command(argv, data, primary_msg, secondary_msg);
 }
 
 /*
- * Get an linktype list using dumpcap.  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().
+ * Get the capabilities of an interface using dumpcap.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_msg are NULL, and 0 is returned.  *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
  */
 int
-sync_linktype_list_open(const gchar *ifname, gchar **msg) {
+sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode,
+                          gchar **data, gchar **primary_msg,
+                          gchar **secondary_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;
+        *primary_msg = g_strdup("We don't know where to find dumpcap.");
+        *secondary_msg = NULL;
+        *data = NULL;
+        return -1;
     }
 
-    /* Ask for the linktype list */
+    /* 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");
-    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).                       */
+    if (monitor_mode)
+        argv = sync_pipe_add_arg(argv, &argc, "-I");
+
 #ifndef DEBUG_CHILD
+    /* Run dumpcap in capture child mode */
     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);
+    return sync_pipe_run_command(argv, data, primary_msg, secondary_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.
+ * 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;
+sync_interface_stats_open(int *data_read_fd, int *fork_child, gchar **msg)
+{
+  int argc;
+  const char **argv;
+  int message_read_fd, ret;
+  char *wait_msg;
+  gchar buffer[PIPE_BUF_SIZE+1];
+  int  nread;
+  char indicator;
+  int  primary_msg_len;
+  char *primary_msg_text;
+  int  secondary_msg_len;
+  char *secondary_msg_text;
+  char *combined_msg;
 
-    if (!msg) {
-        /* We can't return anything */
-        return -1;
-    }
+  g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_stats_open");
 
-    g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_stats_open");
+  argv = init_pipe_args(&argc);
 
-    argv = init_pipe_args(&argc);
+  if (!argv) {
+    *msg = g_strdup("We don't know where to find dumpcap.");
+    return -1;
+  }
 
-    if (!argv) {
-        *msg = g_strdup_printf("We don't know where to find dumpcap.");
-        return CANT_RUN_DUMPCAP;
-    }
+  /* Ask for the interface statistics */
+  argv = sync_pipe_add_arg(argv, &argc, "-S");
 
-    /* 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);
+  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);
+  ret = sync_pipe_open_command(argv, data_read_fd, &message_read_fd,
+                                 fork_child, msg);
+  if (ret == -1)
+    return -1;
+
+  /*
+   * We were able to set up to read dumpcap's output.  Do so.
+   *
+   * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message.
+   */
+  nread = pipe_read_block(message_read_fd, &indicator, SP_MAX_MSG_LEN,
+                          buffer, msg);
+  if(nread <= 0) {
+    /* We got a read error from the sync pipe, or we got no data at
+       all from the sync pipe, so we're not going to be getting any
+       data or error message from the child process.  Pick up its
+       exit status, and complain.
+
+       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. */
+    ret = sync_pipe_wait_for_child(*fork_child, &wait_msg);
+    if(nread == 0) {
+      /* We got an EOF from the sync pipe.  That means that it exited
+         before giving us any data to read.  If ret is -1, we report
+         that as a bad exit (e.g., exiting due to a signal); otherwise,
+         we report it as a premature exit. */
+      if (ret == -1)
+        *msg = wait_msg;
+      else
+        *msg = g_strdup("Child dumpcap closed sync pipe prematurely");
+    } else {
+      /* We got an error from the sync pipe.  If ret is -1, report
+         both the sync pipe I/O error and the wait error. */
+      if (ret == -1) {
+        combined_msg = g_strdup_printf("%s\n\n%s", *msg, wait_msg);
+        g_free(*msg);
+        g_free(wait_msg);
+        *msg = combined_msg;
+      }
+    }
+
+    return -1;
+  }
+
+  /* we got a valid message block from the child, process it */
+  switch(indicator) {
+
+  case SP_ERROR_MSG:
+    /*
+     * Error from dumpcap; there will be a primary message and a
+     * secondary message.
+     */
+
+    /* convert primary message */
+    pipe_convert_header(buffer, 4, &indicator, &primary_msg_len);
+    primary_msg_text = buffer+4;
+    /* convert secondary message */
+    pipe_convert_header(primary_msg_text + primary_msg_len, 4, &indicator,
+                        &secondary_msg_len);
+    secondary_msg_text = primary_msg_text + primary_msg_len + 4;
+    /* the capture child will close the sync_pipe, nothing to do */
+
+    /*
+     * Pick up the child status.
+     */
+    ret = sync_pipe_close_command(data_read_fd, &message_read_fd,
+                                  fork_child, msg);
+    if (ret == -1) {
+      /*
+       * Child process failed unexpectedly, or wait failed; msg is the
+       * error message.
+       */
+    } else {
+      /*
+       * Child process failed, but returned the expected exit status.
+       * Return the messages it gave us, and indicate failure.
+       */
+      *msg = g_strdup(primary_msg_text);
+      ret = -1;
+    }
+    break;
+
+  case SP_SUCCESS:
+    /* Close the message pipe. */
+    ws_close(message_read_fd);
+    break;
+
+  default:
+    /*
+     * Pick up the child status.
+     */
+    ret = sync_pipe_close_command(data_read_fd, &message_read_fd,
+                                  fork_child, msg);
+    if (ret == -1) {
+      /*
+       * Child process failed unexpectedly, or wait failed; msg is the
+       * error message.
+       */
+    } else {
+      /*
+       * Child process returned an unknown status.
+       */
+      *msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x",
+                             indicator);
+      ret = -1;
+    }
+    break;
+  }
+  return ret;
 }
 
 /* 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
+sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg)
+{
+    return sync_pipe_close_command(read_fd, NULL, fork_child, msg);
 }
 
 /* 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, char **msg)
+{
     int newly;
     int offset = 0;
+    int error;
 
     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);
+            *msg = 0;
             return offset;
         }
         if (newly < 0) {
             /* error */
+            error = errno;
             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, error,
+                  strerror(error));
+            *msg = g_strdup_printf("Error reading from sync pipe: %s",
+                                   strerror(error));
             return newly;
         }
 
@@ -1015,12 +1349,13 @@ pipe_read_bytes(int pipe, char *bytes, int required) {
         offset += newly;
     }
 
+    *msg = NULL;
     return offset;
 }
 
-static gboolean pipe_data_available(int pipe) {
+static gboolean pipe_data_available(int pipe_fd) {
 #ifdef _WIN32 /* PeekNamedPipe */
-    HANDLE hPipe = (HANDLE) _get_osfhandle(pipe);
+    HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
     DWORD bytes_avail;
 
     if (hPipe == INVALID_HANDLE_VALUE)
@@ -1037,11 +1372,11 @@ static gboolean pipe_data_available(int pipe) {
     struct timeval timeout;
 
     FD_ZERO(&rfds);
-    FD_SET(pipe, &rfds);
+    FD_SET(pipe_fd, &rfds);
     timeout.tv_sec = 0;
     timeout.tv_usec = 0;
 
-    if (select(pipe+1, &rfds, NULL, NULL, &timeout) > 0)
+    if (select(pipe_fd+1, &rfds, NULL, NULL, &timeout) > 0)
         return TRUE;
 
     return FALSE;
@@ -1079,7 +1414,7 @@ sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max) {
 }
 
 
-/* convert header values (indicator and 4-byte length) */
+/* convert header values (indicator and 3-byte length) */
 static void
 pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) {
 
@@ -1094,17 +1429,35 @@ 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,
+                char **err_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, err_msg);
     if(newly != 4) {
+       if (newly == 0) {
+           /*
+            * Immediate EOF; if the capture child exits normally, this
+            * is an "I'm done" indication, so don't report it as an
+            * error.
+            */
+            g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+                  "read %d got an EOF", pipe_fd);
+            return 0;
+        }
         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);
+        if (newly != -1) {
+            /*
+            * Short read, but not an immediate EOF.
+            */
+            *err_msg = g_strdup_printf("Premature EOF reading from sync pipe: got only %d bytes",
+                                       newly);
+        }
         return -1;
     }
 
@@ -1114,7 +1467,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;
     }
 
@@ -1122,27 +1475,32 @@ 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));
-        g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
+        newly = read(pipe_fd, &msg[sizeof(header)], len-sizeof(header));
+        *err_msg = g_strdup_printf("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, err_msg);
     if(newly != required) {
-        g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
+       if (newly != -1) {
+            *err_msg = g_strdup_printf("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);
+    *err_msg = NULL;
     return newly + 4;
 }
 
@@ -1154,39 +1512,60 @@ static gboolean
 sync_pipe_input_cb(gint source, gpointer user_data)
 {
   capture_options *capture_opts = (capture_options *)user_data;
+  int  ret;
   char buffer[SP_MAX_MSG_LEN+1];
   int  nread;
   char indicator;
   int  primary_len;
-  char * primary_msg;
+  char *primary_msg;
   int  secondary_len;
-  char * secondary_msg;
+  char *secondary_msg;
+  char *wait_msg, *combined_msg;
 
-
-  nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer);
+  nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer,
+                          &primary_msg);
   if(nread <= 0) {
-    if (nread == 0)
-      g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-            "sync_pipe_input_cb: child has closed sync_pipe");
-    else
-      g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
-            "sync_pipe_input_cb: error reading from sync pipe");
-
-    /* The child has closed the sync pipe, meaning it's not going to be
-       capturing any more packets.  Pick up its exit status, and
-       complain if it did anything other than exit with status 0.
+    /* We got a read error, or a bad message, or an EOF, from the sync pipe.
+
+       If we got a read error or a bad message, nread is -1 and
+       primary_msg is set to point to an error message.  We don't
+       have to worry about killing the child; 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.
+
+       If we got an EOF, nread is 0 and primary_msg isn't set.  This
+       is an indication that the capture is finished. */
+    ret = sync_pipe_wait_for_child(capture_opts->fork_child, &wait_msg);
+    if(nread == 0) {
+      /* We got an EOF from the sync pipe.  That means that the capture
+         child exited, and not in the middle of a message; we treat
+         that as an indication that it's done, and only report an
+         error if ret is -1, in which case wait_msg is the error
+         message. */
+      if (ret == -1)
+        primary_msg = wait_msg;
+    } else {
+      /* We got an error from the sync pipe.  If ret is -1, report
+         both the sync pipe I/O error and the wait error. */
+      if (ret == -1) {
+        combined_msg = g_strdup_printf("%s\n\n%s", primary_msg, wait_msg);
+        g_free(primary_msg);
+        g_free(wait_msg);
+        primary_msg = combined_msg;
+      }
+    }
 
-       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);
+    /* No more child process. */
+    capture_opts->fork_child = -1;
+    capture_opts->fork_child_status = ret;
 
 #ifdef _WIN32
     ws_close(capture_opts->signal_pipe_write_fd);
 #endif
-    capture_input_closed(capture_opts);
+    capture_input_closed(capture_opts, primary_msg);
+    g_free(primary_msg);
     return FALSE;
   }
 
@@ -1242,54 +1621,68 @@ sync_pipe_input_cb(gint source, gpointer user_data)
 
 
 /* the child process is going down, wait until it's completely terminated */
-static void
-sync_pipe_wait_for_child(capture_options *capture_opts)
+static int
+sync_pipe_wait_for_child(int fork_child, gchar **msgp)
 {
-  int  wstatus;
-
+  int fork_child_status;
+  int ret;
 
   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: wait till child closed");
-  g_assert(capture_opts->fork_child != -1);
+  g_assert(fork_child != -1);
 
+  *msgp = NULL; /* assume no error */
+  ret = 0;
 #ifdef _WIN32
-  if (_cwait(&wstatus, capture_opts->fork_child, _WAIT_CHILD) == -1) {
-    report_failure("Child capture process stopped unexpectedly (errno:%u)",
-                   errno);
+  if (_cwait(&fork_child_status, fork_child, _WAIT_CHILD) == -1) {
+    *msgp = g_strdup_printf("Error from cwait(): %s", strerror(errno));
+    ret = -1;
+  } else {
+    /*
+     * The child exited; return its exit status.  Do not treat this as
+     * an error.
+     */
+    ret = fork_child_status;
+    if ((fork_child_status & 0xC0000000) == ERROR_SEVERITY_ERROR) {
+      /* Probably an exception code */
+      *msgp = g_strdup_printf("Child dumpcap process died: %s",
+                              win32strexception(fork_child_status));
+      ret = -1;
+    }
   }
 #else
-  if (wait(&wstatus) != -1) {
-    if (WIFEXITED(wstatus)) {
-      /* The child exited; display its exit status, if it seems uncommon (0=ok, 1=error) */
-      /* the child will inform us about errors through the sync_pipe, which will popup */
-      /* an error message, so don't popup another one */
-
-      /* 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) {
-        report_failure("Child capture process exited: exit status %d",
-                      WEXITSTATUS(wstatus));
-      }
-    } else if (WIFSTOPPED(wstatus)) {
+  if (waitpid(fork_child, &fork_child_status, 0) != -1) {
+    if (WIFEXITED(fork_child_status)) {
+      /*
+       * The child exited; return its exit status.  Do not treat this as
+       * an error.
+       */
+      ret = WEXITSTATUS(fork_child_status);
+    } else if (WIFSTOPPED(fork_child_status)) {
       /* It stopped, rather than exiting.  "Should not happen." */
-      report_failure("Child capture process stopped: %s",
-                    sync_pipe_signame(WSTOPSIG(wstatus)));
-    } else if (WIFSIGNALED(wstatus)) {
+      *msgp = g_strdup_printf("Child dumpcap process stopped: %s",
+                              sync_pipe_signame(WSTOPSIG(fork_child_status)));
+      ret = -1;
+    } else if (WIFSIGNALED(fork_child_status)) {
       /* It died with a signal. */
-      report_failure("Child capture process died: %s%s",
-                    sync_pipe_signame(WTERMSIG(wstatus)),
-                    WCOREDUMP(wstatus) ? " - core dumped" : "");
+      *msgp = g_strdup_printf("Child dumpcap process died: %s%s",
+                              sync_pipe_signame(WTERMSIG(fork_child_status)),
+                              WCOREDUMP(fork_child_status) ? " - core dumped" : "");
+      ret = -1;
     } else {
       /* What?  It had to either have exited, or stopped, or died with
          a signal; what happened here? */
-      report_failure("Child capture process died: wait status %#o", wstatus);
+      *msgp = g_strdup_printf("Bad status from waitpid(): %#o",
+                              fork_child_status);
+      ret = -1;
     }
+  } else {
+    *msgp = g_strdup_printf("Error from waitpid(): %s", strerror(errno));
+    ret = -1;
   }
 #endif
 
-  /* No more child process. */
-  capture_opts->fork_child = -1;
-
   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: capture child closed");
+  return ret;
 }
 
 
@@ -1347,13 +1740,13 @@ sync_pipe_signame(int sig)
      Linux is POSIX compliant.  These are not POSIX-defined signals ---
      ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez:
 
-       ``The signals SIGBUS, SIGEMT, SIGIOT, SIGTRAP, and SIGSYS
-       were omitted from POSIX.1 because their behavior is
-       implementation dependent and could not be adequately catego-
-       rized.  Conforming implementations may deliver these sig-
-       nals, but must document the circumstances under which they
-       are delivered and note any restrictions concerning their
-       delivery.''
+        ``The signals SIGBUS, SIGEMT, SIGIOT, SIGTRAP, and SIGSYS
+        were omitted from POSIX.1 because their behavior is
+        implementation dependent and could not be adequately catego-
+        rized.  Conforming implementations may deliver these sig-
+        nals, but must document the circumstances under which they
+        are delivered and note any restrictions concerning their
+        delivery.''
 
      So we only check for SIGSYS on those systems that happen to
      implement them (a system can be POSIX-compliant and implement
@@ -1380,7 +1773,7 @@ sync_pipe_signame(int sig)
     break;
 
   default:
-       /* Returning a static buffer is ok in the context we use it here */
+    /* Returning a static buffer is ok in the context we use it here */
     g_snprintf(sigmsg_buf, sizeof sigmsg_buf, "Signal %d", sig);
     sigmsg = sigmsg_buf;
     break;
@@ -1425,11 +1818,11 @@ sync_pipe_stop(capture_options *capture_opts)
 
   if (capture_opts->fork_child != -1) {
 #ifndef _WIN32
-    /* send the SIGUSR1 signal to close the capture child gracefully. */
-    int sts = 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 SIGUSR1 to child failed: %s\n", strerror(errno));
+              "Sending SIGINT to child failed: %s\n", strerror(errno));
     }
 #else
 #define STOP_SLEEP_TIME 500 /* ms */
@@ -1466,30 +1859,30 @@ sync_pipe_kill(int fork_child)
 {
     if (fork_child != -1) {
 #ifndef _WIN32
-        int sts = kill(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,
-       * then getting window handle hWnd of that process (using EnumChildWindows),
-       * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0)
-       *
-       * Unfortunately, I don't know how to get the process id from the
-       * handle.  OpenProcess will get an handle (not a window handle)
-       * from the process ID; it will not get a window handle from the
-       * process ID.  (How could it?  A process can have more than one
-       * window.  For that matter, a process might have *no* windows,
-       * as a process running dumpcap, the normal child process program,
-       * probably does.)
-       *
-       * Hint: GenerateConsoleCtrlEvent() will only work if both processes are
-       * running in the same console; that's not necessarily the case for
-       * us, as we might not be running in a console.
-       * And this also will require to have the process id.
-       */
+        /* Remark: This is not the preferred method of closing a process!
+         * the clean way would be getting the process id of the child process,
+         * then getting window handle hWnd of that process (using EnumChildWindows),
+         * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0)
+         *
+         * Unfortunately, I don't know how to get the process id from the
+         * handle.  OpenProcess will get an handle (not a window handle)
+         * from the process ID; it will not get a window handle from the
+         * process ID.  (How could it?  A process can have more than one
+         * window.  For that matter, a process might have *no* windows,
+         * as a process running dumpcap, the normal child process program,
+         * probably does.)
+         *
+         * Hint: GenerateConsoleCtrlEvent() will only work if both processes are
+         * running in the same console; that's not necessarily the case for
+         * us, as we might not be running in a console.
+         * And this also will require to have the process id.
+         */
         TerminateProcess((HANDLE) (fork_child), 0);
 #endif
     }