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