Patch from Ben Fowler to rename the global variable "cf" to "cfile", to
[obnox/wireshark/wip.git] / capture.c
index 92293844b289d3d5a3b899d31a5dca335620e77e..ed4f33cca3bd8e47c6a5432bf8fa52ffd4949521 100644 (file)
--- a/capture.c
+++ b/capture.c
@@ -1,7 +1,7 @@
 /* capture.c
  * Routines for packet capture windows
  *
- * $Id: capture.c,v 1.57 1999/08/19 05:42:22 guy Exp $
+ * $Id: capture.c,v 1.109 2000/06/27 04:35:42 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
 # include <sys/stat.h>
 #endif
 
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
 #include <gtk/gtk.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <ctype.h>
 #include <string.h>
+#include <fcntl.h>
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 # include "snprintf.h"
 #endif
 
-#ifdef HAVE_SYS_SOCKIO_H
-# include <sys/sockio.h>
+#ifndef lib_pcap_h
+#include <pcap.h>
+#endif
+
+#ifdef _WIN32
+#include <process.h>    /* For spawning child process */
 #endif
 
-#include "ethereal.h"
+#include "gtk/main.h"
+#include "gtk/gtkglobals.h"
 #include "packet.h"
 #include "file.h"
-#include "menu.h"
 #include "capture.h"
-#include "etypes.h"
 #include "util.h"
+#include "simple_dialog.h"
 #include "prefs.h"
+#include "globals.h"
+
+#include "packet-clip.h"
+#include "packet-eth.h"
+#include "packet-fddi.h"
+#include "packet-null.h"
+#include "packet-ppp.h"
+#include "packet-raw.h"
+#include "packet-tr.h"
+
+int sync_mode; /* fork a child to do the capture, and sync between them */
+static int sync_pipe[2]; /* used to sync father */
+enum PIPES { READ, WRITE }; /* Constants 0 and 1 for READ and WRITE */
+int quit_after_cap; /* Makes a "capture only mode". Implies -k */
+gboolean capture_child;        /* if this is the child for "-S" */
+static guint cap_input_id;
+
+#ifdef _WIN32
+static guint cap_timer_id;
+static int cap_timer_cb(gpointer); /* Win32 kludge to check for pipe input */
+#endif
 
-extern capture_file  cf;
-extern GtkWidget    *info_bar;
-extern guint         file_ctx;
-
-extern gchar *ethereal_path;
-extern gchar *medium_font;
-extern gchar *bold_font;
-extern int fork_mode;
-extern int sync_pipe[];
-extern int sync_mode;
-extern int sigusr2_received;
-extern int quit_after_cap;
-
-/* Capture callback data keys */
-#define E_CAP_IFACE_KEY "cap_iface"
-#define E_CAP_FILT_KEY  "cap_filter"
-#define E_CAP_COUNT_KEY "cap_count"
-#define E_CAP_OPEN_KEY  "cap_open"
-#define E_CAP_SNAP_KEY  "cap_snap"
-
-/* Capture filter key */
-#define E_CAP_FILT_TE_KEY "cap_filt_te"
-
-static void capture_prep_ok_cb(GtkWidget *, gpointer);
-static void capture_prep_close_cb(GtkWidget *, gpointer);
-static float pct(gint, gint);
+static void cap_file_input_cb(gpointer, gint, GdkInputCondition);
+static void capture_delete_cb(GtkWidget *, GdkEvent *, gpointer);
 static void capture_stop_cb(GtkWidget *, gpointer);
 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
   const u_char *);
+static void send_errmsg_to_parent(const char *);
+static float pct(gint, gint);
 
-static GList *
-get_interface_list() {
-  GList  *il = NULL;
-  struct  ifreq *ifr, *last;
-  struct  ifconf ifc;
-  int     sock = socket(AF_INET, SOCK_DGRAM, 0);
-
-  if (sock < 0)
-  {
-    simple_dialog(ESD_TYPE_WARN, NULL,
-      "Can't list interfaces: error opening socket.");
-    return NULL;
-  }
-
-  /* Since we have to grab the interface list all at once, we'll make
-     plenty of room */
-  ifc.ifc_len = 1024 * sizeof(struct ifreq);
-  ifc.ifc_buf = malloc(ifc.ifc_len);
-
-  if (ioctl(sock, SIOCGIFCONF, &ifc) < 0 ||
-    ifc.ifc_len < sizeof(struct ifreq))
-  {
-    simple_dialog(ESD_TYPE_WARN, NULL,
-      "Can't list interfaces: ioctl error.");
-    return NULL;
-  }
+typedef struct _loop_data {
+  gint           go;
+  gint           max;
+  gint           linktype;
+  gint           sync_packets;
+  packet_counts  counts;
+  wtap_dumper   *pdh;
+} loop_data;
 
-  ifr  = (struct ifreq *) ifc.ifc_req;
-  last = (struct ifreq *) ((char *) ifr + ifc.ifc_len);
-  while (ifr < last)
-  {
-    /*
-     * What we want:
-     * - Interfaces that are up, and not loopback
-     * - IP interfaces (do we really need this?)
-     * - Anything that doesn't begin with "lo" (loopback again) or "dummy"
-     * - Anything that doesn't include a ":" (Solaris virtuals)
-     */
-    if (! (ifr->ifr_flags & (IFF_UP | IFF_LOOPBACK)) &&
-        (ifr->ifr_addr.sa_family == AF_INET) &&
-        strncmp(ifr->ifr_name, "lo", 2) &&
-        strncmp(ifr->ifr_name, "dummy", 5) &&
-        ! strchr(ifr->ifr_name, ':')) {
-      il = g_list_append(il, g_strdup(ifr->ifr_name));
-    }
-#ifdef HAVE_SA_LEN
-    ifr = (struct ifreq *) ((char *) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ);
-#else
-    ifr = (struct ifreq *) ((char *) ifr + sizeof(struct ifreq));
+/* Win32 needs the O_BINARY flag for open() */
+#ifndef O_BINARY
+#define O_BINARY       0
 #endif
-  }
 
-  free(ifc.ifc_buf);
-  return il;
-}
+#ifdef _WIN32
+/* Win32 needs a handle to the child capture process */
+int child_process;
+#endif
 
+/* Open a specified file, or create a temporary file, and start a capture
+   to the file in question. */
 void
