From Edgar Gladkich:
[obnox/wireshark/wip.git] / dumpcap.c
index dbc948fb36ffb468d506f141d5357003bd2e6c40..3ba302e3447df46fa0051844d0961d64e3f73308 100644 (file)
--- a/dumpcap.c
+++ b/dumpcap.c
 #include <sys/un.h>
 #endif
 
+#ifdef NEED_INET_V6DEFS_H
+# include "inet_v6defs.h"
+#endif
+
 #include <wsutil/privileges.h>
 
 #include "sync_pipe.h"
@@ -140,6 +144,11 @@ static GAsyncQueue *cap_pipe_pending_q, *cap_pipe_done_q;
 static GMutex *cap_pipe_read_mtx;
 #endif
 
+#ifdef SIGINFO
+static gboolean infodelay;     /* if TRUE, don't print capture info in SIGINFO handler */
+static gboolean infoprint;     /* if TRUE, print capture info after clearing infodelay */
+#endif /* SIGINFO */
+
 /** Stop a low-level capture (stops the capture child). */
 static void capture_loop_stop(void);
 
@@ -208,6 +217,10 @@ typedef struct _loop_data {
   int            err;                   /* if non-zero, error seen while capturing */
   gint           packet_count;          /* Number of packets we have already captured */
   gint           packet_max;            /* Number of packets we're supposed to capture - 0 means infinite */
+  gint           inpkts_to_sync_pipe;   /* Packets not already send out to the sync_pipe */
+#ifdef SIGINFO
+  gboolean       report_packet_count;   /* Set by SIGINFO handler; print packet count */
+#endif
 
   /* pcap "input file" */
   pcap_t        *pcap_h;                /* pcap handle */
@@ -240,13 +253,14 @@ typedef struct _loop_data {
        } cap_pipe_state;
   enum { PIPOK, PIPEOF, PIPERR, PIPNEXIST } cap_pipe_err;
 
-  /* output file */
+  /* output file(s) */
   FILE          *pdh;
+  int            save_file_fd;
   int            linktype;
   int            file_snaplen;
   gint           wtap_linktype;
   long           bytes_written;
-
+  guint32        autostop_files;
 } loop_data;
 
 /*
@@ -290,6 +304,7 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level,
 
 /* capture related options */
 static capture_options global_capture_opts;
+static gboolean quiet;
 
 static void capture_loop_packet_cb(u_char *user, const struct pcap_pkthdr *phdr,
   const u_char *pd);
@@ -364,6 +379,7 @@ print_usage(gboolean print_ver) {
   fprintf(output, "  -n                       use pcapng format instead of pcap\n");
   /*fprintf(output, "\n");*/
   fprintf(output, "Miscellaneous:\n");
+  fprintf(output, "  -q                       don't report packet capture counts\n");
   fprintf(output, "  -v                       print version information and exit\n");
   fprintf(output, "  -h                       display this help and exit\n");
   fprintf(output, "\n");
@@ -475,52 +491,241 @@ capture_interface_list(int *err, char **err_str)
 }
 
 /*
- * Get the data-link types available for a libpcap device.
+ * Get the data-link type for a libpcap device.
+ * This works around AIX 5.x's non-standard and incompatible-with-the-
+ * rest-of-the-universe libpcap.
  */
+static int
+get_pcap_linktype(pcap_t *pch, const char *devname
+#ifndef _AIX
+       _U_
+#endif
+)
+{
+  int linktype;
+#ifdef _AIX
+  const char *ifacename;
+#endif
+
+  linktype = pcap_datalink(pch);
+#ifdef _AIX
+
+  /*
+   * The libpcap that comes with AIX 5.x uses RFC 1573 ifType values
+   * rather than DLT_ values for link-layer types; the ifType values
+   * for LAN devices are:
+   *
+   *   Ethernet        6
+   *   802.3           7
+   *   Token Ring      9
+   *   FDDI            15
+   *
+   * and the ifType value for a loopback device is 24.
+   *
+   * The AIX names for LAN devices begin with:
+   *
+   *   Ethernet                en
+   *   802.3                   et
+   *   Token Ring              tr
+   *   FDDI                    fi
+   *
+   * and the AIX names for loopback devices begin with "lo".
+   *
+   * (The difference between "Ethernet" and "802.3" is presumably
+   * whether packets have an Ethernet header, with a packet type,
+   * or an 802.3 header, with a packet length, followed by an 802.2
+   * header and possibly a SNAP header.)
+   *
+   * If the device name matches "linktype" interpreted as an ifType
+   * value, rather than as a DLT_ value, we will assume this is AIX's
+   * non-standard, incompatible libpcap, rather than a standard libpcap,
+   * and will map the link-layer type to the standard DLT_ value for
+   * that link-layer type, as that's what the rest of Wireshark expects.
+   *
+   * (This means the capture files won't be readable by a tcpdump
+   * linked with AIX's non-standard libpcap, but so it goes.  They
+   * *will* be readable by standard versions of tcpdump, Wireshark,
+   * and so on.)
+   *
+   * XXX - if we conclude we're using AIX libpcap, should we also
+   * set a flag to cause us to assume the time stamps are in
+   * seconds-and-nanoseconds form, and to convert them to
+   * seconds-and-microseconds form before processing them and
+   * writing them out?
+   */
+
+  /*
+   * Find the last component of the device name, which is the
+   * interface name.
+   */
+  ifacename = strchr(devname, '/');
+  if (ifacename == NULL)
+    ifacename = devname;
+
+  /* See if it matches any of the LAN device names. */
+  if (strncmp(ifacename, "en", 2) == 0) {
+    if (linktype == 6) {
+      /*
+       * That's the RFC 1573 value for Ethernet; map it to DLT_EN10MB.
+       */
+      linktype = 1;
+    }
+  } else if (strncmp(ifacename, "et", 2) == 0) {
+    if (linktype == 7) {
+      /*
+       * That's the RFC 1573 value for 802.3; map it to DLT_EN10MB.
+       * (libpcap, tcpdump, Wireshark, etc. don't care if it's Ethernet
+       * or 802.3.)
+       */
+      linktype = 1;
+    }
+  } else if (strncmp(ifacename, "tr", 2) == 0) {
+    if (linktype == 9) {
+      /*
+       * That's the RFC 1573 value for 802.5 (Token Ring); map it to
+       * DLT_IEEE802, which is what's used for Token Ring.
+       */
+      linktype = 6;
+    }
+  } else if (strncmp(ifacename, "fi", 2) == 0) {
+    if (linktype == 15) {
+      /*
+       * That's the RFC 1573 value for FDDI; map it to DLT_FDDI.
+       */
+      linktype = 10;
+    }
+  } else if (strncmp(ifacename, "lo", 2) == 0) {
+    if (linktype == 24) {
+      /*
+       * That's the RFC 1573 value for "software loopback" devices; map it
+       * to DLT_NULL, which is what's used for loopback devices on BSD.
+       */
+      linktype = 0;
+    }
+  }
+#endif
+
+  return linktype;
+}
+
 static data_link_info_t *
 create_data_link_info(int dlt)
 {
-    data_link_info_t *data_link_info;
-    const char *text;
-
-    data_link_info = (data_link_info_t *)g_malloc(sizeof (data_link_info_t));
-    data_link_info->dlt = dlt;
-    text = pcap_datalink_val_to_name(dlt);
-    if (text != NULL)
-        data_link_info->name = g_strdup(text);
-    else
-        data_link_info->name = g_strdup_printf("DLT %d", dlt);
-    text = pcap_datalink_val_to_description(dlt);
-    if (text != NULL)
-        data_link_info->description = g_strdup(text);
-    else
-        data_link_info->description = NULL;
-    return data_link_info;
+  data_link_info_t *data_link_info;
+  const char *text;
+
+  data_link_info = (data_link_info_t *)g_malloc(sizeof (data_link_info_t));
+  data_link_info->dlt = dlt;
+  text = pcap_datalink_val_to_name(dlt);
+  if (text != NULL)
+    data_link_info->name = g_strdup(text);
+  else
+    data_link_info->name = g_strdup_printf("DLT %d", dlt);
+  text = pcap_datalink_val_to_description(dlt);
+  if (text != NULL)
+    data_link_info->description = g_strdup(text);
+  else
+    data_link_info->description = NULL;
+  return data_link_info;
 }
 
