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"
36 #include "socket-util.h"
38 static bool arg_no_pager = false;
39 static bool arg_legend = true;
40 static bool arg_all = false;
42 static void pager_open_if_enabled(void) {
50 static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
54 if (iftype == ARPHRD_ETHER && d) {
55 const char *devtype, *id = NULL;
56 /* WLANs have iftype ARPHRD_ETHER, but we want
57 * to show a more useful type string for
60 devtype = udev_device_get_devtype(d);
61 if (streq_ptr(devtype, "wlan"))
63 else if (streq_ptr(devtype, "wwan"))
76 t = arphrd_to_name(iftype);
92 typedef struct LinkInfo {
98 static int link_info_compare(const void *a, const void *b) {
99 const LinkInfo *x = a, *y = b;
101 return x->ifindex - y->ifindex;
104 static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
105 _cleanup_free_ LinkInfo *links = NULL;
106 size_t size = 0, c = 0;
110 for (i = m; i; i = sd_rtnl_message_next(i)) {
116 r = sd_rtnl_message_get_type(i, &type);
120 if (type != RTM_NEWLINK)
123 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
127 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
131 r = sd_rtnl_message_link_get_type(i, &iftype);
135 if (!GREEDY_REALLOC(links, size, c+1))
138 links[c].name = name;
139 links[c].ifindex = ifindex;
140 links[c].iftype = iftype;
144 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
152 static void operational_state_to_color(const char *state, const char **on, const char **off) {
156 if (streq_ptr(state, "routable")) {
157 *on = ansi_highlight_green();
158 *off = ansi_highlight_off();
159 } else if (streq_ptr(state, "degraded")) {
160 *on = ansi_highlight_yellow();
161 *off = ansi_highlight_off();
166 static void setup_state_to_color(const char *state, const char **on, const char **off) {
170 if (streq_ptr(state, "configured")) {
171 *on = ansi_highlight_green();
172 *off = ansi_highlight_off();
173 } else if (streq_ptr(state, "configuring")) {
174 *on = ansi_highlight_yellow();
175 *off = ansi_highlight_off();
176 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
177 *on = ansi_highlight_red();
178 *off = ansi_highlight_off();
183 static int list_links(char **args, unsigned n) {
184 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
185 _cleanup_udev_unref_ struct udev *udev = NULL;
186 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
187 _cleanup_free_ LinkInfo *links = NULL;
190 pager_open_if_enabled();
192 r = sd_rtnl_open(&rtnl, 0);
194 log_error("Failed to connect to netlink: %s", strerror(-r));
200 log_error("Failed to connect to udev: %m");
204 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
206 return rtnl_log_create_error(r);
208 r = sd_rtnl_message_request_dump(req, true);
210 return rtnl_log_create_error(r);
212 r = sd_rtnl_call(rtnl, req, 0, &reply);
214 log_error("Failed to enumerate links: %s", strerror(-r));
219 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
221 c = decode_and_sort_links(reply, &links);
223 return rtnl_log_parse_error(c);
225 for (i = 0; i < c; i++) {
226 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
227 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
228 const char *on_color_operational, *off_color_operational,
229 *on_color_setup, *off_color_setup;
230 char devid[2 + DECIMAL_STR_MAX(int)];
231 _cleanup_free_ char *t = NULL;
233 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
234 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
236 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
237 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
239 sprintf(devid, "n%i", links[i].ifindex);
240 d = udev_device_new_from_device_id(udev, devid);
242 link_get_type_string(links[i].iftype, d, &t);
244 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
245 links[i].ifindex, links[i].name, strna(t),
246 on_color_operational, strna(operational_state), off_color_operational,
247 on_color_setup, strna(setup_state), off_color_setup);
251 printf("\n%i links listed.\n", c);
256 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
257 _cleanup_free_ struct local_address *local = NULL;
260 n = local_addresses(rtnl, ifindex, &local);
264 for (i = 0; i < n; i++) {
265 _cleanup_free_ char *pretty = NULL;
267 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
272 (int) strlen(prefix),
273 i == 0 ? prefix : "",
280 static void dump_list(const char *prefix, char **l) {
285 (int) strlen(prefix),
286 i == l ? prefix : "",
291 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
292 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
293 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
294 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
295 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
296 char devid[2 + DECIMAL_STR_MAX(int)];
297 _cleanup_free_ char *t = NULL, *network = NULL;
298 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
299 const char *on_color_operational, *off_color_operational,
300 *on_color_setup, *off_color_setup;
311 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
312 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
314 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
316 return rtnl_log_create_error(r);
318 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
322 return rtnl_log_create_error(r);
324 r = sd_rtnl_call(rtnl, req, 0, &reply);
326 log_error("Failed to query link: %s", strerror(-r));
330 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
332 return rtnl_log_parse_error(r);
334 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
336 return rtnl_log_parse_error(r);
338 r = sd_rtnl_message_link_get_type(reply, &iftype);
340 return rtnl_log_parse_error(r);
342 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
346 bool all_zeroes = true;
348 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
358 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
360 sd_network_link_get_operational_state(ifindex, &operational_state);
361 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
363 sd_network_link_get_setup_state(ifindex, &setup_state);
364 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
366 sd_network_link_get_dns(ifindex, &dns);
367 sd_network_link_get_ntp(ifindex, &ntp);
368 sd_network_link_get_domains(ifindex, &domains);
369 r = sd_network_link_get_wildcard_domain(ifindex);
373 wildcard = strdup("*");
377 if (strv_consume(&domains, wildcard) < 0)
381 sprintf(devid, "n%i", ifindex);
382 d = udev_device_new_from_device_id(udev, devid);
384 link_get_type_string(iftype, d, &t);
387 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
388 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
389 path = udev_device_get_property_value(d, "ID_PATH");
391 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
393 vendor = udev_device_get_property_value(d, "ID_VENDOR");
395 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
397 model = udev_device_get_property_value(d, "ID_MODEL");
400 sd_network_link_get_network_file(ifindex, &network);
402 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
404 printf(" Link File: %s\n"
407 " State: %s%s%s (%s%s%s)\n",
411 on_color_operational, strna(operational_state), off_color_operational,
412 on_color_setup, strna(setup_state), off_color_setup);
415 printf(" Path: %s\n", path);
417 printf(" Driver: %s\n", driver);
419 printf(" Vendor: %s\n", vendor);
421 printf(" Model: %s\n", model);
424 char ea[ETHER_ADDR_TO_STRING_MAX];
425 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
429 printf(" MTU: %u\n", mtu);
431 dump_addresses(rtnl, " Address: ", ifindex);
433 if (!strv_isempty(dns))
434 dump_list(" DNS: ", dns);
435 if (!strv_isempty(domains))
436 dump_list(" Domain: ", domains);
437 if (!strv_isempty(ntp))
438 dump_list(" NTP: ", ntp);
443 static int link_status(char **args, unsigned n) {
444 _cleanup_udev_unref_ struct udev *udev = NULL;
445 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
449 r = sd_rtnl_open(&rtnl, 0);
451 log_error("Failed to connect to netlink: %s", strerror(-r));
457 log_error("Failed to connect to udev: %m");
461 if (n <= 1 && !arg_all) {
462 _cleanup_free_ char *operational_state = NULL;
463 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
464 _cleanup_free_ struct local_address *addresses = NULL;
465 const char *on_color_operational, *off_color_operational;
468 sd_network_get_operational_state(&operational_state);
469 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
471 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
473 c = local_addresses(rtnl, 0, &addresses);
474 for (i = 0; i < c; i++) {
475 _cleanup_free_ char *pretty = NULL;
477 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
482 i > 0 ? "" : "Address:", pretty);
485 sd_network_get_dns(&dns);
486 if (!strv_isempty(dns))
487 dump_list(" DNS: ", dns);
489 sd_network_get_domains(&domains);
490 if (!strv_isempty(domains))
491 dump_list(" Domain: ", domains);
493 sd_network_get_ntp(&ntp);
494 if (!strv_isempty(ntp))
495 dump_list(" NTP: ", ntp);
500 pager_open_if_enabled();
503 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
504 _cleanup_free_ LinkInfo *links = NULL;
507 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
509 return rtnl_log_create_error(r);
511 r = sd_rtnl_message_request_dump(req, true);
513 return rtnl_log_create_error(r);
515 r = sd_rtnl_call(rtnl, req, 0, &reply);
517 log_error("Failed to enumerate links: %s", strerror(-r));
521 c = decode_and_sort_links(reply, &links);
523 return rtnl_log_parse_error(c);
525 for (i = 0; i < c; i++) {
529 link_status_one(rtnl, udev, links[i].name);
533 STRV_FOREACH(name, args + 1) {
537 link_status_one(rtnl, udev, *name);
543 static void help(void) {
544 printf("%s [OPTIONS...]\n\n"
545 "Query and control the networking subsystem.\n\n"
546 " -h --help Show this help\n"
547 " --version Show package version\n"
548 " --no-pager Do not pipe output into a pager\n"
549 " --no-legend Do not show the headers and footers\n"
550 " -a --all Show status for all links\n\n"
553 " status LINK Show link status\n"
554 , program_invocation_short_name);
557 static int parse_argv(int argc, char *argv[]) {
565 static const struct option options[] = {
566 { "help", no_argument, NULL, 'h' },
567 { "version", no_argument, NULL, ARG_VERSION },
568 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
569 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
570 { "all", no_argument, NULL, 'a' },
579 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
588 puts(PACKAGE_STRING);
589 puts(SYSTEMD_FEATURES);
608 assert_not_reached("Unhandled option");
615 static int networkctl_main(int argc, char *argv[]) {
617 static const struct {
625 int (* const dispatch)(char **args, unsigned n);
627 { "list", LESS, 1, list_links },
628 { "status", MORE, 1, link_status },
637 left = argc - optind;
640 /* Special rule: no arguments means "list" */
643 if (streq(argv[optind], "help")) {
648 for (i = 0; i < ELEMENTSOF(verbs); i++)
649 if (streq(argv[optind], verbs[i].verb))
652 if (i >= ELEMENTSOF(verbs)) {
653 log_error("Unknown operation %s", argv[optind]);
658 switch (verbs[i].argc_cmp) {
661 if (left != verbs[i].argc) {
662 log_error("Invalid number of arguments.");
669 if (left < verbs[i].argc) {
670 log_error("Too few arguments.");
677 if (left > verbs[i].argc) {
678 log_error("Too many arguments.");
685 assert_not_reached("Unknown comparison operator.");
688 return verbs[i].dispatch(argv + optind, left);
691 int main(int argc, char* argv[]) {
694 log_parse_environment();
697 r = parse_argv(argc, argv);
701 r = networkctl_main(argc, argv);
706 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;