-capture_prep_cb(GtkWidget *w, gpointer d) {
-  GtkWidget     *cap_open_w, *if_cb, *if_lb,
-                *count_lb, *count_cb, *main_vb, *if_hb, *count_hb,
-                *filter_hb, *filter_bt, *filter_te, *caplen_hb,
-                *bbox, *ok_bt, *cancel_bt, *snap_lb,
-                *snap_sb;
-  GtkAdjustment *adj;
-  GList         *if_list, *count_list = NULL;
-  gchar         *count_item1 = "0 (Infinite)", count_item2[16];
-
-  cap_open_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-  gtk_window_set_title(GTK_WINDOW(cap_open_w), "Ethereal: Capture Preferences");
-  
-  /* Container for each row of widgets */
-  main_vb = gtk_vbox_new(FALSE, 3);
-  gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
-  gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
-  gtk_widget_show(main_vb);
-  
-  /* Interface row */
-  if_hb = gtk_hbox_new(FALSE, 3);
-  gtk_container_add(GTK_CONTAINER(main_vb), if_hb);
-  gtk_widget_show(if_hb);
-  
-  if_lb = gtk_label_new("Interface:");
-  gtk_box_pack_start(GTK_BOX(if_hb), if_lb, FALSE, FALSE, 0);
-  gtk_widget_show(if_lb);
-  
-  if_list = get_interface_list();
-  
-  if_cb = gtk_combo_new();
-  gtk_combo_set_popdown_strings(GTK_COMBO(if_cb), if_list);
-  if (cf.iface)
-    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), cf.iface);
-  else if (if_list)
-    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry), if_list->data);
-  gtk_box_pack_start(GTK_BOX(if_hb), if_cb, FALSE, FALSE, 0);
-  gtk_widget_show(if_cb);
-  
-  while (if_list) {
-    g_free(if_list->data);
-    if_list = g_list_remove_link(if_list, if_list);
-  }
-
-  /* Count row */
-  count_hb = gtk_hbox_new(FALSE, 3);
-  gtk_container_add(GTK_CONTAINER(main_vb), count_hb);
-  gtk_widget_show(count_hb);
-  
-  count_lb = gtk_label_new("Count:");
-  gtk_box_pack_start(GTK_BOX(count_hb), count_lb, FALSE, FALSE, 0);
-  gtk_widget_show(count_lb);
-  
-  count_list = g_list_append(count_list, count_item1);
-  if (cf.count) {
-    snprintf(count_item2, 15, "%d", cf.count);
-    count_list = g_list_append(count_list, count_item2);
-  }
-
-  count_cb = gtk_combo_new();
-  gtk_combo_set_popdown_strings(GTK_COMBO(count_cb), count_list);
-  gtk_box_pack_start(GTK_BOX(count_hb), count_cb, FALSE, FALSE, 0);
-  gtk_widget_show(count_cb);
-
-  while (count_list)
-    count_list = g_list_remove_link(count_list, count_list);
-
-  /* Filter row */
-  filter_hb = gtk_hbox_new(FALSE, 3);
-  gtk_container_add(GTK_CONTAINER(main_vb), filter_hb);
-  gtk_widget_show(filter_hb);
-  
-  filter_bt = gtk_button_new_with_label("Filter:");
-  gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
-    GTK_SIGNAL_FUNC(prefs_cb), (gpointer) E_PR_PG_FILTER);
-  gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, TRUE, 0);
-  gtk_widget_show(filter_bt);
-  
-  filter_te = gtk_entry_new();
-  if (cf.cfilter) gtk_entry_set_text(GTK_ENTRY(filter_te), cf.cfilter);
-  gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
-  gtk_box_pack_start(GTK_BOX(filter_hb), filter_te, TRUE, TRUE, 0);
-  gtk_widget_show(filter_te);
-
-  /* Misc row: Capture file checkbox and snap spinbutton */
-  caplen_hb = gtk_hbox_new(FALSE, 3);
-  gtk_container_add(GTK_CONTAINER(main_vb), caplen_hb);
-  gtk_widget_show(caplen_hb);
-
-  snap_lb = gtk_label_new("Capture length");
-  gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5);
-  gtk_box_pack_start(GTK_BOX(caplen_hb), snap_lb, FALSE, FALSE, 6);
-  gtk_widget_show(snap_lb);
-
-  adj = (GtkAdjustment *) gtk_adjustment_new((float) cf.snap,
-    MIN_PACKET_SIZE, MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
-  snap_sb = gtk_spin_button_new (adj, 0, 0);
-  gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
-  gtk_widget_set_usize (snap_sb, 80, 0);
-  gtk_box_pack_start (GTK_BOX(caplen_hb), snap_sb, FALSE, FALSE, 3); 
-  gtk_widget_show(snap_sb);
-  
-  /* Button row: OK and cancel buttons */
-  bbox = gtk_hbutton_box_new();
-  gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
-  gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
-  gtk_container_add(GTK_CONTAINER(main_vb), bbox);
-  gtk_widget_show(bbox);
-
-  ok_bt = gtk_button_new_with_label ("OK");
-  gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
-    GTK_SIGNAL_FUNC(capture_prep_ok_cb), GTK_OBJECT(cap_open_w));
-  GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
-  gtk_box_pack_start (GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
-  gtk_widget_grab_default(ok_bt);
-  gtk_widget_show(ok_bt);
-
-  cancel_bt = gtk_button_new_with_label ("Cancel");
-  gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
-    GTK_SIGNAL_FUNC(capture_prep_close_cb), GTK_OBJECT(cap_open_w));
-  GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
-  gtk_box_pack_start (GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
-  gtk_widget_show(cancel_bt);
-
-  /* Attach pointers to needed widgets to the capture prefs window/object */
-  gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_IFACE_KEY, if_cb);
-  gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_FILT_KEY,  filter_te);
-  gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_COUNT_KEY, count_cb);
-  gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY,  snap_sb);
-
-  gtk_widget_show(cap_open_w);
-}
-
-static void
-capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
-  GtkWidget *if_cb, *filter_te, *count_cb, *snap_sb;
-  gchar *filter_text;
+do_capture(char *capfile_name)
+{
   char tmpname[128+1];
+  gboolean is_tempfile;
+  u_char c;
+  int i;
+  guint byte_count;
+  char *msg;
+  int err;
+  int capture_succeeded;
 
-  if_cb     = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
-  filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
-  count_cb  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
-  snap_sb   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_SNAP_KEY);
-
-  if (cf.iface) g_free(cf.iface);
-  cf.iface =
-    g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(if_cb)->entry)));
-
-  filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
-  if (cf.cfilter) g_free(cf.cfilter);
-  cf.cfilter = NULL; /* ead 06/16/99 */
-  if (filter_text && filter_text[0]) {
-         cf.cfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te))); 
+  if (capfile_name != NULL) {
+    /* Try to open/create the specified file for use as a capture buffer. */
+    cfile.save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600);
+    is_tempfile = FALSE;
+  } else {
+    /* Choose a random name for the capture buffer */
+    cfile.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
+    capfile_name = g_strdup(tmpname);
+    is_tempfile = TRUE;
   }
