]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
treewide: yet more log_*_errno + return simplifications
[thirdparty/systemd.git] / src / network / networkctl.c
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 #include "socket-util.h"
37
38 static bool arg_no_pager = false;
39 static bool arg_legend = true;
40 static bool arg_all = false;
41
42 static void pager_open_if_enabled(void) {
43
44 if (arg_no_pager)
45 return;
46
47 pager_open(false);
48 }
49
50 static 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
92 typedef struct LinkInfo {
93 const char *name;
94 int ifindex;
95 unsigned iftype;
96 } LinkInfo;
97
98 static 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
104 static 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_safe(links, c, sizeof(LinkInfo), link_info_compare);
145
146 *ret = links;
147 links = NULL;
148
149 return (int) c;
150 }
151
152 static 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
166 static 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
183 static 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;
187 _cleanup_free_ LinkInfo *links = NULL;
188 int r, c, i;
189
190 pager_open_if_enabled();
191
192 r = sd_rtnl_open(&rtnl, 0);
193 if (r < 0)
194 return log_error_errno(r, "Failed to connect to netlink: %m");
195
196 udev = udev_new();
197 if (!udev) {
198 log_error("Failed to connect to udev: %m");
199 return -errno;
200 }
201
202 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
203 if (r < 0)
204 return rtnl_log_create_error(r);
205
206 r = sd_rtnl_message_request_dump(req, true);
207 if (r < 0)
208 return rtnl_log_create_error(r);
209
210 r = sd_rtnl_call(rtnl, req, 0, &reply);
211 if (r < 0)
212 return log_error_errno(r, "Failed to enumerate links: %m");
213
214 if (arg_legend)
215 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
216
217 c = decode_and_sort_links(reply, &links);
218 if (c < 0)
219 return rtnl_log_parse_error(c);
220
221 for (i = 0; i < c; i++) {
222 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
223 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
224 const char *on_color_operational, *off_color_operational,
225 *on_color_setup, *off_color_setup;
226 char devid[2 + DECIMAL_STR_MAX(int)];
227 _cleanup_free_ char *t = NULL;
228
229 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
230 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
231
232 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
233 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
234
235 sprintf(devid, "n%i", links[i].ifindex);
236 d = udev_device_new_from_device_id(udev, devid);
237
238 link_get_type_string(links[i].iftype, d, &t);
239
240 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
241 links[i].ifindex, links[i].name, strna(t),
242 on_color_operational, strna(operational_state), off_color_operational,
243 on_color_setup, strna(setup_state), off_color_setup);
244 }
245
246 if (arg_legend)
247 printf("\n%i links listed.\n", c);
248
249 return 0;
250 }
251
252 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
253 _cleanup_free_ struct local_address *local = NULL;
254 int r, n, i;
255
256 n = local_addresses(rtnl, ifindex, &local);
257 if (n < 0)
258 return n;
259
260 for (i = 0; i < n; i++) {
261 _cleanup_free_ char *pretty = NULL;
262
263 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
264 if (r < 0)
265 return r;
266
267 printf("%*s%s\n",
268 (int) strlen(prefix),
269 i == 0 ? prefix : "",
270 pretty);
271 }
272
273 return 0;
274 }
275
276 static void dump_list(const char *prefix, char **l) {
277 char **i;
278
279 STRV_FOREACH(i, l) {
280 printf("%*s%s\n",
281 (int) strlen(prefix),
282 i == l ? prefix : "",
283 *i);
284 }
285 }
286
287 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
288 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
289 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
290 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
291 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
292 char devid[2 + DECIMAL_STR_MAX(int)];
293 _cleanup_free_ char *t = NULL, *network = NULL;
294 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
295 const char *on_color_operational, *off_color_operational,
296 *on_color_setup, *off_color_setup;
297 struct ether_addr e;
298 unsigned iftype;
299 int r, ifindex;
300 bool have_mac;
301 uint32_t mtu;
302
303 assert(rtnl);
304 assert(udev);
305 assert(name);
306
307 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
308 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
309 else {
310 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
311 if (r < 0)
312 return rtnl_log_create_error(r);
313
314 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
315 }
316
317 if (r < 0)
318 return rtnl_log_create_error(r);
319
320 r = sd_rtnl_call(rtnl, req, 0, &reply);
321 if (r < 0)
322 return log_error_errno(r, "Failed to query link: %m");
323
324 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
325 if (r < 0)
326 return rtnl_log_parse_error(r);
327
328 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
329 if (r < 0)
330 return rtnl_log_parse_error(r);
331
332 r = sd_rtnl_message_link_get_type(reply, &iftype);
333 if (r < 0)
334 return rtnl_log_parse_error(r);
335
336 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
337
338 if (have_mac) {
339 const uint8_t *p;
340 bool all_zeroes = true;
341
342 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
343 if (*p != 0) {
344 all_zeroes = false;
345 break;
346 }
347
348 if (all_zeroes)
349 have_mac = false;
350 }
351
352 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
353
354 sd_network_link_get_operational_state(ifindex, &operational_state);
355 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
356
357 sd_network_link_get_setup_state(ifindex, &setup_state);
358 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
359
360 sd_network_link_get_dns(ifindex, &dns);
361 sd_network_link_get_ntp(ifindex, &ntp);
362 sd_network_link_get_domains(ifindex, &domains);
363 r = sd_network_link_get_wildcard_domain(ifindex);
364 if (r > 0) {
365 char *wildcard;
366
367 wildcard = strdup("*");
368 if (!wildcard)
369 return log_oom();
370
371 if (strv_consume(&domains, wildcard) < 0)
372 return log_oom();
373 }
374
375 sprintf(devid, "n%i", ifindex);
376 d = udev_device_new_from_device_id(udev, devid);
377
378 link_get_type_string(iftype, d, &t);
379
380 if (d) {
381 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
382 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
383 path = udev_device_get_property_value(d, "ID_PATH");
384
385 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
386 if (!vendor)
387 vendor = udev_device_get_property_value(d, "ID_VENDOR");
388
389 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
390 if (!model)
391 model = udev_device_get_property_value(d, "ID_MODEL");
392 }
393
394 sd_network_link_get_network_file(ifindex, &network);
395
396 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
397
398 printf(" Link File: %s\n"
399 "Network File: %s\n"
400 " Type: %s\n"
401 " State: %s%s%s (%s%s%s)\n",
402 strna(link),
403 strna(network),
404 strna(t),
405 on_color_operational, strna(operational_state), off_color_operational,
406 on_color_setup, strna(setup_state), off_color_setup);
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
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 }
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);
429 if (!strv_isempty(domains))
430 dump_list(" Domain: ", domains);
431 if (!strv_isempty(ntp))
432 dump_list(" NTP: ", ntp);
433
434 return 0;
435 }
436
437 static 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
443 r = sd_rtnl_open(&rtnl, 0);
444 if (r < 0)
445 return log_error_errno(r, "Failed to connect to netlink: %m");
446
447 udev = udev_new();
448 if (!udev) {
449 log_error("Failed to connect to udev: %m");
450 return -errno;
451 }
452
453 if (n <= 1 && !arg_all) {
454 _cleanup_free_ char *operational_state = NULL;
455 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
456 _cleanup_free_ struct local_address *addresses = NULL;
457 const char *on_color_operational, *off_color_operational;
458 int i, c;
459
460 sd_network_get_operational_state(&operational_state);
461 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
462
463 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
464
465 c = local_addresses(rtnl, 0, &addresses);
466 for (i = 0; i < c; i++) {
467 _cleanup_free_ char *pretty = NULL;
468
469 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
470 if (r < 0)
471 return log_oom();
472
473 printf("%13s %s\n",
474 i > 0 ? "" : "Address:", pretty);
475 }
476
477 sd_network_get_dns(&dns);
478 if (!strv_isempty(dns))
479 dump_list(" DNS: ", dns);
480
481 sd_network_get_domains(&domains);
482 if (!strv_isempty(domains))
483 dump_list(" Domain: ", domains);
484
485 sd_network_get_ntp(&ntp);
486 if (!strv_isempty(ntp))
487 dump_list(" NTP: ", ntp);
488
489 return 0;
490 }
491
492 pager_open_if_enabled();
493
494 if (arg_all) {
495 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
496 _cleanup_free_ LinkInfo *links = NULL;
497 int c, i;
498
499 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
500 if (r < 0)
501 return rtnl_log_create_error(r);
502
503 r = sd_rtnl_message_request_dump(req, true);
504 if (r < 0)
505 return rtnl_log_create_error(r);
506
507 r = sd_rtnl_call(rtnl, req, 0, &reply);
508 if (r < 0)
509 return log_error_errno(r, "Failed to enumerate links: %m");
510
511 c = decode_and_sort_links(reply, &links);
512 if (c < 0)
513 return rtnl_log_parse_error(c);
514
515 for (i = 0; i < c; i++) {
516 if (i > 0)
517 fputc('\n', stdout);
518
519 link_status_one(rtnl, udev, links[i].name);
520 }
521 }
522
523 STRV_FOREACH(name, args + 1) {
524 if (name != args+1)
525 fputc('\n', stdout);
526
527 link_status_one(rtnl, udev, *name);
528 }
529
530 return 0;
531 }
532
533 static void help(void) {
534 printf("%s [OPTIONS...]\n\n"
535 "Query and control the networking subsystem.\n\n"
536 " -h --help Show this help\n"
537 " --version Show package version\n"
538 " --no-pager Do not pipe output into a pager\n"
539 " --no-legend Do not show the headers and footers\n"
540 " -a --all Show status for all links\n\n"
541 "Commands:\n"
542 " list List links\n"
543 " status LINK Show link status\n"
544 , program_invocation_short_name);
545 }
546
547 static int parse_argv(int argc, char *argv[]) {
548
549 enum {
550 ARG_VERSION = 0x100,
551 ARG_NO_PAGER,
552 ARG_NO_LEGEND,
553 };
554
555 static const struct option options[] = {
556 { "help", no_argument, NULL, 'h' },
557 { "version", no_argument, NULL, ARG_VERSION },
558 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
559 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
560 { "all", no_argument, NULL, 'a' },
561 {}
562 };
563
564 int c;
565
566 assert(argc >= 0);
567 assert(argv);
568
569 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
570
571 switch (c) {
572
573 case 'h':
574 help();
575 return 0;
576
577 case ARG_VERSION:
578 puts(PACKAGE_STRING);
579 puts(SYSTEMD_FEATURES);
580 return 0;
581
582 case ARG_NO_PAGER:
583 arg_no_pager = true;
584 break;
585
586 case ARG_NO_LEGEND:
587 arg_legend = false;
588 break;
589
590 case 'a':
591 arg_all = true;
592 break;
593
594 case '?':
595 return -EINVAL;
596
597 default:
598 assert_not_reached("Unhandled option");
599 }
600 }
601
602 return 1;
603 }
604
605 static int networkctl_main(int argc, char *argv[]) {
606
607 static const struct {
608 const char* verb;
609 const enum {
610 MORE,
611 LESS,
612 EQUAL
613 } argc_cmp;
614 const int argc;
615 int (* const dispatch)(char **args, unsigned n);
616 } verbs[] = {
617 { "list", LESS, 1, list_links },
618 { "status", MORE, 1, link_status },
619 };
620
621 int left;
622 unsigned i;
623
624 assert(argc >= 0);
625 assert(argv);
626
627 left = argc - optind;
628
629 if (left <= 0)
630 /* Special rule: no arguments means "list" */
631 i = 0;
632 else {
633 if (streq(argv[optind], "help")) {
634 help();
635 return 0;
636 }
637
638 for (i = 0; i < ELEMENTSOF(verbs); i++)
639 if (streq(argv[optind], verbs[i].verb))
640 break;
641
642 if (i >= ELEMENTSOF(verbs)) {
643 log_error("Unknown operation %s", argv[optind]);
644 return -EINVAL;
645 }
646 }
647
648 switch (verbs[i].argc_cmp) {
649
650 case EQUAL:
651 if (left != verbs[i].argc) {
652 log_error("Invalid number of arguments.");
653 return -EINVAL;
654 }
655
656 break;
657
658 case MORE:
659 if (left < verbs[i].argc) {
660 log_error("Too few arguments.");
661 return -EINVAL;
662 }
663
664 break;
665
666 case LESS:
667 if (left > verbs[i].argc) {
668 log_error("Too many arguments.");
669 return -EINVAL;
670 }
671
672 break;
673
674 default:
675 assert_not_reached("Unknown comparison operator.");
676 }
677
678 return verbs[i].dispatch(argv + optind, left);
679 }
680
681 int main(int argc, char* argv[]) {
682 int r;
683
684 log_parse_environment();
685 log_open();
686
687 r = parse_argv(argc, argv);
688 if (r <= 0)
689 goto finish;
690
691 r = networkctl_main(argc, argv);
692
693 finish:
694 pager_close();
695
696 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
697 }