Ethereal-dev: [Ethereal-dev] [PATCH] Allow tethereal to write packet data to stdout

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

From: Graeme Hewson <ghewson@xxxxxxxxxxxxxxxxxxx>
Date: Sun, 14 Jul 2002 19:03:39 +0100
These patches allow the user to specify "-w -" to tethereal.  There's
also some optimisation of the capture loop.


Graeme Hewson
--- tethereal.c.orig	Sun Jul  7 22:52:48 2002
+++ tethereal.c	Sun Jul 14 10:54:59 2002
@@ -675,23 +675,30 @@
   /* See if we're writing a capture file and the file is a pipe */
   ld.output_to_pipe = FALSE;
   if (cfile.save_file != NULL) {
-    err = test_for_fifo(cfile.save_file);
-    switch (err) {
-
-    case ENOENT:	/* it doesn't exist, so we'll be creating it,
-    			   and it won't be a FIFO */
-    case 0:		/* found it, but it's not a FIFO */
-      break;
-
-    case ESPIPE:	/* it is a FIFO */
+    if (!strcmp(cfile.save_file, "-")) {
+      /* stdout */
+      g_free(cfile.save_file);
+      cfile.save_file = g_strdup("");
       ld.output_to_pipe = TRUE;
-      break;
-
-    default:		/* couldn't stat it */
-      fprintf(stderr,
-              "tethereal: Error testing whether capture file is a pipe: %s\n",
-              strerror(errno));
-      exit(2);
+    } else {
+      err = test_for_fifo(cfile.save_file);
+      switch (err) {
+  
+      case ENOENT:	/* it doesn't exist, so we'll be creating it,
+      			   and it won't be a FIFO */
+      case 0:		/* found it, but it's not a FIFO */
+        break;
+  
+      case ESPIPE:	/* it is a FIFO */
+        ld.output_to_pipe = TRUE;
+        break;
+  
+      default:		/* couldn't stat it */
+        fprintf(stderr,
+                "tethereal: Error testing whether capture file is a pipe: %s\n",
+                strerror(errno));
+        exit(2);
+      }
     }
   }
 
@@ -699,7 +706,8 @@
   /* If they didn't specify a "-w" flag, but specified a maximum capture
      file size, tell them that this doesn't work, and exit. */
   if (capture_opts.has_autostop_filesize && cfile.save_file == NULL) {
-    fprintf(stderr, "tethereal: Maximum capture file size specified, but capture isn't being saved to a file.\n");
+    fprintf(stderr, "tethereal: Maximum capture file size specified, but "
+      "capture isn't being saved to a file.\n");
     exit(2);
   }
 
@@ -882,7 +890,8 @@
   struct bpf_program fcode;
   void        (*oldhandler)(int);
   int         err = 0;
-  volatile int inpkts = 0;
+  int         volatile volatile_err = 0;
+  int         volatile inpkts = 0;
   int         pcap_cnt;
   char        errmsg[1024+1];
   condition  *volatile cnd_stop_capturesize = NULL;
@@ -892,7 +901,7 @@
   char       *libpcap_warn;
 #endif
   struct pcap_stat stats;
-  gboolean    volatile write_err = FALSE;
+  gboolean    write_err;
   gboolean    dump_ok;
 
   /* Initialize all data structures used for dissection. */
@@ -1001,7 +1010,8 @@
 
     if (ld.pdh == NULL) {
       snprintf(errmsg, sizeof errmsg, file_open_error_message(errno, TRUE),
-		cfile.save_file);
+		*cfile.save_file == '\0' ?
+		"stdout" : cfile.save_file);
       goto error;
     }
   }
