X Tutup
Skip to content

Commit bfc2b05

Browse files
committed
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.
1 parent fec7a9e commit bfc2b05

File tree

4 files changed

+153
-2
lines changed

4 files changed

+153
-2
lines changed

man/networkctl.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
367367
</listitem>
368368
</varlistentry>
369369

370+
<xi:include href="standard-options.xml" xpointer="json" />
370371
<xi:include href="standard-options.xml" xpointer="help" />
371372
<xi:include href="standard-options.xml" xpointer="version" />
372373
<xi:include href="standard-options.xml" xpointer="no-legend" />

shell-completion/bash/networkctl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ _networkctl() {
3333
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
3434
local -A OPTS=(
3535
[STANDALONE]='-a --all -h --help --version --no-pager --no-legend -s --stats -l --full'
36-
[ARG]='-n --lines'
36+
[ARG]='-n --lines --json'
3737
)
3838

3939
local -A VERBS=(
@@ -51,6 +51,19 @@ _networkctl() {
5151
fi
5252
done
5353

54+
if __contains_word "$prev" ${OPTS[ARG]}; then
55+
case $prev in
56+
--json)
57+
comps=$(networkctl --json=help | sort 2>/dev/null)
58+
;;
59+
*)
60+
return 0
61+
;;
62+
esac
63+
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
64+
return 0
65+
fi
66+
5467
if [[ "$cur" = -* ]]; then
5568
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
5669
return 0

shell-completion/zsh/_networkctl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,18 @@
3939
fi
4040
}
4141

42+
(( $+functions[_networkctl_get_json] )) || _networkctl_get_json()
43+
{
44+
local -a _json_forms
45+
_json_forms=( $(networkctl --json=help 2>/dev/null) )
46+
_values 'format' $_json_forms
47+
}
48+
4249
_arguments \
4350
{-a,--all}'[Show all links with status]' \
4451
'--no-pager[Do not pipe output into a pager]' \
4552
'--no-legend[Do not print the column headers]' \
4653
{-h,--help}'[Show this help]' \
4754
'--version[Show package version]' \
55+
'--json[Shows output formatted as JSON]:format:_networkctl_get_json' \
4856
'*::networkctl commands:_networkctl_commands'

src/network/networkctl.c

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "network-internal.h"
4747
#include "network-util.h"
4848
#include "pager.h"
49+
#include "parse-argument.h"
4950
#include "parse-util.h"
5051
#include "pretty-print.h"
5152
#include "set.h"
@@ -75,6 +76,110 @@ static bool arg_all = false;
7576
static bool arg_stats = false;
7677
static bool arg_full = false;
7778
static unsigned arg_lines = 10;
79+
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
80+
81+
static int get_description(JsonVariant **ret) {
82+
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
83+
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
84+
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
85+
const char *text = NULL;
86+
int r;
87+
88+
r = sd_bus_open_system(&bus);
89+
if (r < 0)
90+
return log_error_errno(r, "Failed to connect system bus: %m");
91+
92+
r = bus_call_method(bus, bus_network_mgr, "Describe", &error, &reply, NULL);
93+
if (r < 0)
94+
return log_error_errno(r, "Failed to get description: %s", bus_error_message(&error, r));
95+
96+
r = sd_bus_message_read(reply, "s", &text);
97+
if (r < 0)
98+
return bus_log_parse_error(r);
99+
100+
r = json_parse(text, 0, ret, NULL, NULL);
101+
if (r < 0)
102+
return log_error_errno(r, "Failed to parse JSON: %m");
103+
104+
return 0;
105+
}
106+
107+
static int dump_manager_description(void) {
108+
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
109+
int r;
110+
111+
r = get_description(&v);
112+
if (r < 0)
113+
return r;
114+
115+
json_variant_dump(v, arg_json_format_flags, NULL, NULL);
116+
return 0;
117+
}
118+
119+
static int dump_link_description(char **patterns) {
120+
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
121+
_cleanup_free_ bool *matched_patterns = NULL;
122+
JsonVariant *i;
123+
size_t c = 0;
124+
int r;
125+
126+
r = get_description(&v);
127+
if (r < 0)
128+
return r;
129+
130+
matched_patterns = new0(bool, strv_length(patterns));
131+
if (!matched_patterns)
132+
return log_oom();
133+
134+
JSON_VARIANT_ARRAY_FOREACH(i, json_variant_by_key(v, "Interfaces")) {
135+
char ifindex_str[DECIMAL_STR_MAX(intmax_t)];
136+
const char *name;
137+
intmax_t index;
138+
size_t pos;
139+
140+
name = json_variant_string(json_variant_by_key(i, "Name"));
141+
index = json_variant_integer(json_variant_by_key(i, "Index"));
142+
xsprintf(ifindex_str, "%ji", index);
143+
144+
if (!strv_fnmatch_full(patterns, ifindex_str, 0, &pos) &&
145+
!strv_fnmatch_full(patterns, name, 0, &pos)) {
146+
bool match = false;
147+
JsonVariant *a;
148+
149+
JSON_VARIANT_ARRAY_FOREACH(a, json_variant_by_key(i, "AlternativeNames"))
150+
if (strv_fnmatch_full(patterns, json_variant_string(a), 0, &pos)) {
151+
match = true;
152+
break;
153+
}
154+
155+
if (!match)
156+
continue;
157+
}
158+
159+
matched_patterns[pos] = true;
160+
json_variant_dump(i, arg_json_format_flags, NULL, NULL);
161+
c++;
162+
}
163+
164+
/* Look if we matched all our arguments that are not globs. It is OK for a glob to match
165+
* nothing, but not for an exact argument. */
166+
for (size_t pos = 0; pos < strv_length(patterns); pos++) {
167+
if (matched_patterns[pos])
168+
continue;
169+
170+
if (string_is_glob(patterns[pos]))
171+
log_debug("Pattern \"%s\" doesn't match any interface, ignoring.",
172+
patterns[pos]);
173+
else
174+
return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
175+
"Interface \"%s\" not found.", patterns[pos]);
176+
}
177+
178+
if (c == 0)
179+
log_warning("No interfaces matched.");
180+
181+
return 0;
182+
}
78183

