summaryrefslogtreecommitdiffstats
path: root/ui/capture.c
diff options
context:
space:
mode:
Diffstat (limited to 'ui/capture.c')
-rw-r--r--ui/capture.c264
1 files changed, 193 insertions, 71 deletions
diff --git a/ui/capture.c b/ui/capture.c
index 62c8df7e..3f81da2b 100644
--- a/ui/capture.c
+++ b/ui/capture.c
@@ -19,6 +19,8 @@
#include <glib.h>
+#include <ws_exit_codes.h>
+
#include <epan/packet.h>
#include <epan/dfilter/dfilter.h>
#include "extcap.h"
@@ -28,6 +30,7 @@
#include <capture/capture_sync.h>
#include "ui/capture_info.h"
#include "ui/capture_ui_utils.h"
+#include "ui/iface_lists.h"
#include "ui/util.h"
#include "ui/urls.h"
#include "capture/capture-pcap-util.h"
@@ -59,10 +62,10 @@ struct if_stat_cache_s {
/* 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;
+ void *user_data;
} capture_callback_data_t;
-static GList *capture_callbacks = NULL;
+static GList *capture_callbacks;
static void
capture_callback_invoke(int event, capture_session *cap_session)
@@ -82,7 +85,7 @@ capture_callback_invoke(int event, capture_session *cap_session)
void
-capture_callback_add(capture_callback_t func, gpointer user_data)
+capture_callback_add(capture_callback_t func, void *user_data)
{
capture_callback_data_t *cb;
@@ -94,7 +97,7 @@ capture_callback_add(capture_callback_t func, gpointer user_data)
}
void
-capture_callback_remove(capture_callback_t func, gpointer user_data)
+capture_callback_remove(capture_callback_t func, void *user_data)
{
capture_callback_data_t *cb;
GList *cb_item = capture_callbacks;
@@ -115,9 +118,9 @@ capture_callback_remove(capture_callback_t func, gpointer user_data)
/**
* Start a capture.
*
- * @return TRUE if the capture starts successfully, FALSE otherwise.
+ * @return true if the capture starts successfully, false otherwise.
*/
-gboolean
+bool
capture_start(capture_options *capture_opts, GPtrArray *capture_comments,
capture_session *cap_session, info_data_t* cap_data,
void(*update_cb)(void))
@@ -141,7 +144,7 @@ capture_start(capture_options *capture_opts, GPtrArray *capture_comments,
ws_message("Capture Start failed.");
cap_session->state = CAPTURE_STOPPED;
- return FALSE;
+ return false;
}
// Do we need data structures for ignoring duplicate frames?
@@ -179,7 +182,7 @@ capture_start(capture_options *capture_opts, GPtrArray *capture_comments,
capture_info_ui_create(&cap_data->ui, cap_session);
}
- return TRUE;
+ return true;
}
@@ -192,7 +195,7 @@ capture_stop(capture_session *cap_session)
if (!extcap_session_stop(cap_session)) {
extcap_request_stop(cap_session);
- cap_session->capture_opts->stop_after_extcaps = TRUE;
+ cap_session->capture_opts->stop_after_extcaps = true;
} else {
/* stop the capture child gracefully */
sync_pipe_stop(cap_session);
@@ -210,9 +213,9 @@ capture_kill_child(capture_session *cap_session)
}
/* We've succeeded in doing a (non real-time) capture; try to read it into a new capture file */
-static gboolean
-capture_input_read_all(capture_session *cap_session, gboolean is_tempfile,
- gboolean drops_known, guint32 drops)
+static bool
+capture_input_read_all(capture_session *cap_session, bool is_tempfile,
+ bool drops_known, uint32_t drops)
{
capture_options *capture_opts = cap_session->capture_opts;
int err;
@@ -220,7 +223,7 @@ capture_input_read_all(capture_session *cap_session, gboolean is_tempfile,
/* Capture succeeded; attempt to open the capture file. */
if (cf_open((capture_file *)cap_session->cf, capture_opts->save_file, WTAP_TYPE_AUTO, is_tempfile, &err) != CF_OK) {
/* We're not doing a capture any more, so we don't have a save file. */
- return FALSE;
+ return false;
}
/* Set the read filter to NULL. */
@@ -246,7 +249,7 @@ capture_input_read_all(capture_session *cap_session, gboolean is_tempfile,
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_file *)cap_session->cf, TRUE);
+ cf_set_drops_known((capture_file *)cap_session->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
@@ -260,7 +263,7 @@ capture_input_read_all(capture_session *cap_session, gboolean is_tempfile,
}
/* read in the packet data */
- switch (cf_read((capture_file *)cap_session->cf, /*reloading=*/FALSE)) {
+ switch (cf_read((capture_file *)cap_session->cf, /*reloading=*/false)) {
case CF_READ_OK:
case CF_READ_ERROR:
@@ -273,7 +276,7 @@ capture_input_read_all(capture_session *cap_session, gboolean is_tempfile,
/* User wants to quit program. Exit by leaving the main loop,
so that any quit functions we registered get called. */
exit_application(0);
- return FALSE;
+ return false;
}
/* if we didn't capture even a single packet, close the file again */
@@ -297,11 +300,11 @@ capture_input_read_all(capture_session *cap_session, gboolean is_tempfile,
(cf_is_tempfile((capture_file *)cap_session->cf)) ? "temporary " : "");
cf_close((capture_file *)cap_session->cf);
}
- return TRUE;
+ return true;
}
static const char *
-cf_open_error_message(int err, gchar *err_info)
+cf_open_error_message(int err, char *err_info)
{
const char *errmsg;
static char errmsg_errno[1024 + 1];
@@ -381,19 +384,19 @@ cf_open_error_message(int err, gchar *err_info)
}
}
else
- errmsg = file_open_error_message(err, FALSE);
+ errmsg = file_open_error_message(err, false);
return errmsg;
}
/* capture child tells us we have a new (or the first) capture file */
static bool
-capture_input_new_file(capture_session *cap_session, gchar *new_file)
+capture_input_new_file(capture_session *cap_session, char *new_file)
{
capture_options *capture_opts = cap_session->capture_opts;
- gboolean is_tempfile;
+ bool is_tempfile;
int err;
- gchar *err_info;
- gchar *err_msg;
+ char *err_info;
+ char *err_msg;
if(cap_session->state == CAPTURE_PREPARING) {
ws_message("Capture started");
@@ -409,7 +412,7 @@ capture_input_new_file(capture_session *cap_session, gchar *new_file)
if (((capture_file*)cap_session->cf)->state == FILE_READ_PENDING) {
capture_callback_invoke(capture_cb_capture_fixed_finished, cap_session);
} else if (((capture_file*)cap_session->cf)->state != FILE_CLOSED) {
- cap_session->session_will_restart = TRUE;
+ cap_session->session_will_restart = true;
capture_callback_invoke(capture_cb_capture_update_finished, cap_session);
cf_finish_tail((capture_file *)cap_session->cf,
&cap_session->rec, &cap_session->buf, &err,
@@ -417,12 +420,12 @@ capture_input_new_file(capture_session *cap_session, gchar *new_file)
cf_close((capture_file *)cap_session->cf);
}
g_free(capture_opts->save_file);
- is_tempfile = FALSE;
- cf_set_tempfile((capture_file *)cap_session->cf, FALSE);
+ is_tempfile = false;
+ cf_set_tempfile((capture_file *)cap_session->cf, false);
} else {
/* we didn't have a save_file before; must be a tempfile */
- is_tempfile = TRUE;
- cf_set_tempfile((capture_file *)cap_session->cf, TRUE);
+ is_tempfile = true;
+ cf_set_tempfile((capture_file *)cap_session->cf, true);
}
/* save the new filename */
@@ -439,7 +442,7 @@ capture_input_new_file(capture_session *cap_session, gchar *new_file)
for debugging purposes. */
g_free(capture_opts->save_file);
capture_opts->save_file = NULL;
- return FALSE;
+ return false;
}
} else {
capture_callback_invoke(capture_cb_capture_prepared, cap_session);
@@ -450,13 +453,13 @@ capture_input_new_file(capture_session *cap_session, gchar *new_file)
wtap_close(cap_session->wtap);
}
- cap_session->wtap = wtap_open_offline(new_file, WTAP_TYPE_AUTO, &err, &err_info, FALSE);
+ cap_session->wtap = wtap_open_offline(new_file, WTAP_TYPE_AUTO, &err, &err_info, false);
if (!cap_session->wtap) {
err_msg = ws_strdup_printf(cf_open_error_message(err, err_info),
new_file);
ws_warning("capture_input_new_file: %d (%s)", err, err_msg);
g_free(err_msg);
- return FALSE;
+ return false;
}
}
@@ -467,11 +470,11 @@ capture_input_new_file(capture_session *cap_session, gchar *new_file)
}
cap_session->state = CAPTURE_RUNNING;
- return TRUE;
+ return true;
}
static void
-capture_info_packet(info_data_t* cap_info, gint wtap_linktype, const guchar *pd, guint32 caplen, union wtap_pseudo_header *pseudo_header)
+capture_info_packet(info_data_t* cap_info, int wtap_linktype, const unsigned char *pd, uint32_t caplen, union wtap_pseudo_header *pseudo_header)
{
capture_packet_info_t cpinfo;
@@ -488,8 +491,8 @@ static void
capture_info_new_packets(int to_read, wtap *wth, info_data_t* cap_info)
{
int err;
- gchar *err_info;
- gint64 data_offset;
+ char *err_info;
+ int64_t data_offset;
wtap_rec rec;
Buffer buf;
union wtap_pseudo_header *pseudo_header;
@@ -587,7 +590,7 @@ capture_input_new_packets(capture_session *cap_session, int to_read)
/* Capture child told us how many dropped packets it counted.
*/
static void
-capture_input_drops(capture_session *cap_session, guint32 dropped, const char* interface_name)
+capture_input_drops(capture_session *cap_session, uint32_t dropped, const char* interface_name)
{
if (interface_name != NULL) {
ws_info("%u packet%s dropped from %s", dropped, plurality(dropped, "", "s"), interface_name);
@@ -597,7 +600,7 @@ capture_input_drops(capture_session *cap_session, guint32 dropped, const char* i
ws_assert(cap_session->state == CAPTURE_RUNNING);
- cf_set_drops_known((capture_file *)cap_session->cf, TRUE);
+ cf_set_drops_known((capture_file *)cap_session->cf, true);
cf_set_drops((capture_file *)cap_session->cf, dropped);
}
@@ -612,10 +615,15 @@ static void
capture_input_error(capture_session *cap_session _U_, char *error_msg,
char *secondary_error_msg)
{
- gchar *safe_error_msg;
- gchar *safe_secondary_error_msg;
-
- ws_message("Error message from child: \"%s\", \"%s\"", error_msg, secondary_error_msg);
+ char *safe_error_msg;
+ char *safe_secondary_error_msg;
+
+ /* The primary message might be an empty string, e.g. when the error was
+ * from extcap. (The extcap stderr is gathered when the session closes
+ * and printed in capture_input_closed below.) */
+ if (*error_msg != '\0') {
+ ws_message("Error message from child: \"%s\", \"%s\"", error_msg, secondary_error_msg);
+ }
ws_assert(cap_session->state == CAPTURE_PREPARING || cap_session->state == CAPTURE_RUNNING);
@@ -642,14 +650,14 @@ capture_input_error(capture_session *cap_session _U_, char *error_msg,
capture filter when starting/running the capture.
*/
static void
-capture_input_cfilter_error(capture_session *cap_session, guint i,
+capture_input_cfilter_error(capture_session *cap_session, unsigned i,
const char *error_message)
{
capture_options *capture_opts = cap_session->capture_opts;
dfilter_t *rfcode = NULL;
- gchar *safe_cfilter;
- gchar *safe_descr;
- gchar *safe_cfilter_error_msg;
+ char *safe_cfilter;
+ char *safe_descr;
+ char *safe_cfilter_error_msg;
interface_options *interface_opts;
ws_message("Capture filter error message from child: \"%s\"", error_message);
@@ -694,7 +702,7 @@ capture_input_cfilter_error(capture_session *cap_session, guint i,
/* capture child closed its side of the pipe, do the required cleanup */
static void
-capture_input_closed(capture_session *cap_session, gchar *msg)
+capture_input_closed(capture_session *cap_session, char *msg)
{
capture_options *capture_opts = cap_session->capture_opts;
int err;
@@ -702,7 +710,7 @@ capture_input_closed(capture_session *cap_session, gchar *msg)
ws_message("Capture stopped.");
ws_assert(cap_session->state == CAPTURE_PREPARING || cap_session->state == CAPTURE_RUNNING);
- if (msg != NULL) {
+ if (msg != NULL && msg[0] != '\0') {
ESD_TYPE_E dlg_type = ESD_TYPE_ERROR;
if (strstr(msg, " WARNING] ")) {
dlg_type = ESD_TYPE_WARN;
@@ -711,16 +719,27 @@ capture_input_closed(capture_session *cap_session, gchar *msg)
* ws_log prefixes log messages with a timestamp delimited by " -- " and possibly
* a function name delimited by "(): ". Log it to sterr, but omit it in the UI.
*/
- char *plain_msg = strstr(msg, "(): ");
- if (plain_msg != NULL) {
- plain_msg += strlen("(): ");
- } else if ((plain_msg = strstr(msg, " -- ")) != NULL) {
- plain_msg += strlen(" -- ");
- } else {
- plain_msg = msg;
+ char **msg_lines = g_strsplit(msg, "\n", 0);
+ GString* gui_msg = g_string_new(NULL);
+
+ for (char **line = msg_lines; *line != NULL; line++) {
+ if (gui_msg->len > 0) {
+ g_string_append(gui_msg, "\n");
+ }
+ char *plain_msg = strstr(*line, "(): ");
+ if (plain_msg != NULL) {
+ plain_msg += strlen("(): ");
+ } else if ((plain_msg = strstr(*line, " -- ")) != NULL) {
+ plain_msg += strlen(" -- ");
+ } else {
+ plain_msg = *line;
+ }
+ g_string_append(gui_msg, plain_msg);
}
ws_warning("%s", msg);
- simple_dialog(dlg_type, ESD_BTN_OK, "%s", plain_msg);
+ simple_dialog(dlg_type, ESD_BTN_OK, "%s", gui_msg->str);
+ g_string_free(gui_msg, TRUE);
+ g_strfreev(msg_lines);
}
wtap_rec_cleanup(&cap_session->rec);
@@ -755,7 +774,7 @@ capture_input_closed(capture_session *cap_session, gchar *msg)
/* 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 */
- cap_session->session_will_restart = FALSE;
+ cap_session->session_will_restart = false;
capture_callback_invoke(capture_cb_capture_update_finished, cap_session);
/* Finish the capture. */
@@ -823,7 +842,7 @@ capture_input_closed(capture_session *cap_session, gchar *msg)
/* does the user wants to restart the current capture? */
if(capture_opts->restart) {
- capture_opts->restart = FALSE;
+ capture_opts->restart = false;
/* If we have a ring buffer, the original save file has been overwritten
with the "ring filename". Restore it before starting again */
@@ -838,9 +857,26 @@ capture_input_closed(capture_session *cap_session, gchar *msg)
capture_opts->save_file = NULL;
}
} else {
- /* 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;
+
+ /* If we're in multiple file mode, restore the original save file
+ name (template), so that it will be used if a new capture is started
+ without opening the Capture Options dialog. Any files we just wrote
+ won't get overwritten. If we set it to NULL, a tempfile name would
+ be used, but that doesn't work in multiple file mode - we could turn
+ off multiple file mode instead, but that would change the behavior
+ if the Capture Options dialog is re-opened. */
+ if ((capture_opts->multi_files_on) && (capture_opts->orig_save_file != NULL)) {
+ g_free(capture_opts->save_file);
+ capture_opts->save_file = g_strdup(capture_opts->orig_save_file);
+ } else {
+ /* We're not doing a capture any more, so we don't have a save file.
+ If a new capture is started without opening the Capture Options
+ dialog (Start button or double-clicking on an interface from
+ the welcome screen), we'll use a tempfile. Thus if our current
+ capture is to a permanent file, we won't overwrite it. */
+ g_free(capture_opts->save_file);
+ capture_opts->save_file = NULL;
+ }
}
}
@@ -849,10 +885,10 @@ capture_stat_start(capture_options *capture_opts)
{
int stat_fd;
ws_process_id fork_child;
- gchar *msg;
+ char *msg;
if_stat_cache_t *sc = g_new0(if_stat_cache_t, 1);
if_stat_cache_item_t *sc_item;
- guint i;
+ unsigned i;
interface_t *device;
sc->stat_fd = -1;
@@ -877,14 +913,14 @@ capture_stat_start(capture_options *capture_opts)
* 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, NULL) == 0) {
+ if (sync_interface_stats_open(&stat_fd, &fork_child, NULL, &msg, NULL) == 0) {
sc->stat_fd = stat_fd;
sc->fork_child = fork_child;
/* Initialize the cache */
for (i = 0; i < capture_opts->all_ifaces->len; i++) {
device = &g_array_index(capture_opts->all_ifaces, interface_t, i);
- if (device->type != IF_PIPE && device->type != IF_EXTCAP) {
+ if (device->if_info.type != IF_PIPE && device->if_info.type != IF_EXTCAP) {
sc_item = g_new0(if_stat_cache_item_t, 1);
ws_assert(device->if_info.name);
sc_item->name = g_strdup(device->if_info.name);
@@ -898,13 +934,99 @@ capture_stat_start(capture_options *capture_opts)
return sc;
}
+if_stat_cache_t *
+capture_interface_stat_start(capture_options *capture_opts _U_, GList **if_list)
+{
+ int stat_fd;
+ ws_process_id fork_child;
+ char *msg;
+ if_stat_cache_t *sc = g_new0(if_stat_cache_t, 1);
+ if_stat_cache_item_t *sc_item;
+ char *data = NULL;
+
+ sc->stat_fd = -1;
+ sc->fork_child = WS_INVALID_PID;
+
+ /* 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.
+ */
+ int status;
+ status = sync_interface_stats_open(&stat_fd, &fork_child, &data, &msg, NULL);
+ /* In order to initialize the stat cache (below), we need to have
+ * filled in capture_opts->all_ifaces
+ *
+ * Note that the operation above can return a failed status but
+ * valid data, e.g. if dumpcap returns an interface list but none
+ * of them have permission to do a capture.
+ */
+ int err = 0;
+ char *err_msg = NULL;
+ *if_list = deserialize_interface_list(data, &err, &err_msg);
+ if (err != 0) {
+ ws_info("%s", err_msg);
+ g_free(err_msg);
+ }
+ if (status == 0) {
+ sc->stat_fd = stat_fd;
+ sc->fork_child = fork_child;
+
+ /* Initialize the cache */
+ for (GList *if_entry = *if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) {
+ if_info_t *if_info = (if_info_t*)if_entry->data;
+ /* We just got this list from dumpcap so it shouldn't
+ * contain stdin, pipes, extcaps, or remote interfaces
+ * list. We could test if_info->type and the name to
+ * exclude those types from the cache anyway, though.
+ */
+ sc_item = g_new0(if_stat_cache_item_t, 1);
+ ws_assert(if_info->name);
+ sc_item->name = g_strdup(if_info->name);
+ sc->cache_list = g_list_prepend(sc->cache_list, sc_item);
+ }
+ } else if (status == WS_EXIT_NO_INTERFACES) {
+ /*
+ * No interfaces were found. If that's not the
+ * result of an error when fetching the local
+ * interfaces, let the user know.
+ */
+ ws_info("%s", msg);
+ g_free(msg); /* XXX: should we display this to the user via the GUI? */
+ } else {
+ ws_warning("%s", msg);
+ g_free(msg); /* XXX: should we display this to the user via the GUI? */
+ }
+
+#ifdef HAVE_PCAP_REMOTE
+ *if_list = append_remote_list(*if_list);
+#endif
+
+ *if_list = append_extcap_interface_list(*if_list);
+ return sc;
+}
+
#define MAX_STAT_LINE_LEN 500
static void
capture_stat_cache_update(if_stat_cache_t *sc)
{
- gchar stat_line[MAX_STAT_LINE_LEN] = "";
- gchar **stat_parts;
+ char stat_line[MAX_STAT_LINE_LEN] = "";
+ char **stat_parts;
GList *sc_entry;
if_stat_cache_item_t *sc_item;
@@ -931,14 +1053,14 @@ capture_stat_cache_update(if_stat_cache_t *sc)
}
}
-gboolean
+bool
capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps)
{
GList *sc_entry;
if_stat_cache_item_t *sc_item;
if (!sc || sc->fork_child == WS_INVALID_PID || !ifname || !ps) {
- return FALSE;
+ return false;
}
capture_stat_cache_update(sc);
@@ -946,10 +1068,10 @@ capture_stats(if_stat_cache_t *sc, char *ifname, struct pcap_stat *ps)
sc_item = (if_stat_cache_item_t *)sc_entry->data;
if (strcmp(sc_item->name, ifname) == 0) {
memcpy(ps, &sc_item->ps, sizeof(struct pcap_stat));
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
void
@@ -958,7 +1080,7 @@ capture_stat_stop(if_stat_cache_t *sc)
GList *sc_entry;
if_stat_cache_item_t *sc_item;
int ret;
- gchar *msg;
+ char *msg;
if (!sc) {
return;