Implement plugins status save/restore :
[obnox/wireshark/wip.git] / capture.c
index 03320b858f66addbef7e4bbdf6efb591dab66188..afd57a11f0c4b2242a2db442413d4f1445041a35 100644 (file)
--- a/capture.c
+++ b/capture.c
@@ -1,7 +1,7 @@
 /* capture.c
  * Routines for packet capture windows
  *
- * $Id: capture.c,v 1.9 1998/11/15 05:28:54 guy Exp $
+ * $Id: capture.c,v 1.88 2000/01/03 06:59:07 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
 # include "config.h"
 #endif
 
+#ifdef HAVE_LIBPCAP
+
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
 
-#include <gtk/gtk.h>
-#include <pcap.h>
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.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>
+#endif
+
+#include <time.h>
+
+#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
 #include <net/if.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
 
 #ifdef NEED_SNPRINTF_H
 # ifdef HAVE_STDARG_H
 # include "snprintf.h"
 #endif
 
-#ifdef HAVE_SYS_SOCKIO_H
-# include <sys/sockio.h>
+#ifndef lib_pcap_h
+#include <pcap.h>
 #endif
 
-#include "ethereal.h"
+#include "gtk/main.h"
+#include "gtk/gtkglobals.h"
 #include "packet.h"
 #include "file.h"
 #include "capture.h"
-#include "etypes.h"
 #include "util.h"
+#include "simple_dialog.h"
 #include "prefs.h"
-
-extern capture_file  cf;
-extern GtkWidget    *info_bar;
-extern guint         file_ctx;
-
-/* File selection data keys */
-#define E_CAP_PREP_FS_KEY "cap_prep_fs"
-#define E_CAP_PREP_TE_KEY "cap_prep_te"
-
-/* Capture callback data keys */
-#define E_CAP_IFACE_KEY "cap_iface"
-#define E_CAP_FILT_KEY  "cap_filter"
-#define E_CAP_FILE_KEY  "cap_file"
-#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"
-
-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;
+#include "globals.h"
+
+int sync_mode; /* fork a child to do the capture, and sync between them */
+static int sync_pipe[2]; /* used to sync father */
+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;
+
+static void cap_file_input_cb(gpointer, gint, GdkInputCondition);
+static void capture_stop_cb(GtkWidget *, gpointer);
+static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
+  const u_char *);
+static float pct(gint, gint);
+
+typedef struct _loop_data {
+  gint           go;
+  gint           max;
+  gint           linktype;
+  gint           sync_packets;
+  packet_counts  counts;
+  wtap_dumper   *pdh;
+} loop_data;
+
+/* Open a specified file, or create a temporary file, and start a capture
+   to the file in question. */
+void
+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 (capfile_name != NULL) {
+    /* Try to open/create the specified file for use as a capture buffer. */
+    cf.save_file_fd = open(capfile_name, O_RDWR|O_TRUNC|O_CREAT, 0600);
+    is_tempfile = FALSE;
+  } else {
+    /* Choose a random name for the capture buffer */
+    cf.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
+    capfile_name = g_strdup(tmpname);
+    is_tempfile = TRUE;
   }
