diff --git a/capture/capture_sync.c b/capture/capture_sync.c index 75b0828d26..f16d5a8c14 100644 --- a/capture/capture_sync.c +++ b/capture/capture_sync.c @@ -182,12 +182,13 @@ void capture_process_finished(capture_session *cap_session) continue; } - if (interface_opts->extcap_stderr != NULL) { + if ((interface_opts->extcap_stderr != NULL) && + (interface_opts->extcap_stderr->len > 0)) { if (message->len > 0) { g_string_append(message, "\n"); } g_string_append(message, "Error by extcap pipe: "); - g_string_append(message, interface_opts->extcap_stderr); + g_string_append(message, interface_opts->extcap_stderr->str); } } diff --git a/capture_opts.c b/capture_opts.c index d5e008cfe2..8843d5b24c 100644 --- a/capture_opts.c +++ b/capture_opts.c @@ -67,6 +67,8 @@ capture_opts_init(capture_options *capture_opts) capture_opts->default_options.extcap_pipedata = NULL; capture_opts->default_options.extcap_stderr = NULL; capture_opts->default_options.extcap_child_watch = 0; + capture_opts->default_options.extcap_stdout_watch = 0; + capture_opts->default_options.extcap_stderr_watch = 0; #ifdef _WIN32 capture_opts->default_options.extcap_pipe_h = INVALID_HANDLE_VALUE; capture_opts->default_options.extcap_control_in_h = INVALID_HANDLE_VALUE; @@ -788,6 +790,8 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str interface_opts.extcap_pipedata = NULL; interface_opts.extcap_stderr = NULL; interface_opts.extcap_child_watch = 0; + interface_opts.extcap_stdout_watch = 0; + interface_opts.extcap_stderr_watch = 0; #ifdef _WIN32 interface_opts.extcap_pipe_h = INVALID_HANDLE_VALUE; interface_opts.extcap_control_in_h = INVALID_HANDLE_VALUE; @@ -1289,7 +1293,8 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index) if (interface_opts->extcap_pid != WS_INVALID_PID) ws_pipe_close((ws_pipe_t *) interface_opts->extcap_pipedata); g_free(interface_opts->extcap_pipedata); - g_free(interface_opts->extcap_stderr); + if (interface_opts->extcap_stderr) + g_string_free(interface_opts->extcap_stderr, TRUE); g_free(interface_opts->extcap_control_in); g_free(interface_opts->extcap_control_out); #ifdef HAVE_PCAP_REMOTE diff --git a/capture_opts.h b/capture_opts.h index 8a29b8299b..2508b850b7 100644 --- a/capture_opts.h +++ b/capture_opts.h @@ -210,8 +210,10 @@ typedef struct interface_options_tag { GHashTable *extcap_args; GPid extcap_pid; /* pid of running process or WS_INVALID_PID */ gpointer extcap_pipedata; - gchar *extcap_stderr; + GString *extcap_stderr; guint extcap_child_watch; + guint extcap_stdout_watch; + guint extcap_stderr_watch; #ifdef _WIN32 HANDLE extcap_pipe_h; HANDLE extcap_control_in_h; diff --git a/extcap.c b/extcap.c index e09c5b3c68..6ea20fba40 100644 --- a/extcap.c +++ b/extcap.c @@ -1299,6 +1299,55 @@ extcap_add_arg_and_remove_cb(gpointer key, gpointer value, gpointer data) return FALSE; } +static gboolean +extcap_stdout_cb(GIOChannel *source, GIOCondition condition _U_, gpointer data) +{ + interface_options *interface_opts = (interface_options *)data; + char buf[128]; + gsize bytes_read; + + /* Discard data to prevent child process hanging on stdout write */ + g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL); + if (bytes_read == 0) + { + interface_opts->extcap_stdout_watch = 0; + return G_SOURCE_REMOVE; + } + return G_SOURCE_CONTINUE; +} + +static gboolean +extcap_stderr_cb(GIOChannel *source, GIOCondition condition _U_, gpointer data) +{ + interface_options *interface_opts = (interface_options *)data; + char buf[128]; + gsize bytes_read; + + g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL); + if (bytes_read == 0) + { + interface_opts->extcap_stderr_watch = 0; + return G_SOURCE_REMOVE; + } + +#define STDERR_BUFFER_SIZE 1024 + if (interface_opts->extcap_stderr == NULL) + { + interface_opts->extcap_stderr = g_string_new_len(buf, bytes_read); + } + else + { + gssize remaining = STDERR_BUFFER_SIZE - interface_opts->extcap_stderr->len; + if (remaining > 0) + { + gssize bytes = bytes_read; + bytes = MIN(bytes, remaining); + g_string_append_len(interface_opts->extcap_stderr, buf, bytes); + } + } + return G_SOURCE_CONTINUE; +} + static void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data) { guint i; @@ -1320,23 +1369,30 @@ static void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data) interface_opts->extcap_pid); interface_opts->extcap_pid = WS_INVALID_PID; + if (interface_opts->extcap_stdout_watch > 0) + { + g_source_remove(interface_opts->extcap_stdout_watch); + interface_opts->extcap_stdout_watch = 0; + } + + if (interface_opts->extcap_stderr_watch > 0) + { + g_source_remove(interface_opts->extcap_stderr_watch); + interface_opts->extcap_stderr_watch = 0; + } + pipedata = (ws_pipe_t *)interface_opts->extcap_pipedata; if (pipedata != NULL) { g_io_channel_unref(pipedata->stdout_io); - if (pipedata->stderr_io) + while (extcap_stderr_cb(pipedata->stderr_io, + g_io_channel_get_buffer_condition(pipedata->stderr_io), + (gpointer)interface_opts) != G_SOURCE_REMOVE) { -#define STDERR_BUFFER_SIZE 1024 - gchar *buffer = (gchar *)g_malloc0(STDERR_BUFFER_SIZE + 1); - g_io_channel_read_chars(pipedata->stderr_io, buffer, STDERR_BUFFER_SIZE, NULL, NULL); - if (strlen(buffer) > 0) - { - interface_opts->extcap_stderr = g_strdup(buffer); - } - g_free(buffer); - g_io_channel_unref(pipedata->stderr_io); + /* Keep reading until there's nothing left */ } + g_io_channel_unref(pipedata->stderr_io); g_free(pipedata); interface_opts->extcap_pipedata = NULL; @@ -1618,6 +1674,12 @@ extcap_init_interfaces(capture_session *cap_session) interface_opts->extcap_child_watch = g_child_watch_add_full(G_PRIORITY_HIGH, pid, extcap_child_watch_cb, (gpointer)cap_session, NULL); + interface_opts->extcap_stdout_watch = + g_io_add_watch(pipedata->stdout_io, G_IO_IN | G_IO_HUP, + extcap_stdout_cb, (gpointer)interface_opts); + interface_opts->extcap_stderr_watch = + g_io_add_watch(pipedata->stderr_io, G_IO_IN | G_IO_HUP, + extcap_stderr_cb, (gpointer)interface_opts); #ifdef _WIN32 /* On Windows, wait for extcap to connect to named pipe.