from Gisele Vanem:
[obnox/wireshark/wip.git] / tshark.c
index d74f3ccbe166f7bd859e92041ead8d302c3a940a..b03372a1c9f886eaa497aa17002cd5edbf3c65e6 100644 (file)
--- a/tshark.c
+++ b/tshark.c
@@ -155,8 +155,6 @@ static gboolean infoprint;  /* if TRUE, print capture info after clearing infodel
 #endif /* SIGINFO */
 
 static int capture(void);
-static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
-  const u_char *);
 static void report_counts(void);
 #ifdef _WIN32
 static BOOL WINAPI capture_cleanup(DWORD);
@@ -275,7 +273,7 @@ print_usage(gboolean print_ver)
   fprintf(output, "     separator=/t|/s|<char> select tab, space, printable character as separator\n");
   fprintf(output, "     quote=d|s|n           select double, single, no quotes for values\n");
   fprintf(output, "  -t ad|a|r|d|dd|e         output format of time stamps (def: r: rel. to first)\n");
-  fprintf(output, "  -l                       flush output after each packet\n");
+  fprintf(output, "  -l                       flush standard output after each packet\n");
   fprintf(output, "  -q                       be more quiet on stdout (e.g. when using statistics)\n");
   fprintf(output, "  -X <key>:<value>         eXtension options, see the man page for details\n");
   fprintf(output, "  -z <statistics>          various statistics, see the man page for details\n");
@@ -674,8 +672,9 @@ print_current_user() {
 }
 
 static void