-
-  /* 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))
-  {
+  if (cf.save_file_fd == -1) {
     simple_dialog(ESD_TYPE_WARN, NULL,
-      "Can't list interfaces: ioctl error.");
-    return NULL;
+       "The file to which the capture would be saved (\"%s\")"
+       "could not be opened: %s.", capfile_name, strerror(errno));
+    return;
   }
-
-  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));
+  close_cap_file(&cf, info_bar);
+  g_assert(cf.save_file == NULL);
+  cf.save_file = capfile_name;
+
+  if (sync_mode) {     /*  use fork() for capture */
+    int  fork_child;
+    char ssnap[24];
+    char scount[24];   /* need a constant for len of numbers */
+    char save_file_fd[24];
+
+    sprintf(ssnap,"%d",cf.snap); /* in lieu of itoa */
+    sprintf(scount,"%d",cf.count);
+    sprintf(save_file_fd,"%d",cf.save_file_fd);
+    signal(SIGCHLD, SIG_IGN);
+    pipe(sync_pipe);
+    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
+       * -s snaplen
+       * -m / -b fonts
+       * -f "filter expression"
+       */
+      close(1);
+      dup(sync_pipe[1]);
+      close(sync_pipe[0]);
+      execlp(ethereal_path, CHILD_NAME, "-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);    
+    } else {
+      /* Parent process - read messages from the child process over the
+         sync pipe. */
+      close(sync_pipe[1]);
+
+      /* Read a byte count from "sync_pipe[0]", 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[0], &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[0]);
+         unlink(cf.save_file);
+         g_free(cf.save_file);
+         cf.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[0]);
+         unlink(cf.save_file);
+         g_free(cf.save_file);
+         cf.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(cf.save_file, is_tempfile, &cf);
+       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. */
+         cap_input_id = gtk_input_add_full(sync_pipe[0],
+                                      GDK_INPUT_READ,
+                                      cap_file_input_cb,
+                                      NULL,
+                                      (gpointer) &cf,
+                                      NULL);
+       } 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), cf.save_file);
+
+         /* Close the sync pipe. */
+         close(sync_pipe[0]);
+
+         /* Don't unlink the save file - leave it around, for debugging
+            purposes. */
+         g_free(cf.save_file);
+         cf.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[0], 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[0]);
+
+         /* Get rid of the save file - the capture never started. */
+         unlink(cf.save_file);
+         g_free(cf.save_file);
+         cf.save_file = NULL;
+       }
+      }
     }
-#ifdef HAVE_SA_LEN
-    ifr = (struct ifreq *) ((char *) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ);
-#else
-    ifr = (struct ifreq *) ((char *) ifr + sizeof(struct ifreq));
-#endif
+  } 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(cf.save_file, is_tempfile, &cf)) == 0) {
+        /* Set the read filter to NULL. */
+        cf.rfcode = NULL;
+        err = read_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;
   }
-
-  free(ifc.ifc_buf);
-  return il;
 }
 
-void
-capture_prep_cb(GtkWidget *w, gpointer d) {
-  GtkWidget     *cap_open_w, *if_cb, *if_lb, *file_te, *file_bt,
-                *count_lb, *count_cb, *main_vb, *if_hb, *count_hb,
-                *filter_hb, *filter_bt, *filter_te, *file_hb, *caplen_hb,
-                *bbox, *ok_bt, *cancel_bt, *capfile_ck, *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);
-  }
+/* 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;
+
+  /* avoid reentrancy problems and stack overflow */
+  gtk_input_remove(cap_input_id);
+
+  if ((nread = read(sync_pipe[0], 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. */
+    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) {
 
-  /* 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);
-  
-  if (cf.count) {
-    snprintf(count_item2, 15, "%d", cf.count);
-    count_list = g_list_append(count_list, count_item2);
-  }
-  count_list = g_list_append(count_list, count_item1);
+        case SIGHUP:
+          sigmsg = "Hangup";
+          break;
 
-  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);
+        case SIGINT:
+          sigmsg = "Interrupted";
+          break;
 
-  while (count_list)
-    count_list = g_list_remove_link(count_list, count_list);
+        case SIGQUIT:
+          sigmsg = "Quit";
+          break;
 
-  /* 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);
-  
-  /* File row: File: button and text entry */
-  file_hb = gtk_hbox_new(FALSE, 3);
-  gtk_container_add(GTK_CONTAINER(main_vb), file_hb);
-  gtk_widget_show(file_hb);
-  
-  file_bt = gtk_button_new_with_label("File:");
-  gtk_box_pack_start(GTK_BOX(file_hb), file_bt, FALSE, FALSE, 0);
-  gtk_widget_show(file_bt);
-  
-  file_te = gtk_entry_new();
-  if (cf.save_file)
-    gtk_entry_set_text(GTK_ENTRY(file_te), cf.save_file);
-  gtk_box_pack_start(GTK_BOX(file_hb), file_te, TRUE, TRUE, 0);
-  gtk_widget_show(file_te);
-
-  gtk_signal_connect_object(GTK_OBJECT(file_bt), "clicked",
-    GTK_SIGNAL_FUNC(capture_prep_file_cb), GTK_OBJECT(file_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);
-
-  capfile_ck = gtk_check_button_new_with_label("Open file after capture");
-  gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(capfile_ck), TRUE);
-  gtk_box_pack_start(GTK_BOX(caplen_hb), capfile_ck, FALSE, FALSE, 3);
-  gtk_widget_show(capfile_ck);
-
-  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, 1.0, 4096.0,
-    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_object(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_object(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 widges 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_FILE_KEY,  file_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_OPEN_KEY,  capfile_ck);
-  gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY,  snap_sb);
-  
-  gtk_widget_show(cap_open_w);
-}
+        case SIGILL:
+          sigmsg = "Illegal instruction";
+          break;
 
-void
-capture_prep_file_cb(GtkWidget *w, gpointer te) {
-  GtkWidget *fs;
+        case SIGTRAP:
+          sigmsg = "Trace trap";
+          break;
 
-  fs = gtk_file_selection_new ("Ethereal: Open Save File");
+        case SIGABRT:
+          sigmsg = "Abort";
+          break;
 
-  gtk_object_set_data(GTK_OBJECT(w), E_CAP_PREP_FS_KEY, fs);
-  gtk_object_set_data(GTK_OBJECT(w), E_CAP_PREP_TE_KEY, (GtkWidget *) te);
-  
-  gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->ok_button),
-    "clicked", (GtkSignalFunc) cap_prep_fs_ok_cb, w);
+        case SIGFPE:
+          sigmsg = "Arithmetic exception";
+          break;
 
-  /* Connect the cancel_button to destroy the widget */
-  gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(fs)->cancel_button),
-    "clicked", (GtkSignalFunc) cap_prep_fs_cancel_cb, w);
-  
-  gtk_widget_show(fs);
-}
+        case SIGKILL:
+          sigmsg = "Killed";
+          break;
 
