/* capture.c
* Routines for packet capture windows
*
- * $Id: capture.c,v 1.57 1999/08/19 05:42:22 guy Exp $
+ * $Id: capture.c,v 1.109 2000/06/27 04:35:42 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
# include <sys/stat.h>
#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.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>
# include "snprintf.h"
#endif
-#ifdef HAVE_SYS_SOCKIO_H
-# include <sys/sockio.h>
+#ifndef lib_pcap_h
+#include <pcap.h>
+#endif
+
+#ifdef _WIN32
+#include <process.h> /* For spawning child process */
#endif
-#include "ethereal.h"
+#include "gtk/main.h"
+#include "gtk/gtkglobals.h"
#include "packet.h"
#include "file.h"
-#include "menu.h"
#include "capture.h"
-#include "etypes.h"
#include "util.h"
+#include "simple_dialog.h"
#include "prefs.h"
+#include "globals.h"
+
+#include "packet-clip.h"
+#include "packet-eth.h"
+#include "packet-fddi.h"
+#include "packet-null.h"
+#include "packet-ppp.h"
+#include "packet-raw.h"
+#include "packet-tr.h"
+
+int sync_mode; /* fork a child to do the capture, and sync between them */
+static int sync_pipe[2]; /* used to sync father */
+enum PIPES { READ, WRITE }; /* Constants 0 and 1 for READ and WRITE */
+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;
+
+#ifdef _WIN32
+static guint cap_timer_id;
+static int cap_timer_cb(gpointer); /* Win32 kludge to check for pipe input */
+#endif
-extern capture_file cf;
-extern GtkWidget *info_bar;
-extern guint file_ctx;
-
-extern gchar *ethereal_path;
-extern gchar *medium_font;
-extern gchar *bold_font;
-extern int fork_mode;
-extern int sync_pipe[];
-extern int sync_mode;
-extern int sigusr2_received;
-extern int quit_after_cap;
-
-/* Capture callback data keys */
-#define E_CAP_IFACE_KEY "cap_iface"
-#define E_CAP_FILT_KEY "cap_filter"
-#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"
-
-static void capture_prep_ok_cb(GtkWidget *, gpointer);
-static void capture_prep_close_cb(GtkWidget *, gpointer);
-static float pct(gint, gint);
+static void cap_file_input_cb(gpointer, gint, GdkInputCondition);
+static void capture_delete_cb(GtkWidget *, GdkEvent *, gpointer);
static void capture_stop_cb(GtkWidget *, gpointer);
static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
const u_char *);
+static void send_errmsg_to_parent(const char *);
+static float pct(gint, gint);
-static 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;
- }
-
- /* 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))
- {
- simple_dialog(ESD_TYPE_WARN, NULL,
- "Can't list interfaces: ioctl error.");
- return NULL;
- }
+typedef struct _loop_data {
+ gint go;
+ gint max;
+ gint linktype;
+ gint sync_packets;
+ packet_counts counts;
+ wtap_dumper *pdh;
+} loop_data;
- 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));
- }
-#ifdef HAVE_SA_LEN
- ifr = (struct ifreq *) ((char *) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ);
-#else
- ifr = (struct ifreq *) ((char *) ifr + sizeof(struct ifreq));
+/* Win32 needs the O_BINARY flag for open() */
+#ifndef O_BINARY
+#define O_BINARY 0
#endif
- }
- free(ifc.ifc_buf);
- return il;
-}
+#ifdef _WIN32
+/* Win32 needs a handle to the child capture process */
+int child_process;
+#endif
+/* Open a specified file, or create a temporary file, and start a capture
+ to the file in question. */
void
-capture_prep_cb(GtkWidget *w, gpointer d) {
- GtkWidget *cap_open_w, *if_cb, *if_lb,
- *count_lb, *count_cb, *main_vb, *if_hb, *count_hb,
- *filter_hb, *filter_bt, *filter_te, *caplen_hb,
- *bbox, *ok_bt, *cancel_bt, *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);
- }
-
- /* 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);
-
- count_list = g_list_append(count_list, count_item1);
- if (cf.count) {
- snprintf(count_item2, 15, "%d", cf.count);
- count_list = g_list_append(count_list, count_item2);
- }
-
- 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);
-
- while (count_list)
- count_list = g_list_remove_link(count_list, count_list);
-
- /* 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);
-
- /* 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);
-
- 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,
- MIN_PACKET_SIZE, MAX_PACKET_SIZE, 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(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(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 widgets 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_COUNT_KEY, count_cb);
- gtk_object_set_data(GTK_OBJECT(cap_open_w), E_CAP_SNAP_KEY, snap_sb);
-
- gtk_widget_show(cap_open_w);
-}
-
-static void
-capture_prep_ok_cb(GtkWidget *ok_bt, gpointer parent_w) {
- GtkWidget *if_cb, *filter_te, *count_cb, *snap_sb;
- gchar *filter_text;
+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_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_IFACE_KEY);
- filter_te = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_FILT_KEY);
- count_cb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), E_CAP_COUNT_KEY);
- snap_sb = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(parent_w), 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)));
-
- filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
- if (cf.cfilter) g_free(cf.cfilter);
- cf.cfilter = NULL; /* ead 06/16/99 */
- if (filter_text && filter_text[0]) {
- cf.cfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
+ if (capfile_name != NULL) {
+ /* 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, 0600);
+ is_tempfile = FALSE;
+ } else {
+ /* Choose a random name for the capture buffer */
+ cfile.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
+ capfile_name = g_strdup(tmpname);
+ is_tempfile = TRUE;
}
- cf.count = atoi(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(count_cb)->entry)));
- cf.snap = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
- if (cf.snap < 1)
- cf.snap = MAX_PACKET_SIZE;
- else if (cf.snap < MIN_PACKET_SIZE)
- cf.snap = MIN_PACKET_SIZE;
-
- gtk_widget_destroy(GTK_WIDGET(parent_w));
-
- /* Choose a random name for the capture buffer */
- if (cf.save_file && !cf.user_saved) {
- unlink(cf.save_file); /* silently ignore error */
- g_free(cf.save_file);
+ if (cfile.save_file_fd == -1) {
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "The file to which the capture would be saved (\"%s\")"
+ "could not be opened: %s.", capfile_name, strerror(errno));
+ return;
}
- cf.save_file_fd = create_tempfile(tmpname, sizeof tmpname, "ether");
- cf.save_file = g_strdup(tmpname);
- cf.user_saved = 0;
-
- if( fork_mode ){ /* use fork() for capture */
+ close_cap_file(&cfile, info_bar);
+ g_assert(cfile.save_file == NULL);
+ cfile.save_file = capfile_name;
+
+ if (sync_mode) { /* do the capture in a child process */
int fork_child;
char ssnap[24];
char scount[24]; /* need a constant for len of numbers */
char save_file_fd[24];
- int err;
+ char errmsg[1024+1];
+ int error;
+#ifdef _WIN32
+ char sync_pipe_fd[24];
+ char *filterstring;
+#endif
- sprintf(ssnap,"%d",cf.snap); /* in lieu of itoa */
- sprintf(scount,"%d",cf.count);
- sprintf(save_file_fd,"%d",cf.save_file_fd);
+ sprintf(ssnap,"%d",cfile.snap); /* in lieu of itoa */
+ sprintf(scount,"%d",cfile.count);
+ sprintf(save_file_fd,"%d",cfile.save_file_fd);
+
+#ifdef _WIN32
+ /* Create a pipe for the child process */
+
+ if(_pipe(sync_pipe, 512, O_BINARY) < 0) {
+ /* Couldn't create the pipe between parent and child. */
+ error = errno;
+ unlink(cfile.save_file);
+ g_free(cfile.save_file);
+ cfile.save_file = NULL;
+ simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't create sync pipe: %s",
+ strerror(error));
+ return;
+ }
+
+ /* Convert pipe write handle to a string and pass to child */
+ itoa(sync_pipe[WRITE], sync_pipe_fd, 10);
+ /* Convert filter string to a quote delimited string */
+ filterstring = g_new(char, strlen(cfile.cfilter) + 3);
+ sprintf(filterstring, "\"%s\"", cfile.cfilter);
+ filterstring[strlen(cfile.cfilter) + 2] = 0;
+ /* Spawn process */
+ fork_child = spawnlp(_P_NOWAIT, ethereal_path, CHILD_NAME, "-i", cfile.iface,
+ "-w", cfile.save_file, "-W", save_file_fd,
+ "-c", scount, "-s", ssnap,
+ "-Z", sync_pipe_fd,
+ strlen(cfile.cfilter) == 0 ? (const char *)NULL : "-f",
+ strlen(cfile.cfilter) == 0 ? (const char *)NULL : filterstring,
+ (const char *)NULL);
+ g_free(filterstring);
+ /* Keep a copy for later evaluation by _cwait() */
+ child_process = fork_child;
+#else
signal(SIGCHLD, SIG_IGN);
- if (sync_mode) pipe(sync_pipe);
- if((fork_child = fork()) == 0){
- /* args: -k -- capture
- * -i interface specification
+ if (pipe(sync_pipe) < 0) {
+ /* Couldn't create the pipe between parent and child. */
+ error = errno;
+ unlink(cfile.save_file);
+ g_free(cfile.save_file);
+ cfile.save_file = NULL;
+ simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't create sync pipe: %s",
+ strerror(error));
+ return;
+ }
+ 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
- * -Q quit after capture (forces -k)
* -s snaplen
- * -S sync mode
* -m / -b fonts
* -f "filter expression"
*/
- if (sync_mode) {
- close(1);
- dup(sync_pipe[1]);
- close(sync_pipe[0]);
- execlp(ethereal_path, "ethereal", "-k", "-Q", "-i", cf.iface,
- "-w", cf.save_file, "-W", save_file_fd,
- "-c", scount, "-s", ssnap, "-S",
+ close(1);
+ dup(sync_pipe[WRITE]);
+ close(sync_pipe[READ]);
+ execlp(ethereal_path, CHILD_NAME, "-i", cfile.iface,
+ "-w", cfile.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,
+ (cfile.cfilter == NULL)? 0 : "-f",
+ (cfile.cfilter == NULL)? 0 : cfile.cfilter,
(const char *)NULL);
- }
- else {
- execlp(ethereal_path, "ethereal", "-k", "-Q", "-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);
- }
+ snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
+ ethereal_path, strerror(errno));
+ send_errmsg_to_parent(errmsg);
+
+ /* Exit with "_exit()", so that we don't close the connection
+ to the X server (and cause stuff buffered up by our parent but
+ not yet sent to be sent, as that stuff should only be sent by
+ our parent). */
+ _exit(2);
}
- else {
- cf.filename = cf.save_file;
- if (sync_mode) {
- close(sync_pipe[1]);
- while (!sigusr2_received) {
- struct timeval timeout = {1,0};
- select(0, NULL, NULL, NULL, &timeout);
- if (kill(fork_child, 0) == -1 && errno == ESRCH)
- break;
- }
- if (sigusr2_received) {
- err = tail_cap_file(cf.save_file, &cf);
- if (err != 0) {
- simple_dialog(ESD_TYPE_WARN, NULL,
- file_open_error_message(err, FALSE), cf.save_file);
- }
- }
- sigusr2_received = FALSE;
- }
+#endif
+
+ /* Parent process - read messages from the child process over the
+ sync pipe. */
+
+ /* Close the write side of the pipe, so that only the child has it
+ open, and thus it completely closes, and thus returns to us
+ an EOF indication, if the child closes it (either deliberately
+ or by exiting abnormally). */
+ close(sync_pipe[WRITE]);
+
+ /* Close the save file FD, as we won't be using it - we'll be opening
+ it and reading the save file through Wiretap. */
+ close(cfile.save_file_fd);
+
+ if (fork_child == -1) {
+ /* We couldn't even create the child process. */
+ error = errno;
+ close(sync_pipe[READ]);
+ unlink(cfile.save_file);
+ g_free(cfile.save_file);
+ cfile.save_file = NULL;
+ simple_dialog(ESD_TYPE_WARN, NULL, "Couldn't create child process: %s",
+ strerror(error));
+ return;
}
+
+ /* Read a byte count from "sync_pipe[READ]", 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[READ], &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[READ]);
+ unlink(cfile.save_file);
+ g_free(cfile.save_file);
+ cfile.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[READ]);
+ unlink(cfile.save_file);
+ g_free(cfile.save_file);
+ cfile.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(cfile.save_file, is_tempfile, &cfile);
+ 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. */
+#ifdef _WIN32
+ /* 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
+ something similar here, start a timer and check for data on every
+ timeout. */
+ cap_timer_id = gtk_timeout_add(1000, cap_timer_cb, NULL);
+#else
+ cap_input_id = gtk_input_add_full(sync_pipe[READ],
+ GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
+ cap_file_input_cb,
+ NULL,
+ (gpointer) &cfile,
+ NULL);
+#endif
+ } 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), cfile.save_file);
+
+ /* Close the sync pipe. */
+ close(sync_pipe[READ]);
+
+ /* Don't unlink the save file - leave it around, for debugging
+ purposes. */
+ g_free(cfile.save_file);
+ cfile.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[READ], 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[READ]);
+
+ /* Get rid of the save file - the capture never started. */
+ unlink(cfile.save_file);
+ g_free(cfile.save_file);
+ cfile.save_file = NULL;
+ }
+ }
+ } 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(cfile.save_file, is_tempfile, &cfile)) == 0) {
+ /* Set the read filter to NULL. */
+ cfile.rfcode = NULL;
+ err = read_cap_file(&cfile);
+ }
+ }
+ /* We're not doing a capture any more, so we don't have a save
+ file. */
+ g_free(cfile.save_file);
+ cfile.save_file = NULL;
}
- else
- capture();
}
-static void
-capture_prep_close_cb(GtkWidget *close_bt, gpointer parent_w)
+#ifdef _WIN32
+/* The timer has expired, see if there's stuff to read from the pipe,
+ if so call the cap_file_input_cb */
+static gint
+cap_timer_cb(gpointer data)
{
- gtk_grab_remove(GTK_WIDGET(parent_w));
- gtk_widget_destroy(GTK_WIDGET(parent_w));
+ HANDLE handle;
+ DWORD avail = 0;
+ gboolean result, result1;
+ DWORD childstatus;
+
+ /* Oddly enough although Named pipes don't work on win9x,
+ PeekNamedPipe does !!! */
+ handle = (HANDLE) _get_osfhandle (sync_pipe[READ]);
+ result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
+
+ /* Get the child process exit status */
+ result1 = GetExitCodeProcess((HANDLE)child_process, &childstatus);
+
+ /* If the Peek returned an error, or there are bytes to be read
+ or the childwatcher thread has terminated then call the normal
+ callback */
+ if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
+
+ /* avoid reentrancy problems and stack overflow */
+ gtk_timeout_remove(cap_timer_id);
+
+ /* And call the real handler */
+ cap_file_input_cb((gpointer) &cfile, 0, 0);
+
+ /* Return false so that the timer is not run again */
+ return FALSE;
+ }
+ else {
+ /* No data so let timer run again */
+ return TRUE;
+ }
}
+#endif
-typedef struct _loop_data {
- gint go;
- gint max;
- gint linktype;
- gint wtap_linktype;
- gint sync_packets;
- packet_counts counts;
- wtap_dumper *pdh;
-} loop_data;
+/* 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;
+
+#ifndef _WIN32
+ /* avoid reentrancy problems and stack overflow */
+ gtk_input_remove(cap_input_id);
+#endif
-void
-capture(void) {
- GtkWidget *cap_w, *main_vb, *count_lb, *tcp_lb, *udp_lb, *icmp_lb,
- *ospf_lb, *gre_lb, *netbios_lb, *other_lb, *stop_bt;
+ if ((nread = read(sync_pipe[READ], 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. */
+#ifdef _WIN32
+ /* XXX - analyze the wait stuatus and display more information
+ in the dialog box? */
+ if (_cwait(&wstatus, child_process, _WAIT_CHILD) == -1) {
+ simple_dialog(ESD_TYPE_WARN, NULL, "Child capture process stopped unexpectedly");
+ }
+#else
+ 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) {
+
+ case SIGHUP:
+ sigmsg = "Hangup";
+ break;
+
+ case SIGINT:
+ sigmsg = "Interrupted";
+ break;
+
+ case SIGQUIT:
+ sigmsg = "Quit";
+ break;
+
+ case SIGILL:
+ sigmsg = "Illegal instruction";
+ break;
+
+ case SIGTRAP:
+ sigmsg = "Trace trap";
+ break;
+
+ case SIGABRT:
+ sigmsg = "Abort";
+ break;
+
+ case SIGFPE:
+ sigmsg = "Arithmetic exception";
+ break;
+
+ case SIGKILL:
+ sigmsg = "Killed";
+ break;
+
+ case SIGBUS:
+ sigmsg = "Bus error";
+ break;
+
+ case SIGSEGV:
+ sigmsg = "Segmentation violation";
+ break;
+
+ /* 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
+
+ case SIGPIPE:
+ sigmsg = "Broken pipe";
+ break;
+
+ case SIGALRM:
+ sigmsg = "Alarm clock";
+ break;
+
+ case SIGTERM:
+ sigmsg = "Terminated";
+ break;
+
+ 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);
+ }
+ }
+#endif
+
+ /* 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 */
+#ifdef _WIN32
+ cap_timer_id = gtk_timeout_add(1000, cap_timer_cb, NULL);
+#else
+ cap_input_id = gtk_input_add_full (sync_pipe[READ],
+ GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
+ cap_file_input_cb,
+ NULL,
+ (gpointer) cf,
+ NULL);
+#endif
+}
+
+/*
+ * Timeout, in milliseconds, for reads from the stream of captured packets.
+ */
+#define CAP_READ_TIMEOUT 250
+
+/* 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, *sctp_lb, *tcp_lb, *udp_lb, *icmp_lb,
+ *ospf_lb, *gre_lb, *netbios_lb, *ipx_lb, *vines_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;
int err, inpkts;
- char *errmsg;
- char errmsg_errno[1024+1];
+ char errmsg[1024+1];
+#ifdef linux
+ fd_set set1;
+ struct timeval timeout;
+ int pcap_fd;
+#endif
+#ifdef _WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
+#endif
+
+ /* Initialize Windows Socket if we are in a WIN32 OS
+ This needs to be done before querying the interface for network/netmask */
+#ifdef _WIN32
+ wVersionRequested = MAKEWORD( 1, 1 );
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if (err!=0) {
+ snprintf(errmsg, sizeof errmsg,
+ "Couldn't initialize Windows Sockets.");
+ pch=NULL;
+ goto error;
+ }
+#endif
ld.go = TRUE;
ld.counts.total = 0;
- ld.max = cf.count;
- ld.linktype = DLT_NULL;
+ ld.max = cfile.count;
+ ld.linktype = WTAP_ENCAP_UNKNOWN;
ld.sync_packets = 0;
+ ld.counts.sctp = 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.vines = 0;
ld.counts.other = 0;
ld.pdh = NULL;
- close_cap_file(&cf, info_bar, file_ctx);
+ /* Open the network interface to capture from it. */
+ pch = pcap_open_live(cfile.iface, cfile.snap, 1, CAP_READ_TIMEOUT, err_str);
- pch = pcap_open_live(cf.iface, cf.snap, 1, 250, err_str);
+ 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 (pch) {
- ld.linktype = pcap_datalink(pch);
- ld.wtap_linktype = wtap_pcap_encap_to_wtap_encap(ld.linktype);
- if (ld.wtap_linktype == WTAP_ENCAP_UNKNOWN) {
- errmsg = "The network you're capturing from is of a type"
- " that Ethereal doesn't support.";
- goto fail;
+ if (cfile.cfilter) {
+ /* A capture filter was specified; set it up. */
+ if (pcap_lookupnet (cfile.iface, &netnum, &netmask, err_str) < 0) {
+ snprintf(errmsg, sizeof errmsg,
+ "Can't use filter: Couldn't obtain netmask info (%s).", err_str);
+ goto error;
}
- ld.pdh = wtap_dump_fdopen(cf.save_file_fd, WTAP_FILE_PCAP,
- ld.wtap_linktype, pcap_snapshot(pch), &err);
-
- if (ld.pdh == NULL) { /* We have an error */
- switch (err) {
-
- case WTAP_ERR_CANT_OPEN:
- errmsg = "The file to which the capture would be saved"
- " couldn't be created for some unknown reason.";
- break;
-
- case WTAP_ERR_SHORT_WRITE:
- 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_errno, "The file to which the capture would be"
- " saved (\"%%s\") could not be opened: Error %d.",
- err);
- } else {
- sprintf(errmsg_errno, "The file to which the capture would be"
- " saved (\"%%s\") could not be opened: %s.",
- strerror(err));
- }
- errmsg = errmsg_errno;
- break;
- }
-fail:
- snprintf(err_str, PCAP_ERRBUF_SIZE, errmsg, cf.save_file);
- simple_dialog(ESD_TYPE_WARN, NULL, err_str);
- pcap_close(pch);
- return;
+ if (pcap_compile(pch, &cfile.fcode, cfile.cfilter, 1, netmask) < 0) {
+ snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
+ pcap_geterr(pch));
+ goto error;
+ }
+ if (pcap_setfilter(pch, &cfile.fcode) < 0) {
+ snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
+ pcap_geterr(pch));
+ goto error;
}
+ }
+
+ /* 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(cfile.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;
- 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.");
- wtap_dump_close(ld.pdh, NULL);
- unlink(cf.save_file); /* silently ignore error */
- pcap_close(pch);
- return;
- } else if (pcap_compile(pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
- simple_dialog(ESD_TYPE_WARN, NULL, "Unable to parse filter string.");
- wtap_dump_close(ld.pdh, NULL);
- unlink(cf.save_file); /* silently ignore error */
- pcap_close(pch);
- return;
- } else if (pcap_setfilter(pch, &cf.fcode) < 0) {
- simple_dialog(ESD_TYPE_WARN, NULL, "Can't install filter.");
- wtap_dump_close(ld.pdh, NULL);
- unlink(cf.save_file); /* silently ignore error */
- pcap_close(pch);
- return;
+ default:
+ if (err < 0) {
+ sprintf(errmsg, "The file to which the capture would be"
+ " saved (\"%s\") could not be opened: Error %d.",
+ cfile.save_file, err);
+ } else {
+ sprintf(errmsg, "The file to which the capture would be"
+ " saved (\"%s\") could not be opened: %s.",
+ cfile.save_file, strerror(err));
}
+ break;
}
+ goto error;
+ }
- if (sync_mode) {
- /* Sync out the capture file, so the header makes it to the file
- system, and signal 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));
- kill(getppid(), SIGUSR2);
- }
+ 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);
+ }
- 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);
-
- 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);
-
- 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();
+ 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);
+
+ sctp_lb = gtk_label_new("SCTP: 0 (0.0%)");
+ gtk_box_pack_start(GTK_BOX(main_vb), sctp_lb, FALSE, FALSE, 3);
+ gtk_widget_show(sctp_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);
+
+ 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);
+
+ vines_lb = gtk_label_new("VINES: 0 (0.0%)");
+ gtk_box_pack_start(GTK_BOX(main_vb), vines_lb, FALSE, FALSE, 3);
+ gtk_widget_show(vines_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);
+
+ /* allow user to either click a stop button, or the close button on
+ the window to stop a capture in progress. */
+ 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_signal_connect(GTK_OBJECT(cap_w), "delete_event",
+ GTK_SIGNAL_FUNC(capture_delete_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);
+#ifdef linux
+ pcap_fd = pcap_fileno(pch);
+#endif
+ while (ld.go) {
+ while (gtk_events_pending()) gtk_main_iteration();
+#ifdef linux
+ /*
+ * Sigh. The semantics of the read timeout argument to
+ * "pcap_open_live()" aren't particularly well specified by
+ * the "pcap" man page - at least with the BSD BPF code, the
+ * intent appears to be, at least in part, a way of cutting
+ * down the number of reads done on a capture, by blocking
+ * until the buffer fills or a timer expires - and the Linux
+ * libpcap doesn't actually support it, so we can't use it
+ * to break out of the "pcap_dispatch()" every 1/4 of a second
+ * or so.
+ *
+ * Thus, on Linux, we do a "select()" on the file descriptor for the
+ * capture, with a timeout of CAP_READ_TIMEOUT milliseconds, or
+ * CAP_READ_TIMEOUT*1000 microseconds.
+ */
+ FD_ZERO(&set1);
+ FD_SET(pcap_fd, &set1);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = CAP_READ_TIMEOUT*1000;
+ if (select(pcap_fd+1, &set1, NULL, NULL, &timeout) != 0) {
+ /*
+ * "select()" says we can read from it without blocking; go for
+ * it.
+ */
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, "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 (sync_mode && ld.sync_packets) {
- char tmp[20];
- sprintf(tmp, "%d*", ld.sync_packets);
- write(1, tmp, strlen(tmp));
- ld.sync_packets = 0;
- }
- }
- }
-
- if (ld.pdh) {
- if (!wtap_dump_close(ld.pdh, &err)) {
- switch (err) {
+ } else
+ inpkts = 0;
+#else
+ inpkts = pcap_dispatch(pch, 1, capture_pcap_cb, (u_char *) &ld);
+#endif
+ 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;
- case WTAP_ERR_CANT_CLOSE:
- errmsg = "The file to which the capture was being saved"
- " couldn't be closed for some unknown reason.";
- break;
+ sprintf(label_str, "Count: %d", ld.counts.total);
+ gtk_label_set(GTK_LABEL(count_lb), label_str);
- case WTAP_ERR_SHORT_WRITE:
- errmsg = "Not all the data could be written to the file"
- " to which the capture was being saved.";
- break;
+ sprintf(label_str, "SCTP: %d (%.1f%%)", ld.counts.sctp,
+ pct(ld.counts.sctp, ld.counts.total));
+ gtk_label_set(GTK_LABEL(sctp_lb), label_str);
- default:
- if (err < 0) {
- sprintf(errmsg_errno, "The file to which the capture was being"
- " saved (\"%%s\") could not be closed: Error %d.",
- err);
- } else {
- sprintf(errmsg_errno, "The file to which the capture was being"
- " saved (\"%%s\") could not be closed: %s.",
- strerror(err));
- }
- errmsg = errmsg_errno;
- break;
- }
- snprintf(err_str, PCAP_ERRBUF_SIZE, errmsg, cf.save_file);
- simple_dialog(ESD_TYPE_WARN, NULL, err_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, "VINES: %d (%.1f%%)", ld.counts.vines,
+ pct(ld.counts.vines, ld.counts.total));
+ gtk_label_set(GTK_LABEL(vines_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;
}
}
- pcap_close(pch);
-
- gtk_grab_remove(GTK_WIDGET(cap_w));
- gtk_widget_destroy(GTK_WIDGET(cap_w));
- } 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.");
}
+
+ 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;
- if( quit_after_cap ){
- /* DON'T unlink the save file. Presumably someone wants it. */
- gtk_exit(0);
- }
+ 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;
- if (pch) {
- /* "pch" is non-NULL only if we successfully started a capture.
- If we haven't, there's no capture file to load. */
- if ((err = open_cap_file(cf.save_file, &cf)) == 0) {
- /* Set the read filter to NULL. */
- cf.rfcode = NULL;
- err = read_cap_file(&cf);
- set_menu_sensitivity("/File/Save", TRUE);
- set_menu_sensitivity("/File/Save As...", FALSE);
+ default:
+ simple_dialog(ESD_TYPE_WARN, NULL,
+ "The file to which the capture was being"
+ " saved (\"%s\") could not be closed: %s.",
+ cfile.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 can't use the save file, and we have no wtap_dump stream
+ to close in order to close it, so close the FD directly. */
+ close(cfile.save_file_fd);
+
+ /* We couldn't even start the capture, so get rid of the capture
+ file. */
+ unlink(cfile.save_file); /* silently ignore error */
+ g_free(cfile.save_file);
+ cfile.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. */
+ send_errmsg_to_parent(errmsg);
+ } else {
+ /* Display the dialog box ourselves; there's no parent. */
+ simple_dialog(ESD_TYPE_WARN, NULL, errmsg);
+ }
+ if (pch != NULL)
+ pcap_close(pch);
+
+ return FALSE;
+}
+
+static void
+send_errmsg_to_parent(const char *errmsg)
+{
+ int msglen = strlen(errmsg);
+ char lenbuf[10+1+1];
+
+ sprintf(lenbuf, "%u;", msglen);
+ write(1, lenbuf, strlen(lenbuf));
+ write(1, errmsg, msglen);
}
static float
}
}
+static void
+capture_delete_cb(GtkWidget *w, GdkEvent *event, gpointer data) {
+ capture_stop_cb(NULL, data);
+}
+
static void
capture_stop_cb(GtkWidget *w, gpointer data) {
loop_data *ld = (loop_data *) data;
ld->go = FALSE;
}
if (ld->pdh) {
- whdr.ts = phdr->ts;
+ /* "phdr->ts" may not necessarily be a "struct timeval" - it may
+ be a "struct bpf_timeval", with member sizes wired to 32
+ bits - and we may go that way ourselves in the future, so
+ copy the members individually. */
+ whdr.ts.tv_sec = phdr->ts.tv_sec;
+ whdr.ts.tv_usec = phdr->ts.tv_usec;
whdr.caplen = phdr->caplen;
whdr.len = phdr->len;
- whdr.pkt_encap = ld->wtap_linktype;
+ whdr.pkt_encap = ld->linktype;
/* XXX - do something if this fails */
- wtap_dump(ld->pdh, &whdr, pd, &err);
+ wtap_dump(ld->pdh, &whdr, NULL, pd, &err);
}
+
+ /* Set the initial payload to the packet length, and the initial
+ captured payload to the capture length (other protocols may
+ reduce them if their headers say they're less). */
+ pi.len = phdr->len;
+ pi.captured_len = phdr->caplen;
switch (ld->linktype) {
- case DLT_EN10MB :
- capture_eth(pd, phdr->caplen, &ld->counts);
+ case WTAP_ENCAP_ETHERNET:
+ capture_eth(pd, 0, &ld->counts);
+ break;
+ case WTAP_ENCAP_FDDI:
+ case WTAP_ENCAP_FDDI_BITSWAPPED:
+ capture_fddi(pd, &ld->counts);
break;
- case DLT_FDDI :
- capture_fddi(pd, phdr->caplen, &ld->counts);
+ case WTAP_ENCAP_TR:
+ capture_tr(pd, 0, &ld->counts);
break;
- case DLT_IEEE802 :
- capture_tr(pd, phdr->caplen, &ld->counts);
+ case WTAP_ENCAP_NULL:
+ capture_null(pd, &ld->counts);
break;
- case DLT_NULL :
- capture_null(pd, phdr->caplen, &ld->counts);
+ case WTAP_ENCAP_PPP:
+ capture_ppp(pd, 0, &ld->counts);
break;
- case DLT_PPP :
- capture_ppp(pd, phdr->caplen, &ld->counts);
+ case WTAP_ENCAP_RAW_IP:
+ capture_raw(pd, &ld->counts);
break;
- case DLT_RAW :
- capture_raw(pd, phdr->caplen, &ld->counts);
+ case WTAP_ENCAP_LINUX_ATM_CLIP:
+ capture_clip(pd, &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. */
}
}