/* capture.c
- * Routines for packet capture windows
+ * Routines for packet capture
*
* $Id$
*
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* 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
#ifdef HAVE_LIBPCAP
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
#endif
-#ifdef HAVE_IO_H
-# include <io.h>
-#endif
-
-#include <signal.h>
-#include <errno.h>
-
-#include <pcap.h>
+#include <stdio.h>
+#include <string.h>
#include <glib.h>
#include <epan/dfilter/dfilter.h>
#include "file.h"
#include "capture.h"
+#include "capture_ifinfo.h"
#include "capture_sync.h"
+#include "capture_info.h"
#include "capture_ui_utils.h"
#include "util.h"
-#include "pcap-util.h"
-#include "alert_box.h"
+#include "capture-pcap-util.h"
#include "simple_dialog.h"
#include <epan/prefs.h>
-#include "conditions.h"
-#include "ringbuffer.h"
#ifdef _WIN32
#include "capture-wpcap.h"
#endif
#include "ui_util.h"
+#include "wsutil/file_util.h"
+#include "log.h"
+typedef struct if_stat_cache_item_s {
+ char *name;
+ struct pcap_stat ps;
+} if_stat_cache_item_t;
-/* Win32 needs the O_BINARY flag for open() */
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
+struct if_stat_cache_s {
+ int stat_fd;
+ int fork_child;
+ GList *cache_list; /* List of if_stat_chache_entry_t */
+};
+
+/* this callback mechanism should possibly be replaced by the g_signal_...() stuff (if I only would know how :-) */
+typedef struct {
+ capture_callback_t cb_fct;
+ gpointer user_data;
+} capture_callback_data_t;
+
+static GList *capture_callbacks = NULL;
+
+static void
+capture_callback_invoke(int event, capture_options *capture_opts)
+{
+ capture_callback_data_t *cb;
+ GList *cb_item = capture_callbacks;
-static gboolean normal_do_capture(capture_options *capture_opts, gboolean is_tempfile);
-static void stop_capture_signal_handler(int signo);
+ /* there should be at least one interested */
+ g_assert(cb_item != NULL);
+
+ while(cb_item != NULL) {
+ cb = cb_item->data;
+ cb->cb_fct(event, capture_opts, cb->user_data);
+ cb_item = g_list_next(cb_item);
+ }
+}
void
-capture_opts_init(capture_options *capture_opts, void *cfile)
+capture_callback_add(capture_callback_t func, gpointer user_data)
{
- capture_opts->cf = cfile;
- capture_opts->cfilter = g_strdup("");
- capture_opts->iface = NULL;
-#ifdef _WIN32
- capture_opts->buffer_size = 1; /* 1 MB */
-#endif
- capture_opts->has_snaplen = FALSE;
- capture_opts->snaplen = MIN_PACKET_SIZE;
- capture_opts->promisc_mode = TRUE;
- capture_opts->linktype = -1; /* the default linktype */
- capture_opts->capture_child = FALSE;
- capture_opts->save_file = NULL;
- capture_opts->save_file_fd = -1;
- capture_opts->sync_mode = TRUE;
- capture_opts->show_info = TRUE;
- capture_opts->quit_after_cap = FALSE;
-
- capture_opts->multi_files_on = FALSE;
- capture_opts->has_file_duration = FALSE;
- capture_opts->file_duration = 60; /* 1 min */
- capture_opts->has_ring_num_files = TRUE;
- capture_opts->ring_num_files = 2;
-
- capture_opts->has_autostop_files = FALSE;
- capture_opts->autostop_files = 1;
- capture_opts->has_autostop_packets = FALSE;
- capture_opts->autostop_packets = 1;
- capture_opts->has_autostop_filesize = FALSE;
- capture_opts->autostop_filesize = 1024 * 1024; /* 1 MB */
- capture_opts->has_autostop_duration = FALSE;
- capture_opts->autostop_duration = 60; /* 1 min */
+ capture_callback_data_t *cb;
+
+ cb = g_malloc(sizeof(capture_callback_data_t));
+ cb->cb_fct = func;
+ cb->user_data = user_data;
+ capture_callbacks = g_list_append(capture_callbacks, cb);
}
-static int
-get_natural_int(const char *string, const char *name)
+void
+capture_callback_remove(capture_callback_t func)
{
- long number;
- char *p;
-
- number = strtol(string, &p, 10);
- if (p == string || *p != '\0') {
- fprintf(stderr, "ethereal: The specified %s \"%s\" isn't a decimal number\n",
- name, string);
- exit(1);
- }
- if (number < 0) {
- fprintf(stderr, "ethereal: The specified %s \"%s\" is a negative number\n",
- name, string);
- exit(1);
+ capture_callback_data_t *cb;
+ GList *cb_item = capture_callbacks;
+
+ while(cb_item != NULL) {
+ cb = cb_item->data;
+ if(cb->cb_fct == func) {
+ capture_callbacks = g_list_remove(capture_callbacks, cb);
+ g_free(cb);
+ return;
+ }
+ cb_item = g_list_next(cb_item);
+ }
+
+ g_assert_not_reached();
+}
+
+/**
+ * Start a capture.
+ *
+ * @return TRUE if the capture starts successfully, FALSE otherwise.
+ */
+gboolean
+capture_start(capture_options *capture_opts)
+{
+ gboolean ret;
+ GString *source = g_string_new("");
+
+ /* close the currently loaded capture file */
+ cf_close(capture_opts->cf);
+
+ g_assert(capture_opts->state == CAPTURE_STOPPED);
+ capture_opts->state = CAPTURE_PREPARING;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Start ...");
+
+ g_string_printf(source, "%s", get_iface_description(capture_opts));
+ if(capture_opts->cfilter && capture_opts->cfilter[0]) {
+ g_string_append_printf(source, " (%s)", capture_opts->cfilter);
}
- if (number > INT_MAX) {
- fprintf(stderr, "ethereal: The specified %s \"%s\" is too large (greater than %d)\n",
- name, string, INT_MAX);
- exit(1);
+ cf_set_tempfile_source(capture_opts->cf, source->str);
+ g_string_free(source, TRUE);
+
+ /* try to start the capture child process */
+ ret = sync_pipe_start(capture_opts);
+ if(!ret) {
+ if(capture_opts->save_file != NULL) {
+ g_free(capture_opts->save_file);
+ capture_opts->save_file = NULL;
+ }
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Start failed!");
+ capture_opts->state = CAPTURE_STOPPED;
+ } else {
+ /* the capture child might not respond shortly after bringing it up */
+ /* (for example: it will block if no input arrives from an input capture pipe (e.g. mkfifo)) */
+
+ /* to prevent problems, bring the main GUI into "capture mode" right after a successful */
+ /* spawn/exec of the capture child, without waiting for any response from it */
+ capture_callback_invoke(capture_cb_capture_prepared, capture_opts);
+
+ if(capture_opts->show_info)
+ capture_info_open(capture_opts);
}
- return number;
+
+ return ret;
}
-static int
-get_positive_int(const char *string, const char *name)
+void
+capture_stop(capture_options *capture_opts)
{
- long number;
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Stop ...");
- number = get_natural_int(string, name);
+ capture_callback_invoke(capture_cb_capture_stopping, capture_opts);
- if (number == 0) {
- fprintf(stderr, "ethereal: The specified %s is zero\n",
- name);
- exit(1);
- }
+ /* stop the capture child gracefully */
+ sync_pipe_stop(capture_opts);
+}
+
+
+void
+capture_restart(capture_options *capture_opts)
+{
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Restart");
- return number;
+ capture_opts->restart = TRUE;
+ capture_stop(capture_opts);
}
-/*
- * Given a string of the form "<autostop criterion>:<value>", as might appear
- * as an argument to a "-a" option, parse it and set the criterion in
- * question. Return an indication of whether it succeeded or failed
- * in some fashion.
- */
+void
+capture_kill_child(capture_options *capture_opts)
+{
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "Capture Kill");
+
+ /* kill the capture child */
+ sync_pipe_kill(capture_opts->fork_child);
+}
+
+
+
+/* We've succeeded in doing a (non real-time) capture; try to read it into a new capture file */
static gboolean
-set_autostop_criterion(capture_options *capture_opts, const char *autostoparg)
+capture_input_read_all(capture_options *capture_opts, gboolean is_tempfile, gboolean drops_known,
+guint32 drops)
{
- gchar *p, *colonp;
+ int err;
- colonp = strchr(autostoparg, ':');
- if (colonp == NULL)
+ /* Capture succeeded; attempt to open the capture file. */
+ 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. */
return FALSE;
+ }
+
+ /* Set the read filter to 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.
+
+ XXX - there are currently no packet-drop statistics stored
+ in libpcap captures, and that's what we're reading.
+
+ At some point, we will add support in Wiretap to return
+ packet-drop statistics for capture file formats that store it,
+ and will make "cf_read()" get those statistics from Wiretap.
+ We clear the statistics (marking them as "not known") in
+ "cf_open()", and "cf_read()" will only fetch them and mark
+ them as known if Wiretap supplies them, so if we get the
+ statistics now, after calling "cf_open()" but before calling
+ "cf_read()", the values we store will be used by "cf_read()".
+
+ If a future libpcap capture file format stores the statistics,
+ 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 (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
+ bother looking at it.
+
+ Ideally, libpcap would have an interface that gave us
+ 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. */
+ cf_set_drops(capture_opts->cf, drops);
+ }
- p = colonp;
- *p++ = '\0';
-
- /*
- * Skip over any white space (there probably won't be any, but
- * as we allow it in the preferences file, we might as well
- * allow it here).
- */
- while (isspace((guchar)*p))
- p++;
- if (*p == '\0') {
- /*
- * Put the colon back, so if our caller uses, in an
- * error message, the string they passed us, the message
- * looks correct.
- */
- *colonp = ':';
+ /* read in the packet data */
+ switch (cf_read(capture_opts->cf, FALSE)) {
+
+ 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 CF_READ_ABORTED:
+ /* User wants to quit program. Exit by leaving the main loop,
+ so that any quit functions we registered get called. */
+ main_window_nested_quit();
return FALSE;
}
- if (strcmp(autostoparg,"duration") == 0) {
- capture_opts->has_autostop_duration = TRUE;
- capture_opts->autostop_duration = get_positive_int(p,"autostop duration");
- } else if (strcmp(autostoparg,"filesize") == 0) {
- capture_opts->has_autostop_filesize = TRUE;
- capture_opts->autostop_filesize = get_positive_int(p,"autostop filesize");
- } else {
- return FALSE;
+
+ /* if we didn't capture even a single packet, close the file again */
+ if(cf_get_packet_count(capture_opts->cf) == 0 && !capture_opts->restart) {
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+"%sNo packets captured!%s\n"
+"\n"
+"As no data was captured, closing the %scapture file!\n"
+"\n"
+"\n"
+"Help about capturing can be found at:\n"
+"\n"
+" http://wiki.wireshark.org/CaptureSetup"
+#ifdef _WIN32
+"\n\n"
+"Wireless (Wi-Fi/WLAN):\n"
+"Try to switch off promiscuous mode in the Capture Options!"
+#endif
+"",
+ simple_dialog_primary_start(), simple_dialog_primary_end(),
+ (cf_is_tempfile(capture_opts->cf)) ? "temporary " : "");
+ cf_close(capture_opts->cf);
}
- *colonp = ':'; /* put the colon back */
return TRUE;
}
-/*
- * Given a string of the form "<ring buffer file>:<duration>", as might appear
- * as an argument to a "-b" option, parse it and set the arguments in
- * question. Return an indication of whether it succeeded or failed
- * in some fashion.
- */
-static gboolean
-get_ring_arguments(capture_options *capture_opts, const char *arg)
-{
- gchar *p = NULL, *colonp;
- colonp = strchr(arg, ':');
+/* capture child tells us we have a new (or the first) capture file */
+gboolean
+capture_input_new_file(capture_options *capture_opts, gchar *new_file)
+{
+ gboolean is_tempfile;
+ int err;
- if (colonp != NULL) {
- p = colonp;
- *p++ = '\0';
+ if(capture_opts->state == CAPTURE_PREPARING) {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture started!");
+ }
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "File: \"%s\"", new_file);
+
+ g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
+
+ /* free the old filename */
+ if(capture_opts->save_file != NULL) {
+ /* we start a new capture file, close the old one (if we had one before). */
+ /* (we can only have an open capture file in real_time_mode!) */
+ if( ((capture_file *) capture_opts->cf)->state != FILE_CLOSED) {
+ capture_callback_invoke(capture_cb_capture_update_finished, capture_opts);
+ cf_finish_tail(capture_opts->cf, &err);
+ cf_close(capture_opts->cf);
+ }
+ g_free(capture_opts->save_file);
+ is_tempfile = FALSE;
+ cf_set_tempfile(capture_opts->cf, FALSE);
+ } else {
+ /* we didn't have a save_file before; must be a tempfile */
+ is_tempfile = TRUE;
+ cf_set_tempfile(capture_opts->cf, TRUE);
}
- capture_opts->ring_num_files =
- get_natural_int(arg, "number of ring buffer files");
+ /* save the new filename */
+ capture_opts->save_file = g_strdup(new_file);
- if (colonp == NULL)
- return TRUE;
+ /* if we are in real-time mode, open the new file now */
+ if(capture_opts->real_time_mode) {
+ /* Attempt to open the capture file and set up to read from it. */
+ switch(cf_start_tail(capture_opts->cf, capture_opts->save_file, is_tempfile, &err)) {
+ case CF_OK:
+ break;
+ case CF_ERROR:
+ /* Don't unlink (delete) the save file - leave it around,
+ for debugging purposes. */
+ g_free(capture_opts->save_file);
+ capture_opts->save_file = NULL;
+ return FALSE;
+ }
+ }
- /*
- * Skip over any white space (there probably won't be any, but
- * as we allow it in the preferences file, we might as well
- * allow it here).
- */
- while (isspace((guchar)*p))
- p++;
- if (*p == '\0') {
- /*
- * Put the colon back, so if our caller uses, in an
- * error message, the string they passed us, the message
- * looks correct.
- */
- *colonp = ':';
- return FALSE;
+ if(capture_opts->show_info) {
+ if (!capture_info_new_file(new_file))
+ return FALSE;
}
- capture_opts->has_file_duration = TRUE;
- capture_opts->file_duration = get_positive_int(p,
- "ring buffer duration");
+ if(capture_opts->real_time_mode) {
+ capture_callback_invoke(capture_cb_capture_update_started, capture_opts);
+ } else {
+ capture_callback_invoke(capture_cb_capture_fixed_started, capture_opts);
+ }
+ capture_opts->state = CAPTURE_RUNNING;
- *colonp = ':'; /* put the colon back */
return TRUE;
}
+/* capture child tells us we have new packets to read */
void
-capture_opt_add(capture_options *capture_opts, int opt, const char *optarg, gboolean *start_capture)
+capture_input_new_packets(capture_options *capture_opts, int to_read)
{
- int i;
+ int err;
- switch(opt) {
- case 'a': /* autostop criteria */
- if (set_autostop_criterion(capture_opts, optarg) == FALSE) {
- fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
- exit(1);
- }
- break;
- case 'b': /* Ringbuffer option */
- capture_opts->multi_files_on = TRUE;
- capture_opts->has_ring_num_files = TRUE;
- if (get_ring_arguments(capture_opts, optarg) == FALSE) {
- fprintf(stderr, "ethereal: Invalid or unknown -b arg \"%s\"\n", optarg);
- exit(1);
- }
- break;
- case 'c': /* Capture xxx packets */
- capture_opts->has_autostop_packets = TRUE;
- capture_opts->autostop_packets = get_positive_int(optarg, "packet count");
- break;
- case 'f': /* capture filter */
- if (capture_opts->cfilter)
- g_free(capture_opts->cfilter);
- capture_opts->cfilter = g_strdup(optarg);
- break;
- case 'H': /* Hide capture info dialog box */
- capture_opts->show_info = FALSE;
- break;
- case 'i': /* Use interface xxx */
- capture_opts->iface = g_strdup(optarg);
- break;
- case 'k': /* Start capture immediately */
- *start_capture = TRUE;
- break;
- /*case 'l':*/ /* Automatic scrolling in live capture mode */
- case 'p': /* Don't capture in promiscuous mode */
- capture_opts->promisc_mode = FALSE;
- break;
- case 'Q': /* Quit after capture (just capture to file) */
- capture_opts->quit_after_cap = TRUE;
- *start_capture = TRUE; /*** -Q implies -k !! ***/
- break;
- case 's': /* Set the snapshot (capture) length */
- capture_opts->has_snaplen = TRUE;
- capture_opts->snaplen = get_positive_int(optarg, "snapshot length");
- break;
- case 'S': /* "Sync" mode: used for following file ala tail -f */
- capture_opts->sync_mode = TRUE;
- break;
- case 'w': /* Write to capture file xxx */
- capture_opts->save_file = g_strdup(optarg);
- break;
- case 'W': /* Write to capture file FD xxx */
- capture_opts->save_file_fd = atoi(optarg);
- break;
- case 'y': /* Set the pcap data link type */
-#ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL
- capture_opts->linktype = pcap_datalink_name_to_val(optarg);
- if (capture_opts->linktype == -1) {
- fprintf(stderr, "ethereal: The specified data link type \"%s\" isn't valid\n",
- optarg);
- exit(1);
- }
-#else /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
- /* XXX - just treat it as a number */
- capture_opts->linktype = get_natural_int(optarg, "data link type");
-#endif /* HAVE_PCAP_DATALINK_NAME_TO_VAL */
- break;
-#ifdef _WIN32
- /* Hidden option supporting Sync mode */
- case 'Z': /* Write to pipe FD XXX */
- /* associate stdout with pipe */
- i = atoi(optarg);
- if (dup2(i, 1) < 0) {
- fprintf(stderr, "Unable to dup pipe handle\n");
- exit(1);
- }
- break;
-#endif /* _WIN32 */
- default:
- /* the caller is responsible to send us only the right opt's */
- g_assert_not_reached();
- }
-}
+ g_assert(capture_opts->save_file);
-/* open the output file (temporary/specified name/ringbuffer) and close the old one */
-/* 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];
- gchar *capfile_name;
+ if(capture_opts->real_time_mode) {
+ /* Read from the capture file the number of records the child told us it added. */
+ switch (cf_continue_tail(capture_opts->cf, to_read, &err)) {
+ 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.
- 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(capture_opts->save_file);
- if (capture_opts->multi_files_on) {
- /* ringbuffer is enabled */
- 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. */
- capture_opts->save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
- 0600);
+ XXX - abort on a read error? */
+ capture_callback_invoke(capture_cb_capture_update_continue, capture_opts);
+ break;
+
+ case CF_READ_ABORTED:
+ /* Kill the child capture process; the user wants to exit, and we
+ shouldn't just leave it running. */
+ capture_kill_child(capture_opts);
+ break;
}
- *is_tempfile = FALSE;
} else {
- /* 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;
- }
+ /* increase the capture file packet counter by the number of incoming packets */
+ cf_set_packet_count(capture_opts->cf,
+ cf_get_packet_count(capture_opts->cf) + to_read);
+ cf_fake_continue_tail(capture_opts->cf);
- /* 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\")"
- "could not be opened: %s.", capfile_name, strerror(errno));
- } else {
- if (capture_opts->multi_files_on) {
- ringbuf_error_cleanup();
- }
- open_failure_alert_box(capfile_name, errno, TRUE);
- }
- g_free(capfile_name);
- return FALSE;
+ capture_callback_invoke(capture_cb_capture_fixed_continue, capture_opts);
}
- /* close the old file */
- cf_close(capture_opts->cf);
- g_assert(capture_opts->save_file == NULL);
- capture_opts->save_file = capfile_name;
- /* capture_opts.save_file is "g_free"ed later, which is equivalent to
- "g_free(capfile_name)". */
+ /* update the main window so we get events (e.g. from the stop toolbar button) */
+ main_window_update();
- return TRUE;
+ if(capture_opts->show_info)
+ capture_info_new_packets(to_read);
}
-/* 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)
+/* Capture child told us how many dropped packets it counted.
+ */
+void
+capture_input_drops(capture_options *capture_opts, guint32 dropped)
{
- gboolean is_tempfile;
- gboolean ret;
- gchar *title;
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "%u packet%s dropped", dropped, plurality(dropped, "", "s"));
- /* open the output file (temporary/specified name/ringbuffer) and close the old one */
- if(!capture_open_output(capture_opts, &is_tempfile)) {
- return FALSE;
+ g_assert(capture_opts->state == CAPTURE_RUNNING);
+
+ cf_set_drops_known(capture_opts->cf, TRUE);
+ cf_set_drops(capture_opts->cf, dropped);
+}
+
+
+/* Capture child told us that an error has occurred while starting/running
+ the capture.
+ The buffer we're handed has *two* null-terminated strings in it - a
+ primary message and a secondary message, one right after the other.
+ The secondary message might be a null string.
+ */
+void
+capture_input_error_message(capture_options *capture_opts, char *error_msg, char *secondary_error_msg)
+{
+ gchar *safe_error_msg;
+ gchar *safe_secondary_error_msg;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Error message from child: \"%s\", \"%s\"",
+ error_msg, secondary_error_msg);
+
+ g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
+
+ safe_error_msg = simple_dialog_format_message(error_msg);
+ if (*secondary_error_msg != '\0') {
+ /* We have both primary and secondary messages. */
+ safe_secondary_error_msg = simple_dialog_format_message(secondary_error_msg);
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s%s%s\n\n%s",
+ simple_dialog_primary_start(), safe_error_msg,
+ simple_dialog_primary_end(), safe_secondary_error_msg);
+ g_free(safe_secondary_error_msg);
+ } else {
+ /* We have only a primary message. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s%s%s",
+ simple_dialog_primary_start(), safe_error_msg,
+ simple_dialog_primary_end());
}
+ g_free(safe_error_msg);
+
+ /* the capture child will close the sync_pipe if required, nothing to do for now */
+}
- title = g_strdup_printf("%s: Capturing - Ethereal",
- get_interface_descriptive_name(capture_opts->iface));
- if (capture_opts->sync_mode) {
- /* sync mode: do the capture in a child process */
- ret = sync_pipe_do_capture(capture_opts, is_tempfile);
- /* capture is still running */
- set_main_window_name(title);
+
+
+/* Capture child told us that an error has occurred while parsing a
+ capture filter when starting/running the capture.
+ */
+void
+capture_input_cfilter_error_message(capture_options *capture_opts, char *error_message)
+{
+ dfilter_t *rfcode = NULL;
+ gchar *safe_cfilter = simple_dialog_format_message(capture_opts->cfilter);
+ gchar *safe_cfilter_error_msg = simple_dialog_format_message(error_message);
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture filter error message from child: \"%s\"", error_message);
+
+ g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
+
+ /* Did the user try a display filter? */
+ if (dfilter_compile(capture_opts->cfilter, &rfcode) && rfcode != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sInvalid capture filter: \"%s\"!%s\n"
+ "\n"
+ "That string looks like a valid display filter; however, it isn't a valid\n"
+ "capture filter (%s).\n"
+ "\n"
+ "Note that display filters and capture filters don't have the same syntax,\n"
+ "so you can't use most display filter expressions as capture filters.\n"
+ "\n"
+ "See the User's Guide for a description of the capture filter syntax.",
+ simple_dialog_primary_start(), safe_cfilter,
+ simple_dialog_primary_end(), safe_cfilter_error_msg);
+ dfilter_free(rfcode);
} else {
- /* normal mode: do the capture synchronously */
- set_main_window_name(title);
- ret = normal_do_capture(capture_opts, is_tempfile);
- /* capture is finished here */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sInvalid capture filter: \"%s\"!%s\n"
+ "\n"
+ "That string isn't a valid capture filter (%s).\n"
+ "See the User's Guide for a description of the capture filter syntax.",
+ simple_dialog_primary_start(), safe_cfilter,
+ simple_dialog_primary_end(), safe_cfilter_error_msg);
}
- g_free(title);
+ g_free(safe_cfilter_error_msg);
+ g_free(safe_cfilter);
- return ret;
+ /* the capture child will close the sync_pipe if required, nothing to do for now */
}
-/* start a normal capture session */
-static gboolean
-normal_do_capture(capture_options *capture_opts, gboolean is_tempfile)
+/* capture child closed its side of the pipe, do the required cleanup */
+void
+capture_input_closed(capture_options *capture_opts, gchar *msg)
{
- int capture_succeeded;
- gboolean stats_known;
- struct pcap_stat stats;
- int err;
-
- /* Not sync mode. */
- capture_succeeded = capture_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 (!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(capture_opts->save_file);
- }
- capture_opts->save_file = NULL;
- return FALSE;
- }
- /* Capture succeeded; attempt to read in the capture file. */
- 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(capture_opts->save_file);
- }
- capture_opts->save_file = NULL;
- return FALSE;
+ int err;
+ int packet_count_save;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture stopped!");
+ g_assert(capture_opts->state == CAPTURE_PREPARING || capture_opts->state == CAPTURE_RUNNING);
+
+ if (msg != NULL)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", msg);
+
+ /* if we didn't start the capture, do a fake start. */
+ /* (happens if we got an error message - we won't get a filename then). */
+ if(capture_opts->state == CAPTURE_PREPARING) {
+ if(capture_opts->real_time_mode) {
+ capture_callback_invoke(capture_cb_capture_update_started, capture_opts);
+ } else {
+ capture_callback_invoke(capture_cb_capture_fixed_started, capture_opts);
+ }
}
- /* Set the read filter to NULL. */
- cf_set_rfcode(capture_opts->cf, NULL);
-
- /* Get the packet-drop statistics.
-
- XXX - there are currently no packet-drop statistics stored
- in libpcap captures, and that's what we're reading.
-
- At some point, we will add support in Wiretap to return
- packet-drop statistics for capture file formats that store it,
- and will make "cf_read()" get those statistics from Wiretap.
- We clear the statistics (marking them as "not known") in
- "cf_open()", and "cf_read()" will only fetch them and mark
- them as known if Wiretap supplies them, so if we get the
- statistics now, after calling "cf_open()" but before calling
- "cf_read()", the values we store will be used by "cf_read()".
-
- If a future libpcap capture file format stores the statistics,
- 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) {
- 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
- bother looking at it.
-
- Ideally, libpcap would have an interface that gave us
- 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. */
- cf_set_drops(capture_opts->cf, stats.ps_drop);
+ if(capture_opts->real_time_mode) {
+ cf_read_status_t status;
+
+ /* Read what remains of the capture file. */
+ status = cf_finish_tail(capture_opts->cf, &err);
+
+ /* XXX: If -Q (quit-after-cap) then cf->count clr'd below so save it first */
+ packet_count_save = cf_get_packet_count(capture_opts->cf);
+ /* Tell the GUI we are not doing a capture any more.
+ Must be done after the cf_finish_tail(), so file lengths are
+ correctly displayed */
+ capture_callback_invoke(capture_cb_capture_update_finished, capture_opts);
+
+ /* Finish the capture. */
+ switch (status) {
+
+ case CF_READ_OK:
+ if ((packet_count_save == 0) && !capture_opts->restart) {
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+"%sNo packets captured!%s\n"
+"\n"
+"As no data was captured, closing the %scapture file!\n"
+"\n"
+"\n"
+"Help about capturing can be found at:\n"
+"\n"
+" http://wiki.wireshark.org/CaptureSetup"
+#ifdef _WIN32
+"\n\n"
+"Wireless (Wi-Fi/WLAN):\n"
+"Try to switch off promiscuous mode in the Capture Options!"
+#endif
+"",
+ simple_dialog_primary_start(), simple_dialog_primary_end(),
+ cf_is_tempfile(capture_opts->cf) ? "temporary " : "");
+ cf_close(capture_opts->cf);
+ }
+ break;
+ 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 CF_READ_ABORTED:
+ /* Exit by leaving the main loop, so that any quit functions
+ we registered get called. */
+ main_window_quit();
+ }
+
+ } else {
+ /* first of all, we are not doing a capture any more */
+ capture_callback_invoke(capture_cb_capture_fixed_finished, capture_opts);
+
+ /* this is a normal mode capture and if no error happened, read in the capture file data */
+ if(capture_opts->save_file != NULL) {
+ capture_input_read_all(capture_opts, cf_is_tempfile(capture_opts->cf),
+ cf_get_drops_known(capture_opts->cf), cf_get_drops(capture_opts->cf));
+ }
}
- switch (cf_read(capture_opts->cf)) {
- 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;
+ if(capture_opts->show_info)
+ capture_info_close();
- 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;
+ capture_opts->state = CAPTURE_STOPPED;
+
+ /* if we couldn't open a capture file, there's nothing more for us to do */
+ if(capture_opts->save_file == NULL) {
+ cf_close(capture_opts->cf);
+ return;
}
- /* We're not doing a capture any more, so we don't have a save
- file. */
- if (capture_opts->multi_files_on) {
- ringbuf_free();
+ /* does the user wants to restart the current capture? */
+ if(capture_opts->restart) {
+ capture_opts->restart = FALSE;
+
+ ws_unlink(capture_opts->save_file);
+
+ /* if it was a tempfile, throw away the old filename (so it will become a tempfile again) */
+ if(cf_is_tempfile(capture_opts->cf)) {
+ g_free(capture_opts->save_file);
+ capture_opts->save_file = NULL;
+ }
+
+ /* ... and start the capture again */
+ capture_start(capture_opts);
} else {
- g_free(capture_opts->save_file);
+ /* We're not doing a capture any more, so we don't have a save file. */
+ g_free(capture_opts->save_file);
+ capture_opts->save_file = NULL;
}
- capture_opts->save_file = NULL;
-
- /* if we didn't captured even a single packet, close the file again */
- 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(),
- (cf_is_tempfile(capture_opts->cf)) ? "temporary " : "");
- cf_close(capture_opts->cf);
+}
+
+if_stat_cache_t *
+capture_stat_start(GList *if_list) {
+ int stat_fd, fork_child;
+ gchar *msg;
+ if_stat_cache_t *sc = NULL;
+ GList *if_entry;
+ if_info_t *if_info;
+ if_stat_cache_item_t *sc_item;
+
+ /* Fire up dumpcap. */
+ /*
+ * XXX - on systems with BPF, the number of BPF devices limits the
+ * number of devices on which you can capture simultaneously.
+ *
+ * This means that
+ *
+ * 1) this might fail if you run out of BPF devices
+ *
+ * and
+ *
+ * 2) opening every interface could leave too few BPF devices
+ * for *other* programs.
+ *
+ * It also means the system could end up getting a lot of traffic
+ * that it has to pass through the networking stack and capture
+ * mechanism, so opening all the devices and presenting packet
+ * counts might not always be a good idea.
+ */
+ if (sync_interface_stats_open(&stat_fd, &fork_child, &msg) == 0) {
+ sc = g_malloc(sizeof(if_stat_cache_t));
+ sc->stat_fd = stat_fd;
+ sc->fork_child = fork_child;
+ sc->cache_list = NULL;
+
+ /* Initialize the cache */
+ for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) {
+ if_info = if_entry->data;
+ sc_item = g_malloc0(sizeof(if_stat_cache_item_t));
+ sc_item->name = g_strdup(if_info->name);
+ sc->cache_list = g_list_append(sc->cache_list, sc_item);
+ }
}
- return TRUE;
+ return sc;
}
+#define MAX_STAT_LINE_LEN 500
static void
-stop_capture_signal_handler(int signo _U_)
-{
- capture_loop_stop();
+capture_stat_cache_update(if_stat_cache_t *sc) {
+ gchar stat_line[MAX_STAT_LINE_LEN];
+ gchar **stat_parts;
+ GList *sc_entry;
+ if_stat_cache_item_t *sc_item;
+
+ if (!sc)
+ return;
+
+ while (sync_pipe_gets_nonblock(sc->stat_fd, stat_line, MAX_STAT_LINE_LEN) > 0) {
+ g_strstrip(stat_line);
+ stat_parts = g_strsplit(stat_line, "\t", 3);
+ if (stat_parts[0] == NULL || stat_parts[1] == NULL ||
+ stat_parts[2] == NULL) {
+ g_strfreev(stat_parts);
+ continue;
+ }
+ for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
+ sc_item = sc_entry->data;
+ if (strcmp(sc_item->name, stat_parts[0]) == 0) {
+ sc_item->ps.ps_recv = (u_int) strtoul(stat_parts[1], NULL, 10);
+ sc_item->ps.ps_drop = (u_int) strtoul(stat_parts[2], NULL, 10);
+ }
+ }
+ g_strfreev(stat_parts);
+ }
}
+gboolean
+capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps) {
+ GList *sc_entry;
+ if_stat_cache_item_t *sc_item;
-int
-capture_start(capture_options *capture_opts, gboolean *stats_known, struct pcap_stat *stats)
-{
-#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_opts->capture_child)
- signal(SIGUSR1, stop_capture_signal_handler);
-#endif
+ if (!sc || !ifname || !ps) {
+ return FALSE;
+ }
- return capture_loop_start(capture_opts, stats_known, stats);
+ capture_stat_cache_update(sc);
+ for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
+ sc_item = sc_entry->data;
+ if (strcmp(sc_item->name, ifname) == 0) {
+ memcpy(ps, &sc_item->ps, sizeof(struct pcap_stat));
+ return TRUE;
+ }
+ }
+ return FALSE;
}
void
-capture_stop(capture_options *capture_opts)
-{
-
- if (capture_opts->sync_mode) {
- sync_pipe_stop(capture_opts);
- }
-
- capture_loop_stop();
-}
+capture_stat_stop(if_stat_cache_t *sc) {
+ GList *sc_entry;
+ if_stat_cache_item_t *sc_item;
+ int ret;
+ gchar *msg;
+
+ if (!sc)
+ return;
+
+ ret = sync_interface_stats_close(&sc->stat_fd, &sc->fork_child, &msg);
+ if (ret == -1) {
+ /* XXX - report failure? */
+ g_free(msg);
+ }
-void
-capture_kill_child(capture_options *capture_opts)
-{
- if (capture_opts->sync_mode) {
- sync_pipe_kill(capture_opts);
- }
+ for (sc_entry = sc->cache_list; sc_entry != NULL; sc_entry = g_list_next(sc_entry)) {
+ sc_item = sc_entry->data;
+ g_free(sc_item->name);
+ g_free(sc_item);
+ }
+ g_free(sc);
}
-
#endif /* HAVE_LIBPCAP */