-void
-cap_prep_fs_ok_cb(GtkWidget *w, gpointer data) {
-  GtkWidget *fs, *te;
-  
-  fs = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_PREP_FS_KEY);
-  te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_PREP_TE_KEY);
+        case SIGBUS:
+          sigmsg = "Bus error";
+          break;
 
-  gtk_entry_set_text(GTK_ENTRY(te),
-    gtk_file_selection_get_filename (GTK_FILE_SELECTION(fs)));
-  cap_prep_fs_cancel_cb(w, data);
-}
+        case SIGSEGV:
+          sigmsg = "Segmentation violation";
+          break;
 
-void
-cap_prep_fs_cancel_cb(GtkWidget *w, gpointer data) {
-  GtkWidget *fs;
-  
-  fs = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_PREP_FS_KEY);
+       /* 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
 
-  gtk_widget_destroy(fs);
-}  
+        case SIGPIPE:
+          sigmsg = "Broken pipe";
+          break;
 
-void
-capture_prep_ok_cb(GtkWidget *w, gpointer data) {
-  GtkWidget *if_cb, *filter_te, *file_te, *count_cb, *open_ck, *snap_sb;
-  gint     open;
-
-  if_cb     = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_IFACE_KEY);
-  filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_FILT_KEY);
-  file_te   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_FILE_KEY);
-  count_cb  = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_COUNT_KEY);
-  open_ck   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), E_CAP_OPEN_KEY);
-  snap_sb   = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(data), 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)));
-  if (cf.cfilter) g_free(cf.cfilter);
-  cf.cfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
-  if (cf.save_file) g_free(cf.save_file);
-  cf.save_file = g_strdup(gtk_entry_get_text(GTK_ENTRY(file_te)));
-  cf.count =
-    atoi(g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry))));
-  open = GTK_TOGGLE_BUTTON(open_ck)->active;
-  cf.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
-  if (cf.snap < 1)
-    cf.snap = 4096;
-  else if (cf.snap < 68)
-    cf.snap = 68;
-
-  gtk_widget_destroy(GTK_WIDGET(data));
-  
-  capture(open);
-}
+        case SIGALRM:
+          sigmsg = "Alarm clock";
+          break;
 
-void
-capture_prep_close_cb(GtkWidget *w, gpointer win) {
+        case SIGTERM:
+          sigmsg = "Terminated";
+          break;
 
-  gtk_grab_remove(GTK_WIDGET(win));
-  gtk_widget_destroy(GTK_WIDGET(win));
+        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);
+      }
+    }
+      
+    /* 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 */
+  cap_input_id = gtk_input_add_full (sync_pipe[0],
+                                    GDK_INPUT_READ,
+                                    cap_file_input_cb,
+                                    NULL,
+                                    (gpointer) cf,
+                                    NULL);
 }
 