-check_npf_sys() {
+check_capture_privs() {
 #ifdef _WIN32
+  load_wpcap();
   /* Warn the user if npf.sys isn't loaded. */
   if (!npf_sys_is_running() && get_os_major_version() >= 6) {
     fprintf(stderr, "The NPF driver isn't running.  You may have trouble "
@@ -913,6 +912,7 @@ main(int argc, char *argv[])
     g_free(dp_path);
   }
 
+  check_capture_privs();
 
   init_cap_file(&cfile);
 
@@ -960,7 +960,6 @@ main(int argc, char *argv[])
         break;
       case 'D':        /* Print a list of capture devices and exit */
 #ifdef HAVE_LIBPCAP
-        check_npf_sys();
         status = capture_opts_list_interfaces(FALSE);
         exit(status);
 #else
@@ -1024,8 +1023,9 @@ main(int argc, char *argv[])
           g_resolv_flags = RESOLV_NONE;
         badopt = string_to_name_resolve(optarg, &g_resolv_flags);
         if (badopt != '\0') {
-          cmdarg_err("-N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'",
+          cmdarg_err("-N specifies unknown resolving option '%c';",
                      badopt);
+          cmdarg_err_cont( "           Valid options are 'm', 'n', 't', and 'C'");
           exit(1);
         }
         break;
@@ -1537,7 +1537,6 @@ main(int argc, char *argv[])
 
     /* if requested, list the link layer types and exit */
     if (list_link_layer_types) {
-        check_npf_sys();
         status = capture_opts_list_link_layer_types(&capture_opts, FALSE);
         exit(status);
     }
@@ -1610,11 +1609,14 @@ typedef struct pipe_input_tag {
     int                 *child_process;
     pipe_input_cb_t     input_cb;
     guint               pipe_input_id;
-       GStaticMutex            callback_running;
+#ifdef _WIN32
+    GStaticMutex               callback_running;
+#endif
 } pipe_input_t;
 
 static pipe_input_t pipe_input;
 
+#ifdef _WIN32
 /* The timer has expired, see if there's stuff to read from the pipe,
    if so, do the callback */
 static gint
@@ -1674,17 +1676,20 @@ pipe_timer_cb(gpointer data)
        /* we didn't stopped the timer, so let it run */
        return TRUE;
 }
+#endif
 
-void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
+
+void
+pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
 {
 
     pipe_input.source                  = source;
-    pipe_input.child_process   = child_process;
+    pipe_input.child_process           = child_process;
     pipe_input.user_data               = user_data;
     pipe_input.input_cb                        = input_cb;
-       pipe_input.callback_running     = G_STATIC_MUTEX_INIT;
 
 #ifdef _WIN32
+    g_static_mutex_init(&pipe_input.callback_running);
     /* 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
@@ -1692,16 +1697,6 @@ void pipe_input_set_handler(gint source, gpointer user_data, int *child_process,
        timeout. */
        /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
     pipe_input.pipe_input_id = g_timeout_add(200, pipe_timer_cb, &pipe_input);
-#else
-       /* XXX - in gui_utils.c, this was: 
-        * pipe_input->pipe_input_id = gtk_input_add_full (source,
-        *                   GDK_INPUT_READ|GDK_INPUT_EXCEPTION, */
-    pipe_input.pipe_input_id = g_input_add_full(source,
-                                     200,
-                                     pipe_input_cb,
-                                     NULL,
-                                     &pipe_input,
-                                     NULL);
 #endif
 }
 
@@ -1711,7 +1706,12 @@ static int
 capture(void)
 {
   gboolean ret;
-
+#ifdef USE_TSHARK_SELECT
+  fd_set readfds;
+#endif
+#ifndef _WIN32
+  struct sigaction action, oldaction;
+#endif
 
   /*
    * XXX - dropping privileges is still required, until code cleanup is done
@@ -1721,7 +1721,7 @@ capture(void)
    * therefore we don't need to drop these privileges
    * The only thing we might want to keep is a warning if tshark is run as root,
    * as it's no longer necessary and potentially dangerous.
-   * 
+   *
    * THE FOLLOWING IS THE FORMER COMMENT WHICH IS NO LONGER REALLY VALID:
    * We've opened the capture device, so we shouldn't need any special
    * privileges any more; relinquish those privileges.
@@ -1737,28 +1737,31 @@ capture(void)
   relinquish_special_privs_perm();
   print_current_user();
 
-  check_npf_sys();
-
   /* Initialize all data structures used for dissection. */
   init_dissection();
-  
+
 #ifdef _WIN32
   /* Catch a CTRL+C event and, if we get it, clean up and exit. */
   SetConsoleCtrlHandler(capture_cleanup, TRUE);
 #else /* _WIN32 */
   /* Catch SIGINT and SIGTERM and, if we get either of them, clean up
-     and exit.
-     XXX - deal with signal semantics on various UNIX platforms.  Or just
-     use "sigaction()" and be done with it? */
-  signal(SIGTERM, capture_cleanup);
-  signal(SIGINT, capture_cleanup);
-  if ((oldhandler = signal(SIGHUP, capture_cleanup)) != SIG_DFL)
-    signal(SIGHUP, oldhandler);
+     and exit. */
+  action.sa_handler = capture_cleanup;
+  action.sa_flags = 0;
+  sigemptyset(&action.sa_mask);
+  sigaction(SIGTERM, &action, NULL);
+  sigaction(SIGINT, &action, NULL);
+  sigaction(SIGHUP, NULL, &oldaction);
+  if (oldaction.sa_handler == SIG_DFL)
+    sigaction(SIGHUP, &action, NULL);
 
 #ifdef SIGINFO
   /* Catch SIGINFO and, if we get it and we're capturing to a file in
      quiet mode, report the number of packets we've captured. */
-  signal(SIGINFO, report_counts_siginfo);
+  action.sa_handler = report_counts_siginfo;
+  action.sa_flags = 0;
+  sigemptyset(&action.sa_mask);
+  sigaction(SIGINFO, &action, NULL);
 #endif /* SIGINFO */
 #endif /* _WIN32 */
 
@@ -1769,30 +1772,57 @@ capture(void)
   fprintf(stderr, "Capturing on %s\n", capture_opts.iface_descr);
 
   ret = sync_pipe_start(&capture_opts);
-  if(ret) {
-       /* the actual capture loop
-        *
-        * XXX - glib doesn't seem to provide any event based loop handling.
-        *
-        * XXX - for whatever reason, 
-        * calling g_main_loop_new() ends up in 100% cpu load.
-        * Therefore use simple g_usleep() to poll for new input. */
-#ifdef USE_BROKEN_G_MAIN_LOOP
-    /*loop = g_main_loop_new(NULL, FALSE);*/
-       /*g_main_loop_run(loop);*/
-    loop = g_main_new(FALSE);
-       g_main_run(loop);
-#else
-    loop_running = TRUE;
 
-       while(loop_running && pipe_timer_cb(&pipe_input)) {
-               g_usleep(200000);       /* 200 ms */
-       }
+  if (!ret)
+    return FALSE;
+
+    /* the actual capture loop
+     *
+     * XXX - glib doesn't seem to provide any event based loop handling.
+     *
+     * XXX - for whatever reason,
+     * calling g_main_loop_new() ends up in 100% cpu load.
+     *
+     * But that doesn't matter: in UNIX we can use select() to find an input
+     * source with something to do.
+     *
+     * But that doesn't matter because we're in a CLI (that doesn't need to
+     * update a GUI or something at the same time) so it's OK if we block
+     * trying to read from the pipe.
+     *
+     * So all the stuff in USE_TSHARK_SELECT could be removed unless I'm
+     * wrong (but I leave it there in case I am...).
+     */
+
+#ifdef USE_TSHARK_SELECT
+  FD_ZERO(&readfds);
+  FD_SET(pipe_input.source, &readfds);
 #endif
-       return TRUE;
-  } else {
+
+  loop_running = TRUE;
+
+  while (loop_running)
+  {
+#ifdef USE_TSHARK_SELECT
+    ret = select(pipe_input.source+1, &readfds, NULL, NULL, NULL);
+
+    if (ret == -1)
+    {
+      perror("select()");
+      return TRUE;
+    } else if (ret == 1) {
+#endif
+      /* Call the real handler */
+      if (!pipe_input.input_cb(pipe_input.source, pipe_input.user_data)) {
+       g_log(NULL, G_LOG_LEVEL_DEBUG, "input pipe closed");
        return FALSE;
+      }
+#ifdef USE_TSHARK_SELECT
+    }
+#endif
   }
+
+  return TRUE;
 }
 
 
@@ -1806,7 +1836,7 @@ void main_window_update(void)
 #include "simple_dialog.h"
 
 /* capture_sync.c want's to tell us an error */
-gpointer simple_dialog(ESD_TYPE_E type, gint btn_mask,
+gpointer simple_dialog(ESD_TYPE_E type _U_, gint btn_mask _U_,
                                           const gchar *msg_format, ...)
 {
        va_list ap;
@@ -1824,7 +1854,7 @@ gpointer simple_dialog(ESD_TYPE_E type, gint btn_mask,
 
 /* capture child detected an error */
 void
-capture_input_error_message(capture_options *capture_opts, char *error_msg, char *secondary_error_msg)
+capture_input_error_message(capture_options *capture_opts _U_, char *error_msg, char *secondary_error_msg)
 {
        cmdarg_err("%s", error_msg);
        cmdarg_err_cont("%s", secondary_error_msg);
@@ -1940,24 +1970,30 @@ capture_input_new_packets(capture_options *capture_opts, int to_read)
 #endif /* SIGINFO */
 
   if(do_dissection) {
-         while (to_read-- && cf->wth) {
-                 ret = wtap_read(cf->wth, &err, &err_info, &data_offset);
-                 if(ret == FALSE) {
-                         /* read from file failed, tell the capture child to stop */
-                         sync_pipe_stop(capture_opts);
-                         wtap_close(cf->wth);
-                         cf->wth = NULL;
-                 } else {
-                         ret = process_packet(cf, data_offset, wtap_phdr(cf->wth),
-                                                          wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth));
-                 }
-                 if (ret != FALSE) {
-                       /* packet sucessfully read and gone through the "Read Filter" */
-                       packet_count++;
-                 }
-         }
-  } 
-  
+    while (to_read-- && cf->wth) {
+      ret = wtap_read(cf->wth, &err, &err_info, &data_offset);
+      if(ret == FALSE) {
+        /* read from file failed, tell the capture child to stop */
+        sync_pipe_stop(capture_opts);
+        wtap_close(cf->wth);
+        cf->wth = NULL;
+      } else {
+        ret = process_packet(cf, data_offset, wtap_phdr(cf->wth),
+                             wtap_pseudoheader(cf->wth), wtap_buf_ptr(cf->wth));
+      }
+      if (ret != FALSE) {
+        /* packet sucessfully read and gone through the "Read Filter" */
+        packet_count++;
+      }
+    }
+  } else {
+    /*
+     * Dumpcap's doing all the work; we're not doing any dissection.
+     * Count all the packets it wrote.
+     */
+    packet_count += to_read;
+  }
+
   if (print_packet_counts) {
       /* We're printing packet counts. */
       if (packet_count != 0) {
@@ -1984,13 +2020,6 @@ capture_input_new_packets(capture_options *capture_opts, int to_read)
 static void
 report_counts(void)
 {
-#ifdef SIGINFO
-  /* XXX - if we use sigaction, this doesn't have to be done.
-     (Yes, this isn't necessary on BSD, but just in case a system
-     where "signal()" has AT&T semantics adopts SIGINFO....) */
-  signal(SIGINFO, report_counts_siginfo);
-#endif /* SIGINFO */
-
   if (!print_packet_counts) {
     /* Report the count only if we aren't printing a packet count
        as packets arrive. */
@@ -2020,7 +2049,7 @@ report_counts_siginfo(int signum _U_)
 
 /* capture child detected any packet drops? */
 void
-capture_input_drops(capture_options *capture_opts, int dropped)
+capture_input_drops(capture_options *capture_opts _U_, int dropped)
 {
        if (print_packet_counts) {
        /* We're printing packet counts to stderr.
@@ -2040,22 +2069,16 @@ capture_input_drops(capture_options *capture_opts, int dropped)
 void
 capture_input_closed(capture_options *capture_opts)
 {
-       if (!print_packet_counts) {
-    /* Report the count only if we aren't printing a packet count
-       as packets arrive. */
-      fprintf(stderr, "%u packets captured\n", packet_count);
-  }
-
-       /*printf("capture_input_closed\n");*/
+  report_counts();
 
-       if(capture_opts->cf != NULL && ((capture_file *) capture_opts->cf)->wth != NULL) {
-               wtap_close(((capture_file *) capture_opts->cf)->wth);
-       }
+  if(capture_opts->cf != NULL && ((capture_file *) capture_opts->cf)->wth != NULL) {
+    wtap_close(((capture_file *) capture_opts->cf)->wth);
+  }
 #ifdef USE_BROKEN_G_MAIN_LOOP
-       /*g_main_loop_quit(loop);*/
-       g_main_quit(loop);
+  /*g_main_loop_quit(loop);*/
+  g_main_quit(loop);
 #else
-       loop_running = FALSE;
+  loop_running = FALSE;
 #endif
 }
 
@@ -2092,8 +2115,8 @@ capture_cleanup(DWORD ctrltype _U_)
   /* tell the capture child to stop */
   sync_pipe_stop(&capture_opts);
 
-  /* don't stop our own loop already here, otherwise status messages and 
-   * cleanup wouldn't be done properly. The child will indicate the stop of 
+  /* don't stop our own loop already here, otherwise status messages and
+   * cleanup wouldn't be done properly. The child will indicate the stop of
    * everything by calling capture_input_closed() later */
 
   return TRUE;
@@ -2102,11 +2125,8 @@ capture_cleanup(DWORD ctrltype _U_)
 static void
 capture_cleanup(int signum _U_)
 {
-  /* Longjmp back to the starting point; "pcap_dispatch()", on many
-     UNIX platforms, just keeps looping if it gets EINTR, so if we set
-     "ld.go" to FALSE and return, we won't break out of it and quit
-     capturing. */
-  longjmp(ld.stopenv, 1);
+  /* tell the capture child to stop */
+  sync_pipe_stop(&capture_opts);
 }
 #endif /* _WIN32 */
 #endif /* HAVE_LIBPCAP */