]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
shared: add format helpers for printing MAC addresses
[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 #include "ether-addr-util.h"
38
39 static bool arg_no_pager = false;
40 static bool arg_legend = true;
41 static bool arg_all = false;
42
43 static void pager_open_if_enabled(void) {
44
45 if (arg_no_pager)
46 return;
47
48 pager_open(false);
49 }
50
51 static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
52 const char *t;
53 char *p;
54
55 if (iftype == ARPHRD_ETHER && d) {
56 const char *devtype, *id = NULL;
57 /* WLANs have iftype ARPHRD_ETHER, but we want
58 * to show a more useful type string for
59 * them */
60
61 devtype = udev_device_get_devtype(d);
62 if (streq_ptr(devtype, "wlan"))
63 id = "wlan";
64 else if (streq_ptr(devtype, "wwan"))
65 id = "wwan";
66
67 if (id) {
68 p = strdup(id);
69 if (!p)
70 return -ENOMEM;
71
72 *ret = p;
73 return 1;
74 }
75 }
76
77 t = arphrd_to_name(iftype);
78 if (!t) {
79 *ret = NULL;
80 return 0;
81 }
82
83 p = strdup(t);
84 if (!p)
85 return -ENOMEM;
86
87 ascii_strlower(p);
88 *ret = p;
89
90 return 0;
91 }
92
93 typedef struct LinkInfo {
94 const char *name;
95 int ifindex;
96 unsigned iftype;
97 } LinkInfo;
98
99 static int link_info_compare(const void *a, const void *b) {
100 const LinkInfo *x = a, *y = b;
101
102 return x->ifindex - y->ifindex;
103 }
104
105 static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
106 _cleanup_free_ LinkInfo *links = NULL;
107 size_t size = 0, c = 0;
108 sd_rtnl_message *i;
109 int r;
110
111 for (i = m; i; i = sd_rtnl_message_next(i)) {
112 const char *name;
113 unsigned iftype;
114 uint16_t type;
115 int ifindex;
116
117 r = sd_rtnl_message_get_type(i, &type);
118 if (r < 0)
119 return r;
120
121 if (type != RTM_NEWLINK)
122 continue;
123
124 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
125 if (r < 0)
126 return r;
127
128 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
129 if (r < 0)
130 return r;
131
132 r = sd_rtnl_message_link_get_type(i, &iftype);
133 if (r < 0)
134 return r;
135
136 if (!GREEDY_REALLOC(links, size, c+1))
137 return -ENOMEM;
138
139 links[c].name = name;
140 links[c].ifindex = ifindex;
141 links[c].iftype = iftype;
142 c++;
143 }
144
145 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
146
147 *ret = links;
148 links = NULL;
149
150 return (int) c;
151 }
152
153 static void operational_state_to_color(const char *state, const char **on, const char **off) {
154 assert(on);
155 assert(off);
156
157 if (streq_ptr(state, "routable")) {
158 *on = ansi_highlight_green();
159 *off = ansi_highlight_off();
160 } else if (streq_ptr(state, "degraded")) {
161 *on = ansi_highlight_yellow();
162 *off = ansi_highlight_off();
163 } else
164 *on = *off = "";
165 }
166
167 static void setup_state_to_color(const char *state, const char **on, const char **off) {
168 assert(on);
169 assert(off);
170
171 if (streq_ptr(state, "configured")) {
172 *on = ansi_highlight_green();
173 *off = ansi_highlight_off();
174 } else if (streq_ptr(state, "configuring")) {
175 *on = ansi_highlight_yellow();
176 *off = ansi_highlight_off();
177 } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
178 *on = ansi_highlight_red();
179 *off = ansi_highlight_off();
180 } else
181 *on = *off = "";
182 }
183
184 static int list_links(char **args, unsigned n) {
185 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
186 _cleanup_udev_unref_ struct udev *udev = NULL;
187 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
188 _cleanup_free_ LinkInfo *links = NULL;
189 int r, c, i;
190
191 pager_open_if_enabled();
192
193 r = sd_rtnl_open(&rtnl, 0);
194 if (r < 0)
195 return log_error_errno(r, "Failed to connect to netlink: %m");
196
197 udev = udev_new();
198 if (!udev)
199 return log_error_errno(errno, "Failed to connect to udev: %m");
200
201 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
202 if (r < 0)
203 return rtnl_log_create_error(r);
204
205 r = sd_rtnl_message_request_dump(req, true);
206 if (r < 0)
207 return rtnl_log_create_error(r);
208
209 r = sd_rtnl_call(rtnl, req, 0, &reply);
210 if (r < 0)
211 return log_error_errno(r, "Failed to enumerate links: %m");
212
213 if (arg_legend)
214 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
215
216 c = decode_and_sort_links(reply, &links);
217 if (c < 0)
218 return rtnl_log_parse_error(c);
219
220 for (i = 0; i < c; i++) {
221 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
222 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
223 const char *on_color_operational, *off_color_operational,
224 *on_color_setup, *off_color_setup;
225 char devid[2 + DECIMAL_STR_MAX(int)];
226 _cleanup_free_ char *t = NULL;
227
228 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
229 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
230
231 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
232 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
233
234 sprintf(devid, "n%i", links[i].ifindex);
235 d = udev_device_new_from_device_id(udev, devid);
236
237 link_get_type_string(links[i].iftype, d, &t);
238
239 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
240 links[i].ifindex, links[i].name, strna(t),
241 on_color_operational, strna(operational_state), off_color_operational,
242 on_color_setup, strna(setup_state), off_color_setup);
243 }
244
245 if (arg_legend)
246 printf("\n%i links listed.\n", c);
247
248 return 0;
249 }
250
251 /* IEEE Organizationally Unique Identifier vendor string */
252 static int ieee_oui(struct udev_hwdb *hwdb, struct ether_addr *mac, char **ret) {
253 struct udev_list_entry *entry;
254 char *description;
255 char str[strlen("OUI:XXYYXXYYXXYY") + 1];
256
257 /* skip commonly misused 00:00:00 (Xerox) prefix */
258 if (memcmp(mac, "\0\0\0", 3) == 0)
259 return -EINVAL;
260
261 snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
262
263 udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, str, 0))
264 if (strcmp(udev_list_entry_get_name(entry), "ID_OUI_FROM_DATABASE") == 0) {
265 description = strdup(udev_list_entry_get_value(entry));
266 if (!description)
267 return -ENOMEM;
268
269 *ret = description;
270 return 0;
271 }
272
273 return -ENODATA;
274 }
275
276 static int get_gateway_description(sd_rtnl *rtnl, struct udev_hwdb *hwdb, int ifindex, int family,
277 union in_addr_union *gateway, char **gateway_description) {
278 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
279 sd_rtnl_message *m;
280 int r;
281
282 assert(rtnl);
283 assert(ifindex >= 0);
284 assert(family == AF_INET || family == AF_INET6);
285 assert(gateway);
286 assert(gateway_description);
287
288 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
289 if (r < 0)
290 return r;
291
292 r = sd_rtnl_message_request_dump(req, true);
293 if (r < 0)
294 return r;
295
296 r = sd_rtnl_call(rtnl, req, 0, &reply);
297 if (r < 0)
298 return r;
299
300 for (m = reply; m; m = sd_rtnl_message_next(m)) {
301 union in_addr_union gw = {};
302 struct ether_addr mac = {};
303 uint16_t type;
304 int ifi, fam;
305
306 r = sd_rtnl_message_get_errno(m);
307 if (r < 0) {
308 log_error_errno(r, "got error: %m");
309 continue;
310 }
311
312 r = sd_rtnl_message_get_type(m, &type);
313 if (r < 0) {
314 log_error_errno(r, "could not get type: %m");
315 continue;
316 }
317
318 if (type != RTM_NEWNEIGH) {
319 log_error("type is not RTM_NEWNEIGH");
320 continue;
321 }
322
323 r = sd_rtnl_message_neigh_get_family(m, &fam);
324 if (r < 0) {
325 log_error_errno(r, "could not get family: %m");
326 continue;
327 }
328
329 if (fam != family) {
330 log_error("family is not correct");
331 continue;
332 }
333
334 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
335 if (r < 0) {
336 log_error_errno(r, "colud not get ifindex: %m");
337 continue;
338 }
339
340 if (ifindex > 0 && ifi != ifindex)
341 continue;
342
343 switch (fam) {
344 case AF_INET:
345 r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in);
346 if (r < 0)
347 continue;
348
349 break;
350 case AF_INET6:
351 r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6);
352 if (r < 0)
353 continue;
354
355 break;
356 default:
357 continue;
358 }
359
360 if (!in_addr_equal(fam, &gw, gateway))
361 continue;
362
363 r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac);
364 if (r < 0)
365 continue;
366
367 r = ieee_oui(hwdb, &mac, gateway_description);
368 if (r < 0)
369 continue;
370
371 return 0;
372 }
373
374 return -ENODATA;
375 }
376
377 static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) {
378 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
379 sd_rtnl_message *m;
380 bool first = true;
381 int r;
382
383 assert(rtnl);
384 assert(ifindex >= 0);
385
386 r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, AF_UNSPEC, RTPROT_UNSPEC);
387 if (r < 0)
388 return r;
389
390 r = sd_rtnl_message_request_dump(req, true);
391 if (r < 0)
392 return r;
393
394 r = sd_rtnl_call(rtnl, req, 0, &reply);
395 if (r < 0)
396 return r;
397
398 for (m = reply; m; m = sd_rtnl_message_next(m)) {
399 _cleanup_free_ char *gateway = NULL, *gateway_description = NULL;
400 union in_addr_union gw = {};
401 uint16_t type;
402 uint32_t ifi;
403 int family;
404
405 r = sd_rtnl_message_get_errno(m);
406 if (r < 0) {
407 log_error_errno(r, "got error: %m");
408 continue;
409 }
410
411 r = sd_rtnl_message_get_type(m, &type);
412 if (r < 0) {
413 log_error_errno(r, "could not get type: %m");
414 continue;
415 }
416
417 if (type != RTM_NEWROUTE) {
418 log_error("type is not RTM_NEWROUTE");
419 continue;
420 }
421
422 r = sd_rtnl_message_route_get_family(m, &family);
423 if (r < 0) {
424 log_error_errno(r, "could not get family: %m");
425 continue;
426 }
427
428 r = sd_rtnl_message_read_u32(m, RTA_OIF, &ifi);
429 if (r < 0) {
430 log_error_errno(r, "colud not get RTA_OIF: %m");
431 continue;
432 }
433
434 if (ifindex > 0 && ifi != (unsigned) ifindex)
435 continue;
436
437 switch (family) {
438 case AF_INET:
439 r = sd_rtnl_message_read_in_addr(m, RTA_GATEWAY, &gw.in);
440 if (r < 0)
441 continue;
442
443 r = sd_rtnl_message_read_in_addr(m, RTA_DST, NULL);
444 if (r >= 0)
445 continue;
446
447 r = sd_rtnl_message_read_in_addr(m, RTA_SRC, NULL);
448 if (r >= 0)
449 continue;
450
451 break;
452 case AF_INET6:
453 r = sd_rtnl_message_read_in6_addr(m, RTA_GATEWAY, &gw.in6);
454 if (r < 0)
455 continue;
456
457 r = sd_rtnl_message_read_in6_addr(m, RTA_DST, NULL);
458 if (r >= 0)
459 continue;
460
461 r = sd_rtnl_message_read_in6_addr(m, RTA_SRC, NULL);
462 if (r >= 0)
463 continue;
464
465 break;
466 default:
467 continue;
468 }
469
470 r = in_addr_to_string(family, &gw, &gateway);
471 if (r < 0)
472 continue;
473
474 r = get_gateway_description(rtnl, hwdb, ifi, family, &gw, &gateway_description);
475 if (r < 0)
476 log_debug("could not get description of gateway: %s", strerror(-r));
477
478 if (gateway_description)
479 printf("%*s%s (%s)\n",
480 (int) strlen(prefix),
481 first ? prefix : "",
482 gateway, gateway_description);
483 else
484 printf("%*s%s\n",
485 (int) strlen(prefix),
486 first ? prefix : "",
487 gateway);
488
489 first = false;
490 }
491
492 return 0;
493 }
494
495 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
496 _cleanup_free_ struct local_address *local = NULL;
497 int r, n, i;
498
499 n = local_addresses(rtnl, ifindex, &local);
500 if (n < 0)
501 return n;
502
503 for (i = 0; i < n; i++) {
504 _cleanup_free_ char *pretty = NULL;
505
506 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
507 if (r < 0)
508 return r;
509
510 printf("%*s%s\n",
511 (int) strlen(prefix),
512 i == 0 ? prefix : "",
513 pretty);
514 }
515
516 return 0;
517 }
518
519 static void dump_list(const char *prefix, char **l) {
520 char **i;
521
522 STRV_FOREACH(i, l) {
523 printf("%*s%s\n",
524 (int) strlen(prefix),
525 i == l ? prefix : "",
526 *i);
527 }
528 }
529
530 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
531 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
532 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *gateway = NULL, *gateway_description = NULL,
533 *gateway6 = NULL, *gateway6_description = NULL;
534 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
535 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
536 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
537 char devid[2 + DECIMAL_STR_MAX(int)];
538 _cleanup_free_ char *t = NULL, *network = NULL;
539 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
540 const char *on_color_operational, *off_color_operational,
541 *on_color_setup, *off_color_setup;
542 struct ether_addr e;
543 unsigned iftype;
544 int r, ifindex;
545 bool have_mac;
546 uint32_t mtu;
547
548 assert(rtnl);
549 assert(udev);
550 assert(name);
551
552 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
553 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
554 else {
555 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
556 if (r < 0)
557 return rtnl_log_create_error(r);
558
559 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
560 }
561
562 if (r < 0)
563 return rtnl_log_create_error(r);
564
565 r = sd_rtnl_call(rtnl, req, 0, &reply);
566 if (r < 0)
567 return log_error_errno(r, "Failed to query link: %m");
568
569 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
570 if (r < 0)
571 return rtnl_log_parse_error(r);
572
573 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
574 if (r < 0)
575 return rtnl_log_parse_error(r);
576
577 r = sd_rtnl_message_link_get_type(reply, &iftype);
578 if (r < 0)
579 return rtnl_log_parse_error(r);
580
581 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
582
583 if (have_mac) {
584 const uint8_t *p;
585 bool all_zeroes = true;
586
587 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
588 if (*p != 0) {
589 all_zeroes = false;
590 break;
591 }
592
593 if (all_zeroes)
594 have_mac = false;
595 }
596
597 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
598
599 sd_network_link_get_operational_state(ifindex, &operational_state);
600 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
601
602 sd_network_link_get_setup_state(ifindex, &setup_state);
603 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
604
605 sd_network_link_get_dns(ifindex, &dns);
606 sd_network_link_get_ntp(ifindex, &ntp);
607 sd_network_link_get_domains(ifindex, &domains);
608 r = sd_network_link_get_wildcard_domain(ifindex);
609 if (r > 0) {
610 char *wildcard;
611
612 wildcard = strdup("*");
613 if (!wildcard)
614 return log_oom();
615
616 if (strv_consume(&domains, wildcard) < 0)
617 return log_oom();
618 }
619
620 sprintf(devid, "n%i", ifindex);
621 d = udev_device_new_from_device_id(udev, devid);
622
623 link_get_type_string(iftype, d, &t);
624
625 if (d) {
626 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
627 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
628 path = udev_device_get_property_value(d, "ID_PATH");
629
630 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
631 if (!vendor)
632 vendor = udev_device_get_property_value(d, "ID_VENDOR");
633
634 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
635 if (!model)
636 model = udev_device_get_property_value(d, "ID_MODEL");
637 }
638
639 sd_network_link_get_network_file(ifindex, &network);
640
641 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
642
643 printf(" Link File: %s\n"
644 "Network File: %s\n"
645 " Type: %s\n"
646 " State: %s%s%s (%s%s%s)\n",
647 strna(link),
648 strna(network),
649 strna(t),
650 on_color_operational, strna(operational_state), off_color_operational,
651 on_color_setup, strna(setup_state), off_color_setup);
652
653 if (path)
654 printf(" Path: %s\n", path);
655 if (driver)
656 printf(" Driver: %s\n", driver);
657 if (vendor)
658 printf(" Vendor: %s\n", vendor);
659 if (model)
660 printf(" Model: %s\n", model);
661
662 if (have_mac) {
663 char ea[ETHER_ADDR_TO_STRING_MAX];
664 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
665 }
666
667 if (mtu > 0)
668 printf(" MTU: %u\n", mtu);
669
670 hwdb = udev_hwdb_new(udev);
671
672 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
673
674 dump_addresses(rtnl, " Address: ", ifindex);
675
676 if (!strv_isempty(dns))
677 dump_list(" DNS: ", dns);
678 if (!strv_isempty(domains))
679 dump_list(" Domain: ", domains);
680 if (!strv_isempty(ntp))
681 dump_list(" NTP: ", ntp);
682
683 return 0;
684 }
685
686 static int link_status(char **args, unsigned n) {
687 _cleanup_udev_unref_ struct udev *udev = NULL;
688 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
689 char **name;
690 int r;
691
692 r = sd_rtnl_open(&rtnl, 0);
693 if (r < 0)
694 return log_error_errno(r, "Failed to connect to netlink: %m");
695
696 udev = udev_new();
697 if (!udev)
698 return log_error_errno(errno, "Failed to connect to udev: %m");
699
700 if (n <= 1 && !arg_all) {
701 _cleanup_free_ char *operational_state = NULL;
702 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
703 _cleanup_free_ struct local_address *addresses = NULL;
704 const char *on_color_operational, *off_color_operational;
705 int i, c;
706
707 sd_network_get_operational_state(&operational_state);
708 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
709
710 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
711
712 c = local_addresses(rtnl, 0, &addresses);
713 for (i = 0; i < c; i++) {
714 _cleanup_free_ char *pretty = NULL;
715
716 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
717 if (r < 0)
718 return log_oom();
719
720 printf("%13s %s\n",
721 i > 0 ? "" : "Address:", pretty);
722 }
723
724 sd_network_get_dns(&dns);
725 if (!strv_isempty(dns))
726 dump_list(" DNS: ", dns);
727
728 sd_network_get_domains(&domains);
729 if (!strv_isempty(domains))
730 dump_list(" Domain: ", domains);
731
732 sd_network_get_ntp(&ntp);
733 if (!strv_isempty(ntp))
734 dump_list(" NTP: ", ntp);
735
736 return 0;
737 }
738
739 pager_open_if_enabled();
740
741 if (arg_all) {
742 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
743 _cleanup_free_ LinkInfo *links = NULL;
744 int c, i;
745
746 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
747 if (r < 0)
748 return rtnl_log_create_error(r);
749
750 r = sd_rtnl_message_request_dump(req, true);
751 if (r < 0)
752 return rtnl_log_create_error(r);
753
754 r = sd_rtnl_call(rtnl, req, 0, &reply);
755 if (r < 0)
756 return log_error_errno(r, "Failed to enumerate links: %m");
757
758 c = decode_and_sort_links(reply, &links);
759 if (c < 0)
760 return rtnl_log_parse_error(c);
761
762 for (i = 0; i < c; i++) {
763 if (i > 0)
764 fputc('\n', stdout);
765
766 link_status_one(rtnl, udev, links[i].name);
767 }
768 }
769
770 STRV_FOREACH(name, args + 1) {
771 if (name != args+1)
772 fputc('\n', stdout);
773
774 link_status_one(rtnl, udev, *name);
775 }
776
777 return 0;
778 }
779
780 static void help(void) {
781 printf("%s [OPTIONS...]\n\n"
782 "Query and control the networking subsystem.\n\n"
783 " -h --help Show this help\n"
784 " --version Show package version\n"
785 " --no-pager Do not pipe output into a pager\n"
786 " --no-legend Do not show the headers and footers\n"
787 " -a --all Show status for all links\n\n"
788 "Commands:\n"
789 " list List links\n"
790 " status LINK Show link status\n"
791 , program_invocation_short_name);
792 }
793
794 static int parse_argv(int argc, char *argv[]) {
795
796 enum {
797 ARG_VERSION = 0x100,
798 ARG_NO_PAGER,
799 ARG_NO_LEGEND,
800 };
801
802 static const struct option options[] = {
803 { "help", no_argument, NULL, 'h' },
804 { "version", no_argument, NULL, ARG_VERSION },
805 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
806 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
807 { "all", no_argument, NULL, 'a' },
808 {}
809 };
810
811 int c;
812
813 assert(argc >= 0);
814 assert(argv);
815
816 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
817
818 switch (c) {
819
820 case 'h':
821 help();
822 return 0;
823
824 case ARG_VERSION:
825 puts(PACKAGE_STRING);
826 puts(SYSTEMD_FEATURES);
827 return 0;
828
829 case ARG_NO_PAGER:
830 arg_no_pager = true;
831 break;
832
833 case ARG_NO_LEGEND:
834 arg_legend = false;
835 break;
836
837 case 'a':
838 arg_all = true;
839 break;
840
841 case '?':
842 return -EINVAL;
843
844 default:
845 assert_not_reached("Unhandled option");
846 }
847 }
848
849 return 1;
850 }
851
852 static int networkctl_main(int argc, char *argv[]) {
853
854 static const struct {
855 const char* verb;
856 const enum {
857 MORE,
858 LESS,
859 EQUAL
860 } argc_cmp;
861 const int argc;
862 int (* const dispatch)(char **args, unsigned n);
863 } verbs[] = {
864 { "list", LESS, 1, list_links },
865 { "status", MORE, 1, link_status },
866 };
867
868 int left;
869 unsigned i;
870
871 assert(argc >= 0);
872 assert(argv);
873
874 left = argc - optind;
875
876 if (left <= 0)
877 /* Special rule: no arguments means "list" */
878 i = 0;
879 else {
880 if (streq(argv[optind], "help")) {
881 help();
882 return 0;
883 }
884
885 for (i = 0; i < ELEMENTSOF(verbs); i++)
886 if (streq(argv[optind], verbs[i].verb))
887 break;
888
889 if (i >= ELEMENTSOF(verbs)) {
890 log_error("Unknown operation %s", argv[optind]);
891 return -EINVAL;
892 }
893 }
894
895 switch (verbs[i].argc_cmp) {
896
897 case EQUAL:
898 if (left != verbs[i].argc) {
899 log_error("Invalid number of arguments.");
900 return -EINVAL;
901 }
902
903 break;
904
905 case MORE:
906 if (left < verbs[i].argc) {
907 log_error("Too few arguments.");
908 return -EINVAL;
909 }
910
911 break;
912
913 case LESS:
914 if (left > verbs[i].argc) {
915 log_error("Too many arguments.");
916 return -EINVAL;
917 }
918
919 break;
920
921 default:
922 assert_not_reached("Unknown comparison operator.");
923 }
924
925 return verbs[i].dispatch(argv + optind, left);
926 }
927
928 int main(int argc, char* argv[]) {
929 int r;
930
931 log_parse_environment();
932 log_open();
933
934 r = parse_argv(argc, argv);
935 if (r <= 0)
936 goto finish;
937
938 r = networkctl_main(argc, argv);
939
940 finish:
941 pager_close();
942
943 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
944 }