-void
-capture(gint open) {
-  GtkWidget  *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb, 
-             *ospf_lb, *other_lb, *stop_bt;
+/* 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, *tcp_lb, *udp_lb, *icmp_lb,
+             *ospf_lb, *gre_lb, *netbios_lb, *ipx_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;
-  
-  ld.go    = TRUE;
-  ld.count = 0;
-  ld.max   = cf.count;
-  ld.tcp   = 0;
-  ld.udp   = 0;
-  ld.ospf  = 0;
-  ld.other = 0;
-  ld.pdh   = NULL;
-
-  close_cap_file(&cf, info_bar, file_ctx);
-
+  int         err, inpkts;
+  char        errmsg[1024+1];
+
+  ld.go             = TRUE;
+  ld.counts.total   = 0;
+  ld.max            = cf.count;
+  ld.linktype       = WTAP_ENCAP_UNKNOWN;
+  ld.sync_packets   = 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.other   = 0;
+  ld.pdh            = NULL;
+
+  /* Open the network interface to capture from it. */
   pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
 
-  if (pch) {
-    if (cf.save_file[0]) {
-      ld.pdh = pcap_dump_open(pch, cf.save_file);
-      if (ld.pdh == NULL) {  /* We have an error */
-        snprintf(err_str, PCAP_ERRBUF_SIZE, "Error trying to open dump "
-          "file:\n%s", pcap_geterr(pch));
-        simple_dialog(ESD_TYPE_WARN, NULL, err_str);
-        g_free(cf.save_file);
-        cf.save_file = NULL;
-        pcap_close(pch);
-        return;
-      }
+  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 (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.");
-        return;
-      } else if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
-        simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
-        return;
-      } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
-        simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
-        return;
-      }
+  if (cf.cfilter) {
+    /* A capture filter was specified; set it up. */
+    if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
+      snprintf(errmsg, sizeof errmsg,
+        "Can't use filter:  Couldn't obtain netmask info (%s).", err_str);
+      goto error;
     }
+    if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
+      snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
+       pcap_geterr(pch));
+      goto error;
+    }
+    if (pcap_setfilter(pch, &cf.fcode) < 0) {
+      snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
+       pcap_geterr(pch));
+      goto error;
+    }
+  }
 
-    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);
-
-    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);
-
-    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();
-      pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
-
-      /* 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;
+  /* 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(cf.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;
+
+    default:
+      if (err < 0) {
+        sprintf(errmsg, "The file to which the capture would be"
+                     " saved (\"%s\") could not be opened: Error %d.",
+                       cf.save_file, err);
+      } else {
+        sprintf(errmsg, "The file to which the capture would be"
+                     " saved (\"%s\") could not be opened: %s.",
+                       cf.save_file, strerror(err));
+      }
+      break;
+    }
+    goto error;
+  }
 
-        sprintf(label_str, "Count: %d", ld.count);
-        gtk_label_set(GTK_LABEL(count_lb), label_str);
+  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);
+  }
 
-        sprintf(label_str, "TCP: %d (%.1f%%)", ld.tcp, pct(ld.tcp, ld.count));
-        gtk_label_set(GTK_LABEL(tcp_lb), label_str);
+  cap_w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title(GTK_WINDOW(cap_w), "Ethereal: Capture / Playback");
 
-        sprintf(label_str, "UDP: %d (%.1f%%)", ld.udp, pct(ld.udp, ld.count));
-        gtk_label_set(GTK_LABEL(udp_lb), label_str);
+  /* 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);
 
-        sprintf(label_str, "OSPF: %d (%.1f%%)", ld.ospf, pct(ld.ospf, ld.count));
-        gtk_label_set(GTK_LABEL(ospf_lb), label_str);
+  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);
 
-        sprintf(label_str, "Other: %d (%.1f%%)", ld.other,
-          pct(ld.other, ld.count));
-        gtk_label_set(GTK_LABEL(other_lb), label_str);
+  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);
+
+  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();
+    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, "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, "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;
       }
     }
+  }
     
-    if (ld.pdh) pcap_dump_close(ld.pdh);
-    pcap_close(pch);
-
-    gtk_grab_remove(GTK_WIDGET(cap_w));
-    gtk_widget_destroy(GTK_WIDGET(cap_w));
+  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;
+
+    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;
+
+    default:
+      simple_dialog(ESD_TYPE_WARN, NULL,
+               "The file to which the capture was being"
+               " saved (\"%s\") could not be closed: %s.",
+               cf.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 couldn't even start the capture, so get rid of the capture
+     file. */
+  unlink(cf.save_file); /* silently ignore error */
+  g_free(cf.save_file);
+  cf.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. */
+    int msglen = strlen(errmsg);
+    char lenbuf[10+1+1];
+    sprintf(lenbuf, "%u;", msglen);
+    write(1, lenbuf, strlen(lenbuf));
+    write(1, errmsg, msglen);
   } 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.");