@@ -1044,6 +1054,33 @@
     ld.go = FALSE;
   ld.packet_count = 0;
   while (ld.go) {
+       /* We need to be careful with automatic variables defined in the
+       outer scope which are changed inside the loop.  Most compilers
+       don't try to roll them back to their original values after the
+       longjmp which causes the loop to finish, but all that the
+       standards say is that their values are indeterminate.  If we
+       don't want them to be rolled back, we should define them with the
+       volatile attribute (paraphrasing W. Richard Stevens, Advanced
+       Programming in the UNIX Environment, p. 178).
+
+       The "err" variable causes a particular problem.  If we give it
+       the volatile attribute, then when we pass a reference to it (as
+       in "&err") to a function, GCC warns: "passing arg <n> of
+       <function> discards qualifiers from pointer target type".
+       Therefore within the loop and just beyond we don't use "err".
+       Within the loop we define "loop_err", and assign its value to
+       "volatile_err", which is in the outer scope and is checked when
+       the loop finishes.
+
+       We also define "packet_count_prev" here to keep things tidy,
+       since it's used only inside the loop.  If it were defined in the
+       outer scope, GCC would give a warning (unnecessary in this case)
+       that it might be clobbered, and we'd need to give it the volatile
+       attribute to suppress the warning. */
+
+    int loop_err = 0;
+    int packet_count_prev = 0;
+
     if (cnd_stop_capturesize == NULL && cnd_stop_timeout == NULL) {
       /* We're not stopping at a particular capture file size, and we're
          not stopping after some particular amount of time has expired,
@@ -1064,6 +1101,7 @@
         pcap_cnt = -1;
       else {
         if (ld.packet_count >= capture_opts.autostop_count) {
+          /* XXX do we need this test here? */
           /* It appears there's nothing more to capture. */
           break;
         }
@@ -1078,43 +1116,47 @@
     if (inpkts < 0) {
       /* Error from "pcap_dispatch()". */
       ld.go = FALSE;
-    } else if (capture_opts.autostop_count != 0 &&
-               ld.packet_count >= capture_opts.autostop_count) {
-      /* The specified number of packets have been captured and have
-         passed both any capture filter in effect and any read filter
-         in effect. */
-      ld.go = FALSE;
     } else if (cnd_stop_timeout != NULL && cnd_eval(cnd_stop_timeout)) {
       /* The specified capture time has elapsed; stop the capture. */
       ld.go = FALSE;
-    } else if (cnd_stop_capturesize != NULL &&
-                  cnd_eval(cnd_stop_capturesize, 
-                            (guint32)wtap_get_bytes_dumped(ld.pdh))) {
-      /* We're saving the capture to a file, and the capture file reached
-         its maximum size. */
-      if (capture_opts.ringbuffer_on) {
-        /* Switch to the next ringbuffer file */
-        if (ringbuf_switch_file(&cfile, &ld.pdh, &err)) {
-          /* File switch succeeded: reset the condition */
-          cnd_reset(cnd_stop_capturesize);
+    } else if (inpkts > 0) {
+      if (capture_opts.autostop_count != 0 &&
+                 ld.packet_count >= capture_opts.autostop_count) {
+        /* The specified number of packets have been captured and have
+           passed both any capture filter in effect and any read filter
+           in effect. */
+        ld.go = FALSE;
+      } else if (cnd_stop_capturesize != NULL &&
+                    cnd_eval(cnd_stop_capturesize, 
+                              (guint32)wtap_get_bytes_dumped(ld.pdh))) {
+        /* We're saving the capture to a file, and the capture file reached
+           its maximum size. */
+        if (capture_opts.ringbuffer_on) {
+          /* Switch to the next ringbuffer file */
+          if (ringbuf_switch_file(&cfile, &ld.pdh, &loop_err)) {
+            /* File switch succeeded: reset the condition */
+            cnd_reset(cnd_stop_capturesize);
+          } else {
+            /* File switch failed: stop here */
+            volatile_err = loop_err;
+            ld.go = FALSE;
+          }
         } else {
-          /* File switch failed: stop here */
+          /* No ringbuffer - just stop. */
           ld.go = FALSE;
-          continue;
         }
-      } else {
-        /* No ringbuffer - just stop. */
-        ld.go = FALSE;
       }
-    }
-    if (ld.output_to_pipe) {
-      /* XXX - flush only if ld.packet_count changed? */
-      if (fflush(wtap_dump_file(ld.pdh))) {
-        err = errno;
-        ld.go = FALSE;
+      if (ld.output_to_pipe) {
+        if (ld.packet_count > packet_count_prev) {
+          if (fflush(wtap_dump_file(ld.pdh))) {
+            volatile_err = errno;
+            ld.go = FALSE;
+          }
+          packet_count_prev = ld.packet_count;
+        }
       }
-    }
-  }
+    } /* inpkts > 0 */
+  } /* while (ld.go) */
   
   /* delete stop conditions */
   if (cnd_stop_capturesize != NULL)
@@ -1124,7 +1166,7 @@
 
   if ((cfile.save_file != NULL) && !quiet) {
     /* We're saving to a file, which means we're printing packet counts
-       to the standard output if we are not running silent and deep.
+       to stderr if we are not running silent and deep.
        Send a newline so that we move to the line after the packet count. */
     fprintf(stderr, "\n");
   }
@@ -1135,8 +1177,10 @@
 	pcap_geterr(ld.pch));
   }
 
-  if (err != 0) {
-    show_capture_file_io_error(cfile.save_file, err, FALSE);
+  if (volatile_err == 0)
+    write_err = FALSE;
+  else {
+    show_capture_file_io_error(cfile.save_file, volatile_err, FALSE);
     write_err = TRUE;
   }
 
@@ -1147,7 +1191,9 @@
     } else {
       dump_ok = wtap_dump_close(ld.pdh, &err);
     }
-    if (!dump_ok && ! write_err)
+    /* If we've displayed a message about a write error, there's no point
+       in displaying another message about an error on close. */
+    if (!dump_ok && !write_err)
       show_capture_file_io_error(cfile.save_file, err, TRUE);
   }
 
