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 static int list_links(char **args, unsigned n) {
92 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
93 _cleanup_udev_unref_ struct udev *udev = NULL;
94 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
99 pager_open_if_enabled();
101 r = sd_rtnl_open(&rtnl, 0);
103 log_error("Failed to connect to netlink: %s", strerror(-r));
109 log_error("Failed to connect to udev: %m");
113 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
115 return rtnl_log_create_error(r);
117 r = sd_rtnl_message_request_dump(req, true);
119 return rtnl_log_create_error(r);
121 r = sd_rtnl_call(rtnl, req, 0, &reply);
123 log_error("Failed to enumerate links: %s", strerror(-r));
128 printf("%3s %-16s %-10s %-10s %-10s\n", "IDX", "LINK", "TYPE", "ADMIN", "OPERATIONAL");
130 for (i = reply; i; i = sd_rtnl_message_next(i)) {
131 _cleanup_free_ char *state = NULL, *operational_state = NULL;
132 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
133 const char *on_color = "", *off_color = "";
134 char devid[2 + DECIMAL_STR_MAX(int)];
135 _cleanup_free_ char *t = NULL;
141 r = sd_rtnl_message_get_type(i, &type);
143 return rtnl_log_parse_error(r);
145 if (type != RTM_NEWLINK)
148 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
150 return rtnl_log_parse_error(r);
152 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
154 return rtnl_log_parse_error(r);
156 r = sd_rtnl_message_link_get_type(i, &iftype);
158 return rtnl_log_parse_error(r);
160 sd_network_get_link_state(ifindex, &state);
161 sd_network_get_link_operational_state(ifindex, &operational_state);
163 sprintf(devid, "n%i", ifindex);
164 d = udev_device_new_from_device_id(udev, devid);
166 link_get_type_string(iftype, d, &t);
168 if (streq_ptr(operational_state, "routable")) {
169 on_color = ansi_highlight_green();
170 off_color = ansi_highlight_off();
171 } else if (streq_ptr(operational_state, "degraded")) {
172 on_color = ansi_highlight_yellow();
173 off_color = ansi_highlight_off();
176 printf("%3i %-16s %-10s %-10s %s%-10s%s\n", ifindex, name, strna(t), strna(state), on_color, strna(operational_state), off_color);
181 printf("\n%u links listed.\n", c);
186 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
187 _cleanup_free_ struct local_address *local = NULL;
190 n = local_addresses(rtnl, ifindex, &local);
194 for (i = 0; i < n; i++) {
195 _cleanup_free_ char *pretty = NULL;
197 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
202 (int) strlen(prefix),
203 i == 0 ? prefix : "",
210 static void dump_list(const char *prefix, char **l) {
215 (int) strlen(prefix),
216 i == l ? prefix : "",
221 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
222 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
223 _cleanup_free_ char *state = NULL, *operational_state = NULL;
224 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
225 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
226 char devid[2 + DECIMAL_STR_MAX(int)];
227 _cleanup_free_ char *t = NULL;
228 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
229 const char *on_color = "", *off_color = "";
240 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
241 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
243 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
245 return rtnl_log_create_error(r);
247 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
251 return rtnl_log_create_error(r);
253 r = sd_rtnl_call(rtnl, req, 0, &reply);
255 log_error("Failed to query link: %s", strerror(-r));
259 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
261 return rtnl_log_parse_error(r);
263 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
265 return rtnl_log_parse_error(r);
267 r = sd_rtnl_message_link_get_type(reply, &iftype);
269 return rtnl_log_parse_error(r);
271 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
275 bool all_zeroes = true;
277 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
287 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
289 sd_network_get_link_state(ifindex, &state);
290 sd_network_get_link_operational_state(ifindex, &operational_state);
292 sd_network_get_link_dns(ifindex, &dns);
293 sd_network_get_link_ntp(ifindex, &ntp);
295 sprintf(devid, "n%i", ifindex);
296 d = udev_device_new_from_device_id(udev, devid);
298 link_get_type_string(iftype, d, &t);
301 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
302 path = udev_device_get_property_value(d, "ID_PATH");
304 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
306 vendor = udev_device_get_property_value(d, "ID_VENDOR");
308 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
310 model = udev_device_get_property_value(d, "ID_MODEL");
313 if (streq_ptr(operational_state, "routable")) {
314 on_color = ansi_highlight_green();
315 off_color = ansi_highlight_off();
316 } else if (streq_ptr(operational_state, "degraded")) {
317 on_color = ansi_highlight_yellow();
318 off_color = ansi_highlight_off();
321 printf("%s%s%s %i: %s\n", on_color, draw_special_char(DRAW_BLACK_CIRCLE), off_color, ifindex, name);
324 " State: %s%s%s (%s)\n",
326 on_color, strna(operational_state), off_color,
330 printf(" Path: %s\n", path);
332 printf(" Driver: %s\n", driver);
334 printf(" Vendor: %s\n", vendor);
336 printf(" Model: %s\n", model);
339 printf(" HW Address: %s\n", ether_ntoa(&e));
342 printf(" MTU: %u\n", mtu);
344 dump_addresses(rtnl, " Address: ", ifindex);
346 if (!strv_isempty(dns))
347 dump_list(" DNS: ", dns);
348 if (!strv_isempty(ntp))
349 dump_list(" NTP: ", ntp);
354 static int link_status(char **args, unsigned n) {
355 _cleanup_udev_unref_ struct udev *udev = NULL;
356 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
360 if (n <= 1 && !arg_all) {
361 _cleanup_free_ char *operational_state = NULL;
362 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
364 sd_network_get_operational_state(&operational_state);
365 if (operational_state)
366 printf(" State: %s\n", operational_state);
368 sd_network_get_dns(&dns);
369 if (!strv_isempty(dns))
370 dump_list(" DNS: ", dns);
372 sd_network_get_dns(&ntp);
373 if (!strv_isempty(ntp))
374 dump_list(" NTP: ", ntp);
379 pager_open_if_enabled();
381 r = sd_rtnl_open(&rtnl, 0);
383 log_error("Failed to connect to netlink: %s", strerror(-r));
389 log_error("Failed to connect to udev: %m");
394 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
399 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
401 return rtnl_log_create_error(r);
403 r = sd_rtnl_message_request_dump(req, true);
405 return rtnl_log_create_error(r);
407 r = sd_rtnl_call(rtnl, req, 0, &reply);
409 log_error("Failed to enumerate links: %s", strerror(-r));
413 for (i = reply; i; i = sd_rtnl_message_next(i)) {
416 r = sd_rtnl_message_get_type(i, &type);
418 return rtnl_log_parse_error(r);
420 if (type != RTM_NEWLINK)
423 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &nn);
425 return rtnl_log_parse_error(r);
430 link_status_one(rtnl, udev, nn);
435 STRV_FOREACH(name, args + 1) {
439 link_status_one(rtnl, udev, *name);
445 static void help(void) {
446 printf("%s [OPTIONS...]\n\n"
447 "Query and control the networking subsystem.\n\n"
448 " -h --help Show this help\n"
449 " --version Show package version\n"
450 " --no-pager Do not pipe output into a pager\n"
451 " --no-legend Do not show the headers and footers\n"
452 " -a --all Show status for all links\n\n"
455 " status LINK Show link status\n"
456 , program_invocation_short_name);
459 static int parse_argv(int argc, char *argv[]) {
467 static const struct option options[] = {
468 { "help", no_argument, NULL, 'h' },
469 { "version", no_argument, NULL, ARG_VERSION },
470 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
471 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
472 { "all", no_argument, NULL, 'a' },
481 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
490 puts(PACKAGE_STRING);
491 puts(SYSTEMD_FEATURES);
510 assert_not_reached("Unhandled option");
517 static int networkctl_main(int argc, char *argv[]) {
519 static const struct {
527 int (* const dispatch)(char **args, unsigned n);
529 { "list", LESS, 1, list_links },
530 { "status", MORE, 1, link_status },
539 left = argc - optind;
542 /* Special rule: no arguments means "list" */
545 if (streq(argv[optind], "help")) {
550 for (i = 0; i < ELEMENTSOF(verbs); i++)
551 if (streq(argv[optind], verbs[i].verb))
554 if (i >= ELEMENTSOF(verbs)) {
555 log_error("Unknown operation %s", argv[optind]);
560 switch (verbs[i].argc_cmp) {
563 if (left != verbs[i].argc) {
564 log_error("Invalid number of arguments.");
571 if (left < verbs[i].argc) {
572 log_error("Too few arguments.");
579 if (left > verbs[i].argc) {
580 log_error("Too many arguments.");
587 assert_not_reached("Unknown comparison operator.");
590 return verbs[i].dispatch(argv + optind, left);
593 int main(int argc, char* argv[]) {
596 log_parse_environment();
599 r = parse_argv(argc, argv);
603 r = networkctl_main(argc, argv);
608 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;