-    g_free(cf.save_file);
-    cf.save_file = NULL;
+    /* Display the dialog box ourselves; there's no parent. */
+    simple_dialog(ESD_TYPE_WARN, NULL, errmsg);
   }
+  if (pch != NULL)
+    pcap_close(pch);
 
-  if (cf.save_file && open) load_cap_file(cf.save_file, &cf);
+  return FALSE;
 }
 
-float
+static float
 pct(gint num, gint denom) {
   if (denom) {
     return (float) num * 100.0 / (float) denom;
@@ -525,59 +782,58 @@ pct(gint num, gint denom) {
   }
 }
 
-void
+static void
 capture_stop_cb(GtkWidget *w, gpointer data) {
   loop_data *ld = (loop_data *) data;
   
   ld->go = FALSE;
 }
 
-void
+static void
 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
   const u_char *pd) {
-  
-  guint16 etype;
-  guint8  iptype = 0;
-  gint    offset = 14;
-  
+  struct wtap_pkthdr whdr;
   loop_data *ld = (loop_data *) user;
-  
-  if ((++ld->count >= ld->max) && (ld->max > 0)) 
+  int err;
+
+  if ((++ld->counts.total >= ld->max) && (ld->max > 0)) 
   {
      ld->go = FALSE;
   }
-  /* Currently, pcap_dumper_t is a FILE *.  Let's hope that doesn't change. */
-  if (ld->pdh) pcap_dump((u_char *) ld->pdh, phdr, pd);
-  
-  etype = etype = (pd[12] << 8) | pd[13];
-  if (etype <= IEEE_802_3_MAX_LEN) {
-    etype = (pd[20] << 8) | pd[21];
-    offset = 22;
+  if (ld->pdh) {
+     whdr.ts = phdr->ts;
+     whdr.caplen = phdr->caplen;
+     whdr.len = phdr->len;
+     whdr.pkt_encap = ld->linktype;
+
+     /* XXX - do something if this fails */
+     wtap_dump(ld->pdh, &whdr, pd, &err);
   }
-  
-  switch(etype){ 
-    case ETHERTYPE_IP:
-      iptype = pd[offset + 9];
-      switch (iptype) {
-        case IP_PROTO_TCP:
-          ld->tcp++;
-          break;
-        case IP_PROTO_UDP:
-          ld->udp++;
-          break;
-        case IP_PROTO_OSPF:
-          ld->ospf++;
-          break;
-        default:
-          ld->other++;
-        }
-        break;
-      case ETHERTYPE_IPX:
-      case ETHERTYPE_IPv6:
-      case ETHERTYPE_ATALK:
-      case ETHERTYPE_VINES:
-      case ETHERTYPE_ARP:
-      default:
-        ld->other++;
+    
+  switch (ld->linktype) {
+    case WTAP_ENCAP_ETHERNET:
+      capture_eth(pd, phdr->caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_FDDI:
+    case WTAP_ENCAP_FDDI_BITSWAPPED:
+      capture_fddi(pd, phdr->caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_TR:
+      capture_tr(pd, phdr->caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_NULL:
+      capture_null(pd, phdr->caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_PPP:
+      capture_ppp(pd, phdr->caplen, &ld->counts);
+      break;
+    case WTAP_ENCAP_RAW_IP:
+      capture_raw(pd, phdr->caplen, &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. */
   }
 }
+
+#endif /* HAVE_LIBPCAP */