-  cf.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
-  cf.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
-  if (cf.snap < 1)
-    cf.snap = MAX_PACKET_SIZE;
-  else if (cf.snap < MIN_PACKET_SIZE)
-    cf.snap = MIN_PACKET_SIZE;
-
-  gtk_widget_destroy(GTK_WIDGET(parent_w));
-
-  /* Choose a random name for the capture buffer */
-  if (cf.save_file && !cf.user_saved) {
-       unlink(cf.save_file); /* silently ignore error */
-       g_free(cf.save_file);
+  if (cfile.save_file_fd == -1) {
+    simple_dialog(ESD_TYPE_WARN, NULL,
+       "The file to which the capture would be saved (\"%s\")"
+       "could not be opened: %s.", capfile_name, strerror(errno));
+    return;
   }
-  cf.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
-  cf.save_file = g_strdup(tmpname);
-  cf.user_saved = 0;
-  
-  if( fork_mode ){     /*  use fork() for capture */
+  close_cap_file(&cfile, info_bar);
+  g_assert(cfile.save_file == NULL);
+  cfile.save_file = capfile_name;
+
+  if (sync_mode) {     /* do the capture in a child process */
     int  fork_child;
     char ssnap[24];
     char scount[24];   /* need a constant for len of numbers */
     char save_file_fd[24];
-    int err;
+    char errmsg[1024+1];
+    int error;
+#ifdef _WIN32
+    char sync_pipe_fd[24];
+    char *filterstring;
+#endif
 
-    sprintf(ssnap,"%d",cf.snap); /* in lieu of itoa */
-    sprintf(scount,"%d",cf.count);
-    sprintf(save_file_fd,"%d",cf.save_file_fd);
+    sprintf(ssnap,"%d",cfile.snap); /* in lieu of itoa */
+    sprintf(scount,"%d",cfile.count);
+    sprintf(save_file_fd,"%d",cfile.save_file_fd);
+
+#ifdef _WIN32
+    /* Create a pipe for the child process */
+
+    if(_pipe(sync_pipe, 512, O_BINARY) < 0) {
+      /* Couldn't create the pipe between parent and child. */
+      error = errno;
+      unlink(cfile.save_file);
+      g_free(cfile.save_file);
+      cfile.save_file = NULL;
+      simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't create sync pipe: %s",
+                        strerror(error));
+      return;
+    }
+
+    /* Convert pipe write handle to a string and pass to child */
+    itoa(sync_pipe[WRITE], sync_pipe_fd, 10);
+    /* Convert filter string to a quote delimited string */
+    filterstring = g_new(char, strlen(cfile.cfilter) + 3);
+    sprintf(filterstring, "\"%s\"", cfile.cfilter);
+    filterstring[strlen(cfile.cfilter) + 2] = 0;
+    /* Spawn process */
+    fork_child = spawnlp(_P_NOWAIT, ethereal_path, CHILD_NAME, "-i", cfile.iface,
+                         "-w", cfile.save_file, "-W", save_file_fd,
+                         "-c", scount, "-s", ssnap,
+                         "-Z", sync_pipe_fd,
+                         strlen(cfile.cfilter) == 0 ? (const char *)NULL : "-f",
+                         strlen(cfile.cfilter) == 0 ? (const char *)NULL : filterstring,
+                         (const char *)NULL);
+    g_free(filterstring);
+    /* Keep a copy for later evaluation by _cwait() */
+    child_process = fork_child;
+#else
     signal(SIGCHLD, SIG_IGN);
-    if (sync_mode) pipe(sync_pipe);
-    if((fork_child = fork()) == 0){
-      /* args: -k -- capture
-       * -i interface specification
+    if (pipe(sync_pipe) < 0) {
+      /* Couldn't create the pipe between parent and child. */
+      error = errno;
+      unlink(cfile.save_file);
+      g_free(cfile.save_file);
+      cfile.save_file = NULL;
+      simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't create sync pipe: %s",
+                       strerror(error));
+      return;
+    }
+    if ((fork_child = fork()) == 0) {
+      /*
+       * Child process - run Ethereal with the right arguments to make
+       * it just pop up the live capture dialog box and capture with
+       * the specified capture parameters, writing to the specified file.
+       *
+       * args: -i interface specification
        * -w file to write
        * -W file descriptor to write
        * -c count to capture
-       * -Q quit after capture (forces -k)
        * -s snaplen
-       * -S sync mode
        * -m / -b fonts
        * -f "filter expression"
        */
-       if (sync_mode) {
-        close(1);
-        dup(sync_pipe[1]);
-        close(sync_pipe[0]);
-        execlp(ethereal_path, "ethereal", "-k", "-Q", "-i", cf.iface,
-               "-w", cf.save_file, "-W", save_file_fd,
-               "-c", scount, "-s", ssnap, "-S", 
+      close(1);
+      dup(sync_pipe[WRITE]);
+      close(sync_pipe[READ]);
+      execlp(ethereal_path, CHILD_NAME, "-i", cfile.iface,
+               "-w", cfile.save_file, "-W", save_file_fd,
+               "-c", scount, "-s", ssnap, 
                "-m", medium_font, "-b", bold_font,
-               (cf.cfilter == NULL)? 0 : "-f",
-               (cf.cfilter == NULL)? 0 : cf.cfilter,
+               (cfile.cfilter == NULL)? 0 : "-f",
+               (cfile.cfilter == NULL)? 0 : cfile.cfilter,
                (const char *)NULL);    
-       }
-       else {
-        execlp(ethereal_path, "ethereal", "-k", "-Q", "-i", cf.iface,
-               "-w", cf.save_file, "-W", save_file_fd,
-               "-c", scount, "-s", ssnap,
-               "-m", medium_font, "-b", bold_font,
-               (cf.cfilter == NULL)? 0 : "-f",
-               (cf.cfilter == NULL)? 0 : cf.cfilter,
-               (const char *)NULL);
-       }
+      snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
+               ethereal_path, strerror(errno));
+      send_errmsg_to_parent(errmsg);
+
+      /* Exit with "_exit()", so that we don't close the connection
+         to the X server (and cause stuff buffered up by our parent but
+        not yet sent to be sent, as that stuff should only be sent by
+        our parent). */
+      _exit(2);
     }
-    else {
-       cf.filename = cf.save_file;
-       if (sync_mode) {
-        close(sync_pipe[1]);
-        while (!sigusr2_received) {
-          struct timeval timeout = {1,0};
-          select(0, NULL, NULL, NULL, &timeout);
-          if (kill(fork_child, 0) == -1 && errno == ESRCH) 
-            break;
-        }
-        if (sigusr2_received) {
-          err = tail_cap_file(cf.save_file, &cf);
-          if (err != 0) {
-            simple_dialog(ESD_TYPE_WARN, NULL,
-                       file_open_error_message(err, FALSE), cf.save_file);
-          }
-        }
-        sigusr2_received = FALSE;
-       }
+#endif
+
+    /* Parent process - read messages from the child process over the
+       sync pipe. */
+
+    /* Close the write side of the pipe, so that only the child has it
+       open, and thus it completely closes, and thus returns to us
+       an EOF indication, if the child closes it (either deliberately
+       or by exiting abnormally). */
+    close(sync_pipe[WRITE]);
+
+    /* Close the save file FD, as we won't be using it - we'll be opening
+       it and reading the save file through Wiretap. */
+    close(cfile.save_file_fd);
+
+    if (fork_child == -1) {
+      /* We couldn't even create the child process. */
+      error = errno;
+      close(sync_pipe[READ]);
+      unlink(cfile.save_file);
+      g_free(cfile.save_file);
+      cfile.save_file = NULL;
+      simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't create child process: %s",
+                       strerror(error));
+      return;
     }
+
+    /* Read a byte count from "sync_pipe[READ]", terminated with a
+       colon; if the count is 0, the child process created the
+       capture file and we should start reading from it, otherwise
+       the capture couldn't start and the count is a count of bytes
+       of error message, and we should display the message. */
+    byte_count = 0;
+    for (;;) {
+      i = read(sync_pipe[READ], &c, 1);
+      if (i == 0) {
+       /* EOF - the child process died.
+          Close the read side of the sync pipe, remove the capture file,
+          and report the failure.
+          XXX - reap the child process and report the status in detail. */
+       close(sync_pipe[READ]);
+       unlink(cfile.save_file);
+       g_free(cfile.save_file);
+       cfile.save_file = NULL;
+       simple_dialog(ESD_TYPE_WARN, NULL, "Capture child process died");
+       return;
+      }
+      if (c == ';')
+       break;
+      if (!isdigit(c)) {
+       /* Child process handed us crap.
+          Close the read side of the sync pipe, remove the capture file,
+          and report the failure. */
+       close(sync_pipe[READ]);
+       unlink(cfile.save_file);
+       g_free(cfile.save_file);
+       cfile.save_file = NULL;
+       simple_dialog(ESD_TYPE_WARN, NULL,
+                       "Capture child process sent us a bad message");
+       return;
+      }
+      byte_count = byte_count*10 + c - '0';
+    }
+    if (byte_count == 0) {
+      /* Success.  Open the capture file, and set up to read it. */
+      err = start_tail_cap_file(cfile.save_file, is_tempfile, &cfile);
+      if (err == 0) {
+       /* We were able to open and set up to read the capture file;
+          arrange that our callback be called whenever it's possible
+          to read from the sync pipe, so that it's called when
+          the child process wants to tell us something. */
+#ifdef _WIN32
+       /* Tricky to use pipes in win9x, as no concept of wait.  NT can
+          do this but that doesn't cover all win32 platforms.  GTK can do
+          this but doesn't seem to work over processes.  Attempt to do
+          something similar here, start a timer and check for data on every
+          timeout. */
+       cap_timer_id = gtk_timeout_add(1000, cap_timer_cb, NULL);
+#else
+       cap_input_id = gtk_input_add_full(sync_pipe[READ],
+                                      GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
+                                      cap_file_input_cb,
+                                      NULL,
+                                      (gpointer) &cfile,
+                                      NULL);
+#endif
+      } else {
+       /* We weren't able to open the capture file; complain, and
+          close the sync pipe. */
+       simple_dialog(ESD_TYPE_WARN, NULL,
+                       file_open_error_message(err, FALSE), cfile.save_file);
+
+       /* Close the sync pipe. */
+       close(sync_pipe[READ]);
+
+       /* Don't unlink the save file - leave it around, for debugging
+          purposes. */
+       g_free(cfile.save_file);
+       cfile.save_file = NULL;
+      }
+    } else {
+      /* Failure - the child process sent us a message indicating
+        what the problem was. */
+      msg = g_malloc(byte_count + 1);
+      if (msg == NULL) {
+       simple_dialog(ESD_TYPE_WARN, NULL,
+               "Capture child process failed, but its error message was too big.");
+      } else {
+       i = read(sync_pipe[READ], msg, byte_count);
+       if (i < 0) {
+         simple_dialog(ESD_TYPE_WARN, NULL,
+                 "Capture child process failed: Error %s reading its error message.",
+                 strerror(errno));
+       } else if (i == 0) {
+         simple_dialog(ESD_TYPE_WARN, NULL,
+                 "Capture child process failed: EOF reading its error message.");
+       } else
+         simple_dialog(ESD_TYPE_WARN, NULL, msg);
+       g_free(msg);
+
+       /* Close the sync pipe. */
+       close(sync_pipe[READ]);
+
+       /* Get rid of the save file - the capture never started. */
+       unlink(cfile.save_file);
+       g_free(cfile.save_file);
+       cfile.save_file = NULL;
+      }
+    }
+  } else {
+    /* Not sync mode. */
+    capture_succeeded = capture();
+    if (quit_after_cap) {
+      /* DON'T unlink the save file.  Presumably someone wants it. */
+      gtk_exit(0);
+    }
+    if (capture_succeeded) {
+      /* Capture succeeded; read in the capture file. */
+      if ((err = open_cap_file(cfile.save_file, is_tempfile, &cfile)) == 0) {
+        /* Set the read filter to NULL. */
+        cfile.rfcode = NULL;
+        err = read_cap_file(&cfile);
+      }
+    }
+    /* We're not doing a capture any more, so we don't have a save
+       file. */
+    g_free(cfile.save_file);
+    cfile.save_file = NULL;
   }
