Another step towards using the parent/child mode for ALL captures.
[obnox/wireshark/wip.git] / capture.c
index 0ecf9bda585b592a98b7aacf9b402622ee4252f5..dfee5f82b7078840236bfb5780ffe1dbee9878c1 100644 (file)
--- a/capture.c
+++ b/capture.c
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-/* With MSVC and a libethereal.dll this file needs to import some variables 
-   in a special way. Therefore _NEED_VAR_IMPORT_ is defined. */
-#define _NEED_VAR_IMPORT_
-
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
@@ -34,6 +30,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #include "file.h"
 #include "capture.h"
 #include "capture_sync.h"
+#include "capture_ui_utils.h"
 #include "util.h"
 #include "pcap-util.h"
 #include "alert_box.h"
 #include "simple_dialog.h"
 #include <epan/prefs.h>
-#include "globals.h"
 #include "conditions.h"
 #include "ringbuffer.h"
 
 #endif
 #include "ui_util.h"
 
-/*
- * Capture options.
- */
-capture_options capture_opts;
-gboolean capture_child;                /* if this is the child for "-S" */
-
 
 /* Win32 needs the O_BINARY flag for open() */
 #ifndef O_BINARY
 #define O_BINARY       0
 #endif
 
-static gboolean normal_do_capture(gboolean is_tempfile);
+static gboolean normal_do_capture(capture_options *capture_opts, gboolean is_tempfile);
 static void stop_capture_signal_handler(int signo);
 
