Update some links.
[obnox/wireshark/wip.git] / capture_sync.c
index df9d85deea23ebbba151eff6b0e3fda9d8942537..68b79f79280acbc1fad8f0245cf2c084b46792c1 100644 (file)
@@ -115,7 +115,8 @@ static const char *sync_pipe_signame(int);
 static gboolean sync_pipe_input_cb(gint source, gpointer user_data);
 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);
+static int pipe_read_block(int pipe_fd, char *indicator, int len, char *msg,
+                           char **err_msg);
 
 
 
@@ -179,7 +180,7 @@ protect_arg (const gchar *argv)
                 pp++;
             if (*pp == '"')
                 len++;
-       }
+        }
         len++;
         p++;
     }
@@ -200,9 +201,9 @@ protect_arg (const gchar *argv)
                 pp++;
             if (*pp == '"')
                 *q++ = '\\';
-       }
-       *q++ = *p;
-       p++;
+        }
+        *q++ = *p;
+        p++;
     }
 
     if (need_dblquotes)
@@ -211,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. */
@@ -436,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;
@@ -450,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;
@@ -484,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]);
@@ -520,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);
     }
 
@@ -540,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
@@ -562,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();
 
@@ -635,7 +714,8 @@ sync_pipe_open_command(const char** argv, int *data_read_fd,
     /* (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", strerror(errno));
+        *msg = g_strdup_printf("Couldn't create sync pipe: %s",
+                               win32strerror(GetLastError()));
         g_free( (gpointer) argv[0]);
         g_free( (gpointer) argv);
         return -1;
@@ -645,7 +725,8 @@ sync_pipe_open_command(const char** argv, int *data_read_fd,
     /* (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", strerror(errno));
+        *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]);
@@ -680,8 +761,8 @@ sync_pipe_open_command(const char** argv, int *data_read_fd,
     /* 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());
+        *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]);
@@ -731,7 +812,7 @@ sync_pipe_open_command(const char** argv, int *data_read_fd,
         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));
+                   argv[0], strerror(errno));
         sync_pipe_errmsg_to_parent(2, errmsg, "");
 
         /* Exit with "_exit()", so that we don't close the connection
@@ -752,7 +833,7 @@ sync_pipe_open_command(const char** argv, int *data_read_fd,
 
     /* 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 sides of the pipes, so that only the child has them
        open, and thus they completely close, and thus return to us
@@ -822,6 +903,7 @@ sync_pipe_run_command(const char** argv, gchar **data, gchar **primary_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;
@@ -829,6 +911,7 @@ sync_pipe_run_command(const char** argv, gchar **data, gchar **primary_msg,
   char *primary_msg_text;
   int  secondary_msg_len;
   char *secondary_msg_text;
+  char *combined_msg;
   GString *data_buf = NULL;
   int count;
 
@@ -844,10 +927,10 @@ sync_pipe_run_command(const char** argv, gchar **data, gchar **primary_msg,
   /*
    * We were able to set up to read dumpcap's output.  Do so.
    *
-   * First, wait for an SP_ERROR_MESSAGE or SP_SUCCESS message.
+   * 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);
+                          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
@@ -859,13 +942,25 @@ sync_pipe_run_command(const char** argv, gchar **data, gchar **primary_msg,
        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, primary_msg);
-    if (ret == 0) {
-      /* No unusual exit status; just report the read problem. */
-      if (nread == 0)
-        *primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely");
+    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("Error reading from sync pipe");
+        *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;
 
@@ -1074,6 +1169,7 @@ 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;
@@ -1081,6 +1177,7 @@ sync_interface_stats_open(int *data_read_fd, int *fork_child, gchar **msg)
   char *primary_msg_text;
   int  secondary_msg_len;
   char *secondary_msg_text;
+  char *combined_msg;
 
   g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_stats_open");
 
@@ -1106,10 +1203,10 @@ sync_interface_stats_open(int *data_read_fd, int *fork_child, gchar **msg)
   /*
    * We were able to set up to read dumpcap's output.  Do so.
    *
-   * First, wait for an SP_ERROR_MESSAGE or SP_SUCCESS message.
+   * 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);
+                          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
@@ -1121,13 +1218,25 @@ sync_interface_stats_open(int *data_read_fd, int *fork_child, gchar **msg)
        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, msg);
-    if (ret == 0) {
-      /* No unusual exit status; just report the read problem. */
-      if (nread == 0)
-        *msg = g_strdup("Child dumpcap closed sync pipe prematurely");
+    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("Error reading from sync pipe");
+        *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;
@@ -1210,10 +1319,11 @@ sync_interface_stats_close(int *read_fd, int *fork_child, gchar **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_fd, 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_fd, &bytes[offset], required);
@@ -1221,12 +1331,17 @@ pipe_read_bytes(int pipe_fd, char *bytes, int required)
             /* EOF */
             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
                   "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_fd, 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;
         }
 