-static GList *
-get_pcap_linktype_list(const char *devname, char **err_str)
+/*
+ * Get the capabilities of a network device.
+ */
+static if_capabilities_t *
+get_if_capabilities(const char *devname, gboolean monitor_mode
+#ifndef HAVE_PCAP_CREATE
+       _U_
+#endif
+, char **err_str)
 {
-    GList *linktype_list = NULL;
+    if_capabilities_t *caps;
+    char errbuf[PCAP_ERRBUF_SIZE];
     pcap_t *pch;
+#ifdef HAVE_PCAP_CREATE
+    int status;
+#endif
     int deflt;
-    char errbuf[PCAP_ERRBUF_SIZE];
 #ifdef HAVE_PCAP_LIST_DATALINKS
     int *linktypes;
     int i, nlt;
 #endif
     data_link_info_t *data_link_info;
 
+    /*
+     * Allocate the interface capabilities structure.
+     */
+    caps = g_malloc(sizeof *caps);
+
 #ifdef HAVE_PCAP_OPEN
     pch = pcap_open(devname, MIN_PACKET_SIZE, 0, 0, NULL, errbuf);
+    caps->can_set_rfmon = FALSE;
+    if (pch == NULL) {
+        if (err_str != NULL)
+            *err_str = g_strdup(errbuf);
+        g_free(caps);
+        return NULL;
+    }
+#elif defined(HAVE_PCAP_CREATE)
+    pch = pcap_create(devname, errbuf);
+    if (pch == NULL) {
+        if (err_str != NULL)
+            *err_str = g_strdup(errbuf);
+        g_free(caps);
+        return NULL;
+    }
+    status = pcap_can_set_rfmon(pch); 
+    if (status < 0) {
+        /* Error. */
+        if (status == PCAP_ERROR)
+            *err_str = g_strdup_printf("pcap_can_set_rfmon() failed: %s",
+                                       pcap_geterr(pch));
+        else
+            *err_str = g_strdup(pcap_statustostr(status));
+        pcap_close(pch);
+        g_free(caps);
+        return NULL;
+    }
+    if (status == 0)
+        caps->can_set_rfmon = FALSE;
+    else if (status == 1) {
+        caps->can_set_rfmon = TRUE;
+        if (monitor_mode)
+            pcap_set_rfmon(pch, 1);
+    } else {
+        if (err_str != NULL) {
+            *err_str = g_strdup_printf("pcap_can_set_rfmon() returned %d",
+                                       status);
+        }
+        pcap_close(pch);
+        g_free(caps);
+        return NULL;
+    }
+
+    status = pcap_activate(pch);
+    if (status < 0) {
+        /* Error.  We ignore warnings (status > 0). */
+        if (err_str != NULL) {
+            if (status == PCAP_ERROR)
+                *err_str = g_strdup_printf("pcap_activate() failed: %s",
+                                           pcap_geterr(pch));
+            else
+                *err_str = g_strdup(pcap_statustostr(status));
+        }
+        pcap_close(pch);
+        g_free(caps);
+        return NULL;
+    }
 #else
     pch = pcap_open_live(devname, MIN_PACKET_SIZE, 0, 0, errbuf);
-#endif
+    caps->can_set_rfmon = FALSE;
     if (pch == NULL) {
         if (err_str != NULL)
             *err_str = g_strdup(errbuf);
-            return NULL;
+        g_free(caps);
+        return NULL;
     }
+#endif
     deflt = get_pcap_linktype(pch, devname);
 #ifdef HAVE_PCAP_LIST_DATALINKS
     nlt = pcap_list_datalinks(pch, &linktypes);
@@ -530,6 +735,7 @@ get_pcap_linktype_list(const char *devname, char **err_str)
             *err_str = NULL; /* an empty list doesn't mean an error */
         return NULL;
     }
