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 return log_error_errno(r, "Failed to connect to netlink: %m");
198 log_error("Failed to connect to udev: %m");
202 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
204 return rtnl_log_create_error(r);
206 r = sd_rtnl_message_request_dump(req, true);
208 return rtnl_log_create_error(r);
210 r = sd_rtnl_call(rtnl, req, 0, &reply);
212 return log_error_errno(r, "Failed to enumerate links: %m");
215 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
217 c = decode_and_sort_links(reply, &links);
219 return rtnl_log_parse_error(c);
221 for (i = 0; i < c; i++) {
222 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
223 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
224 const char *on_color_operational, *off_color_operational,
225 *on_color_setup, *off_color_setup;
226 char devid[2 + DECIMAL_STR_MAX(int)];
227 _cleanup_free_ char *t = NULL;
229 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
230 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
232 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
233 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
235 sprintf(devid, "n%i", links[i].ifindex);
236 d = udev_device_new_from_device_id(udev, devid);
238 link_get_type_string(links[i].iftype, d, &t);
240 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
241 links[i].ifindex, links[i].name, strna(t),
242 on_color_operational, strna(operational_state), off_color_operational,
243 on_color_setup, strna(setup_state), off_color_setup);
247 printf("\n%i links listed.\n", c);
252 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
253 _cleanup_free_ struct local_address *local = NULL;
256 n = local_addresses(rtnl, ifindex, &local);
260 for (i = 0; i < n; i++) {
261 _cleanup_free_ char *pretty = NULL;
263 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
268 (int) strlen(prefix),
269 i == 0 ? prefix : "",
276 static void dump_list(const char *prefix, char **l) {
281 (int) strlen(prefix),
282 i == l ? prefix : "",
287 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
288 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
289 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
290 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
291 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
292 char devid[2 + DECIMAL_STR_MAX(int)];
293 _cleanup_free_ char *t = NULL, *network = NULL;
294 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
295 const char *on_color_operational, *off_color_operational,
296 *on_color_setup, *off_color_setup;
307 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
308 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
310 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
312 return rtnl_log_create_error(r);
314 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
318 return rtnl_log_create_error(r);
320 r = sd_rtnl_call(rtnl, req, 0, &reply);
322 return log_error_errno(r, "Failed to query link: %m");
324 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
326 return rtnl_log_parse_error(r);
328 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
330 return rtnl_log_parse_error(r);
332 r = sd_rtnl_message_link_get_type(reply, &iftype);
334 return rtnl_log_parse_error(r);
336 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
340 bool all_zeroes = true;
342 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
352 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
354 sd_network_link_get_operational_state(ifindex, &operational_state);
355 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
357 sd_network_link_get_setup_state(ifindex, &setup_state);
358 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
360 sd_network_link_get_dns(ifindex, &dns);
361 sd_network_link_get_ntp(ifindex, &ntp);
362 sd_network_link_get_domains(ifindex, &domains);
363 r = sd_network_link_get_wildcard_domain(ifindex);
367 wildcard = strdup("*");
371 if (strv_consume(&domains, wildcard) < 0)
375 sprintf(devid, "n%i", ifindex);
376 d = udev_device_new_from_device_id(udev, devid);
378 link_get_type_string(iftype, d, &t);
381 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
382 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
383 path = udev_device_get_property_value(d, "ID_PATH");
385 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
387 vendor = udev_device_get_property_value(d, "ID_VENDOR");
389 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
391 model = udev_device_get_property_value(d, "ID_MODEL");
394 sd_network_link_get_network_file(ifindex, &network);
396 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
398 printf(" Link File: %s\n"
401 " State: %s%s%s (%s%s%s)\n",
405 on_color_operational, strna(operational_state), off_color_operational,
406 on_color_setup, strna(setup_state), off_color_setup);
409 printf(" Path: %s\n", path);
411 printf(" Driver: %s\n", driver);
413 printf(" Vendor: %s\n", vendor);
415 printf(" Model: %s\n", model);
418 char ea[ETHER_ADDR_TO_STRING_MAX];
419 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
423 printf(" MTU: %u\n", mtu);
425 dump_addresses(rtnl, " Address: ", ifindex);
427 if (!strv_isempty(dns))
428 dump_list(" DNS: ", dns);
429 if (!strv_isempty(domains))
430 dump_list(" Domain: ", domains);
431 if (!strv_isempty(ntp))
432 dump_list(" NTP: ", ntp);
437 static int link_status(char **args, unsigned n) {
438 _cleanup_udev_unref_ struct udev *udev = NULL;
439 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
443 r = sd_rtnl_open(&rtnl, 0);
445 return log_error_errno(r, "Failed to connect to netlink: %m");
449 log_error("Failed to connect to udev: %m");
453 if (n <= 1 && !arg_all) {
454 _cleanup_free_ char *operational_state = NULL;
455 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
456 _cleanup_free_ struct local_address *addresses = NULL;
457 const char *on_color_operational, *off_color_operational;
460 sd_network_get_operational_state(&operational_state);
461 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
463 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
465 c = local_addresses(rtnl, 0, &addresses);
466 for (i = 0; i < c; i++) {
467 _cleanup_free_ char *pretty = NULL;
469 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
474 i > 0 ? "" : "Address:", pretty);
477 sd_network_get_dns(&dns);
478 if (!strv_isempty(dns))
479 dump_list(" DNS: ", dns);
481 sd_network_get_domains(&domains);
482 if (!strv_isempty(domains))
483 dump_list(" Domain: ", domains);
485 sd_network_get_ntp(&ntp);
486 if (!strv_isempty(ntp))
487 dump_list(" NTP: ", ntp);
492 pager_open_if_enabled();
495 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
496 _cleanup_free_ LinkInfo *links = NULL;
499 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
501 return rtnl_log_create_error(r);
503 r = sd_rtnl_message_request_dump(req, true);
505 return rtnl_log_create_error(r);
507 r = sd_rtnl_call(rtnl, req, 0, &reply);
509 return log_error_errno(r, "Failed to enumerate links: %m");
511 c = decode_and_sort_links(reply, &links);
513 return rtnl_log_parse_error(c);
515 for (i = 0; i < c; i++) {
519 link_status_one(rtnl, udev, links[i].name);
523 STRV_FOREACH(name, args + 1) {
527 link_status_one(rtnl, udev, *name);
533 static void help(void) {
534 printf("%s [OPTIONS...]\n\n"
535 "Query and control the networking subsystem.\n\n"
536 " -h --help Show this help\n"
537 " --version Show package version\n"
538 " --no-pager Do not pipe output into a pager\n"
539 " --no-legend Do not show the headers and footers\n"
540 " -a --all Show status for all links\n\n"
543 " status LINK Show link status\n"
544 , program_invocation_short_name);
547 static int parse_argv(int argc, char *argv[]) {
555 static const struct option options[] = {
556 { "help", no_argument, NULL, 'h' },
557 { "version", no_argument, NULL, ARG_VERSION },
558 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
559 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
560 { "all", no_argument, NULL, 'a' },
569 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
578 puts(PACKAGE_STRING);
579 puts(SYSTEMD_FEATURES);
598 assert_not_reached("Unhandled option");
605 static int networkctl_main(int argc, char *argv[]) {
607 static const struct {
615 int (* const dispatch)(char **args, unsigned n);
617 { "list", LESS, 1, list_links },
618 { "status", MORE, 1, link_status },
627 left = argc - optind;
630 /* Special rule: no arguments means "list" */
633 if (streq(argv[optind], "help")) {
638 for (i = 0; i < ELEMENTSOF(verbs); i++)
639 if (streq(argv[optind], verbs[i].verb))
642 if (i >= ELEMENTSOF(verbs)) {
643 log_error("Unknown operation %s", argv[optind]);
648 switch (verbs[i].argc_cmp) {
651 if (left != verbs[i].argc) {
652 log_error("Invalid number of arguments.");
659 if (left < verbs[i].argc) {
660 log_error("Too few arguments.");
667 if (left > verbs[i].argc) {
668 log_error("Too many arguments.");
675 assert_not_reached("Unknown comparison operator.");
678 return verbs[i].dispatch(argv + optind, left);
681 int main(int argc, char* argv[]) {
684 log_parse_environment();
687 r = parse_argv(argc, argv);
691 r = networkctl_main(argc, argv);
696 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;