-  else
-    capture();
 }
 
-static void
-capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
+#ifdef _WIN32
+/* The timer has expired, see if there's stuff to read from the pipe,
+   if so call the cap_file_input_cb */
+static gint
+cap_timer_cb(gpointer data)
 {
-  gtk_grab_remove(GTK_WIDGET(parent_w));
-  gtk_widget_destroy(GTK_WIDGET(parent_w));
+  HANDLE handle;
+  DWORD avail = 0;
+  gboolean result, result1;
+  DWORD childstatus;
+
+  /* Oddly enough although Named pipes don't work on win9x,
+     PeekNamedPipe does !!! */
+  handle = (HANDLE) _get_osfhandle (sync_pipe[READ]);
+  result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
+
+  /* Get the child process exit status */
+  result1 = GetExitCodeProcess((HANDLE)child_process, &childstatus);
+
+  /* If the Peek returned an error, or there are bytes to be read
+     or the childwatcher thread has terminated then call the normal
+     callback */
+  if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
+
+    /* avoid reentrancy problems and stack overflow */
+    gtk_timeout_remove(cap_timer_id);
+
+    /* And call the real handler */
+    cap_file_input_cb((gpointer) &cfile, 0, 0);
+
+    /* Return false so that the timer is not run again */
+    return FALSE;
+  }
+  else {
+    /* No data so let timer run again */
+    return TRUE;
+  }
 }
+#endif
 