+    caps->data_link_types = NULL;
     for (i = 0; i < nlt; i++) {
         data_link_info = create_data_link_info(linktypes[i]);
 
@@ -539,9 +745,11 @@ get_pcap_linktype_list(const char *devname, char **err_str)
          * device has as the default?
          */
         if (linktypes[i] == deflt)
-            linktype_list = g_list_prepend(linktype_list, data_link_info);
+            caps->data_link_types = g_list_prepend(caps->data_link_types,
+                                                   data_link_info);
         else
-            linktype_list = g_list_append(linktype_list, data_link_info);
+            caps->data_link_types = g_list_append(caps->data_link_types,
+                                                  data_link_info);
     }
 #ifdef HAVE_PCAP_FREE_DATALINKS
     pcap_free_datalinks(linktypes);
@@ -571,14 +779,15 @@ get_pcap_linktype_list(const char *devname, char **err_str)
 #else /* HAVE_PCAP_LIST_DATALINKS */
 
     data_link_info = create_data_link_info(deflt);
-    linktype_list = g_list_append(linktype_list, data_link_info);
+    caps->data_link_types = g_list_append(caps->data_link_types,
+                                          data_link_info);
 #endif /* HAVE_PCAP_LIST_DATALINKS */
 
     pcap_close(pch);
 
     if (err_str != NULL)
         *err_str = NULL;
-    return linktype_list;
+    return caps;
 }
 
 #define ADDRSTRLEN 46 /* Covers IPv4 & IPv6 */
@@ -592,6 +801,11 @@ print_machine_readable_interfaces(GList *if_list)
     if_addr_t   *if_addr;
     char        addr_str[ADDRSTRLEN];
 
