Ethereal-dev: [Ethereal-dev] [patch] read capture from a child process, V1

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Thomas Steffen <steffen.list.account@xxxxxxxxx>
Date: Fri, 5 Aug 2005 10:27:10 +0200
Following up the recent discussion, I have written some code that
starts a child processes and reads a libpcap format capture file from
its stdout. With this feature, you can do remote capture, read a trace
live from an application, merge several traces in real time etc.

Please give it a try and tell me what you think. I definitely would
like to see this in the next release, but it probably needs some
polish first. You can start with something easy like:

ethereal -k -i '|cat /tmp/trace.cap' 

to read an existing capture file (| is the magic character that
triggers the child process code). If you have ssh-agent set up (or
x-askpass), you can also try a remote capture:

ethereal -k -i '|ssh -l root host.domain tethereal -w - icmp'

Note the icmp: you have to exclude the ssh traffic somehow, or you
will get a nice loop. You can also try netcat in different
combinations, although that is slightly less convenient.

Problems I would like to see worked out are:

* Windows: since I use glib, it might work, but does it know about /bin/sh?
* Is the magic character the right approach? It feels good, and uses
much less code, but a checkbox in the GUI may be more intuitive.
* Shall I connect stdin? That is needed for typing the ssh password,
but it may not be sufficient.
* It would be nice if tethereal could ignore the ssh traffic based on
$SSH_CONNECTION.
* If the child process fails, there are two error pop ups. (This also
happens during normal capture, but it would be nice to fix.)
* The child is never killed, only stdout is closed. Misbehaving
programs may stay alive.
* A few preset examples in the GUI would be helpful to demonstrate the feature.
* It does not work with tethereal, but then again it is not very useful there. 
* Documentation needs to be updated in several places. 

The patch applies against ethereal-0.10.12, I hope it also works for CVS head. 

Thomas
diff -u ethereal-0.10.12/capture_loop.c ethereal-0.10.12.new/capture_loop.c
--- ethereal-0.10.12/capture_loop.c	2005-07-26 21:26:51.000000000 +0200
+++ ethereal-0.10.12.new/capture_loop.c	2005-08-04 22:54:08.000000000 +0200
@@ -225,6 +225,7 @@
   }
 }
 
+
 /* Mimic pcap_open_live() for pipe captures
  * We check if "pipename" is "-" (stdin) or a FIFO, open it, and read the
  * header.
@@ -241,16 +242,30 @@
   unsigned int bytes_read;
   fd_set      rfds;
   struct timeval timeout;
-
+  gboolean    ok;
+  GError      error;
+  char       *argv[] = { "/bin/sh", "-c", "arg", NULL };
 
   g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: %s", pipename);
 
+  fprintf(stderr, "open pipe %s\n", pipename);
   /*
    * XXX Ethereal blocks until we return
    */
   if (strcmp(pipename, "-") == 0)
     fd = 0; /* read from stdin */
-  else {
+  else if (pipename[0] == '|') {
+      argv[2] = pipename + 1;
+      ok = g_spawn_async_with_pipes(NULL, argv, NULL, 0, NULL, NULL,
+                                    NULL, NULL, &fd, NULL, &error);
+      if (!ok) {
+          g_snprintf(errmsg, errmsgl,
+                     "The capture child process could not be started: %s.",
+                     error.message);
+          ld->cap_pipe_err = PIPERR;
+          return -1;
+      }
+  } else {
     if (stat(pipename, &pipe_stat) < 0) {
       if (errno == ENOENT || errno == ENOTDIR)
         ld->cap_pipe_err = PIPNEXIST;
@@ -1162,6 +1177,8 @@
   int         save_file_fd;
 
 
+  fprintf(stderr, "LOG start %s\n", capture_opts->iface);
+
   /* init the loop data */
   ld.go                 = TRUE;
   if (capture_opts->has_autostop_packets)
diff -u ethereal-0.10.12/capture_ui_utils.c ethereal-0.10.12.new/capture_ui_utils.c
--- ethereal-0.10.12/capture_ui_utils.c	2005-07-26 21:26:51.000000000 +0200
+++ ethereal-0.10.12.new/capture_ui_utils.c	2005-08-04 23:03:30.000000000 +0200
@@ -336,6 +336,8 @@
    * (An interface name might, however, contain a colon in it, which
    * is why we don't use the colon search on UNIX.)
    */
+  if (if_text && if_text[0] == '|') 
+    return if_text;
   if_name = strrchr(if_text, ' ');
   if (if_name == NULL) {
     if_name = if_text;