-typedef struct _loop_data {
-  gint           go;
-  gint           max;
-  gint           linktype;
-  gint           wtap_linktype;
-  gint           sync_packets;
-  packet_counts  counts;
-  wtap_dumper   *pdh;
-} loop_data;
+/* There's stuff to read from the sync pipe, meaning the child has sent
+   us a message, or the sync pipe has closed, meaning the child has
+   closed it (perhaps because it exited). */
+static void 
+cap_file_input_cb(gpointer data, gint source, GdkInputCondition condition)
+{
+  capture_file *cf = (capture_file *)data;
+  char buffer[256+1], *p = buffer, *q = buffer;
+  int  nread;
+  int  to_read = 0;
+  gboolean exit_loop = FALSE;
+  int  err;
+  int  wstatus;
+  int  wsignal;
+  char *msg;
+  char *sigmsg;
+  char sigmsg_buf[6+1+3+1];
+  char *coredumped;
+
+#ifndef _WIN32
+  /* avoid reentrancy problems and stack overflow */
+  gtk_input_remove(cap_input_id);
+#endif
 
-void
-capture(void) {
-  GtkWidget  *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb, *icmp_lb,
-             *ospf_lb, *gre_lb, *netbios_lb, *other_lb, *stop_bt;
+  if ((nread = read(sync_pipe[READ], buffer, 256)) <= 0) {
+    /* 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 died of a signal. */
+#ifdef _WIN32
+    /* XXX - analyze the wait stuatus and display more information
+       in the dialog box? */
+    if (_cwait(&wstatus, child_process, _WAIT_CHILD) == -1) {
+      simple_dialog(ESD_TYPE_WARN, NULL, "Child capture process stopped unexpectedly");
+    }
+#else
+    if (wait(&wstatus) != -1) {
+      /* XXX - are there any platforms on which we can run that *don't*
+         support POSIX.1's <sys/wait.h> and macros therein? */
+      wsignal = wstatus & 0177;
+      coredumped = "";
+      if (wstatus == 0177) {
+       /* It stopped, rather than exiting.  "Should not happen." */
+       msg = "stopped";
+       wsignal = (wstatus >> 8) & 0xFF;
+      } else {
+        msg = "terminated";
+        if (wstatus & 0200)
+          coredumped = " - core dumped";
+      }
+      if (wsignal != 0) {
+        switch (wsignal) {
+
+        case SIGHUP:
+          sigmsg = "Hangup";
+          break;
+
+        case SIGINT:
+          sigmsg = "Interrupted";
+          break;
+
+        case SIGQUIT:
+          sigmsg = "Quit";
+          break;
+
+        case SIGILL:
+          sigmsg = "Illegal instruction";
+          break;
+
+        case SIGTRAP:
+          sigmsg = "Trace trap";
+          break;
+
+        case SIGABRT:
+          sigmsg = "Abort";
+          break;
+
+        case SIGFPE:
+          sigmsg = "Arithmetic exception";
+          break;
+
+        case SIGKILL:
+          sigmsg = "Killed";
+          break;
+
+        case SIGBUS:
+          sigmsg = "Bus error";
+          break;
+
+        case SIGSEGV:
+          sigmsg = "Segmentation violation";
+          break;
+
+       /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO 
+               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.''
+       */
+
+       #ifdef SIGSYS
+        case SIGSYS:
+          sigmsg = "Bad system call";
+          break;
+       #endif
+
+        case SIGPIPE:
+          sigmsg = "Broken pipe";
+          break;
+
+        case SIGALRM:
+          sigmsg = "Alarm clock";
+          break;
+
+        case SIGTERM:
+          sigmsg = "Terminated";
+          break;
+
+        default:
+          sprintf(sigmsg_buf, "Signal %d", wsignal);
+          sigmsg = sigmsg_buf;
+          break;
+        }
+       simple_dialog(ESD_TYPE_WARN, NULL,
+               "Child capture process %s: %s%s", msg, sigmsg, coredumped);
+      }
+    }
+#endif
+      
+    /* Read what remains of the capture file, and finish the capture.
+       XXX - do something if this fails? */
+    err = finish_tail_cap_file(cf);
+
+    /* We're not doing a capture any more, so we don't have a save
+       file. */
+    g_free(cf->save_file);
+    cf->save_file = NULL;
+
+    return;
+  }
+
+  buffer[nread] = '\0';
+
+  while(!exit_loop) {
+    /* look for (possibly multiple) '*' */
+    switch (*q) {
+    case '*' :
+      to_read += atoi(p);
+      p = q + 1; 
+      q++;
+      break;
+    case '\0' :
+      /* XXX should handle the case of a pipe full (i.e. no star found) */
+      exit_loop = TRUE;
+      break;
+    default :
+      q++;
+      break;
+    } 
+  }
+
+  /* Read from the capture file the number of records the child told us
+     it added.
+     XXX - do something if this fails? */
+  err = continue_tail_cap_file(cf, to_read);
+
+  /* restore pipe handler */
+#ifdef _WIN32
+  cap_timer_id = gtk_timeout_add(1000, cap_timer_cb, NULL);
+#else
+  cap_input_id = gtk_input_add_full (sync_pipe[READ],
+                                    GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
+                                    cap_file_input_cb,
+                                    NULL,
+                                    (gpointer) cf,
+                                    NULL);
+#endif
+}
+
+/*
+ * Timeout, in milliseconds, for reads from the stream of captured packets.
+ */
+#define        CAP_READ_TIMEOUT        250
+
+/* Do the low-level work of a capture.
+   Returns TRUE if it succeeds, FALSE otherwise. */
+int
+capture(void)
+{
+  GtkWidget  *cap_w, *main_vb, *count_lb, *sctp_lb, *tcp_lb, *udp_lb, *icmp_lb,
+             *ospf_lb, *gre_lb, *netbios_lb, *ipx_lb, *vines_lb, *other_lb, *stop_bt;
   pcap_t     *pch;
   gchar       err_str[PCAP_ERRBUF_SIZE], label_str[32];
   loop_data   ld;
   bpf_u_int32 netnum, netmask;
   time_t      upd_time, cur_time;
   int         err, inpkts;
-  char       *errmsg;
-  char        errmsg_errno[1024+1];
+  char        errmsg[1024+1];
+#ifdef linux
+  fd_set      set1;
+  struct timeval timeout;
+  int         pcap_fd;
+#endif
+#ifdef _WIN32 
+  WORD wVersionRequested; 
+  WSADATA wsaData; 
+#endif
+
+  /* Initialize Windows Socket if we are in a WIN32 OS 
+     This needs to be done before querying the interface for network/netmask */
+#ifdef _WIN32 
+  wVersionRequested = MAKEWORD( 1, 1 ); 
+  err = WSAStartup( wVersionRequested, &wsaData ); 
+  if (err!=0) { 
+    snprintf(errmsg, sizeof errmsg, 
+      "Couldn't initialize Windows Sockets."); 
+       pch=NULL; 
+    goto error; 
+  } 
+#endif 
 
   ld.go             = TRUE;
   ld.counts.total   = 0;
-  ld.max            = cf.count;
-  ld.linktype       = DLT_NULL;
+  ld.max            = cfile.count;
+  ld.linktype       = WTAP_ENCAP_UNKNOWN;
   ld.sync_packets   = 0;
+  ld.counts.sctp    = 0;
   ld.counts.tcp     = 0;
   ld.counts.udp     = 0;
   ld.counts.icmp    = 0;
   ld.counts.ospf    = 0;
   ld.counts.gre     = 0;
+  ld.counts.ipx     = 0;
   ld.counts.netbios = 0;
+  ld.counts.vines   = 0;
   ld.counts.other   = 0;
   ld.pdh            = NULL;
 
-  close_cap_file(&cf, info_bar, file_ctx);
+  /* Open the network interface to capture from it. */
+  pch = pcap_open_live(cfile.iface, cfile.snap, 1, CAP_READ_TIMEOUT, err_str);
 
-  pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
+  if (pch == NULL) {
+    /* Well, we couldn't start the capture.
+       If this is a child process that does the capturing in sync
+       mode or fork mode, it shouldn't do any UI stuff until we pop up the
+       capture-progress window, and, since we couldn't start the
+       capture, we haven't popped it up. */
+    if (!capture_child) {
+      while (gtk_events_pending()) gtk_main_iteration();
+    }
+    snprintf(errmsg, sizeof errmsg,
+      "The capture session could not be initiated (%s).\n"
+      "Please check to make sure you have sufficient permissions, and that\n"
+      "you have the proper interface specified.", err_str);
+    goto error;
+  }
 
-  if (pch) {
-    ld.linktype = pcap_datalink(pch);
-    ld.wtap_linktype = wtap_pcap_encap_to_wtap_encap(ld.linktype);
-    if (ld.wtap_linktype == WTAP_ENCAP_UNKNOWN) {
-      errmsg = "The network you're capturing from is of a type"
-               " that Ethereal doesn't support.";
-      goto fail;
+  if (cfile.cfilter) {
+    /* A capture filter was specified; set it up. */
+    if (pcap_lookupnet (cfile.iface, &netnum, &netmask, err_str) < 0) {
+      snprintf(errmsg, sizeof errmsg,
+        "Can't use filter:  Couldn't obtain netmask info (%s).", err_str);
+      goto error;
     }
-    ld.pdh = wtap_dump_fdopen(cf.save_file_fd, WTAP_FILE_PCAP,
-               ld.wtap_linktype, pcap_snapshot(pch), &err);
-
-    if (ld.pdh == NULL) {  /* We have an error */
-      switch (err) {
-
-      case WTAP_ERR_CANT_OPEN:
-        errmsg = "The file to which the capture would be saved"
-                 " couldn't be created for some unknown reason.";
-        break;
-
-      case WTAP_ERR_SHORT_WRITE:
-        errmsg = "A full header couldn't be written to the file"
-                 " to which the capture would be saved.";
-        break;
-
-      default:
-        if (err < 0) {
-          sprintf(errmsg_errno, "The file to which the capture would be"
-                             " saved (\"%%s\") could not be opened: Error %d.",
-                               err);
-        } else {
-          sprintf(errmsg_errno, "The file to which the capture would be"
-                             " saved (\"%%s\") could not be opened: %s.",
-                               strerror(err));
-       }
-       errmsg = errmsg_errno;
-       break;
-      }
-fail:
-      snprintf(err_str, PCAP_ERRBUF_SIZE, errmsg, cf.save_file);
-      simple_dialog(ESD_TYPE_WARN, NULL, err_str);
-      pcap_close(pch);
-      return;
+    if (pcap_compile(pch, &cfile.fcode, cfile.cfilter, 1, netmask) < 0) {
+      snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
+       pcap_geterr(pch));
+      goto error;
+    }
+    if (pcap_setfilter(pch, &cfile.fcode) < 0) {
+      snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
+       pcap_geterr(pch));
+      goto error;
     }
+  }
+
+  /* Set up to write to the capture file. */
+  ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(pch));
+  if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
+    strcpy(errmsg, "The network you're capturing from is of a type"
+             " that Ethereal doesn't support.");
+    goto error;
+  }
+  ld.pdh = wtap_dump_fdopen(cfile.save_file_fd, WTAP_FILE_PCAP,
+               ld.linktype, pcap_snapshot(pch), &err);
+
+  if (ld.pdh == NULL) {
+    /* We couldn't set up to write to the capture file. */
+    switch (err) {
+
+    case WTAP_ERR_CANT_OPEN:
+      strcpy(errmsg, "The file to which the capture would be saved"
+               " couldn't be created for some unknown reason.");
+      break;
+
+    case WTAP_ERR_SHORT_WRITE:
+      strcpy(errmsg, "A full header couldn't be written to the file"
+               " to which the capture would be saved.");
+      break;
 
-    if (cf.cfilter) {
-      if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
-        simple_dialog(ESD_TYPE_WARN, NULL,
-          "Can't use filter:  Couldn't obtain netmask info.");
-        wtap_dump_close(ld.pdh, NULL);
-        unlink(cf.save_file); /* silently ignore error */
-        pcap_close(pch);
-        return;
-      } else if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
-        simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
-        wtap_dump_close(ld.pdh, NULL);
-        unlink(cf.save_file); /* silently ignore error */
-        pcap_close(pch);
-        return;
-      } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
-        simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
-        wtap_dump_close(ld.pdh, NULL);
-        unlink(cf.save_file); /* silently ignore error */
-        pcap_close(pch);
-        return;
+    default:
+      if (err < 0) {
+        sprintf(errmsg, "The file to which the capture would be"
+                     " saved (\"%s\") could not be opened: Error %d.",
+                       cfile.save_file, err);
+      } else {
+        sprintf(errmsg, "The file to which the capture would be"
+                     " saved (\"%s\") could not be opened: %s.",
+                       cfile.save_file, strerror(err));
       }
+      break;
     }
+    goto error;
+  }
 