+    if (capture_child) {
+        /* Let our parent know we succeeded. */
+        pipe_write_block(2, SP_SUCCESS, NULL);
+    }
+
     i = 1;  /* Interface id number */
     for (if_entry = g_list_first(if_list); if_entry != NULL;
          if_entry = g_list_next(if_entry)) {
@@ -648,16 +862,26 @@ print_machine_readable_interfaces(GList *if_list)
 
 /*
  * If you change the machine-readable output format of this function,
- * you MUST update capture_sync.c:sync_linktype_list_open() accordingly!
+ * you MUST update capture_ifinfo.c:capture_get_if_capabilities() accordingly!
  */
 static void
-print_machine_readable_link_layer_types(GList *lt_list)
+print_machine_readable_if_capabilities(if_capabilities_t *caps)
 {
     GList *lt_entry;
     data_link_info_t *data_link_info;
     const gchar *desc_str;
 
-    for (lt_entry = lt_list; lt_entry != NULL; lt_entry = g_list_next(lt_entry)) {
+    if (capture_child) {
+        /* Let our parent know we succeeded. */
+        pipe_write_block(2, SP_SUCCESS, NULL);
+    }
+
+    if (caps->can_set_rfmon)
+        printf("1\n");
+    else
+        printf("0\n");
+    for (lt_entry = caps->data_link_types; lt_entry != NULL;
+         lt_entry = g_list_next(lt_entry)) {
       data_link_info = (data_link_info_t *)lt_entry->data;
       if (data_link_info->description != NULL)
         desc_str = data_link_info->description;
@@ -722,6 +946,11 @@ print_statistics_loop(gboolean machine_readable)
         return 2;
     }
 
+    if (capture_child) {
+        /* Let our parent know we succeeded. */
+        pipe_write_block(2, SP_SUCCESS, NULL);
+    }
+
     if (!machine_readable) {
         printf("%-15s  %10s  %10s\n", "Interface", "Received",
             "Dropped");
@@ -813,6 +1042,39 @@ capture_cleanup_handler(int signum _U_)
 }
 #endif
 
+
+#ifdef SIGINFO
+static void
+report_counts(void)
+{
+  /* Don't print this if we're a capture child. */
+  if (!capture_child) {
+    if (quiet) {
+      /* Report the count only if we aren't printing a packet count
+         as packets arrive. */
+      fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count,
+              plurality(global_ld.packet_count, "", "s"));
+    }
+  }
+  infoprint = FALSE; /* we just reported it */
+}
+
+static void
+report_counts_siginfo(int signum _U_)
+{
+  int sav_errno = errno;
+
+  /* If we've been told to delay printing, just set a flag asking
+     that we print counts (if we're supposed to), otherwise print
+     the count of packets captured (if we're supposed to). */
+  if (infodelay)
+    infoprint = TRUE;
+  else
+    report_counts();
+  errno = sav_errno;
+}
+#endif /* SIGINFO */
+
 static void exit_main(int status)
 {
 #ifdef _WIN32
@@ -920,19 +1182,19 @@ relinquish_all_capabilities(void)
 static const char *
 set_pcap_linktype(pcap_t *pch, char *devname
 #ifdef HAVE_PCAP_SET_DATALINK
-       _U_
+                  _U_
 #endif
-       , int dlt)
+                , int dlt)
 {
 #ifdef HAVE_PCAP_SET_DATALINK
-       if (pcap_set_datalink(pch, dlt) == 0)
-               return NULL;    /* no error */
-       return pcap_geterr(pch);
+  if (pcap_set_datalink(pch, dlt) == 0)
+    return NULL;       /* no error */
+  return pcap_geterr(pch);
 #else
-       /* Let them set it to the type it is; reject any other request. */
-       if (get_pcap_linktype(pch, devname) == dlt)
-               return NULL;    /* no error */
-       return "That DLT isn't one of the DLTs supported by this device";
+  /* Let them set it to the type it is; reject any other request. */
+  if (get_pcap_linktype(pch, devname) == dlt)
+    return NULL;       /* no error */
+  return "That DLT isn't one of the DLTs supported by this device";
 #endif
 }
 
@@ -1613,10 +1875,12 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
   gchar      *sync_msg_str;
   static const char ppamsg[] = "can't find PPA for ";
   const char *set_linktype_err_str;
-  const char  *libpcap_warn;
+  const char *libpcap_warn;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+  int         err;
+#endif
 #ifdef _WIN32
   gchar      *sync_secondary_msg_str;
-  int         err;
   WORD        wVersionRequested;
   WSADATA     wsaData;
 #endif
@@ -1682,6 +1946,10 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
      the error buffer, and check if it's still a null string.  */
   open_err_str[0] = '\0';
 #ifdef HAVE_PCAP_OPEN
+  /*
+   * If we're opening a remote device, use pcap_open(); that's currently
+   * the only open routine that supports remote devices.
+   */
   if (strncmp (capture_opts->iface, "rpcap://", 8) == 0) {
     auth.type = capture_opts->auth_type == CAPTURE_AUTH_PWD ?
       RPCAP_RMTAUTH_PWD : RPCAP_RMTAUTH_NULL;
@@ -1697,8 +1965,14 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
                  (capture_opts->nocap_rpcap ? PCAP_OPENFLAG_NOCAPTURE_RPCAP : 0),
                  CAP_READ_TIMEOUT, &auth, open_err_str);
   } else
-#elif defined(HAVE_PCAP_CREATE)
+#endif /* HAVE_PCAP_OPEN */
   {
+    /*
+     * If we're not opening a remote device, use pcap_create() and
+     * pcap_activate() if we have them, so that we can set the buffer
+     * size, otherwise use pcap_open_live().
+     */
+#ifdef HAVE_PCAP_CREATE
     ld->pcap_h = pcap_create(capture_opts->iface, open_err_str);
     if (ld->pcap_h != NULL) {
       pcap_set_snaplen(ld->pcap_h, capture_opts->has_snaplen ? capture_opts->snaplen : WTAP_MAX_PACKET_SIZE);
@@ -1710,26 +1984,31 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
       }
       if (capture_opts->monitor_mode)
         pcap_set_rfmon(ld->pcap_h, 1);
-      if (pcap_activate(ld->pcap_h) != 0) {
+      err = pcap_activate(ld->pcap_h);
+      if (err < 0) {
         /* Failed to activate, set to NULL */
+        if (err == PCAP_ERROR)
+          g_strlcpy(open_err_str, pcap_geterr(ld->pcap_h), sizeof open_err_str);
+        else
+          g_strlcpy(open_err_str, pcap_statustostr(err), sizeof open_err_str);
         pcap_close(ld->pcap_h);
         ld->pcap_h = NULL;
       }
     }
-  }
 #else
-  ld->pcap_h = pcap_open_live(capture_opts->iface,
-                              capture_opts->has_snaplen ? capture_opts->snaplen :
-                                                          WTAP_MAX_PACKET_SIZE,
-                              capture_opts->promisc_mode, CAP_READ_TIMEOUT,
-                              open_err_str);
-#endif
-
-/* If not using libcap: we now can now set euid/egid to ruid/rgid         */
-/*  to remove any suid privileges.                                        */
-/* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities  */
-/*  (euid/egid have already previously been set to ruid/rgid.             */
-/* (See comment in main() for details)                                    */
+    ld->pcap_h = pcap_open_live(capture_opts->iface,
+                                capture_opts->has_snaplen ? capture_opts->snaplen :
+                                                            WTAP_MAX_PACKET_SIZE,
+                                capture_opts->promisc_mode, CAP_READ_TIMEOUT,
+                                open_err_str);
+#endif
+  }
+
+  /* If not using libcap: we now can now set euid/egid to ruid/rgid         */
+  /*  to remove any suid privileges.                                        */
+  /* If using libcap: we can now remove NET_RAW and NET_ADMIN capabilities  */
+  /*  (euid/egid have already previously been set to ruid/rgid.             */
+  /* (See comment in main() for details)                                    */
 #ifndef HAVE_LIBCAP
   relinquish_special_privs_perm();
 #else
@@ -1976,7 +2255,7 @@ capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe, gchar * iface,
 
 /* set up to write to the already-opened capture output file/files */
 static gboolean
-capture_loop_init_output(capture_options *capture_opts, int save_file_fd, loop_data *ld, char *errmsg, int errmsg_len) {
+capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *errmsg, int errmsg_len) {
   int         err;
 
 
@@ -1994,7 +2273,7 @@ capture_loop_init_output(capture_options *capture_opts, int save_file_fd, loop_d
   if (capture_opts->multi_files_on) {
     ld->pdh = ringbuf_init_libpcap_fdopen(&err);
   } else {
-    ld->pdh = libpcap_fdopen(save_file_fd, &err);
+    ld->pdh = libpcap_fdopen(ld->save_file_fd, &err);
   }
   if (ld->pdh) {
     gboolean successful;
@@ -2343,6 +2622,67 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd,
 #define TIME_GET() time(NULL)
 #endif
 
+/* Do the work of handling either the file size or file duration capture
+   conditions being reached, and switching files or stopping. */
+static gboolean
+do_file_switch_or_stop(capture_options *capture_opts,
+                       condition *cnd_autostop_files,
+                       condition *cnd_autostop_size,
+                       condition *cnd_file_duration)
+{
+  if (capture_opts->multi_files_on) {
+    if (cnd_autostop_files != NULL &&
+        cnd_eval(cnd_autostop_files, ++global_ld.autostop_files)) {
+      /* no files left: stop here */
+      global_ld.go = FALSE;
+      return FALSE;
+    }
+
+    /* Switch to the next ringbuffer file */
+    if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file,
+                            &global_ld.save_file_fd, &global_ld.err)) {
+      gboolean successful;
+
+      /* File switch succeeded: reset the conditions */
+      global_ld.bytes_written = 0;
+      if (capture_opts->use_pcapng) {
+        char appname[100];
+
+        g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
+        successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) &&
+                     libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err);
+      } else {
+        successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen,
+                                               &global_ld.bytes_written, &global_ld.err);
+      }
+      if (!successful) {
+        fclose(global_ld.pdh);
+        global_ld.pdh = NULL;
+        global_ld.go = FALSE;
+        return FALSE;
+      }
+      if(cnd_autostop_size)
+        cnd_reset(cnd_autostop_size);
+      if(cnd_file_duration)
+        cnd_reset(cnd_file_duration);
+      libpcap_dump_flush(global_ld.pdh, NULL);
+      if (!quiet)
+        report_packet_count(global_ld.inpkts_to_sync_pipe);
+      global_ld.inpkts_to_sync_pipe = 0;
+      report_new_capture_file(capture_opts->save_file);
+    } else {
+      /* File switch failed: stop here */
+      global_ld.go = FALSE;
+      return FALSE;
+    }
+  } else {
+    /* single file, stop now */
+    global_ld.go = FALSE;
+    return FALSE;
+  }
+  return TRUE;
+}
+
 /* Do the low-level work of a capture.
    Returns TRUE if it succeeds, FALSE otherwise. */
 static gboolean
@@ -2352,43 +2692,46 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
   time_t      start_time;
   int         err_close;
   int         inpkts;
-  gint        inpkts_to_sync_pipe = 0;     /* packets not already send out to the sync_pipe */
   condition  *cnd_file_duration = NULL;
   condition  *cnd_autostop_files = NULL;
   condition  *cnd_autostop_size = NULL;
   condition  *cnd_autostop_duration = NULL;
-  guint32     autostop_files = 0;
   gboolean    write_ok;
   gboolean    close_ok;
   gboolean    cfilter_error = FALSE;
 #define MSG_MAX_LENGTH 4096
   char        errmsg[MSG_MAX_LENGTH+1];
   char        secondary_errmsg[MSG_MAX_LENGTH+1];
-  int         save_file_fd = -1;
 
   *errmsg           = '\0';
   *secondary_errmsg = '\0';
 
   /* init the loop data */
-  global_ld.go                 = TRUE;
-  global_ld.packet_count       = 0;
+  global_ld.go                  = TRUE;
+  global_ld.packet_count        = 0;
+#ifdef SIGINFO
+  global_ld.report_packet_count = FALSE;
+#endif
   if (capture_opts->has_autostop_packets)
-    global_ld.packet_max       = capture_opts->autostop_packets;
+    global_ld.packet_max        = capture_opts->autostop_packets;
   else
-    global_ld.packet_max       = 0;    /* no limit */
-  global_ld.err                = 0;    /* no error seen yet */
-  global_ld.wtap_linktype      = WTAP_ENCAP_UNKNOWN;
-  global_ld.pcap_err           = FALSE;
-  global_ld.from_cap_pipe      = FALSE;
-  global_ld.pdh                = NULL;
+    global_ld.packet_max        = 0;   /* no limit */
+  global_ld.inpkts_to_sync_pipe = 0;
+  global_ld.err                 = 0;   /* no error seen yet */
+  global_ld.wtap_linktype       = WTAP_ENCAP_UNKNOWN;
+  global_ld.pcap_err            = FALSE;
+  global_ld.from_cap_pipe       = FALSE;
+  global_ld.pdh                 = NULL;
 #ifndef _WIN32
-  global_ld.cap_pipe_fd        = -1;
+  global_ld.cap_pipe_fd         = -1;
 #else
-  global_ld.cap_pipe_h         = INVALID_HANDLE_VALUE;
+  global_ld.cap_pipe_h          = INVALID_HANDLE_VALUE;
 #endif
 #ifdef MUST_DO_SELECT
-  global_ld.pcap_fd            = 0;
+  global_ld.pcap_fd             = 0;
 #endif
+  global_ld.autostop_files      = 0;
+  global_ld.save_file_fd        = -1;
 
   /* We haven't yet gotten the capture statistics. */
   *stats_known      = FALSE;
