]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
networkctl: name setup state variable setup_state
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
ee8c4568
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <stdbool.h>
23#include <getopt.h>
24
25#include "sd-network.h"
26#include "sd-rtnl.h"
27#include "libudev.h"
28
29#include "build.h"
30#include "util.h"
31#include "pager.h"
32#include "rtnl-util.h"
33#include "udev-util.h"
34#include "arphrd-list.h"
35#include "local-addresses.h"
36
37static bool arg_no_pager = false;
38static bool arg_legend = true;
9085f64a 39static bool arg_all = false;
ee8c4568
LP
40
41static void pager_open_if_enabled(void) {
42
43 if (arg_no_pager)
44 return;
45
46 pager_open(false);
47}
48
49static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
50 const char *t;
51 char *p;
52
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
57 * them */
58
59 devtype = udev_device_get_devtype(d);
60 if (streq_ptr(devtype, "wlan"))
61 id = "wlan";
62 else if (streq_ptr(devtype, "wwan"))
63 id = "wwan";
64
65 if (id) {
66 p = strdup(id);
67 if (!p)
68 return -ENOMEM;
69
70 *ret = p;
71 return 1;
72 }
73 }
74
75 t = arphrd_to_name(iftype);
76 if (!t) {
77 *ret = NULL;
78 return 0;
79 }
80
81 p = strdup(t);
82 if (!p)
83 return -ENOMEM;
84
85 ascii_strlower(p);
86 *ret = p;
87
88 return 0;
89}
90
6d0c65ff
LP
91typedef struct LinkInfo {
92 const char *name;
93 int ifindex;
94 unsigned iftype;
95} LinkInfo;
96
97static int link_info_compare(const void *a, const void *b) {
98 const LinkInfo *x = a, *y = b;
99
100 return x->ifindex - y->ifindex;
101}
102
103static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
104 _cleanup_free_ LinkInfo *links = NULL;
105 size_t size = 0, c = 0;
106 sd_rtnl_message *i;
107 int r;
108
109 for (i = m; i; i = sd_rtnl_message_next(i)) {
110 const char *name;
111 unsigned iftype;
112 uint16_t type;
113 int ifindex;
114
115 r = sd_rtnl_message_get_type(i, &type);
116 if (r < 0)
117 return r;
118
119 if (type != RTM_NEWLINK)
120 continue;
121
122 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
123 if (r < 0)
124 return r;
125
126 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
127 if (r < 0)
128 return r;
129
130 r = sd_rtnl_message_link_get_type(i, &iftype);
131 if (r < 0)
132 return r;
133
134 if (!GREEDY_REALLOC(links, size, c+1))
135 return -ENOMEM;
136
137 links[c].name = name;
138 links[c].ifindex = ifindex;
139 links[c].iftype = iftype;
140 c++;
141 }
142
143 qsort(links, c, sizeof(LinkInfo), link_info_compare);
144
145 *ret = links;
146 links = NULL;
147
148 return (int) c;
149}
150
ee8c4568
LP
151static 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;
6d0c65ff
LP
155 _cleanup_free_ LinkInfo *links = NULL;
156 int r, c, i;
ee8c4568
LP
157
158 pager_open_if_enabled();
159
160 r = sd_rtnl_open(&rtnl, 0);
161 if (r < 0) {
162 log_error("Failed to connect to netlink: %s", strerror(-r));
163 return r;
164 }
165
166 udev = udev_new();
167 if (!udev) {
168 log_error("Failed to connect to udev: %m");
169 return -errno;
170 }
171
172 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
173 if (r < 0)
174 return rtnl_log_create_error(r);
175
176 r = sd_rtnl_message_request_dump(req, true);
177 if (r < 0)
178 return rtnl_log_create_error(r);
179
180 r = sd_rtnl_call(rtnl, req, 0, &reply);
181 if (r < 0) {
182 log_error("Failed to enumerate links: %s", strerror(-r));
183 return r;
184 }
185
186 if (arg_legend)
2368512a 187 printf("%3s %-16s %-10s %-11s %-10s\n", "IDX", "LINK", "TYPE", "SETUP", "OPERATIONAL");
ee8c4568 188
6d0c65ff
LP
189 c = decode_and_sort_links(reply, &links);
190 if (c < 0)
191 return rtnl_log_parse_error(c);
192
193 for (i = 0; i < c; i++) {
ab1525bc 194 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
ee8c4568 195 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
636db02d
TG
196 const char *on_color_oper = "", *off_color_oper = "",
197 *on_color = "", *off_color = "";
df3fb561 198 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 199 _cleanup_free_ char *t = NULL;
ee8c4568 200
ab1525bc 201 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
d6731e4c 202 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
ee8c4568 203
6d0c65ff 204 sprintf(devid, "n%i", links[i].ifindex);
ee8c4568
LP
205 d = udev_device_new_from_device_id(udev, devid);
206
6d0c65ff 207 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 208
df3fb561 209 if (streq_ptr(operational_state, "routable")) {
636db02d
TG
210 on_color_oper = ansi_highlight_green();
211 off_color_oper = ansi_highlight_off();
212 } else if (streq_ptr(operational_state, "degraded")) {
213 on_color_oper = ansi_highlight_yellow();
214 off_color_oper = ansi_highlight_off();
215 }
216
ab1525bc 217 if (streq_ptr(setup_state, "configured")) {
df3fb561
LP
218 on_color = ansi_highlight_green();
219 off_color = ansi_highlight_off();
ab1525bc 220 } else if (streq_ptr(setup_state, "configuring")) {
df3fb561
LP
221 on_color = ansi_highlight_yellow();
222 off_color = ansi_highlight_off();
ab1525bc
LP
223 } else if (streq_ptr(setup_state, "failed") ||
224 streq_ptr(setup_state, "linger")) {
636db02d
TG
225 on_color = ansi_highlight_red();
226 off_color = ansi_highlight_off();
df3fb561
LP
227 }
228
636db02d 229 printf("%3i %-16s %-10s %s%-11s%s %s%-10s%s\n", links[i].ifindex,
ab1525bc 230 links[i].name, strna(t), on_color, strna(setup_state), off_color,
636db02d 231 on_color_oper, strna(operational_state), off_color_oper);
ee8c4568
LP
232 }
233
234 if (arg_legend)
6d0c65ff 235 printf("\n%i links listed.\n", c);
ee8c4568
LP
236
237 return 0;
238}
239
240static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
241 _cleanup_free_ struct local_address *local = NULL;
242 int r, n, i;
243
244 n = local_addresses(rtnl, ifindex, &local);
245 if (n < 0)
246 return n;
247
248 for (i = 0; i < n; i++) {
249 _cleanup_free_ char *pretty = NULL;
250
251 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
252 if (r < 0)
253 return r;
254
255 printf("%*s%s\n",
256 (int) strlen(prefix),
257 i == 0 ? prefix : "",
258 pretty);
259 }
260
261 return 0;
262}
263
264static void dump_list(const char *prefix, char **l) {
265 char **i;
266
267 STRV_FOREACH(i, l) {
268 printf("%*s%s\n",
269 (int) strlen(prefix),
270 i == l ? prefix : "",
271 *i);
272 }
273}
274
9085f64a
LP
275static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
276 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
ab1525bc 277 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
9085f64a
LP
278 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
279 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
280 char devid[2 + DECIMAL_STR_MAX(int)];
281 _cleanup_free_ char *t = NULL;
282 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
df3fb561 283 const char *on_color = "", *off_color = "";
9085f64a
LP
284 struct ether_addr e;
285 unsigned iftype;
286 int r, ifindex;
287 bool have_mac;
288 uint32_t mtu;
289
290 assert(rtnl);
291 assert(udev);
292 assert(name);
293
294 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
295 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
296 else {
297 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
298 if (r < 0)
299 return rtnl_log_create_error(r);
300
301 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
302 }
303
304 if (r < 0)
305 return rtnl_log_create_error(r);
306
307 r = sd_rtnl_call(rtnl, req, 0, &reply);
308 if (r < 0) {
309 log_error("Failed to query link: %s", strerror(-r));
310 return r;
311 }
312
313 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
314 if (r < 0)
315 return rtnl_log_parse_error(r);
316
317 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
318 if (r < 0)
319 return rtnl_log_parse_error(r);
320
321 r = sd_rtnl_message_link_get_type(reply, &iftype);
322 if (r < 0)
323 return rtnl_log_parse_error(r);
324
325 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
326
327 if (have_mac) {
328 const uint8_t *p;
329 bool all_zeroes = true;
330
331 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
332 if (*p != 0) {
333 all_zeroes = false;
334 break;
335 }
336
337 if (all_zeroes)
338 have_mac = false;
339 }
340
341 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
342
ab1525bc 343 sd_network_link_get_setup_state(ifindex, &setup_state);
d6731e4c 344 sd_network_link_get_operational_state(ifindex, &operational_state);
9085f64a 345
d6731e4c
TG
346 sd_network_link_get_dns(ifindex, &dns);
347 sd_network_link_get_ntp(ifindex, &ntp);
9085f64a
LP
348
349 sprintf(devid, "n%i", ifindex);
350 d = udev_device_new_from_device_id(udev, devid);
351
352 link_get_type_string(iftype, d, &t);
353
354 if (d) {
355 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
356 path = udev_device_get_property_value(d, "ID_PATH");
357
358 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
359 if (!vendor)
360 vendor = udev_device_get_property_value(d, "ID_VENDOR");
361
362 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
363 if (!model)
364 model = udev_device_get_property_value(d, "ID_MODEL");
365 }
366
df3fb561
LP
367 if (streq_ptr(operational_state, "routable")) {
368 on_color = ansi_highlight_green();
369 off_color = ansi_highlight_off();
370 } else if (streq_ptr(operational_state, "degraded")) {
371 on_color = ansi_highlight_yellow();
372 off_color = ansi_highlight_off();
373 }
374
375 printf("%s%s%s %i: %s\n", on_color, draw_special_char(DRAW_BLACK_CIRCLE), off_color, ifindex, name);
9085f64a
LP
376
377 printf(" Type: %s\n"
df3fb561 378 " State: %s%s%s (%s)\n",
9085f64a 379 strna(t),
df3fb561 380 on_color, strna(operational_state), off_color,
ab1525bc 381 strna(setup_state));
9085f64a
LP
382
383 if (path)
384 printf(" Path: %s\n", path);
385 if (driver)
386 printf(" Driver: %s\n", driver);
387 if (vendor)
388 printf(" Vendor: %s\n", vendor);
389 if (model)
390 printf(" Model: %s\n", model);
391
392 if (have_mac)
393 printf(" HW Address: %s\n", ether_ntoa(&e));
394
395 if (mtu > 0)
396 printf(" MTU: %u\n", mtu);
397
398 dump_addresses(rtnl, " Address: ", ifindex);
399
400 if (!strv_isempty(dns))
401 dump_list(" DNS: ", dns);
402 if (!strv_isempty(ntp))
403 dump_list(" NTP: ", ntp);
404
405 return 0;
406}
407
ee8c4568
LP
408static int link_status(char **args, unsigned n) {
409 _cleanup_udev_unref_ struct udev *udev = NULL;
410 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
411 char **name;
412 int r;
413
f7d68aa8
LP
414 r = sd_rtnl_open(&rtnl, 0);
415 if (r < 0) {
416 log_error("Failed to connect to netlink: %s", strerror(-r));
417 return r;
418 }
419
420 udev = udev_new();
421 if (!udev) {
422 log_error("Failed to connect to udev: %m");
423 return -errno;
424 }
425
9085f64a 426 if (n <= 1 && !arg_all) {
ee8c4568 427 _cleanup_free_ char *operational_state = NULL;
03cc0fd1 428 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
f7d68aa8 429 _cleanup_free_ struct local_address *addresses = NULL;
5323ead1 430 const char *on_color_oper = "", *off_color_oper = "";
f7d68aa8 431 int i, c;
ee8c4568 432
03cc0fd1 433 sd_network_get_operational_state(&operational_state);
5323ead1
DB
434 if (streq_ptr(operational_state, "routable")) {
435 on_color_oper = ansi_highlight_green();
436 off_color_oper = ansi_highlight_off();
437 } else if (streq_ptr(operational_state, "degraded")) {
438 on_color_oper = ansi_highlight_yellow();
439 off_color_oper = ansi_highlight_off();
440 }
441
442 printf(" State: %s%s%s\n", on_color_oper, strna(operational_state), off_color_oper);
03cc0fd1 443
f7d68aa8
LP
444 c = local_addresses(rtnl, 0, &addresses);
445 for (i = 0; i < c; i++) {
446 _cleanup_free_ char *pretty = NULL;
447
448 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
449 if (r < 0)
450 return log_oom();
451
452 printf("%13s %s\n",
453 i > 0 ? "" : "Address:", pretty);
454 }
455
03cc0fd1
LP
456 sd_network_get_dns(&dns);
457 if (!strv_isempty(dns))
458 dump_list(" DNS: ", dns);
459
460 sd_network_get_dns(&ntp);
461 if (!strv_isempty(ntp))
462 dump_list(" NTP: ", ntp);
ee8c4568 463
ee8c4568
LP
464 return 0;
465 }
466
467 pager_open_if_enabled();
468
9085f64a 469 if (arg_all) {
ee8c4568 470 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
471 _cleanup_free_ LinkInfo *links = NULL;
472 int c, i;
ee8c4568 473
9085f64a
LP
474 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
475 if (r < 0)
476 return rtnl_log_create_error(r);
ee8c4568 477
9085f64a 478 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
479 if (r < 0)
480 return rtnl_log_create_error(r);
481
482 r = sd_rtnl_call(rtnl, req, 0, &reply);
483 if (r < 0) {
9085f64a
LP
484 log_error("Failed to enumerate links: %s", strerror(-r));
485 return r;
ee8c4568
LP
486 }
487
6d0c65ff
LP
488 c = decode_and_sort_links(reply, &links);
489 if (c < 0)
490 return rtnl_log_parse_error(c);
ee8c4568 491
6d0c65ff
LP
492 for (i = 0; i < c; i++) {
493 if (i > 0)
9085f64a 494 fputc('\n', stdout);
ee8c4568 495
6d0c65ff 496 link_status_one(rtnl, udev, links[i].name);
ee8c4568 497 }
9085f64a 498 }
ee8c4568 499
9085f64a
LP
500 STRV_FOREACH(name, args + 1) {
501 if (name != args+1)
502 fputc('\n', stdout);
ee8c4568 503
9085f64a 504 link_status_one(rtnl, udev, *name);
ee8c4568
LP
505 }
506
507 return 0;
508}
509
510static void help(void) {
511 printf("%s [OPTIONS...]\n\n"
512 "Query and control the networking subsystem.\n\n"
513 " -h --help Show this help\n"
9085f64a
LP
514 " --version Show package version\n"
515 " --no-pager Do not pipe output into a pager\n"
516 " --no-legend Do not show the headers and footers\n"
517 " -a --all Show status for all links\n\n"
ee8c4568
LP
518 "Commands:\n"
519 " list List links\n"
520 " status LINK Show link status\n"
521 , program_invocation_short_name);
522}
523
524static int parse_argv(int argc, char *argv[]) {
525
526 enum {
527 ARG_VERSION = 0x100,
528 ARG_NO_PAGER,
529 ARG_NO_LEGEND,
530 };
531
532 static const struct option options[] = {
533 { "help", no_argument, NULL, 'h' },
534 { "version", no_argument, NULL, ARG_VERSION },
535 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
536 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 537 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
538 {}
539 };
540
541 int c;
542
543 assert(argc >= 0);
544 assert(argv);
545
9085f64a 546 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
547
548 switch (c) {
549
550 case 'h':
551 help();
552 return 0;
553
554 case ARG_VERSION:
555 puts(PACKAGE_STRING);
556 puts(SYSTEMD_FEATURES);
557 return 0;
558
559 case ARG_NO_PAGER:
560 arg_no_pager = true;
561 break;
562
563 case ARG_NO_LEGEND:
564 arg_legend = false;
565 break;
566
9085f64a
LP
567 case 'a':
568 arg_all = true;
569 break;
570
ee8c4568
LP
571 case '?':
572 return -EINVAL;
573
574 default:
575 assert_not_reached("Unhandled option");
576 }
577 }
578
579 return 1;
580}
581
582static int networkctl_main(int argc, char *argv[]) {
583
584 static const struct {
585 const char* verb;
586 const enum {
587 MORE,
588 LESS,
589 EQUAL
590 } argc_cmp;
591 const int argc;
592 int (* const dispatch)(char **args, unsigned n);
593 } verbs[] = {
594 { "list", LESS, 1, list_links },
595 { "status", MORE, 1, link_status },
596 };
597
598 int left;
599 unsigned i;
600
601 assert(argc >= 0);
602 assert(argv);
603
604 left = argc - optind;
605
606 if (left <= 0)
607 /* Special rule: no arguments means "list" */
608 i = 0;
609 else {
610 if (streq(argv[optind], "help")) {
611 help();
612 return 0;
613 }
614
615 for (i = 0; i < ELEMENTSOF(verbs); i++)
616 if (streq(argv[optind], verbs[i].verb))
617 break;
618
619 if (i >= ELEMENTSOF(verbs)) {
620 log_error("Unknown operation %s", argv[optind]);
621 return -EINVAL;
622 }
623 }
624
625 switch (verbs[i].argc_cmp) {
626
627 case EQUAL:
628 if (left != verbs[i].argc) {
629 log_error("Invalid number of arguments.");
630 return -EINVAL;
631 }
632
633 break;
634
635 case MORE:
636 if (left < verbs[i].argc) {
637 log_error("Too few arguments.");
638 return -EINVAL;
639 }
640
641 break;
642
643 case LESS:
644 if (left > verbs[i].argc) {
645 log_error("Too many arguments.");
646 return -EINVAL;
647 }
648
649 break;
650
651 default:
652 assert_not_reached("Unknown comparison operator.");
653 }
654
655 return verbs[i].dispatch(argv + optind, left);
656}
657
658int main(int argc, char* argv[]) {
659 int r;
660
661 log_parse_environment();
662 log_open();
663
664 r = parse_argv(argc, argv);
665 if (r <= 0)
666 goto finish;
667
668 r = networkctl_main(argc, argv);
669
670finish:
671 pager_close();
672
673 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
674}