-    if (sync_mode) {
-      /* Sync out the capture file, so the header makes it to the file
-         system, and signal our parent so that they'll open the capture
-        file and update its windows to indicate that we have a live
-        capture in progress. */
-      fflush(wtap_dump_file(ld.pdh));
-      kill(getppid(), SIGUSR2);
-    }
+  if (capture_child) {
+    /* Well, we should be able to start capturing.
+
+       This is the child process for a sync mode capture, so sync out
+       the capture file, so the header makes it to the file system,
+       and send a "capture started successfully and capture file created"
+       message to our parent so that they'll open the capture file and
+       update its windows to indicate that we have a live capture in
+       progress. */
+    fflush(wtap_dump_file(ld.pdh));
+    write(1, "0;", 2);
+  }
 
-    cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-    gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
-
-    /* Container for capture display widgets */
-    main_vb = gtk_vbox_new(FALSE, 1);
-    gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
-    gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
-    gtk_widget_show(main_vb);
-
-    count_lb = gtk_label_new("Count: 0");
-    gtk_box_pack_start(GTK_BOX(main_vb), count_lb, FALSE, FALSE, 3);
-    gtk_widget_show(count_lb);
-
-    tcp_lb = gtk_label_new("TCP: 0 (0.0%)");
-    gtk_box_pack_start(GTK_BOX(main_vb), tcp_lb, FALSE, FALSE, 3);
-    gtk_widget_show(tcp_lb);
-
-    udp_lb = gtk_label_new("UDP: 0 (0.0%)");
-    gtk_box_pack_start(GTK_BOX(main_vb), udp_lb, FALSE, FALSE, 3);
-    gtk_widget_show(udp_lb);
-
-    icmp_lb = gtk_label_new("ICMP: 0 (0.0%)");
-    gtk_box_pack_start(GTK_BOX(main_vb), icmp_lb, FALSE, FALSE, 3);
-    gtk_widget_show(icmp_lb);
-
-    ospf_lb = gtk_label_new("OSPF: 0 (0.0%)");
-    gtk_box_pack_start(GTK_BOX(main_vb), ospf_lb, FALSE, FALSE, 3);
-    gtk_widget_show(ospf_lb);
-
-    gre_lb = gtk_label_new("GRE: 0 (0.0%)");
-    gtk_box_pack_start(GTK_BOX(main_vb), gre_lb, FALSE, FALSE, 3);
-    gtk_widget_show(gre_lb);
-
-    netbios_lb = gtk_label_new("NetBIOS: 0 (0.0%)");
-    gtk_box_pack_start(GTK_BOX(main_vb), netbios_lb, FALSE, FALSE, 3);
-    gtk_widget_show(netbios_lb);
-
-    other_lb = gtk_label_new("Other: 0 (0.0%)");
-    gtk_box_pack_start(GTK_BOX(main_vb), other_lb, FALSE, FALSE, 3);
-    gtk_widget_show(other_lb);
-
-    stop_bt = gtk_button_new_with_label ("Stop");
-    gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
-      GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
-    gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
-    GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
-    gtk_widget_grab_default(stop_bt);
-    GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
-    gtk_widget_grab_default(stop_bt);
-    gtk_widget_show(stop_bt);
-
-    gtk_widget_show(cap_w);
-    gtk_grab_add(cap_w);
-
-    upd_time = time(NULL);
-    while (ld.go) {
-      while (gtk_events_pending()) gtk_main_iteration();
+  cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
+
+  /* Container for capture display widgets */
+  main_vb = gtk_vbox_new(FALSE, 1);
+  gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
+  gtk_container_add(GTK_CONTAINER(cap_w), main_vb);
+  gtk_widget_show(main_vb);
+
+  count_lb = gtk_label_new("Count: 0");
+  gtk_box_pack_start(GTK_BOX(main_vb), count_lb, FALSE, FALSE, 3);
+  gtk_widget_show(count_lb);
+
+  sctp_lb = gtk_label_new("SCTP: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), sctp_lb, FALSE, FALSE, 3);
+  gtk_widget_show(sctp_lb);
+
+  tcp_lb = gtk_label_new("TCP: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), tcp_lb, FALSE, FALSE, 3);
+  gtk_widget_show(tcp_lb);
+
+  udp_lb = gtk_label_new("UDP: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), udp_lb, FALSE, FALSE, 3);
+  gtk_widget_show(udp_lb);
+
+  icmp_lb = gtk_label_new("ICMP: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), icmp_lb, FALSE, FALSE, 3);
+  gtk_widget_show(icmp_lb);
+
+  ospf_lb = gtk_label_new("OSPF: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), ospf_lb, FALSE, FALSE, 3);
+  gtk_widget_show(ospf_lb);
+
+  gre_lb = gtk_label_new("GRE: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), gre_lb, FALSE, FALSE, 3);
+  gtk_widget_show(gre_lb);
+
+  netbios_lb = gtk_label_new("NetBIOS: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), netbios_lb, FALSE, FALSE, 3);
+  gtk_widget_show(netbios_lb);
+
+  ipx_lb = gtk_label_new("IPX: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), ipx_lb, FALSE, FALSE, 3);
+  gtk_widget_show(ipx_lb);
+
+  vines_lb = gtk_label_new("VINES: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), vines_lb, FALSE, FALSE, 3);
+  gtk_widget_show(vines_lb);
+
+  other_lb = gtk_label_new("Other: 0 (0.0%)");
+  gtk_box_pack_start(GTK_BOX(main_vb), other_lb, FALSE, FALSE, 3);
+  gtk_widget_show(other_lb);
+
+  /* allow user to either click a stop button, or the close button on
+       the window to stop a capture in progress. */
+  stop_bt = gtk_button_new_with_label ("Stop");
+  gtk_signal_connect(GTK_OBJECT(stop_bt), "clicked",
+    GTK_SIGNAL_FUNC(capture_stop_cb), (gpointer) &ld);
+  gtk_signal_connect(GTK_OBJECT(cap_w), "delete_event",
+       GTK_SIGNAL_FUNC(capture_delete_cb), (gpointer) &ld);
+  gtk_box_pack_end(GTK_BOX(main_vb), stop_bt, FALSE, FALSE, 3);
+  GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
+  gtk_widget_grab_default(stop_bt);
+  GTK_WIDGET_SET_FLAGS(stop_bt, GTK_CAN_DEFAULT);
+  gtk_widget_grab_default(stop_bt);
+  gtk_widget_show(stop_bt);
+
+  gtk_widget_show(cap_w);
+  gtk_grab_add(cap_w);
+
+  upd_time = time(NULL);
+#ifdef linux
+  pcap_fd = pcap_fileno(pch);
+#endif
+  while (ld.go) {
+    while (gtk_events_pending()) gtk_main_iteration();
+#ifdef linux
+    /*
+     * Sigh.  The semantics of the read timeout argument to
+     * "pcap_open_live()" aren't particularly well specified by
+     * the "pcap" man page - at least with the BSD BPF code, the
+     * intent appears to be, at least in part, a way of cutting
+     * down the number of reads done on a capture, by blocking
+     * until the buffer fills or a timer expires - and the Linux
+     * libpcap doesn't actually support it, so we can't use it
+     * to break out of the "pcap_dispatch()" every 1/4 of a second
+     * or so.
+     *
+     * Thus, on Linux, we do a "select()" on the file descriptor for the
+     * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or
+     * CAP_READ_TIMEOUT*1000 microseconds.
+     */
+    FD_ZERO(&set1);
+    FD_SET(pcap_fd, &set1);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+    if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+      /*
+       * "select()" says we can read from it without blocking; go for
+       * it.
+       */
       inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
-      if (inpkts > 0)
-        ld.sync_packets += inpkts;
-      /* Only update once a second so as not to overload slow displays */
-      cur_time = time(NULL);
-      if (cur_time > upd_time) {
-
-        upd_time = cur_time;
-
-        sprintf(label_str, "Count: %d", ld.counts.total);
-        gtk_label_set(GTK_LABEL(count_lb), label_str);
-
-        sprintf(label_str, "TCP: %d (%.1f%%)", ld.counts.tcp,
-          pct(ld.counts.tcp, ld.counts.total));
-        gtk_label_set(GTK_LABEL(tcp_lb), label_str);
-
-        sprintf(label_str, "UDP: %d (%.1f%%)", ld.counts.udp,
-         pct(ld.counts.udp, ld.counts.total));
-        gtk_label_set(GTK_LABEL(udp_lb), label_str);
-
-        sprintf(label_str, "ICMP: %d (%.1f%%)", ld.counts.icmp,
-         pct(ld.counts.icmp, ld.counts.total));
-        gtk_label_set(GTK_LABEL(icmp_lb), label_str);
-
-        sprintf(label_str, "OSPF: %d (%.1f%%)", ld.counts.ospf,
-         pct(ld.counts.ospf, ld.counts.total));
-        gtk_label_set(GTK_LABEL(ospf_lb), label_str);
-
-        sprintf(label_str, "GRE: %d (%.1f%%)", ld.counts.gre,
-         pct(ld.counts.gre, ld.counts.total));
-        gtk_label_set(GTK_LABEL(gre_lb), label_str);
-
-        sprintf(label_str, "NetBIOS: %d (%.1f%%)", ld.counts.netbios,
-         pct(ld.counts.netbios, ld.counts.total));
-        gtk_label_set(GTK_LABEL(netbios_lb), label_str);
-
-        sprintf(label_str, "Other: %d (%.1f%%)", ld.counts.other,
-          pct(ld.counts.other, ld.counts.total));
-        gtk_label_set(GTK_LABEL(other_lb), label_str);
-
-       /* do sync here, too */
-       fflush(wtap_dump_file(ld.pdh));
-       if (sync_mode && ld.sync_packets) {
-         char tmp[20];
-         sprintf(tmp, "%d*", ld.sync_packets);
-         write(1, tmp, strlen(tmp));
-         ld.sync_packets = 0;
-       }
-      }
-    }
-    
-    if (ld.pdh) {
-      if (!wtap_dump_close(ld.pdh, &err)) {
-        switch (err) {
+    } else
+      inpkts = 0;
+#else
+    inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+#endif
+    if (inpkts > 0)
+      ld.sync_packets += inpkts;
+    /* Only update once a second so as not to overload slow displays */
+    cur_time = time(NULL);
+    if (cur_time > upd_time) {
+      upd_time = cur_time;
 
-        case WTAP_ERR_CANT_CLOSE:
-          errmsg = "The file to which the capture was being saved"
-                 " couldn't be closed for some unknown reason.";
-          break;
+      sprintf(label_str, "Count: %d", ld.counts.total);
+      gtk_label_set(GTK_LABEL(count_lb), label_str);
 
-        case WTAP_ERR_SHORT_WRITE:
-          errmsg = "Not all the data could be written to the file"
-                   " to which the capture was being saved.";
-          break;
+      sprintf(label_str, "SCTP: %d (%.1f%%)", ld.counts.sctp,
+                pct(ld.counts.sctp, ld.counts.total));
+      gtk_label_set(GTK_LABEL(sctp_lb), label_str);
 
-        default:
-          if (err < 0) {
-            sprintf(errmsg_errno, "The file to which the capture was being"
-                             " saved (\"%%s\") could not be closed: Error %d.",
-                               err);
-          } else {
-            sprintf(errmsg_errno, "The file to which the capture was being"
-                             " saved (\"%%s\") could not be closed: %s.",
-                               strerror(err));
-         }
-         errmsg = errmsg_errno;
-         break;
-        }
-        snprintf(err_str, PCAP_ERRBUF_SIZE, errmsg, cf.save_file);
-        simple_dialog(ESD_TYPE_WARN, NULL, err_str);
+      sprintf(label_str, "TCP: %d (%.1f%%)", ld.counts.tcp,
+               pct(ld.counts.tcp, ld.counts.total));
+      gtk_label_set(GTK_LABEL(tcp_lb), label_str);
+
+      sprintf(label_str, "UDP: %d (%.1f%%)", ld.counts.udp,
+               pct(ld.counts.udp, ld.counts.total));
+      gtk_label_set(GTK_LABEL(udp_lb), label_str);
+
+      sprintf(label_str, "ICMP: %d (%.1f%%)", ld.counts.icmp,
+               pct(ld.counts.icmp, ld.counts.total));
+      gtk_label_set(GTK_LABEL(icmp_lb), label_str);
+
+      sprintf(label_str, "OSPF: %d (%.1f%%)", ld.counts.ospf,
+               pct(ld.counts.ospf, ld.counts.total));
+      gtk_label_set(GTK_LABEL(ospf_lb), label_str);
+
+      sprintf(label_str, "GRE: %d (%.1f%%)", ld.counts.gre,
+               pct(ld.counts.gre, ld.counts.total));
+      gtk_label_set(GTK_LABEL(gre_lb), label_str);
+
+      sprintf(label_str, "NetBIOS: %d (%.1f%%)", ld.counts.netbios,
+               pct(ld.counts.netbios, ld.counts.total));
+      gtk_label_set(GTK_LABEL(netbios_lb), label_str);
+
+      sprintf(label_str, "IPX: %d (%.1f%%)", ld.counts.ipx,
+               pct(ld.counts.ipx, ld.counts.total));
+      gtk_label_set(GTK_LABEL(ipx_lb), label_str);
+
+      sprintf(label_str, "VINES: %d (%.1f%%)", ld.counts.vines,
+               pct(ld.counts.vines, ld.counts.total));
+      gtk_label_set(GTK_LABEL(vines_lb), label_str);
+
+      sprintf(label_str, "Other: %d (%.1f%%)", ld.counts.other,
+               pct(ld.counts.other, ld.counts.total));
+      gtk_label_set(GTK_LABEL(other_lb), label_str);
+
+      /* do sync here, too */
+      fflush(wtap_dump_file(ld.pdh));
+      if (capture_child && ld.sync_packets) {
+       /* This is the child process for a sync mode capture, so send
+          our parent a message saying we've written out "ld.sync_packets"
+          packets to the capture file. */
+       char tmp[20];
+       sprintf(tmp, "%d*", ld.sync_packets);
+       write(1, tmp, strlen(tmp));
+       ld.sync_packets = 0;
       }
     }
-    pcap_close(pch);
-
-    gtk_grab_remove(GTK_WIDGET(cap_w));
-    gtk_widget_destroy(GTK_WIDGET(cap_w));
-  } else {
-    while (gtk_events_pending()) gtk_main_iteration();
-    simple_dialog(ESD_TYPE_WARN, NULL,
-      "The capture session could not be initiated.  Please\n"
-      "check to make sure you have sufficient permissions, and\n"
-      "that you have the proper interface specified.");
   }
+    
+  if (!wtap_dump_close(ld.pdh, &err)) {
+    /* XXX - in fork mode, this may not pop up, or, if it does,
+       it may disappear as soon as we exit.
+
+       We should have the parent process, while it's reading
+       the packet count update messages, catch error messages
+       and pop up a message box if it sees one. */
+    switch (err) {
+
+    case WTAP_ERR_CANT_CLOSE:
+      simple_dialog(ESD_TYPE_WARN, NULL,
+               "The file to which the capture was being saved"
+               " couldn't be closed for some unknown reason.");
+      break;
 
-  if( quit_after_cap ){
-    /* DON'T unlink the save file.  Presumably someone wants it. */
-    gtk_exit(0);
-  }
+    case WTAP_ERR_SHORT_WRITE:
+      simple_dialog(ESD_TYPE_WARN, NULL,
+               "Not all the data could be written to the file"
+               " to which the capture was being saved.");
+      break;
 
-  if (pch) {
-    /* "pch" is non-NULL only if we successfully started a capture.
-       If we haven't, there's no capture file to load. */
-    if ((err = open_cap_file(cf.save_file, &cf)) == 0) {
-      /* Set the read filter to NULL. */
-      cf.rfcode = NULL;
-      err = read_cap_file(&cf);
-      set_menu_sensitivity("/File/Save", TRUE);
-      set_menu_sensitivity("/File/Save As...", FALSE);
+    default:
+      simple_dialog(ESD_TYPE_WARN, NULL,
+               "The file to which the capture was being"
+               " saved (\"%s\") could not be closed: %s.",
+               cfile.save_file, wtap_strerror(err));
+      break;
     }
   }
+  pcap_close(pch);
+
+  gtk_grab_remove(GTK_WIDGET(cap_w));
+  gtk_widget_destroy(GTK_WIDGET(cap_w));
+
+  return TRUE;
+
+error:
+  /* We can't use the save file, and we have no wtap_dump stream
+     to close in order to close it, so close the FD directly. */
+  close(cfile.save_file_fd);
+
+  /* We couldn't even start the capture, so get rid of the capture
+     file. */
+  unlink(cfile.save_file); /* silently ignore error */
+  g_free(cfile.save_file);
+  cfile.save_file = NULL;
+  if (capture_child) {
+    /* This is the child process for a sync mode capture.
+       Send the error message to our parent, so they can display a
+       dialog box containing it. */
+    send_errmsg_to_parent(errmsg);
+  } else {
+    /* Display the dialog box ourselves; there's no parent. */
+    simple_dialog(ESD_TYPE_WARN, NULL, errmsg);
+  }
+  if (pch != NULL)
+    pcap_close(pch);
+
+  return FALSE;
+}
+
+static void
+send_errmsg_to_parent(const char *errmsg)
+{
+    int msglen = strlen(errmsg);
+    char lenbuf[10+1+1];
+
+    sprintf(lenbuf, "%u;", msglen);
+    write(1, lenbuf, strlen(lenbuf));
+    write(1, errmsg, msglen);
 }
 
 static float
@@ -718,6 +1050,11 @@ pct(gint num, gint denom) {
   }
 }
 
+static void
+capture_delete_cb(GtkWidget *w, GdkEvent *event, gpointer data) {
+  capture_stop_cb(NULL, data);
+}
+
 static void
 capture_stop_cb(GtkWidget *w, gpointer data) {
   loop_data *ld = (loop_data *) data;
@@ -737,34 +1074,52 @@ capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
      ld->go = FALSE;
   }
   if (ld->pdh) {
-     whdr.ts = phdr->ts;
+     /* "phdr->ts" may not necessarily be a "struct timeval" - it may
+        be a "struct bpf_timeval", with member sizes wired to 32
+       bits - and we may go that way ourselves in the future, so
+       copy the members individually. */
+     whdr.ts.tv_sec = phdr->ts.tv_sec;
+     whdr.ts.tv_usec = phdr->ts.tv_usec;
      whdr.caplen = phdr->caplen;
      whdr.len = phdr->len;
-     whdr.pkt_encap = ld->wtap_linktype;
+     whdr.pkt_encap = ld->linktype;
 
      /* XXX - do something if this fails */
-     wtap_dump(ld->pdh, &whdr, pd, &err);
+     wtap_dump(ld->pdh, &whdr, NULL, pd, &err);
   }
+
+  /* Set the initial payload to the packet length, and the initial
+     captured payload to the capture length (other protocols may
+     reduce them if their headers say they're less). */
+  pi.len = phdr->len;
+  pi.captured_len = phdr->caplen;
     
   switch (ld->linktype) {
-    case DLT_EN10MB :
-      capture_eth(pd, phdr->caplen, &ld->counts);
+    case WTAP_ENCAP_ETHERNET:
+      capture_eth(pd, 0, &ld->counts);
+      break;
+    case WTAP_ENCAP_FDDI:
+    case WTAP_ENCAP_FDDI_BITSWAPPED:
+      capture_fddi(pd, &ld->counts);
       break;
-    case DLT_FDDI :
-      capture_fddi(pd, phdr->caplen, &ld->counts);
+    case WTAP_ENCAP_TR:
+      capture_tr(pd, 0, &ld->counts);
       break;
-    case DLT_IEEE802 :
-      capture_tr(pd, phdr->caplen, &ld->counts);
+    case WTAP_ENCAP_NULL:
+      capture_null(pd, &ld->counts);
       break;
-    case DLT_NULL :
-      capture_null(pd, phdr->caplen, &ld->counts);
+    case WTAP_ENCAP_PPP:
+      capture_ppp(pd, 0, &ld->counts);
       break;
-    case DLT_PPP :
-      capture_ppp(pd, phdr->caplen, &ld->counts);
+    case WTAP_ENCAP_RAW_IP:
+      capture_raw(pd, &ld->counts);
       break;
-    case DLT_RAW :
-      capture_raw(pd, phdr->caplen, &ld->counts);
+    case WTAP_ENCAP_LINUX_ATM_CLIP:
+      capture_clip(pd, &ld->counts);
       break;
+    /* XXX - FreeBSD may append 4-byte ATM pseudo-header to DLT_ATM_RFC1483,
+       with LLC header following; we should implement it at some
+       point. */
   }
 }