Skip to content

Commit

Permalink
dumpcap: Add a welcome mode
Browse files Browse the repository at this point in the history
Allow dumpcap to have the "-D" "-L" and "-S" flags all specified.
This is a "welcome mode" that prints out the interfaces and their
capabilities, and then prints out the running statistics.
("-L" and "-S" without "-D" will just print the statistics, but
doesn't give an error.)

In capture child mode, the interface information is sent as a
message on the sync pipe before the success message; the statistics
are sent on the data pipe as usual.

Actually using this in Wireshark will be added next.

Related to #15082
  • Loading branch information
johnthacker committed Dec 19, 2023
1 parent b22ed21 commit 607b367
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 117 deletions.
228 changes: 124 additions & 104 deletions capture/capture_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion capture/capture_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 19 additions & 11 deletions dumpcap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
};
Expand Down Expand Up @@ -865,15 +865,21 @@ 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;
if (capture_child) {
sync_pipe_write_errmsgs_to_parent(2, "Unexpected JSON error", "");
}
}
g_string_free(dumper.output_string, TRUE);
return status;
}

Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
}

/*
Expand Down
1 change: 1 addition & 0 deletions sync_pipe.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion ui/capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit 607b367

Please sign in to comment.