mirror of
https://github.com/systemd/systemd.git
synced 2025-11-09 04:24:06 +03:00
networkctl: introduce --json option for "status" and "list" commands
When `--json` option is specified, "status" and "list" commands gives the same information, as originally "list" just gives partial information of "status" in different format.
This commit is contained in:
@@ -367,6 +367,7 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<xi:include href="standard-options.xml" xpointer="json" />
|
||||||
<xi:include href="standard-options.xml" xpointer="help" />
|
<xi:include href="standard-options.xml" xpointer="help" />
|
||||||
<xi:include href="standard-options.xml" xpointer="version" />
|
<xi:include href="standard-options.xml" xpointer="version" />
|
||||||
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ _networkctl() {
|
|||||||
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
|
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
|
||||||
local -A OPTS=(
|
local -A OPTS=(
|
||||||
[STANDALONE]='-a --all -h --help --version --no-pager --no-legend -s --stats -l --full'
|
[STANDALONE]='-a --all -h --help --version --no-pager --no-legend -s --stats -l --full'
|
||||||
[ARG]='-n --lines'
|
[ARG]='-n --lines --json'
|
||||||
)
|
)
|
||||||
|
|
||||||
local -A VERBS=(
|
local -A VERBS=(
|
||||||
@@ -51,6 +51,19 @@ _networkctl() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if __contains_word "$prev" ${OPTS[ARG]}; then
|
||||||
|
case $prev in
|
||||||
|
--json)
|
||||||
|
comps=$(networkctl --json=help | sort 2>/dev/null)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$cur" = -* ]]; then
|
if [[ "$cur" = -* ]]; then
|
||||||
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
|
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -39,10 +39,18 @@
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(( $+functions[_networkctl_get_json] )) || _networkctl_get_json()
|
||||||
|
{
|
||||||
|
local -a _json_forms
|
||||||
|
_json_forms=( $(networkctl --json=help 2>/dev/null) )
|
||||||
|
_values 'format' $_json_forms
|
||||||
|
}
|
||||||
|
|
||||||
_arguments \
|
_arguments \
|
||||||
{-a,--all}'[Show all links with status]' \
|
{-a,--all}'[Show all links with status]' \
|
||||||
'--no-pager[Do not pipe output into a pager]' \
|
'--no-pager[Do not pipe output into a pager]' \
|
||||||
'--no-legend[Do not print the column headers]' \
|
'--no-legend[Do not print the column headers]' \
|
||||||
{-h,--help}'[Show this help]' \
|
{-h,--help}'[Show this help]' \
|
||||||
'--version[Show package version]' \
|
'--version[Show package version]' \
|
||||||
|
'--json[Shows output formatted as JSON]:format:_networkctl_get_json' \
|
||||||
'*::networkctl commands:_networkctl_commands'
|
'*::networkctl commands:_networkctl_commands'
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
#include "network-internal.h"
|
#include "network-internal.h"
|
||||||
#include "network-util.h"
|
#include "network-util.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "pretty-print.h"
|
#include "pretty-print.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
@@ -75,6 +76,110 @@ static bool arg_all = false;
|
|||||||
static bool arg_stats = false;
|
static bool arg_stats = false;
|
||||||
static bool arg_full = false;
|
static bool arg_full = false;
|
||||||
static unsigned arg_lines = 10;
|
static unsigned arg_lines = 10;
|
||||||
|
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
|
||||||
|
|
||||||
|
static int get_description(JsonVariant **ret) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
const char *text = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_bus_open_system(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to connect system bus: %m");
|
||||||
|
|
||||||
|
r = bus_call_method(bus, bus_network_mgr, "Describe", &error, &reply, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to get description: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
r = sd_bus_message_read(reply, "s", &text);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
r = json_parse(text, 0, ret, NULL, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse JSON: %m");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_manager_description(void) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = get_description(&v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
json_variant_dump(v, arg_json_format_flags, NULL, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_link_description(char **patterns) {
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||||
|
_cleanup_free_ bool *matched_patterns = NULL;
|
||||||
|
JsonVariant *i;
|
||||||
|
size_t c = 0;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = get_description(&v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
matched_patterns = new0(bool, strv_length(patterns));
|
||||||
|
if (!matched_patterns)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
JSON_VARIANT_ARRAY_FOREACH(i, json_variant_by_key(v, "Interfaces")) {
|
||||||
|
char ifindex_str[DECIMAL_STR_MAX(intmax_t)];
|
||||||
|
const char *name;
|
||||||
|
intmax_t index;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
name = json_variant_string(json_variant_by_key(i, "Name"));
|
||||||
|
index = json_variant_integer(json_variant_by_key(i, "Index"));
|
||||||
|
xsprintf(ifindex_str, "%ji", index);
|
||||||
|
|
||||||
|
if (!strv_fnmatch_full(patterns, ifindex_str, 0, &pos) &&
|
||||||
|
!strv_fnmatch_full(patterns, name, 0, &pos)) {
|
||||||
|
bool match = false;
|
||||||
|
JsonVariant *a;
|
||||||
|
|
||||||
|
JSON_VARIANT_ARRAY_FOREACH(a, json_variant_by_key(i, "AlternativeNames"))
|
||||||
|
if (strv_fnmatch_full(patterns, json_variant_string(a), 0, &pos)) {
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
matched_patterns[pos] = true;
|
||||||
|
json_variant_dump(i, arg_json_format_flags, NULL, NULL);
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look if we matched all our arguments that are not globs. It is OK for a glob to match
|
||||||
|
* nothing, but not for an exact argument. */
|
||||||
|
for (size_t pos = 0; pos < strv_length(patterns); pos++) {
|
||||||
|
if (matched_patterns[pos])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (string_is_glob(patterns[pos]))
|
||||||
|
log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
|
||||||
|
patterns[pos]);
|
||||||
|
else
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
|
||||||
|
"Interface \"%s\" not found.", patterns[pos]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 0)
|
||||||
|
log_warning("No interfaces matched.");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
|
static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
|
||||||
if (STRPTR_IN_SET(state, "routable", "enslaved") ||
|
if (STRPTR_IN_SET(state, "routable", "enslaved") ||
|
||||||
@@ -674,6 +779,13 @@ static int list_links(int argc, char *argv[], void *userdata) {
|
|||||||
TableCell *cell;
|
TableCell *cell;
|
||||||
int c, r;
|
int c, r;
|
||||||
|
|
||||||
|
if (arg_json_format_flags != JSON_FORMAT_OFF) {
|
||||||
|
if (arg_all || argc <= 1)
|
||||||
|
return dump_manager_description();
|
||||||
|
else
|
||||||
|
return dump_link_description(strv_skip(argv, 1));
|
||||||
|
}
|
||||||
|
|
||||||
r = sd_netlink_open(&rtnl);
|
r = sd_netlink_open(&rtnl);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to connect to netlink: %m");
|
return log_error_errno(r, "Failed to connect to netlink: %m");
|
||||||
@@ -2238,6 +2350,13 @@ static int link_status(int argc, char *argv[], void *userdata) {
|
|||||||
_cleanup_(link_info_array_freep) LinkInfo *links = NULL;
|
_cleanup_(link_info_array_freep) LinkInfo *links = NULL;
|
||||||
int r, c;
|
int r, c;
|
||||||
|
|
||||||
|
if (arg_json_format_flags != JSON_FORMAT_OFF) {
|
||||||
|
if (arg_all || argc <= 1)
|
||||||
|
return dump_manager_description();
|
||||||
|
else
|
||||||
|
return dump_link_description(strv_skip(argv, 1));
|
||||||
|
}
|
||||||
|
|
||||||
(void) pager_open(arg_pager_flags);
|
(void) pager_open(arg_pager_flags);
|
||||||
|
|
||||||
r = sd_bus_open_system(&bus);
|
r = sd_bus_open_system(&bus);
|
||||||
@@ -2728,6 +2847,8 @@ static int help(void) {
|
|||||||
" -s --stats Show detailed link statics\n"
|
" -s --stats Show detailed link statics\n"
|
||||||
" -l --full Do not ellipsize output\n"
|
" -l --full Do not ellipsize output\n"
|
||||||
" -n --lines=INTEGER Number of journal entries to show\n"
|
" -n --lines=INTEGER Number of journal entries to show\n"
|
||||||
|
" --json=pretty|short|off\n"
|
||||||
|
" Generate JSON output\n"
|
||||||
"\nSee the %s for details.\n",
|
"\nSee the %s for details.\n",
|
||||||
program_invocation_short_name,
|
program_invocation_short_name,
|
||||||
ansi_highlight(),
|
ansi_highlight(),
|
||||||
@@ -2743,6 +2864,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_VERSION = 0x100,
|
ARG_VERSION = 0x100,
|
||||||
ARG_NO_PAGER,
|
ARG_NO_PAGER,
|
||||||
ARG_NO_LEGEND,
|
ARG_NO_LEGEND,
|
||||||
|
ARG_JSON,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
@@ -2754,10 +2876,11 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "stats", no_argument, NULL, 's' },
|
{ "stats", no_argument, NULL, 's' },
|
||||||
{ "full", no_argument, NULL, 'l' },
|
{ "full", no_argument, NULL, 'l' },
|
||||||
{ "lines", required_argument, NULL, 'n' },
|
{ "lines", required_argument, NULL, 'n' },
|
||||||
|
{ "json", required_argument, NULL, ARG_JSON },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c, r;
|
||||||
|
|
||||||
assert(argc >= 0);
|
assert(argc >= 0);
|
||||||
assert(argv);
|
assert(argv);
|
||||||
@@ -2798,6 +2921,12 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
"Failed to parse lines '%s'", optarg);
|
"Failed to parse lines '%s'", optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_JSON:
|
||||||
|
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user