@@ -2425,13 +2768,14 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
   /* If we're supposed to write to a capture file, open it for output
      (temporary/specified name/ringbuffer) */
   if (capture_opts->saving_to_file) {
-    if (!capture_loop_open_output(capture_opts, &save_file_fd, errmsg, sizeof(errmsg))) {
+    if (!capture_loop_open_output(capture_opts, &global_ld.save_file_fd,
+                                  errmsg, sizeof(errmsg))) {
       goto error;
     }
 
     /* set up to write to the already-opened capture output file/files */
-    if (!capture_loop_init_output(capture_opts, save_file_fd, &global_ld,
-                                  errmsg, sizeof(errmsg))) {
+    if (!capture_loop_init_output(capture_opts, &global_ld, errmsg,
+                                  sizeof(errmsg))) {
       goto error;
     }
 
@@ -2485,6 +2829,15 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
     inpkts = capture_loop_dispatch(capture_opts, &global_ld, errmsg,
                                    sizeof(errmsg));
 
+#ifdef SIGINFO
+    /* Were we asked to print packet counts by the SIGINFO handler? */
+    if (global_ld.report_packet_count) {
+        fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count,
+                plurality(global_ld.packet_count, "", "s"));
+        global_ld.report_packet_count = FALSE;
+    }
+#endif
+
 #ifdef _WIN32
     /* any news from our parent (signal pipe)? -> just stop the capture */
     if (!signal_pipe_check_running()) {
@@ -2493,61 +2846,15 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 #endif
 
     if (inpkts > 0) {
-      inpkts_to_sync_pipe += inpkts;
+      global_ld.inpkts_to_sync_pipe += inpkts;
 
       /* check capture size condition */
       if (cnd_autostop_size != NULL &&
-          cnd_eval(cnd_autostop_size, (guint32)global_ld.bytes_written)){
+          cnd_eval(cnd_autostop_size, (guint32)global_ld.bytes_written)) {
         /* Capture size limit reached, do we have another file? */
-        if (capture_opts->multi_files_on) {
-          if (cnd_autostop_files != NULL &&
-              cnd_eval(cnd_autostop_files, ++autostop_files)) {
-             /* no files left: stop here */
-            global_ld.go = FALSE;
-            continue;
-          }
-
-          /* Switch to the next ringbuffer file */
-          if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file,
-                                  &save_file_fd, &global_ld.err)) {
-            gboolean successful;
-
-            /* File switch succeeded: reset the conditions */
-            global_ld.bytes_written = 0;
-            if (capture_opts->use_pcapng) {
-              char appname[100];
-
-              g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
-              successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) &&
-                           libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err);
-            } else {
-              successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen,
-                                                     &global_ld.bytes_written, &global_ld.err);
-            }
-            if (!successful) {
-              fclose(global_ld.pdh);
-              global_ld.pdh = NULL;
-              global_ld.go = FALSE;
-              continue;
-            }
-            cnd_reset(cnd_autostop_size);
-            if (cnd_file_duration) {
-              cnd_reset(cnd_file_duration);
-            }
-            libpcap_dump_flush(global_ld.pdh, NULL);
-            report_packet_count(inpkts_to_sync_pipe);
-            inpkts_to_sync_pipe = 0;
-            report_new_capture_file(capture_opts->save_file);
-          } else {
-            /* File switch failed: stop here */
-            global_ld.go = FALSE;
-            continue;
-          }
-        } else {
-          /* single file, stop now */
-          global_ld.go = FALSE;
+        if (!do_file_switch_or_stop(capture_opts, cnd_autostop_files,
+                                    cnd_autostop_size, cnd_file_duration))
           continue;
-        }
       } /* cnd_autostop_size */
       if (capture_opts->output_to_pipe) {
         libpcap_dump_flush(global_ld.pdh, NULL);
@@ -2570,15 +2877,16 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
       }*/
 
       /* Let the parent process know. */
-      if (inpkts_to_sync_pipe) {
+      if (global_ld.inpkts_to_sync_pipe) {
         /* do sync here */
         libpcap_dump_flush(global_ld.pdh, NULL);
 
-        /* Send our parent a message saying we've written out "inpkts_to_sync_pipe"
-           packets to the capture file. */
-        report_packet_count(inpkts_to_sync_pipe);
+        /* Send our parent a message saying we've written out
+           "global_ld.inpkts_to_sync_pipe" packets to the capture file. */
+        if (!quiet)
+          report_packet_count(global_ld.inpkts_to_sync_pipe);
 
-        inpkts_to_sync_pipe = 0;
+        global_ld.inpkts_to_sync_pipe = 0;
       }
 
       /* check capture duration condition */
@@ -2591,54 +2899,9 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
       /* check capture file duration condition */
       if (cnd_file_duration != NULL && cnd_eval(cnd_file_duration)) {
         /* duration limit reached, do we have another file? */
-        if (capture_opts->multi_files_on) {
-          if (cnd_autostop_files != NULL &&
-              cnd_eval(cnd_autostop_files, ++autostop_files)) {
-            /* no files left: stop here */
-            global_ld.go = FALSE;
-            continue;
-          }
-
-          /* Switch to the next ringbuffer file */
-          if (ringbuf_switch_file(&global_ld.pdh, &capture_opts->save_file,
-                                  &save_file_fd, &global_ld.err)) {
-            gboolean successful;
-
-            /* file switch succeeded: reset the conditions */
-            global_ld.bytes_written = 0;
-            if (capture_opts->use_pcapng) {
-              char appname[100];
-
-              g_snprintf(appname, sizeof(appname), "Dumpcap " VERSION "%s", wireshark_svnversion);
-              successful = libpcap_write_session_header_block(global_ld.pdh, appname, &global_ld.bytes_written, &global_ld.err) &&
-                           libpcap_write_interface_description_block(global_ld.pdh, capture_opts->iface, capture_opts->cfilter, global_ld.linktype, global_ld.file_snaplen, &global_ld.bytes_written, &global_ld.err);
-            } else {
-              successful = libpcap_write_file_header(global_ld.pdh, global_ld.linktype, global_ld.file_snaplen,
-                                                     &global_ld.bytes_written, &global_ld.err);
-            }
-            if (!successful) {
-              fclose(global_ld.pdh);
-              global_ld.pdh = NULL;
-              global_ld.go = FALSE;
-              continue;
-            }
-            cnd_reset(cnd_file_duration);
-            if(cnd_autostop_size)
-              cnd_reset(cnd_autostop_size);
-            libpcap_dump_flush(global_ld.pdh, NULL);
-            report_packet_count(inpkts_to_sync_pipe);
-            inpkts_to_sync_pipe = 0;
-            report_new_capture_file(capture_opts->save_file);
-          } else {
-            /* File switch failed: stop here */
-            global_ld.go = FALSE;
-            continue;
-          }
-        } else {
-          /* single file, stop now */
-          global_ld.go = FALSE;
+        if (!do_file_switch_or_stop(capture_opts, cnd_autostop_files,
+                                    cnd_autostop_size, cnd_file_duration))
           continue;
-        }
       } /* cnd_file_duration */
     }
 
@@ -2706,9 +2969,10 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 
   /* there might be packets not yet notified to the parent */
   /* (do this after closing the file, so all packets are already flushed) */