@@ -1234,6 +1349,7 @@ pipe_read_bytes(int pipe_fd, char *bytes, int required)
         offset += newly;
     }
 
+    *msg = NULL;
     return offset;
 }
 
@@ -1313,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_fd, 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_fd, 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_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;
     }
 
@@ -1346,15 +1480,19 @@ pipe_read_block(int pipe_fd, char *indicator, int len, char *msg)
         /* 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_fd, &msg[sizeof(header)], len-sizeof(header));
-        g_warning("Unknown message from dumpcap, try to show it as a string: %s", msg);
+        *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_fd, 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;
     }
 
@@ -1362,6 +1500,7 @@ pipe_read_block(int pipe_fd, char *indicator, int len, char *msg)
     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
           "read %d ok indicator: %c len: %u msg: %s", pipe_fd, *indicator,
           len, msg);
+    *err_msg = NULL;
     return newly + 4;
 }
 
@@ -1378,39 +1517,55 @@ sync_pipe_input_cb(gint source, gpointer user_data)
   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) {
-    /* 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(capture_opts->fork_child, &primary_msg);
-    if (ret == 0) {
-      /* No unusual exit status; just report the read problem. */
-      if (nread == 0)
-        primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely");
-      else
-        primary_msg = g_strdup("Error reading from sync pipe");
+    /* 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;
+      }
     }
-    g_free(primary_msg); /* XXX - display this */
 
     /* 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;
   }
 
@@ -1479,30 +1634,29 @@ sync_pipe_wait_for_child(int fork_child, gchar **msgp)
   ret = 0;
 #ifdef _WIN32
   if (_cwait(&fork_child_status, fork_child, _WAIT_CHILD) == -1) {
-    *msgp = g_strdup_printf("Error from cwait(): %u", errno);
+    *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 (waitpid(fork_child, &fork_child_status, 0) != -1) {
     if (WIFEXITED(fork_child_status)) {
       /*
-       * The child exited; return its exit status, if it seems uncommon
-       * (0=ok, 1=command syntax error, 2=other error).
-       *
-       * For an exit status of 0, there's no error to tell the user about.
-       * For an exit status of 1 or 2, the child will inform us about errors
-       * through the sync_pipe, so don't return an error.
-       * If there are situations where the child won't send us such an error
-       * message, this should be fixed in the child and not worked around
-       * here!
+       * The child exited; return its exit status.  Do not treat this as
+       * an error.
        */
-      if (WEXITSTATUS(fork_child_status) != 0 &&
-          WEXITSTATUS(fork_child_status) != 1 &&
-          WEXITSTATUS(fork_child_status) != 2) {
-        *msgp = g_strdup_printf("Child dumpcap process exited: exit status %d",
-                                WEXITSTATUS(fork_child_status));
-        ret = -1;
-      }
+      ret = WEXITSTATUS(fork_child_status);
     } else if (WIFSTOPPED(fork_child_status)) {
       /* It stopped, rather than exiting.  "Should not happen." */
       *msgp = g_strdup_printf("Child dumpcap process stopped: %s",
@@ -1517,12 +1671,12 @@ sync_pipe_wait_for_child(int fork_child, gchar **msgp)
     } else {
       /* What?  It had to either have exited, or stopped, or died with
          a signal; what happened here? */
-      *msgp = g_strdup_printf("Bad status from wait(): %#o",
+      *msgp = g_strdup_printf("Bad status from waitpid(): %#o",
                               fork_child_status);
       ret = -1;
     }
   } else {
-    *msgp = g_strdup_printf("Error from wait(): %s", strerror(errno));
+    *msgp = g_strdup_printf("Error from waitpid(): %s", strerror(errno));
     ret = -1;
   }
 #endif
@@ -1586,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
@@ -1619,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;
@@ -1705,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
     }