@@ -1195,8 +1241,8 @@
   int err;
 
   /* Convert from libpcap to Wiretap format.
-     If that fails, ignore the packet.
-     XXX - print a message. */
+     If that fails, ignore the packet (wtap_process_pcap_packet has
+     written an error message). */
   pd = wtap_process_pcap_packet(ld->linktype, phdr, pd, &pseudo_header,
 				&whdr, &err);
   if (pd == NULL) {
@@ -1295,30 +1341,33 @@
       case WTAP_ERR_UNSUPPORTED_ENCAP:
       case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
         fprintf(stderr,
-"tethereal: The capture file being read cannot be written in that format.\n");
+          "tethereal: The capture file being read cannot be written in "
+          "that format.\n");
         break;
 
       case WTAP_ERR_CANT_OPEN:
         fprintf(stderr,
-"tethereal: The file \"%s\" couldn't be created for some unknown reason.\n",
-                 cf->save_file);
+          "tethereal: The file \"%s\" couldn't be created for some "
+          "unknown reason.\n",
+            *cf->save_file == '\0' ? "stdout" : cf->save_file);
         break;
 
       case WTAP_ERR_SHORT_WRITE:
         fprintf(stderr,
-"tethereal: A full header couldn't be written to the file \"%s\".\n",
-		cf->save_file);
+          "tethereal: A full header couldn't be written to the file \"%s\".\n",
+		*cf->save_file == '\0' ? "stdout" : cf->save_file);
         break;
 
       default:
         if (err < 0) {
           fprintf(stderr,
 		"tethereal: The file \"%s\" could not be opened: Error %d.\n",
-   		cf->save_file, err);
+   		*cf->save_file == '\0' ? "stdout" : cf->save_file, err);
         } else {
           fprintf(stderr,
 		"tethereal: The file \"%s\" could not be opened: %s\n.",
- 		cf->save_file, strerror(err));
+ 		*cf->save_file == '\0' ? "stdout" : cf->save_file,
+		strerror(err));
         }
         break;
       }
@@ -1499,6 +1548,9 @@
 static void
 show_capture_file_io_error(const char *fname, int err, gboolean is_close)
 {
+  if (*fname == '\0')
+    fname = "stdout";
+
   switch (err) {
 
   case ENOSPC:
--- file.c.orig	Thu Jun 27 23:46:47 2002
+++ file.c	Sun Jul 14 11:32:36 2002
@@ -513,20 +513,26 @@
 	if (wdh == NULL)
 		return NULL;	/* couldn't allocate it */
 
-	/* In case "fopen()" fails but doesn't set "errno", set "errno"
-	   to a generic "the open failed" error. */
-	errno = WTAP_ERR_CANT_OPEN;
-	fh = fopen(filename, "wb");
-	if (fh == NULL) {
-		*err = errno;
-		return NULL;	/* can't create file */
+	/* Empty filename means stdout */
+	if (*filename == '\0')
+		wdh->fh = stdout;
+	else {
+		/* In case "fopen()" fails but doesn't set "errno", set "errno"
+		   to a generic "the open failed" error. */
+		errno = WTAP_ERR_CANT_OPEN;
+		fh = fopen(filename, "wb");
+		if (fh == NULL) {
+			*err = errno;
+			return NULL;	/* can't create file */
+		}
+		wdh->fh = fh;
 	}
-	wdh->fh = fh;
 
 	if (!wtap_dump_open_finish(wdh, filetype, err)) {
 		/* Get rid of the file we created; we couldn't finish
 		   opening it. */
-		unlink(filename);
+		if (wdh->fh != stdout)
+			unlink(filename);
 		return NULL;
 	}
 	return wdh;
@@ -609,7 +615,8 @@
 		/* The attempt failed.  Close the stream for the file.
 		   NOTE: this means the FD handed to "wtap_dump_fdopen()"
 		   will be closed if the open fails. */
-		fclose(wdh->fh);
+		if (wdh->fh != stdout)
+			fclose(wdh->fh);
 
 		/* Now free up the dumper handle. */
 		g_free(wdh);
@@ -640,15 +647,18 @@
 			ret = FALSE;
 	}
 	errno = WTAP_ERR_CANT_CLOSE;
-	if (fclose(wdh->fh) == EOF) {
-		if (ret) {
-			/* The per-format close function succeeded,
-			   but the fclose didn't.  Save the reason
-			   why, if our caller asked for it. */
-			if (err != NULL)
-				*err = errno;
+	/* Don't close stdout */
+	if (wdh->fh != stdout) {
+		if (fclose(wdh->fh) == EOF) {
+			if (ret) {
+				/* The per-format close function succeeded,
+				   but the fclose didn't.  Save the reason
+				   why, if our caller asked for it. */
+				if (err != NULL)
+					*err = errno;
+			}
+			ret = FALSE;
 		}
-		ret = FALSE;
 	}
 	if (wdh->dump.opaque != NULL)
 		g_free(wdh->dump.opaque);
--- tethereal.pod.template.orig	Sun May 26 22:18:17 2002
+++ tethereal.pod.template	Sun Jul 14 11:40:30 2002
@@ -288,7 +288,7 @@
 
 =item -w
 
-Write packet data to I<savefile>.
+Write packet data to I<savefile> or to stdout if I<savefile> is "-".
 
 =item -x