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 %-10s %-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 = "", *off_color = "";
197 char devid[2 + DECIMAL_STR_MAX(int)];
198 _cleanup_free_ char *t = NULL;
200 sd_network_get_link_state(links[i].ifindex, &state);
201 sd_network_get_link_operational_state(links[i].ifindex, &operational_state);
203 sprintf(devid, "n%i", links[i].ifindex);
204 d = udev_device_new_from_device_id(udev, devid);
206 link_get_type_string(links[i].iftype, d, &t);
208 if (streq_ptr(operational_state, "routable")) {
209 on_color = ansi_highlight_green();
210 off_color = ansi_highlight_off();
211 } else if (streq_ptr(operational_state, "degraded")) {
212 on_color = ansi_highlight_yellow();
213 off_color = ansi_highlight_off();
216 printf("%3i %-16s %-10s %-10s %s%-10s%s\n", links[i].ifindex, links[i].name, strna(t), strna(state), on_color, strna(operational_state), off_color);
220 printf("\n%i links listed.\n", c);
225 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
226 _cleanup_free_ struct local_address *local = NULL;
229 n = local_addresses(rtnl, ifindex, &local);
233 for (i = 0; i < n; i++) {
234 _cleanup_free_ char *pretty = NULL;
236 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
241 (int) strlen(prefix),
242 i == 0 ? prefix : "",
249 static void dump_list(const char *prefix, char **l) {
254 (int) strlen(prefix),
255 i == l ? prefix : "",
260 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
261 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
262 _cleanup_free_ char *state = NULL, *operational_state = NULL;
263 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
264 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
265 char devid[2 + DECIMAL_STR_MAX(int)];
266 _cleanup_free_ char *t = NULL;
267 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
268 const char *on_color = "", *off_color = "";
279 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
280 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
282 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
284 return rtnl_log_create_error(r);
286 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
290 return rtnl_log_create_error(r);
292 r = sd_rtnl_call(rtnl, req, 0, &reply);
294 log_error("Failed to query link: %s", strerror(-r));
298 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
300 return rtnl_log_parse_error(r);
302 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
304 return rtnl_log_parse_error(r);
306 r = sd_rtnl_message_link_get_type(reply, &iftype);
308 return rtnl_log_parse_error(r);
310 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
314 bool all_zeroes = true;
316 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
326 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
328 sd_network_get_link_state(ifindex, &state);
329 sd_network_get_link_operational_state(ifindex, &operational_state);
331 sd_network_get_link_dns(ifindex, &dns);
332 sd_network_get_link_ntp(ifindex, &ntp);
334 sprintf(devid, "n%i", ifindex);
335 d = udev_device_new_from_device_id(udev, devid);
337 link_get_type_string(iftype, d, &t);
340 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
341 path = udev_device_get_property_value(d, "ID_PATH");
343 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
345 vendor = udev_device_get_property_value(d, "ID_VENDOR");
347 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
349 model = udev_device_get_property_value(d, "ID_MODEL");
352 if (streq_ptr(operational_state, "routable")) {
353 on_color = ansi_highlight_green();
354 off_color = ansi_highlight_off();
355 } else if (streq_ptr(operational_state, "degraded")) {
356 on_color = ansi_highlight_yellow();
357 off_color = ansi_highlight_off();
360 printf("%s%s%s %i: %s\n", on_color, draw_special_char(DRAW_BLACK_CIRCLE), off_color, ifindex, name);
363 " State: %s%s%s (%s)\n",
365 on_color, strna(operational_state), off_color,
369 printf(" Path: %s\n", path);
371 printf(" Driver: %s\n", driver);
373 printf(" Vendor: %s\n", vendor);
375 printf(" Model: %s\n", model);
378 printf(" HW Address: %s\n", ether_ntoa(&e));
381 printf(" MTU: %u\n", mtu);
383 dump_addresses(rtnl, " Address: ", ifindex);
385 if (!strv_isempty(dns))
386 dump_list(" DNS: ", dns);
387 if (!strv_isempty(ntp))
388 dump_list(" NTP: ", ntp);
393 static int link_status(char **args, unsigned n) {
394 _cleanup_udev_unref_ struct udev *udev = NULL;
395 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
399 r = sd_rtnl_open(&rtnl, 0);
401 log_error("Failed to connect to netlink: %s", strerror(-r));
407 log_error("Failed to connect to udev: %m");
411 if (n <= 1 && !arg_all) {
412 _cleanup_free_ char *operational_state = NULL;
413 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
414 _cleanup_free_ struct local_address *addresses = NULL;
417 sd_network_get_operational_state(&operational_state);
418 if (operational_state)
419 printf(" State: %s\n", operational_state);
421 c = local_addresses(rtnl, 0, &addresses);
422 for (i = 0; i < c; i++) {
423 _cleanup_free_ char *pretty = NULL;
425 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
430 i > 0 ? "" : "Address:", pretty);
433 sd_network_get_dns(&dns);
434 if (!strv_isempty(dns))
435 dump_list(" DNS: ", dns);
437 sd_network_get_dns(&ntp);
438 if (!strv_isempty(ntp))
439 dump_list(" NTP: ", ntp);
444 pager_open_if_enabled();
447 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
448 _cleanup_free_ LinkInfo *links = NULL;
451 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
453 return rtnl_log_create_error(r);
455 r = sd_rtnl_message_request_dump(req, true);
457 return rtnl_log_create_error(r);
459 r = sd_rtnl_call(rtnl, req, 0, &reply);
461 log_error("Failed to enumerate links: %s", strerror(-r));
465 c = decode_and_sort_links(reply, &links);
467 return rtnl_log_parse_error(c);
469 for (i = 0; i < c; i++) {
473 link_status_one(rtnl, udev, links[i].name);
477 STRV_FOREACH(name, args + 1) {
481 link_status_one(rtnl, udev, *name);
487 static void help(void) {
488 printf("%s [OPTIONS...]\n\n"
489 "Query and control the networking subsystem.\n\n"
490 " -h --help Show this help\n"
491 " --version Show package version\n"
492 " --no-pager Do not pipe output into a pager\n"
493 " --no-legend Do not show the headers and footers\n"
494 " -a --all Show status for all links\n\n"
497 " status LINK Show link status\n"
498 , program_invocation_short_name);
501 static int parse_argv(int argc, char *argv[]) {
509 static const struct option options[] = {
510 { "help", no_argument, NULL, 'h' },
511 { "version", no_argument, NULL, ARG_VERSION },
512 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
513 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
514 { "all", no_argument, NULL, 'a' },
523 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
532 puts(PACKAGE_STRING);
533 puts(SYSTEMD_FEATURES);
552 assert_not_reached("Unhandled option");
559 static int networkctl_main(int argc, char *argv[]) {
561 static const struct {
569 int (* const dispatch)(char **args, unsigned n);
571 { "list", LESS, 1, list_links },
572 { "status", MORE, 1, link_status },
581 left = argc - optind;
584 /* Special rule: no arguments means "list" */
587 if (streq(argv[optind], "help")) {
592 for (i = 0; i < ELEMENTSOF(verbs); i++)
593 if (streq(argv[optind], verbs[i].verb))
596 if (i >= ELEMENTSOF(verbs)) {
597 log_error("Unknown operation %s", argv[optind]);
602 switch (verbs[i].argc_cmp) {
605 if (left != verbs[i].argc) {
606 log_error("Invalid number of arguments.");
613 if (left < verbs[i].argc) {
614 log_error("Too few arguments.");
621 if (left > verbs[i].argc) {
622 log_error("Too many arguments.");
629 assert_not_reached("Unknown comparison operator.");
632 return verbs[i].dispatch(argv + optind, left);
635 int main(int argc, char* argv[]) {
638 log_parse_environment();
641 r = parse_argv(argc, argv);
645 r = networkctl_main(argc, argv);
650 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;