1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "sd-network.h"
32 #include "rtnl-util.h"
33 #include "udev-util.h"
34 #include "arphrd-list.h"
35 #include "local-addresses.h"
37 static bool arg_no_pager = false;
38 static bool arg_legend = true;
39 static bool arg_all = false;
41 static void pager_open_if_enabled(void) {
49 static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
53 if (iftype == ARPHRD_ETHER && d) {
54 const char *devtype, *id = NULL;
55 /* WLANs have iftype ARPHRD_ETHER, but we want
56 * to show a more useful type string for
59 devtype = udev_device_get_devtype(d);
60 if (streq_ptr(devtype, "wlan"))
62 else if (streq_ptr(devtype, "wwan"))
75 t = arphrd_to_name(iftype);
91 typedef struct LinkInfo {
97 static int link_info_compare(const void *a, const void *b) {
98 const LinkInfo *x = a, *y = b;
100 return x->ifindex - y->ifindex;
103 static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
104 _cleanup_free_ LinkInfo *links = NULL;
105 size_t size = 0, c = 0;
109 for (i = m; i; i = sd_rtnl_message_next(i)) {
115 r = sd_rtnl_message_get_type(i, &type);
119 if (type != RTM_NEWLINK)
122 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
126 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
130 r = sd_rtnl_message_link_get_type(i, &iftype);
134 if (!GREEDY_REALLOC(links, size, c+1))
137 links[c].name = name;
138 links[c].ifindex = ifindex;
139 links[c].iftype = iftype;
143 qsort(links, c, sizeof(LinkInfo), link_info_compare);
151 static int list_links(char **args, unsigned n) {
152 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
153 _cleanup_udev_unref_ struct udev *udev = NULL;
154 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
155 _cleanup_free_ LinkInfo *links = NULL;
158 pager_open_if_enabled();
160 r = sd_rtnl_open(&rtnl, 0);
162 log_error("Failed to connect to netlink: %s", strerror(-r));
168 log_error("Failed to connect to udev: %m");
172 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
174 return rtnl_log_create_error(r);
176 r = sd_rtnl_message_request_dump(req, true);
178 return rtnl_log_create_error(r);
180 r = sd_rtnl_call(rtnl, req, 0, &reply);
182 log_error("Failed to enumerate links: %s", strerror(-r));
187 printf("%3s %-16s %-10s %-11s %-10s\n", "IDX", "LINK", "TYPE", "ADMIN", "OPERATIONAL");
189 c = decode_and_sort_links(reply, &links);
191 return rtnl_log_parse_error(c);
193 for (i = 0; i < c; i++) {
194 _cleanup_free_ char *state = NULL, *operational_state = NULL;
195 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
196 const char *on_color_oper = "", *off_color_oper = "",
197 *on_color = "", *off_color = "";
198 char devid[2 + DECIMAL_STR_MAX(int)];
199 _cleanup_free_ char *t = NULL;
201 sd_network_get_link_state(links[i].ifindex, &state);
202 sd_network_get_link_operational_state(links[i].ifindex, &operational_state);
204 sprintf(devid, "n%i", links[i].ifindex);
205 d = udev_device_new_from_device_id(udev, devid);
207 link_get_type_string(links[i].iftype, d, &t);
209 if (streq_ptr(operational_state, "routable")) {
210 on_color_oper = ansi_highlight_green();
211 off_color_oper = ansi_highlight_off();
212 } else if (streq_ptr(operational_state, "degraded")) {
213 on_color_oper = ansi_highlight_yellow();
214 off_color_oper = ansi_highlight_off();
217 if (streq_ptr(state, "configured")) {
218 on_color = ansi_highlight_green();
219 off_color = ansi_highlight_off();
220 } else if (streq_ptr(state, "configuring")) {
221 on_color = ansi_highlight_yellow();
222 off_color = ansi_highlight_off();
223 } else if (streq_ptr(state, "failed") ||
224 streq_ptr(state, "linger")) {
225 on_color = ansi_highlight_red();
226 off_color = ansi_highlight_off();
229 printf("%3i %-16s %-10s %s%-11s%s %s%-10s%s\n", links[i].ifindex,
230 links[i].name, strna(t), on_color, strna(state), off_color,
231 on_color_oper, strna(operational_state), off_color_oper);
235 printf("\n%i links listed.\n", c);
240 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
241 _cleanup_free_ struct local_address *local = NULL;
244 n = local_addresses(rtnl, ifindex, &local);
248 for (i = 0; i < n; i++) {
249 _cleanup_free_ char *pretty = NULL;
251 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
256 (int) strlen(prefix),
257 i == 0 ? prefix : "",
264 static void dump_list(const char *prefix, char **l) {
269 (int) strlen(prefix),
270 i == l ? prefix : "",
275 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
276 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
277 _cleanup_free_ char *state = NULL, *operational_state = NULL;
278 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
279 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
280 char devid[2 + DECIMAL_STR_MAX(int)];
281 _cleanup_free_ char *t = NULL;
282 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
283 const char *on_color = "", *off_color = "";
294 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
295 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
297 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
299 return rtnl_log_create_error(r);
301 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
305 return rtnl_log_create_error(r);
307 r = sd_rtnl_call(rtnl, req, 0, &reply);
309 log_error("Failed to query link: %s", strerror(-r));
313 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
315 return rtnl_log_parse_error(r);
317 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
319 return rtnl_log_parse_error(r);
321 r = sd_rtnl_message_link_get_type(reply, &iftype);
323 return rtnl_log_parse_error(r);
325 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
329 bool all_zeroes = true;
331 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
341 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
343 sd_network_get_link_state(ifindex, &state);
344 sd_network_get_link_operational_state(ifindex, &operational_state);
346 sd_network_get_link_dns(ifindex, &dns);
347 sd_network_get_link_ntp(ifindex, &ntp);
349 sprintf(devid, "n%i", ifindex);
350 d = udev_device_new_from_device_id(udev, devid);
352 link_get_type_string(iftype, d, &t);
355 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
356 path = udev_device_get_property_value(d, "ID_PATH");
358 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
360 vendor = udev_device_get_property_value(d, "ID_VENDOR");
362 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
364 model = udev_device_get_property_value(d, "ID_MODEL");
367 if (streq_ptr(operational_state, "routable")) {
368 on_color = ansi_highlight_green();
369 off_color = ansi_highlight_off();
370 } else if (streq_ptr(operational_state, "degraded")) {
371 on_color = ansi_highlight_yellow();
372 off_color = ansi_highlight_off();
375 printf("%s%s%s %i: %s\n", on_color, draw_special_char(DRAW_BLACK_CIRCLE), off_color, ifindex, name);
378 " State: %s%s%s (%s)\n",
380 on_color, strna(operational_state), off_color,
384 printf(" Path: %s\n", path);
386 printf(" Driver: %s\n", driver);
388 printf(" Vendor: %s\n", vendor);
390 printf(" Model: %s\n", model);
393 printf(" HW Address: %s\n", ether_ntoa(&e));
396 printf(" MTU: %u\n", mtu);
398 dump_addresses(rtnl, " Address: ", ifindex);
400 if (!strv_isempty(dns))
401 dump_list(" DNS: ", dns);
402 if (!strv_isempty(ntp))
403 dump_list(" NTP: ", ntp);
408 static int link_status(char **args, unsigned n) {
409 _cleanup_udev_unref_ struct udev *udev = NULL;
410 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
414 r = sd_rtnl_open(&rtnl, 0);
416 log_error("Failed to connect to netlink: %s", strerror(-r));
422 log_error("Failed to connect to udev: %m");
426 if (n <= 1 && !arg_all) {
427 _cleanup_free_ char *operational_state = NULL;
428 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
429 _cleanup_free_ struct local_address *addresses = NULL;
430 const char *on_color_oper = "", *off_color_oper = "";
433 sd_network_get_operational_state(&operational_state);
434 if (streq_ptr(operational_state, "routable")) {
435 on_color_oper = ansi_highlight_green();
436 off_color_oper = ansi_highlight_off();
437 } else if (streq_ptr(operational_state, "degraded")) {
438 on_color_oper = ansi_highlight_yellow();
439 off_color_oper = ansi_highlight_off();
442 printf(" State: %s%s%s\n", on_color_oper, strna(operational_state), off_color_oper);
444 c = local_addresses(rtnl, 0, &addresses);
445 for (i = 0; i < c; i++) {
446 _cleanup_free_ char *pretty = NULL;
448 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
453 i > 0 ? "" : "Address:", pretty);
456 sd_network_get_dns(&dns);
457 if (!strv_isempty(dns))
458 dump_list(" DNS: ", dns);
460 sd_network_get_dns(&ntp);
461 if (!strv_isempty(ntp))
462 dump_list(" NTP: ", ntp);
467 pager_open_if_enabled();
470 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
471 _cleanup_free_ LinkInfo *links = NULL;
474 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
476 return rtnl_log_create_error(r);
478 r = sd_rtnl_message_request_dump(req, true);
480 return rtnl_log_create_error(r);
482 r = sd_rtnl_call(rtnl, req, 0, &reply);
484 log_error("Failed to enumerate links: %s", strerror(-r));
488 c = decode_and_sort_links(reply, &links);
490 return rtnl_log_parse_error(c);
492 for (i = 0; i < c; i++) {
496 link_status_one(rtnl, udev, links[i].name);
500 STRV_FOREACH(name, args + 1) {
504 link_status_one(rtnl, udev, *name);
510 static void help(void) {
511 printf("%s [OPTIONS...]\n\n"
512 "Query and control the networking subsystem.\n\n"
513 " -h --help Show this help\n"
514 " --version Show package version\n"
515 " --no-pager Do not pipe output into a pager\n"
516 " --no-legend Do not show the headers and footers\n"
517 " -a --all Show status for all links\n\n"
520 " status LINK Show link status\n"
521 , program_invocation_short_name);
524 static int parse_argv(int argc, char *argv[]) {
532 static const struct option options[] = {
533 { "help", no_argument, NULL, 'h' },
534 { "version", no_argument, NULL, ARG_VERSION },
535 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
536 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
537 { "all", no_argument, NULL, 'a' },
546 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
555 puts(PACKAGE_STRING);
556 puts(SYSTEMD_FEATURES);
575 assert_not_reached("Unhandled option");
582 static int networkctl_main(int argc, char *argv[]) {
584 static const struct {
592 int (* const dispatch)(char **args, unsigned n);
594 { "list", LESS, 1, list_links },
595 { "status", MORE, 1, link_status },
604 left = argc - optind;
607 /* Special rule: no arguments means "list" */
610 if (streq(argv[optind], "help")) {
615 for (i = 0; i < ELEMENTSOF(verbs); i++)
616 if (streq(argv[optind], verbs[i].verb))
619 if (i >= ELEMENTSOF(verbs)) {
620 log_error("Unknown operation %s", argv[optind]);
625 switch (verbs[i].argc_cmp) {
628 if (left != verbs[i].argc) {
629 log_error("Invalid number of arguments.");
636 if (left < verbs[i].argc) {
637 log_error("Too few arguments.");
644 if (left > verbs[i].argc) {
645 log_error("Too many arguments.");
652 assert_not_reached("Unknown comparison operator.");
655 return verbs[i].dispatch(argv + optind, left);
658 int main(int argc, char* argv[]) {
661 log_parse_environment();
664 r = parse_argv(argc, argv);
668 r = networkctl_main(argc, argv);
673 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;