diff --git a/capture/capture_sync.c b/capture/capture_sync.c index 2f906170070..178973c095e 100644 --- a/capture/capture_sync.c +++ b/capture/capture_sync.c @@ -1407,9 +1407,11 @@ sync_if_list_capabilities_open(GList *if_queries, * contains the file descriptor for the pipe's stdout, *msg is unchanged, * and zero is returned. On failure, *msg will point to an error message * that must be g_free()d, and -1 will be returned. + * If data is not NULL, then it will also be set to point to a JSON + * serialization of the list of local interfaces and their capabilities. */ int -sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **msg, void (*update_cb)(void)) +sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **data, char **msg, void (*update_cb)(void)) { int argc; char **argv; @@ -1438,6 +1440,12 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **m /* Ask for the interface statistics */ argv = sync_pipe_add_arg(argv, &argc, "-S"); + /* If requested, ask for the interface list and capabilities. */ + if (data) { + argv = sync_pipe_add_arg(argv, &argc, "-D"); + argv = sync_pipe_add_arg(argv, &argc, "-L"); + } + #ifndef DEBUG_CHILD argv = sync_pipe_add_arg(argv, &argc, "-Z"); #ifdef _WIN32 @@ -1462,126 +1470,138 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **m * * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. */ - nread = pipe_read_block(message_read_io, &indicator, SP_MAX_MSG_LEN, - buffer, msg); - if(nread <= 0) { - /* We got a read error from the sync pipe, or we got no data at - all from the sync pipe, so we're not going to be getting any - data or error message from the child process. Pick up its - exit status, and complain. - - We don't have to worry about killing the child, if the sync pipe - returned an error. Usually this error is caused as the child killed - itself while going down. Even in the rare cases that this isn't the - case, the child will get an error when writing to the broken pipe - the next time, cleaning itself up then. */ - ret = sync_pipe_wait_for_child(*fork_child, &wait_msg); - g_io_channel_unref(message_read_io); - ws_close(*data_read_fd); - if(nread == 0) { - /* We got an EOF from the sync pipe. That means that it exited - before giving us any data to read. If ret is -1, we report - that as a bad exit (e.g., exiting due to a signal); otherwise, - we report it as a premature exit. */ - if (ret == -1) - *msg = wait_msg; - else - *msg = g_strdup("Child dumpcap closed sync pipe prematurely"); - } else { - /* We got an error from the sync pipe. If ret is -1, report - both the sync pipe I/O error and the wait error. */ - if (ret == -1) { - combined_msg = ws_strdup_printf("%s\n\n%s", *msg, wait_msg); - g_free(*msg); - g_free(wait_msg); - *msg = combined_msg; + do { + nread = pipe_read_block(message_read_io, &indicator, SP_MAX_MSG_LEN, + buffer, msg); + if(nread <= 0) { + /* We got a read error from the sync pipe, or we got no data at + all from the sync pipe, so we're not going to be getting any + data or error message from the child process. Pick up its + exit status, and complain. + + We don't have to worry about killing the child, if the sync pipe + returned an error. Usually this error is caused as the child killed + itself while going down. Even in the rare cases that this isn't the + case, the child will get an error when writing to the broken pipe + the next time, cleaning itself up then. */ + ret = sync_pipe_wait_for_child(*fork_child, &wait_msg); + g_io_channel_unref(message_read_io); + ws_close(*data_read_fd); + if(nread == 0) { + /* We got an EOF from the sync pipe. That means that it exited + before giving us any data to read. If ret is -1, we report + that as a bad exit (e.g., exiting due to a signal); otherwise, + we report it as a premature exit. */ + if (ret == -1) + *msg = wait_msg; + else + *msg = g_strdup("Child dumpcap closed sync pipe prematurely"); + } else { + /* We got an error from the sync pipe. If ret is -1, report + both the sync pipe I/O error and the wait error. */ + if (ret == -1) { + combined_msg = ws_strdup_printf("%s\n\n%s", *msg, wait_msg); + g_free(*msg); + g_free(wait_msg); + *msg = combined_msg; + } } + return -1; } - return -1; - } - /* we got a valid message block from the child, process it */ - switch(indicator) { + /* we got a valid message block from the child, process it */ + switch(indicator) { - case SP_EXEC_FAILED: - /* - * Exec of dumpcap failed. Get the errno for the failure. - */ - if (!ws_strtoi32(buffer, NULL, &exec_errno)) { - ws_warning("Invalid errno: %s", buffer); - } - *msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", - g_strerror(exec_errno)); + case SP_EXEC_FAILED: + /* + * Exec of dumpcap failed. Get the errno for the failure. + */ + if (!ws_strtoi32(buffer, NULL, &exec_errno)) { + ws_warning("Invalid errno: %s", buffer); + } + *msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", + g_strerror(exec_errno)); - /* - * Pick up the child status. - */ - sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - ret = -1; - break; + /* + * Pick up the child status. + */ + sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, msg); + ret = -1; + break; - case SP_ERROR_MSG: - /* - * Error from dumpcap; there will be a primary message and a - * secondary message. - */ + case SP_ERROR_MSG: + /* + * Error from dumpcap; there will be a primary message and a + * secondary message. + */ - /* convert primary message */ - pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((unsigned char*)primary_msg_text + primary_msg_len, 4, &indicator, - &secondary_msg_len); - /*secondary_msg_text = primary_msg_text + primary_msg_len + 4;*/ - /* the capture child will close the sync_pipe, nothing to do */ + /* convert primary message */ + pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); + primary_msg_text = buffer+4; + /* convert secondary message */ + pipe_convert_header((unsigned char*)primary_msg_text + primary_msg_len, 4, &indicator, + &secondary_msg_len); + /*secondary_msg_text = primary_msg_text + primary_msg_len + 4;*/ + /* the capture child will close the sync_pipe, nothing to do */ - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - } else { + ret = sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + } else { + /* + * Child process failed, but returned the expected exit status. + * Return the messages it gave us, and indicate failure. + */ + *msg = g_strdup(primary_msg_text); + ret = -1; + } + break; + + case SP_IFACE_LIST: /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Dumpcap giving us the interface list */ - *msg = g_strdup(primary_msg_text); - ret = -1; - } - break; - case SP_SUCCESS: - /* Close the message pipe. */ - g_io_channel_unref(message_read_io); - break; + /* convert primary message */ + *data = g_strdup(buffer); + break; - default: - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - if (ret == -1) { + case SP_SUCCESS: + /* Close the message pipe. */ + g_io_channel_unref(message_read_io); + break; + + default: /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - } else { - /* - * Child process returned an unknown status. - */ - *msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", - indicator); - ret = -1; + ret = sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + } else { + /* + * Child process returned an unknown status. + */ + *msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + indicator); + ret = -1; + } + break; } - break; - } + } while (indicator == SP_IFACE_LIST); + return ret; } diff --git a/capture/capture_sync.h b/capture/capture_sync.h index 8bca74c18b0..76e5e095ebc 100644 --- a/capture/capture_sync.h +++ b/capture/capture_sync.h @@ -101,7 +101,7 @@ sync_if_list_capabilities_open(GList *ifqueries, /** Start getting interface statistics using dumpcap. */ extern int -sync_interface_stats_open(int *read_fd, ws_process_id *fork_child, char **msg, void (*update_cb)(void)); +sync_interface_stats_open(int *read_fd, ws_process_id *fork_child, char **data, char **msg, void (*update_cb)(void)); /** Stop gathering statistics. */ extern int diff --git a/dumpcap.c b/dumpcap.c index 1e9db0cd9cc..1a294fe571b 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -789,7 +789,7 @@ print_machine_readable_if_capabilities(json_dumper *dumper, if_capabilities_t *c * The actual output of this function can be viewed with the command "dumpcap -D -Z none" */ static int -print_machine_readable_interfaces(GList *if_list, int caps_queries) +print_machine_readable_interfaces(GList *if_list, int caps_queries, bool print_statistics) { GList *if_entry; if_info_t *if_info; @@ -799,7 +799,7 @@ print_machine_readable_interfaces(GList *if_list, int caps_queries) int status; json_dumper dumper = { - .output_file = stdout, + .output_string = g_string_new(NULL), .flags = JSON_DUMPER_FLAGS_NO_DEBUG, // Don't abort on failure }; @@ -865,8 +865,13 @@ print_machine_readable_interfaces(GList *if_list, int caps_queries) if (json_dumper_finish(&dumper)) { status = 0; if (capture_child) { - /* Let our parent know we succeeded. */ - sync_pipe_write_string_msg(2, SP_SUCCESS, NULL); + if (print_statistics) { + sync_pipe_write_string_msg(2, SP_IFACE_LIST, dumper.output_string->str); + } else { + /* Let our parent know we succeeded. */ + sync_pipe_write_string_msg(2, SP_SUCCESS, NULL); + printf("%s", dumper.output_string->str); + } } } else { status = 2; @@ -874,6 +879,7 @@ print_machine_readable_interfaces(GList *if_list, int caps_queries) sync_pipe_write_errmsgs_to_parent(2, "Unexpected JSON error", ""); } } + g_string_free(dumper.output_string, TRUE); return status; } @@ -5468,19 +5474,19 @@ main(int argc, char *argv[]) break; /*** all non capture option specific ***/ case 'D': /* Print a list of capture devices and exit */ - if (!list_interfaces && !caps_queries) { + if (!list_interfaces && !caps_queries & !print_statistics) { run_once_args++; } list_interfaces = TRUE; break; case 'L': /* Print list of link-layer types and exit */ - if (!list_interfaces && !caps_queries) { + if (!list_interfaces && !caps_queries & !print_statistics) { run_once_args++; } caps_queries |= CAPS_QUERY_LINK_TYPES; break; case LONGOPT_LIST_TSTAMP_TYPES: - if (!list_interfaces && !caps_queries) { + if (!list_interfaces && !caps_queries & !print_statistics) { run_once_args++; } caps_queries |= CAPS_QUERY_TIMESTAMP_TYPES; @@ -5492,10 +5498,10 @@ main(int argc, char *argv[]) } break; case 'S': /* Print interface statistics once a second */ - if (!print_statistics) { - print_statistics = TRUE; + if (!list_interfaces && !caps_queries & !print_statistics) { run_once_args++; } + print_statistics = TRUE; break; case 'k': /* Set wireless channel */ if (!set_chan) { @@ -5697,10 +5703,12 @@ main(int argc, char *argv[]) } if (machine_readable) { - status = print_machine_readable_interfaces(if_list, caps_queries); + status = print_machine_readable_interfaces(if_list, caps_queries, print_statistics); } free_interface_list(if_list); - exit_main(status); + if (!print_statistics) { + exit_main(status); + } } /* diff --git a/sync_pipe.h b/sync_pipe.h index 4b54c6222c5..84f35458cb6 100644 --- a/sync_pipe.h +++ b/sync_pipe.h @@ -44,6 +44,7 @@ #define SP_DROPS 'D' /* count of packets dropped in capture */ #define SP_SUCCESS 'S' /* success indication, no extra data */ #define SP_TOOLBAR_CTRL 'T' /* interface toolbar control packet */ +#define SP_IFACE_LIST 'I' /* interface list */ /* * Win32 only: Indications sent out on the signal pipe (from parent to child) * (UNIX-like sends signals for this) diff --git a/ui/capture.c b/ui/capture.c index 62c8df7e359..590590a0a5b 100644 --- a/ui/capture.c +++ b/ui/capture.c @@ -877,7 +877,7 @@ 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;