79184
static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
80185
if (STRPTR_IN_SET(state, "routable", "enslaved") ||
@@ -674,6 +779,13 @@ static int list_links(int argc, char *argv[], void *userdata) {
674779
TableCell *cell;
675780
int c, r;
676781

782+
if (arg_json_format_flags != JSON_FORMAT_OFF) {
783+
if (arg_all || argc <= 1)
784+
return dump_manager_description();
785+
else
786+
return dump_link_description(strv_skip(argv, 1));
787+
}
788+
677789
r = sd_netlink_open(&rtnl);
678790
if (r < 0)
679791
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) {
22382350
_cleanup_(link_info_array_freep) LinkInfo *links = NULL;
22392351
int r, c;
22402352

2353+
if (arg_json_format_flags != JSON_FORMAT_OFF) {
2354+
if (arg_all || argc <= 1)
2355+
return dump_manager_description();
2356+
else
2357+
return dump_link_description(strv_skip(argv, 1));
2358+
}
2359+
22412360
(void) pager_open(arg_pager_flags);
22422361

22432362
r = sd_bus_open_system(&bus);
@@ -2728,6 +2847,8 @@ static int help(void) {
27282847
" -s --stats Show detailed link statics\n"
27292848
" -l --full Do not ellipsize output\n"
27302849
" -n --lines=INTEGER Number of journal entries to show\n"
2850+
" --json=pretty|short|off\n"
2851+
" Generate JSON output\n"
27312852
"\nSee the %s for details.\n",
27322853
program_invocation_short_name,
27332854
ansi_highlight(),
@@ -2743,6 +2864,7 @@ static int parse_argv(int argc, char *argv[]) {
27432864
ARG_VERSION = 0x100,
27442865
ARG_NO_PAGER,
27452866
ARG_NO_LEGEND,
2867+
ARG_JSON,
27462868
};
27472869

27482870
static const struct option options[] = {
@@ -2754,10 +2876,11 @@ static int parse_argv(int argc, char *argv[]) {
27542876
{ "stats", no_argument, NULL, 's' },
27552877
{ "full", no_argument, NULL, 'l' },
27562878
{ "lines", required_argument, NULL, 'n' },
2879+
{ "json", required_argument, NULL, ARG_JSON },
27572880
{}
27582881
};
27592882

2760-
int c;
2883+
int c, r;
27612884

27622885
assert(argc >= 0);
27632886
assert(argv);
@@ -2798,6 +2921,12 @@ static int parse_argv(int argc, char *argv[]) {
27982921
"Failed to parse lines '%s'", optarg);
27992922
break;
28002923

2924+
case ARG_JSON:
2925+
r = parse_json_argument(optarg, &arg_json_format_flags);
2926+
if (r <= 0)
2927+
return r;
2928+
break;
2929+
28012930
case '?':
28022931
return -EINVAL;
28032932

0 commit comments

Comments
 (0)
X Tutup