dumpcap: Serialize machine readable interface caps as JSON
Serialize the machine readable version of the interface capability output as JSON, using an array to support multiple interfaces. When querying multiple interfaces, try all of them, exit with success (unless unexpected errors occur) and report any per-interface errors and exit codes inside the JSON rather than stopping after the first interface with error. Update capture_get_if_capabilities to process the JSON. It (and sync_if_capabilities_open) still only query a single interface at, but this will allow modification to handle multiple interfaces at once later. Related to #16191, #15082
This commit is contained in:
parent
212cfe132c
commit
0d93782443
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <capture/capture_ifinfo.h>
|
||||
#include <wsutil/inet_addr.h>
|
||||
#include <wsutil/wsjson.h>
|
||||
|
||||
#ifdef HAVE_PCAP_REMOTE
|
||||
static GList *remote_interface_list = NULL;
|
||||
|
@ -199,8 +200,8 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode,
|
|||
if_capabilities_t *caps;
|
||||
GList *linktype_list = NULL, *timestamp_list = NULL;
|
||||
int err, i;
|
||||
char *data, *primary_msg, *secondary_msg;
|
||||
char **raw_list;
|
||||
char *data, *primary_msg, *secondary_msg, *val_s;
|
||||
jsmntok_t *tokens, *inf_tok, *array_tok, *cur_tok;
|
||||
|
||||
/* see if the interface is from extcap */
|
||||
caps = extcap_get_if_dlts(ifname, err_primary_msg);
|
||||
|
@ -228,23 +229,81 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Split our lines */
|
||||
#ifdef _WIN32
|
||||
raw_list = g_strsplit(data, "\r\n", 0);
|
||||
#else
|
||||
raw_list = g_strsplit(data, "\n", 0);
|
||||
#endif
|
||||
g_free(data);
|
||||
int num_tokens = json_parse(data, NULL, 0);
|
||||
if (num_tokens <= 0) {
|
||||
ws_warning("Capture Interface Capabilities failed with invalid JSON.");
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* First line is 0 if monitor mode isn't supported, 1 if it is.
|
||||
*/
|
||||
if (raw_list[0] == NULL || *raw_list[0] == '\0') {
|
||||
tokens = wmem_alloc_array(NULL, jsmntok_t, num_tokens);
|
||||
if (json_parse(data, tokens, num_tokens) <= 0) {
|
||||
ws_info("Capture Interface Capabilities returned no information.");
|
||||
if (err_primary_msg) {
|
||||
*err_primary_msg = g_strdup("Dumpcap returned no interface capability information");
|
||||
}
|
||||
g_strfreev(raw_list);
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cur_tok = json_get_array_index(tokens, 0);
|
||||
if (cur_tok == NULL) {
|
||||
ws_warning("Capture Interface Capabilities failed with invalid JSON.");
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inf_tok = json_get_object(data, cur_tok, ifname);
|
||||
if (inf_tok == NULL) {
|
||||
ws_warning("Capture Interface Capabilities failed with invalid JSON.");
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double val_d;
|
||||
if (!json_get_double(data, inf_tok, "status", &val_d)) {
|
||||
ws_warning("Capture Interface Capabilities failed with invalid JSON.");
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
err = (int)val_d;
|
||||
if (err != 0) {
|
||||
primary_msg = json_get_string(data, inf_tok, "primary_msg");
|
||||
if (primary_msg) {
|
||||
primary_msg = g_strdup(primary_msg);
|
||||
}
|
||||
secondary_msg = json_get_string(data, inf_tok, "secondary_msg");
|
||||
if (secondary_msg) {
|
||||
secondary_msg = g_strdup(secondary_msg);
|
||||
}
|
||||
ws_info("Capture Interface Capabilities failed. Error %d, %s",
|
||||
err, primary_msg ? primary_msg : "no message");
|
||||
if (err_primary_msg)
|
||||
*err_primary_msg = primary_msg;
|
||||
else
|
||||
g_free(primary_msg);
|
||||
if (err_secondary_msg)
|
||||
*err_secondary_msg = secondary_msg;
|
||||
else
|
||||
g_free(secondary_msg);
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool rfmon;
|
||||
if (!json_get_boolean(data, inf_tok, "rfmon", &rfmon)) {
|
||||
ws_message("Capture Interface Capabilities returned bad information.");
|
||||
ws_message("Didn't return monitor-mode cap");
|
||||
if (err_primary_msg) {
|
||||
*err_primary_msg = ws_strdup_printf("Dumpcap didn't return monitor-mode capability");
|
||||
}
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -252,75 +311,67 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode,
|
|||
* Allocate the interface capabilities structure.
|
||||
*/
|
||||
caps = (if_capabilities_t *)g_malloc(sizeof *caps);
|
||||
switch (*raw_list[0]) {
|
||||
|
||||
case '0':
|
||||
caps->can_set_rfmon = false;
|
||||
break;
|
||||
|
||||
case '1':
|
||||
caps->can_set_rfmon = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
ws_info("Capture Interface Capabilities returned bad information.");
|
||||
if (err_primary_msg) {
|
||||
*err_primary_msg = ws_strdup_printf("Dumpcap returned \"%s\" for monitor-mode capability",
|
||||
raw_list[0]);
|
||||
}
|
||||
g_free(caps);
|
||||
g_strfreev(raw_list);
|
||||
return NULL;
|
||||
}
|
||||
caps->can_set_rfmon = rfmon;
|
||||
|
||||
/*
|
||||
* The following are link-layer types.
|
||||
*/
|
||||
for (i = 1; raw_list[i] != NULL && *raw_list[i] != '\0'; i++) {
|
||||
data_link_info_t *data_link_info;
|
||||
/* ...and what if the interface name has a tab in it, Mr. Clever Programmer? */
|
||||
char **lt_parts = g_strsplit(raw_list[i], "\t", 3);
|
||||
if (lt_parts[0] == NULL || lt_parts[1] == NULL || lt_parts[2] == NULL) {
|
||||
g_strfreev(lt_parts);
|
||||
array_tok = json_get_array(data, inf_tok, "data_link_types");
|
||||
if (!array_tok) {
|
||||
ws_message("Capture Interface Capabilities returned bad data_link information.");
|
||||
if (err_primary_msg) {
|
||||
*err_primary_msg = ws_strdup_printf("Dumpcap didn't return data link types capability");
|
||||
}
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
g_free(caps);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < json_get_array_len(array_tok); i++) {
|
||||
cur_tok = json_get_array_index(array_tok, i);
|
||||
|
||||
if (!json_get_double(data, cur_tok, "dlt", &val_d)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data_link_info_t *data_link_info;
|
||||
data_link_info = g_new(data_link_info_t,1);
|
||||
data_link_info->dlt = (int) strtol(lt_parts[0], NULL, 10);
|
||||
data_link_info->name = g_strdup(lt_parts[1]);
|
||||
if (strcmp(lt_parts[2], "(not supported)") != 0)
|
||||
data_link_info->description = g_strdup(lt_parts[2]);
|
||||
else
|
||||
data_link_info->description = NULL;
|
||||
g_strfreev(lt_parts);
|
||||
|
||||
data_link_info->dlt = (int)val_d;
|
||||
val_s = json_get_string(data, cur_tok, "name");
|
||||
data_link_info->name = val_s ? g_strdup(val_s) : NULL;
|
||||
val_s = json_get_string(data, cur_tok, "description");
|
||||
if (!val_s || strcmp(val_s, "(not supported)") == 0) {
|
||||
data_link_info->description = NULL;
|
||||
} else {
|
||||
data_link_info->description = g_strdup(val_s);
|
||||
}
|
||||
linktype_list = g_list_append(linktype_list, data_link_info);
|
||||
}
|
||||
|
||||
if (raw_list[i]) { /* Oh, timestamp types! */
|
||||
for (i++; raw_list[i] != NULL && *raw_list[i] != '\0'; i++) {
|
||||
timestamp_info_t *timestamp_info;
|
||||
char **tt_parts = g_strsplit(raw_list[i], "\t", 2);
|
||||
if (tt_parts[0] == NULL || tt_parts[1] == NULL) {
|
||||
g_strfreev(tt_parts);
|
||||
continue;
|
||||
}
|
||||
array_tok = json_get_array(data, inf_tok, "timestamp_types");
|
||||
if (array_tok) {
|
||||
for (i = 0; i < json_get_array_len(array_tok); i++) {
|
||||
cur_tok = json_get_array_index(array_tok, i);
|
||||
|
||||
timestamp_info_t *timestamp_info;
|
||||
timestamp_info = g_new(timestamp_info_t,1);
|
||||
timestamp_info->name = g_strdup(tt_parts[0]);
|
||||
timestamp_info->description = g_strdup(tt_parts[1]);
|
||||
g_strfreev(tt_parts);
|
||||
val_s = json_get_string(data, cur_tok, "name");
|
||||
timestamp_info->name = val_s ? g_strdup(val_s) : NULL;
|
||||
val_s = json_get_string(data, cur_tok, "description");
|
||||
timestamp_info->description = val_s ? g_strdup(val_s) : NULL;
|
||||
|
||||
timestamp_list = g_list_append(timestamp_list, timestamp_info);
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev(raw_list);
|
||||
|
||||
caps->data_link_types = linktype_list;
|
||||
/* Might be NULL. Not all systems report timestamp types */
|
||||
caps->timestamp_types = timestamp_list;
|
||||
|
||||
wmem_free(NULL, tokens);
|
||||
g_free(data);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
|
|
141
dumpcap.c
141
dumpcap.c
|
@ -78,6 +78,7 @@
|
|||
#include "wsutil/time_util.h"
|
||||
#include "wsutil/please_report_bug.h"
|
||||
#include "wsutil/glib-compat.h"
|
||||
#include <wsutil/json_dumper.h>
|
||||
#include <wsutil/ws_assert.h>
|
||||
|
||||
#include "capture/ws80211_utils.h"
|
||||
|
@ -1089,21 +1090,17 @@ print_machine_readable_interfaces(GList *if_list)
|
|||
* you MUST update capture_ifinfo.c:capture_get_if_capabilities() accordingly!
|
||||
*/
|
||||
static void
|
||||
print_machine_readable_if_capabilities(if_capabilities_t *caps, int queries)
|
||||
print_machine_readable_if_capabilities(json_dumper *dumper, if_capabilities_t *caps, int queries)
|
||||
{
|
||||
GList *lt_entry, *ts_entry;
|
||||
const gchar *desc_str;
|
||||
|
||||
if (capture_child) {
|
||||
/* Let our parent know we succeeded. */
|
||||
sync_pipe_write_string_msg(2, SP_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
if (queries & CAPS_QUERY_LINK_TYPES) {
|
||||
if (caps->can_set_rfmon)
|
||||
printf("1\n");
|
||||
else
|
||||
printf("0\n");
|
||||
json_dumper_set_member_name(dumper, "rfmon");
|
||||
// XXX: wsjson.h doesn't have a function to read booleans
|
||||
json_dumper_value_anyf(dumper, "%s", caps->can_set_rfmon ? "true" : "false");
|
||||
json_dumper_set_member_name(dumper, "data_link_types");
|
||||
json_dumper_begin_array(dumper);
|
||||
for (lt_entry = caps->data_link_types; lt_entry != NULL;
|
||||
lt_entry = g_list_next(lt_entry)) {
|
||||
data_link_info_t *data_link_info = (data_link_info_t *)lt_entry->data;
|
||||
|
@ -1111,12 +1108,20 @@ print_machine_readable_if_capabilities(if_capabilities_t *caps, int queries)
|
|||
desc_str = data_link_info->description;
|
||||
else
|
||||
desc_str = "(not supported)";
|
||||
printf("%d\t%s\t%s\n", data_link_info->dlt, data_link_info->name,
|
||||
desc_str);
|
||||
json_dumper_begin_object(dumper);
|
||||
json_dumper_set_member_name(dumper, "dlt");
|
||||
json_dumper_value_anyf(dumper, "%d", data_link_info->dlt);
|
||||
json_dumper_set_member_name(dumper, "name");
|
||||
json_dumper_value_string(dumper, data_link_info->name);
|
||||
json_dumper_set_member_name(dumper, "description");
|
||||
json_dumper_value_string(dumper, desc_str);
|
||||
json_dumper_end_object(dumper);
|
||||
}
|
||||
json_dumper_end_array(dumper);
|
||||
}
|
||||
printf("\n");
|
||||
if (queries & CAPS_QUERY_TIMESTAMP_TYPES) {
|
||||
json_dumper_set_member_name(dumper, "timestamp_types");
|
||||
json_dumper_begin_array(dumper);
|
||||
for (ts_entry = caps->timestamp_types; ts_entry != NULL;
|
||||
ts_entry = g_list_next(ts_entry)) {
|
||||
timestamp_info_t *timestamp = (timestamp_info_t *)ts_entry->data;
|
||||
|
@ -1124,8 +1129,14 @@ print_machine_readable_if_capabilities(if_capabilities_t *caps, int queries)
|
|||
desc_str = timestamp->description;
|
||||
else
|
||||
desc_str = "(none)";
|
||||
printf("%s\t%s\n", timestamp->name, desc_str);
|
||||
json_dumper_begin_object(dumper);
|
||||
json_dumper_set_member_name(dumper, "name");
|
||||
json_dumper_value_string(dumper, timestamp->name);
|
||||
json_dumper_set_member_name(dumper, "description");
|
||||
json_dumper_value_string(dumper, desc_str);
|
||||
json_dumper_end_object(dumper);
|
||||
}
|
||||
json_dumper_end_array(dumper);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5870,43 +5881,89 @@ main(int argc, char *argv[])
|
|||
gchar *open_status_str;
|
||||
guint ii;
|
||||
|
||||
for (ii = 0; ii < global_capture_opts.ifaces->len; ii++) {
|
||||
interface_options *interface_opts;
|
||||
if (machine_readable) {
|
||||
status = 0;
|
||||
json_dumper dumper = {
|
||||
.output_file = stdout,
|
||||
.flags = JSON_DUMPER_FLAGS_NO_DEBUG,
|
||||
// Don't abort on failure
|
||||
};
|
||||
json_dumper_begin_array(&dumper);
|
||||
for (ii = 0; ii < global_capture_opts.ifaces->len; ii++) {
|
||||
interface_options *interface_opts;
|
||||
|
||||
interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, ii);
|
||||
interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, ii);
|
||||
|
||||
caps = get_if_capabilities(interface_opts, &open_status, &open_status_str);
|
||||
if (caps == NULL) {
|
||||
if (capture_child) {
|
||||
char *error_msg = ws_strdup_printf("The capabilities of the capture device "
|
||||
"\"%s\" could not be obtained (%s)",
|
||||
interface_opts->name, open_status_str);
|
||||
sync_pipe_write_errmsgs_to_parent(2, error_msg,
|
||||
get_pcap_failure_secondary_error_message(open_status, open_status_str));
|
||||
g_free(error_msg);
|
||||
json_dumper_begin_object(&dumper);
|
||||
json_dumper_set_member_name(&dumper, interface_opts->name);
|
||||
|
||||
json_dumper_begin_object(&dumper);
|
||||
|
||||
open_status = CAP_DEVICE_OPEN_NO_ERR;
|
||||
caps = get_if_capabilities(interface_opts, &open_status, &open_status_str);
|
||||
json_dumper_set_member_name(&dumper, "status");
|
||||
json_dumper_value_anyf(&dumper, "%i", open_status);
|
||||
if (caps == NULL) {
|
||||
json_dumper_set_member_name(&dumper, "primary_msg");
|
||||
json_dumper_value_string(&dumper, open_status_str);
|
||||
json_dumper_set_member_name(&dumper, "secondary_msg");
|
||||
json_dumper_value_string(&dumper, get_pcap_failure_secondary_error_message(open_status, open_status_str));
|
||||
g_free(open_status_str);
|
||||
} else {
|
||||
/* XXX: We need to change the format and adapt consumers */
|
||||
print_machine_readable_if_capabilities(&dumper, caps, caps_queries);
|
||||
free_if_capabilities(caps);
|
||||
}
|
||||
else {
|
||||
cmdarg_err("The capabilities of the capture device "
|
||||
"\"%s\" could not be obtained (%s).\n%s",
|
||||
interface_opts->name, open_status_str,
|
||||
get_pcap_failure_secondary_error_message(open_status, open_status_str));
|
||||
}
|
||||
g_free(open_status_str);
|
||||
exit_main(2);
|
||||
json_dumper_end_object(&dumper);
|
||||
json_dumper_end_object(&dumper);
|
||||
}
|
||||
|
||||
if (machine_readable) { /* tab-separated values to stdout */
|
||||
/* XXX: We need to change the format and adapt consumers */
|
||||
print_machine_readable_if_capabilities(caps, caps_queries);
|
||||
json_dumper_end_array(&dumper);
|
||||
if (json_dumper_finish(&dumper)) {
|
||||
status = 0;
|
||||
} else
|
||||
if (capture_child) {
|
||||
/* Let our parent know we succeeded. */
|
||||
sync_pipe_write_string_msg(2, SP_SUCCESS, NULL);
|
||||
}
|
||||
} else {
|
||||
status = 2;
|
||||
if (capture_child) {
|
||||
sync_pipe_write_errmsgs_to_parent(2, "Unexpected JSON error", "");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (ii = 0; ii < global_capture_opts.ifaces->len; ii++) {
|
||||
interface_options *interface_opts;
|
||||
|
||||
interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, ii);
|
||||
|
||||
caps = get_if_capabilities(interface_opts, &open_status, &open_status_str);
|
||||
if (caps == NULL) {
|
||||
if (capture_child) {
|
||||
char *error_msg = ws_strdup_printf("The capabilities of the capture device "
|
||||
"\"%s\" could not be obtained (%s)",
|
||||
interface_opts->name, open_status_str);
|
||||
sync_pipe_write_errmsgs_to_parent(2, error_msg,
|
||||
get_pcap_failure_secondary_error_message(open_status, open_status_str));
|
||||
g_free(error_msg);
|
||||
}
|
||||
else {
|
||||
cmdarg_err("The capabilities of the capture device "
|
||||
"\"%s\" could not be obtained (%s).\n%s",
|
||||
interface_opts->name, open_status_str,
|
||||
get_pcap_failure_secondary_error_message(open_status, open_status_str));
|
||||
}
|
||||
g_free(open_status_str);
|
||||
exit_main(2);
|
||||
}
|
||||
|
||||
/* XXX: We might want to print also the interface name */
|
||||
status = capture_opts_print_if_capabilities(caps,
|
||||
interface_opts,
|
||||
caps_queries);
|
||||
free_if_capabilities(caps);
|
||||
if (status != 0)
|
||||
break;
|
||||
free_if_capabilities(caps);
|
||||
if (status != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit_main(status);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue