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 if (n <= 1 && !arg_all) {
400 _cleanup_free_ char *operational_state = NULL;
401 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
403 sd_network_get_operational_state(&operational_state);
404 if (operational_state)
405 printf(" State: %s\n", operational_state);
407 sd_network_get_dns(&dns);
408 if (!strv_isempty(dns))
409 dump_list(" DNS: ", dns);
411 sd_network_get_dns(&ntp);
412 if (!strv_isempty(ntp))
413 dump_list(" NTP: ", ntp);
418 pager_open_if_enabled();
420 r = sd_rtnl_open(&rtnl, 0);
422 log_error("Failed to connect to netlink: %s", strerror(-r));
428 log_error("Failed to connect to udev: %m");
433 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
434 _cleanup_free_ LinkInfo *links = NULL;
437 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
439 return rtnl_log_create_error(r);
441 r = sd_rtnl_message_request_dump(req, true);
443 return rtnl_log_create_error(r);
445 r = sd_rtnl_call(rtnl, req, 0, &reply);
447 log_error("Failed to enumerate links: %s", strerror(-r));
451 c = decode_and_sort_links(reply, &links);
453 return rtnl_log_parse_error(c);
455 for (i = 0; i < c; i++) {
459 link_status_one(rtnl, udev, links[i].name);
463 STRV_FOREACH(name, args + 1) {
467 link_status_one(rtnl, udev, *name);
473 static void help(void) {
474 printf("%s [OPTIONS...]\n\n"
475 "Query and control the networking subsystem.\n\n"
476 " -h --help Show this help\n"
477 " --version Show package version\n"
478 " --no-pager Do not pipe output into a pager\n"
479 " --no-legend Do not show the headers and footers\n"
480 " -a --all Show status for all links\n\n"
483 " status LINK Show link status\n"
484 , program_invocation_short_name);
487 static int parse_argv(int argc, char *argv[]) {
495 static const struct option options[] = {
496 { "help", no_argument, NULL, 'h' },
497 { "version", no_argument, NULL, ARG_VERSION },
498 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
499 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
500 { "all", no_argument, NULL, 'a' },
509 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
518 puts(PACKAGE_STRING);
519 puts(SYSTEMD_FEATURES);
538 assert_not_reached("Unhandled option");
545 static int networkctl_main(int argc, char *argv[]) {
547 static const struct {
555 int (* const dispatch)(char **args, unsigned n);
557 { "list", LESS, 1, list_links },
558 { "status", MORE, 1, link_status },
567 left = argc - optind;
570 /* Special rule: no arguments means "list" */
573 if (streq(argv[optind], "help")) {
578 for (i = 0; i < ELEMENTSOF(verbs); i++)
579 if (streq(argv[optind], verbs[i].verb))
582 if (i >= ELEMENTSOF(verbs)) {
583 log_error("Unknown operation %s", argv[optind]);
588 switch (verbs[i].argc_cmp) {
591 if (left != verbs[i].argc) {
592 log_error("Invalid number of arguments.");
599 if (left < verbs[i].argc) {
600 log_error("Too few arguments.");
607 if (left > verbs[i].argc) {
608 log_error("Too many arguments.");
615 assert_not_reached("Unknown comparison operator.");
618 return verbs[i].dispatch(argv + optind, left);
621 int main(int argc, char* argv[]) {
624 log_parse_environment();
627 r = parse_argv(argc, argv);
631 r = networkctl_main(argc, argv);
636 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;