-  if(inpkts_to_sync_pipe) {
-    report_packet_count(inpkts_to_sync_pipe);
-    inpkts_to_sync_pipe = 0;
+  if(global_ld.inpkts_to_sync_pipe) {
+    if (!quiet)
+      report_packet_count(global_ld.inpkts_to_sync_pipe);
+    global_ld.inpkts_to_sync_pipe = 0;
   }
 
   /* If we've displayed a message about a write error, there's no point
@@ -2760,8 +3024,8 @@ error:
   } else {
     /* We can't use the save file, and we have no FILE * for the stream
        to close in order to close it, so close the FD directly. */
-    if(save_file_fd != -1) {
-      ws_close(save_file_fd);
+    if(global_ld.save_file_fd != -1) {
+      ws_close(global_ld.save_file_fd);
     }
 
     /* We couldn't even start the capture, so get rid of the capture
@@ -2949,7 +3213,7 @@ main(int argc, char *argv[])
 #define OPTSTRING_I ""
 #endif
 
-#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:Df:hi:" OPTSTRING_I "L" OPTSTRING_m "Mnp" OPTSTRING_r "Ss:" OPTSTRING_u "vw:y:Z:"
+#define OPTSTRING "a:" OPTSTRING_A "b:" OPTSTRING_B "c:Df:hi:" OPTSTRING_I "L" OPTSTRING_m "Mnpq" OPTSTRING_r "Ss:" OPTSTRING_u "vw:y:Z:"
 
 #ifdef DEBUG_CHILD_DUMPCAP
   if ((debug_log = ws_fopen("dumpcap_debug_log.tmp","w")) == NULL) {
@@ -2985,24 +3249,36 @@ main(int argc, char *argv[])
   }
 #endif
 
-  /* Determine if dumpcap is being requested to run in a special       */
-  /* capture_child mode by going thru the command line args to see if  */
-  /* a -Z is present. (-Z is a hidden option).                         */
-  /* The primary result of running in capture_child mode is that       */
-  /* all messages sent out on stderr are in a special type/len/string  */
-  /* format to allow message processing by type.                       */
-  /* These messages include various 'status' messages which are sent   */
-  /* when an actual capture is in progress. Capture_child mode         */
-  /* would normally be requested by a parent process which invokes     */
-  /* dumpcap and obtains dumpcap stderr output via a pipe to which     */
-  /* dumpcap stderr has been redirected.                               */
-  /* Capture_child mode needs to be determined immediately upon        */
-  /* startup so that any messages generated by dumpcap in this mode    */
-  /* (eg: during initialization) will be formatted properly.           */
+  /*
+   * Determine if dumpcap is being requested to run in a special
+   * capture_child mode by going thru the command line args to see if
+   * a -Z is present. (-Z is a hidden option).
+   *
+   * The primary result of running in capture_child mode is that
+   * all messages sent out on stderr are in a special type/len/string
+   * format to allow message processing by type.  These messages include
+   * error messages if dumpcap fails to start the operation it was
+   * requested to do, as well as various "status" messages which are sent
+   * when an actual capture is in progress, and a "success" message sent
+   * if dumpcap was requested to perform an operation other than a
+   * capture.
+   *
+   * Capture_child mode would normally be requested by a parent process
+   * which invokes dumpcap and obtains dumpcap stderr output via a pipe
+   * to which dumpcap stderr has been redirected.  It might also have
+   * another pipe to obtain dumpcap stdout output; for operations other
+   * than a capture, that information is formatted specially for easier
+   * parsing by the parent process.
+   *
+   * Capture_child mode needs to be determined immediately upon
+   * startup so that any messages generated by dumpcap in this mode
+   * (eg: during initialization) will be formatted properly.
+   */
 
   for (i=1; i<argc; i++) {
     if (strcmp("-Z", argv[i]) == 0) {
       capture_child = TRUE;
+      machine_readable = TRUE;  /* request machine-readable output */
 #ifdef _WIN32
       /* set output pipe to binary mode, to avoid ugly text conversions */
       _setmode(2, O_BINARY);
@@ -3077,6 +3353,15 @@ main(int argc, char *argv[])
   sigaction(SIGHUP, NULL, &oldaction);
   if (oldaction.sa_handler == SIG_DFL)
     sigaction(SIGHUP, &action, NULL);
+
+#ifdef SIGINFO
+  /* Catch SIGINFO and, if we get it and we're capturing in
+     quiet mode, report the number of packets we've captured. */
+  action.sa_handler = report_counts_siginfo;
+  action.sa_flags = SA_RESTART;
+  sigemptyset(&action.sa_mask);
+  sigaction(SIGINFO, &action, NULL);
+#endif /* SIGINFO */
 #endif  /* _WIN32 */
 
   /* ----------------------------------------------------------------- */
@@ -3252,6 +3537,10 @@ main(int argc, char *argv[])
 #endif
         break;
 
+      case 'q':        /* Quiet */
+        quiet = TRUE;
+        break;
+
       /*** all non capture option specific ***/
       case 'D':        /* Print a list of capture devices and exit */
         list_interfaces = TRUE;
@@ -3265,7 +3554,7 @@ main(int argc, char *argv[])
         print_statistics = TRUE;
         run_once_args++;
         break;
-      case 'M':        /* For -D and -L, print machine-readable output */
+      case 'M':        /* For -D, -L, and -S, print machine-readable output */
         machine_readable = TRUE;
         break;
       default:
@@ -3301,17 +3590,16 @@ main(int argc, char *argv[])
   if (run_once_args > 1) {
     cmdarg_err("Only one of -D, -L, or -S may be supplied.");
     exit_main(1);
-  } else if (list_link_layer_types) {
-    /* We're supposed to list the link-layer types for an interface;
-       did the user also specify a capture file to be read? */
-    /* No - did they specify a ring buffer option? */
+  } else if (run_once_args == 1) {
+    /* We're supposed to print some information, rather than
+       to capture traffic; did they specify a ring buffer option? */
     if (global_capture_opts.multi_files_on) {
       cmdarg_err("Ring buffer requested, but a capture isn't being done.");
       exit_main(1);
     }
   } else {
-    /* No - was the ring buffer option specified and, if so, does it make
-       sense? */
+    /* We're supposed to capture traffic; was the ring buffer option
+       specified and, if so, does it make sense? */
     if (global_capture_opts.multi_files_on) {
       /* Ring buffer works only under certain conditions:
         a) ring buffer does not work with temporary files;
@@ -3329,15 +3617,10 @@ main(int argc, char *argv[])
     }
   }
 
-  if (capture_opts_trim_iface(&global_capture_opts, NULL) == FALSE) {
-    /* cmdarg_err() already called .... */
-    exit_main(1);
-  }
-
-  /* Let the user know what interface was chosen. */
-  /* get_interface_descriptive_name() is not available! */
-  g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", global_capture_opts.iface);
-
+  /*
+   * "-D" requires no interface to be selected; it's supposed to list
+   * all interfaces.
+   */
   if (list_interfaces) {
     /* Get the list of interfaces */
     GList       *if_list;
@@ -3350,13 +3633,22 @@ main(int argc, char *argv[])
         case CANT_GET_INTERFACE_LIST:
             cmdarg_err("%s", err_str);
             g_free(err_str);
+            exit_main(2);
             break;
 
         case NO_INTERFACES_FOUND:
-            cmdarg_err("There are no interfaces on which a capture can be done");
+            /*
+            * If we're being run by another program, just give them
+            * an empty list of interfaces, don't report this as
+            * an error; that lets them decide whether to report
+            * this as an error or not.
+            */
+            if (!machine_readable) {
+                cmdarg_err("There are no interfaces on which a capture can be done");
+                exit_main(2);
+            }
             break;
         }
-        exit_main(2);
     }
 
     if (machine_readable)      /* tab-separated values to stdout */
@@ -3365,33 +3657,58 @@ main(int argc, char *argv[])
       capture_opts_print_interfaces(if_list);
     free_interface_list(if_list);
     exit_main(0);
-  } else if (list_link_layer_types) {
+  }
+
+  /*
+   * "-S" requires no interface to be selected; it gives statistics
+   * for all interfaces.
+   */
+  if (print_statistics) {
+    status = print_statistics_loop(machine_readable);
+    exit_main(status);
+  }
+
+  /*
+   * "-L", and capturing, act on a particular interface, so we have to
+   * have an interface; if none was specified, pick a default.
+   */
+  if (capture_opts_trim_iface(&global_capture_opts, NULL) == FALSE) {
+    /* cmdarg_err() already called .... */
+    exit_main(1);
+  }
+
+  /* Let the user know what interface was chosen. */
+  /* get_interface_descriptive_name() is not available! */
+  g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Interface: %s\n", global_capture_opts.iface);
+
+  if (list_link_layer_types) {
     /* Get the list of link-layer types for the capture device. */
-    GList *lt_list;
+    if_capabilities_t *caps;
     gchar *err_str;
 
-    lt_list = get_pcap_linktype_list(global_capture_opts.iface, &err_str);
-    if (lt_list == NULL) {
-      if (err_str != NULL) {
-        cmdarg_err("The list of data link types for the capture device \"%s\" could not be obtained (%s)."
-         "Please check to make sure you have sufficient permissions, and that\n"
-         "you have the proper interface or pipe specified.\n", global_capture_opts.iface, err_str);
-        g_free(err_str);
-      } else
-        cmdarg_err("The capture device \"%s\" has no data link types.", global_capture_opts.iface);
+    caps = get_if_capabilities(global_capture_opts.iface,
+                               global_capture_opts.monitor_mode, &err_str);
+    if (caps == NULL) {
+      cmdarg_err("The capabilities of the capture device \"%s\" could not be obtained (%s).\n"
+       "Please check to make sure you have sufficient permissions, and that\n"
+       "you have the proper interface or pipe specified.", global_capture_opts.iface, err_str);
+      g_free(err_str);
+      exit_main(2);
+    }
+    if (caps->data_link_types == NULL) {
+      cmdarg_err("The capture device \"%s\" has no data link types.", global_capture_opts.iface);
       exit_main(2);
     }
     if (machine_readable)      /* tab-separated values to stdout */
-      print_machine_readable_link_layer_types(lt_list);
+      print_machine_readable_if_capabilities(caps);
     else
-      capture_opts_print_link_layer_types(lt_list);
-    free_pcap_linktype_list(lt_list);
+      capture_opts_print_if_capabilities(caps,
+                                         global_capture_opts.monitor_mode);
+    free_if_capabilities(caps);
     exit_main(0);
-  } else if (print_statistics) {
-    status = print_statistics_loop(machine_readable);
-    exit_main(status);
   }
 
+  /* We're supposed to do a capture.  Process the remaining arguments. */
   capture_opts_trim_snaplen(&global_capture_opts, MIN_PACKET_SIZE);
   capture_opts_trim_ring_num_files(&global_capture_opts);
 
@@ -3496,7 +3813,7 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level,
 /* indication report routines */
 
 
-void
+static void
 report_packet_count(int packet_count)
 {
     char tmp[SP_DECISIZE+1+1];
@@ -3521,9 +3838,30 @@ report_new_capture_file(const char *filename)
         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "File: %s", filename);
         pipe_write_block(2, SP_FILE, filename);
     } else {
+#ifdef SIGINFO
+        /*
+         * Prevent a SIGINFO handler from writing to the standard error
+         * while we're doing so; instead, have it just set a flag telling
+         * us to print that information when we're done.
+         */
+        infodelay = TRUE;
+#endif /* SIGINFO */
         fprintf(stderr, "File: %s\n", filename);
         /* stderr could be line buffered */
         fflush(stderr);
+
+#ifdef SIGINFO
+        /*
+         * Allow SIGINFO handlers to write.
+         */
+        infodelay = FALSE;
+
+        /*
+         * If a SIGINFO handler asked us to write out capture counts, do so.
+         */
+        if (infoprint)
+          report_counts();
+#endif /* SIGINFO */
     }
 }