-/* Open a specified file, or create a temporary file, and start a capture
-   to the file in question.  Returns TRUE if the capture starts
-   successfully, FALSE otherwise. */
-gboolean
-do_capture(const char *save_file)
-{
+
+/* open the output file (temporary/specified name/ringbuffer) */
+/* Returns TRUE if the file opened successfully, FALSE otherwise. */
+static gboolean
+capture_open_output(capture_options *capture_opts, gboolean *is_tempfile) {
   char tmpname[128+1];
-  gboolean is_tempfile;
   gchar *capfile_name;
-  gboolean ret;
 
-  if (save_file != NULL) {
+
+  if (capture_opts->save_file != NULL) {
     /* If the Sync option is set, we return to the caller while the capture
      * is in progress.  Therefore we need to take a copy of save_file in
      * case the caller destroys it after we return.
      */
-    capfile_name = g_strdup(save_file);
-    if (capture_opts.multi_files_on) {
+    capfile_name = g_strdup(capture_opts->save_file);
+    if (capture_opts->multi_files_on) {
       /* ringbuffer is enabled */
-      cfile.save_file_fd = ringbuf_init(capfile_name,
-          (capture_opts.has_ring_num_files) ? capture_opts.ring_num_files : 0);
+      capture_opts->save_file_fd = ringbuf_init(capfile_name,
+          (capture_opts->has_ring_num_files) ? capture_opts->ring_num_files : 0);
     } else {
       /* 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,
+      capture_opts->save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
                                0600);
     }
-    is_tempfile = FALSE;
+    *is_tempfile = FALSE;
   } else {
-    /* Choose a random name for the capture buffer */
-    cfile.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
+    /* Choose a random name for the temporary capture buffer */
+    capture_opts->save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
     capfile_name = g_strdup(tmpname);
-    is_tempfile = TRUE;
+    *is_tempfile = TRUE;
   }
-  if (cfile.save_file_fd == -1) {
-    if (is_tempfile) {
+
+  /* did we fail to open the output file? */
+  if (capture_opts->save_file_fd == -1) {
+    if (*is_tempfile) {
       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
-       "The temporary file to which the capture would be saved (\"%s\")"
+       "The temporary file to which the capture would be saved (\"%s\") "
        "could not be opened: %s.", capfile_name, strerror(errno));
     } else {
-      if (capture_opts.multi_files_on) {
+      if (capture_opts->multi_files_on) {
         ringbuf_error_cleanup();
       }
       open_failure_alert_box(capfile_name, errno, TRUE);
@@ -131,21 +122,66 @@ do_capture(const char *save_file)
     g_free(capfile_name);
     return FALSE;
   }
-  cf_close(&cfile);
-  g_assert(cfile.save_file == NULL);
-  cfile.save_file = capfile_name;
-  /* cfile.save_file is "g_free"ed below, which is equivalent to
+
+  if(capture_opts->save_file != NULL) {
+    g_free(capture_opts->save_file);
+  }
+  capture_opts->save_file = capfile_name;
+  /* capture_opts.save_file is "g_free"ed later, which is equivalent to
      "g_free(capfile_name)". */
 
-  if (capture_opts.sync_mode) {        
+  return TRUE;
+}
+
+
+/* close the output file (NOT the capture file) */
+static void
+capture_close_output(capture_options *capture_opts)
+{
+    if (capture_opts->multi_files_on) {
+        ringbuf_free();
+    } else {
+        g_free(capture_opts->save_file);
+    }
+    capture_opts->save_file = NULL;
+}
+
+
+/* Open a specified file, or create a temporary file, and start a capture
+   to the file in question.  */
+/* Returns TRUE if the capture starts successfully, FALSE otherwise. */
+gboolean
+do_capture(capture_options *capture_opts)
+{
+  gboolean is_tempfile;
+  gboolean ret;
+
+
+  /* open the new output file (temporary/specified name/ringbuffer) */
+  if(!capture_open_output(capture_opts, &is_tempfile)) {
+    return FALSE;
+  }
+
+  /* close the currently loaded capture file */
+  cf_close(capture_opts->cf);
+
+  /* We could simply use TRUE for this expression now, this will work for all 
+   * captures except for some of the multiple files options, as these capture 
+   * options currently cannot be passed through the command line to the 
+   * capture child.
+   *
+   * If this is fixed, we could always use the sync mode, throwing away the 
+   * normal mode completely and doing some more cleanup. */
+/*  if (TRUE) {*/
+  if (capture_opts->sync_mode) {
     /* sync mode: do the capture in a child process */
-    ret = sync_pipe_do_capture(is_tempfile);
+    ret = sync_pipe_do_capture(capture_opts, is_tempfile);
     /* capture is still running */
-    set_main_window_name("(Live Capture in Progress) - Ethereal");
+    cf_callback_invoke(cf_cb_live_capture_prepare, capture_opts);
   } else {
     /* normal mode: do the capture synchronously */
-    set_main_window_name("(Live Capture in Progress) - Ethereal");
-    ret = normal_do_capture(is_tempfile);
+    cf_callback_invoke(cf_cb_live_capture_prepare, capture_opts);
+    ret = normal_do_capture(capture_opts, is_tempfile);
     /* capture is finished here */
   }
 
@@ -153,47 +189,24 @@ do_capture(const char *save_file)
 }
 
 
-/* start a normal capture session */
-static gboolean
-normal_do_capture(gboolean is_tempfile)
+/* we've succeeded a capture, try to read it into a new capture file */
+gboolean
+capture_read(capture_options *capture_opts, gboolean is_tempfile, gboolean drops_known,
+guint32 drops)
 {
-    int capture_succeeded;
-    gboolean stats_known;
-    struct pcap_stat stats;
     int err;
 
-    /* Not sync mode. */
-    capture_succeeded = capture_start(&stats_known, &stats);
-    if (capture_opts.quit_after_cap) {
-      /* DON'T unlink the save file.  Presumably someone wants it. */
-        main_window_exit();
-    }
-    if (!capture_succeeded) {
-      /* We didn't succeed in doing the capture, so we don't have a save
-        file. */
-      if (capture_opts.multi_files_on) {
-       ringbuf_free();
-      } else {
-       g_free(cfile.save_file);
-      }
-      cfile.save_file = NULL;
-      return FALSE;
-    }
+
     /* Capture succeeded; attempt to read in the capture file. */
-    if ((err = cf_open(cfile.save_file, is_tempfile, &cfile)) != 0) {
+    if (cf_open(capture_opts->cf, capture_opts->save_file, is_tempfile, &err) != CF_OK) {
       /* We're not doing a capture any more, so we don't have a save
         file. */
-      if (capture_opts.multi_files_on) {
-       ringbuf_free();
-      } else {
-       g_free(cfile.save_file);
-      }
-      cfile.save_file = NULL;
       return FALSE;
     }
 
     /* Set the read filter to NULL. */
-    cfile.rfcode = NULL;
+    /* XXX - this is odd here, try to put it somewhere, where it fits better */
+    cf_set_rfcode(capture_opts->cf, NULL);
 
     /* Get the packet-drop statistics.
 
@@ -213,8 +226,8 @@ normal_do_capture(gboolean is_tempfile)
        we'll put them into the capture file that we write, and will
        thus not have to set them here - "cf_read()" will get them from
        the file and use them. */
-    if (stats_known) {
-      cfile.drops_known = TRUE;
+    if (drops_known) {
+      cf_set_drops_known(capture_opts->cf, TRUE);
 
       /* XXX - on some systems, libpcap doesn't bother filling in
          "ps_ifdrop" - it doesn't even set it to zero - so we don't
@@ -224,46 +237,63 @@ normal_do_capture(gboolean is_tempfile)
          several statistics - perhaps including various interface
          error statistics - and would tell us which of them it
          supplies, allowing us to display only the ones it does. */
-      cfile.drops = stats.ps_drop;
+      cf_set_drops(capture_opts->cf, drops);
     }
-    switch (cf_read(&cfile)) {
+    switch (cf_read(capture_opts->cf)) {
 
-    case READ_SUCCESS:
-    case READ_ERROR:
+    case CF_READ_OK:
+    case CF_READ_ERROR:
       /* Just because we got an error, that doesn't mean we were unable
          to read any of the file; we handle what we could get from the
          file. */
       break;
 
-    case READ_ABORTED:
+    case CF_READ_ABORTED:
       /* Exit by leaving the main loop, so that any quit functions
          we registered get called. */
       main_window_nested_quit();
       return FALSE;
     }
 
-    /* We're not doing a capture any more, so we don't have a save
-       file. */
-    if (capture_opts.multi_files_on) {
-      ringbuf_free();
-    } else {
-      g_free(cfile.save_file);
-    }
-    cfile.save_file = NULL;
-
     /* if we didn't captured even a single packet, close the file again */
-    if(cfile.count == 0) {
+    if(cf_packet_count(capture_opts->cf) == 0) {
       simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, 
       "%sNo packets captured!%s\n\n"
       "As no data was captured, closing the %scapture file!",
       simple_dialog_primary_start(), simple_dialog_primary_end(),
-      (cfile.is_tempfile) ? "temporary " : "");
-      cf_close(&cfile);
+      (cf_is_tempfile(capture_opts->cf)) ? "temporary " : "");
+      cf_close(capture_opts->cf);
     }
   return TRUE;
 }
 
 
+/* start a normal capture session */
+static gboolean
+normal_do_capture(capture_options *capture_opts, gboolean is_tempfile)
+{
+    gboolean succeeded;
+    gboolean stats_known;
+    struct pcap_stat stats;
+
+
+    /* Not sync mode. */
+    succeeded = capture_loop_start(capture_opts, &stats_known, &stats);
+    if (capture_opts->quit_after_cap) {
+      /* DON'T unlink the save file.  Presumably someone wants it. */
+        main_window_exit();
+    }
+    if (succeeded) {
+        /* We succeed in doing the capture, try to read it in. */
+        succeeded = capture_read(capture_opts, is_tempfile, stats_known, stats.ps_drop);
+    }
+
+    /* wether the capture suceeded or not, we have to close the output file here */
+    capture_close_output(capture_opts);
+    return succeeded;
+}
+
+
 static void
 stop_capture_signal_handler(int signo _U_)
 {
@@ -272,36 +302,51 @@ stop_capture_signal_handler(int signo _U_)
 
 
 int  
-capture_start(gboolean *stats_known, struct pcap_stat *stats)
+capture_child_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats)
 {
+  gchar *err_msg;
+
+  g_assert(capture_opts->capture_child);
+
 #ifndef _WIN32
   /*
    * Catch SIGUSR1, so that we exit cleanly if the parent process
    * kills us with it due to the user selecting "Capture->Stop".
    */
-  if (capture_child)
     signal(SIGUSR1, stop_capture_signal_handler);
 #endif
 
-  return capture_loop_start(stats_known, stats);
+    /* parent must have send us a file descriptor for the opened output file */
+    if (capture_opts->save_file_fd == -1) {
+      /* send this to the standard output as something our parent
+            should put in an error message box */
+      err_msg = g_strdup_printf("%s: \"-W\" flag not specified (internal error)\n", CHILD_NAME);
+      sync_pipe_errmsg_to_parent(err_msg);
+      g_free(err_msg);
+      return FALSE;
+    }
+
+  return capture_loop_start(capture_opts, stats_known, stats);
 }
 
 void
-capture_stop(void)
+capture_stop(capture_options *capture_opts)
 {
-
-  if (capture_opts.sync_mode) {        
-    sync_pipe_stop();
-  } else {
-    capture_loop_stop();
+  /* stop the capture child, if we have one */
+  if (!capture_opts->capture_child) {  
+    sync_pipe_stop(capture_opts);
   }
+
+  /* stop the capture loop */
+  capture_loop_stop();
 }
 
 void
-kill_capture_child(void)
+capture_kill_child(capture_options *capture_opts)
 {
-  if (capture_opts.sync_mode) {        
-    sync_pipe_kill();
+  /* kill the capture child, if we have one */
+  if (!capture_opts->capture_child) {  
+    sync_pipe_kill(capture_opts);
   }
 }