]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
rtnl: when querying local addresses and gateways, take address family into account
[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, "could 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, "could 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, AF_UNSPEC, &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;
533 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
534 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
535 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
536 char devid[2 + DECIMAL_STR_MAX(int)];
537 _cleanup_free_ char *t = NULL, *network = NULL;
538 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
539 const char *on_color_operational, *off_color_operational,
540 *on_color_setup, *off_color_setup;
541 struct ether_addr e;
542 unsigned iftype;
543 int r, ifindex;
544 bool have_mac;
545 uint32_t mtu;
546
547 assert(rtnl);
548 assert(udev);
549 assert(name);
550
551 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
552 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
553 else {
554 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
555 if (r < 0)
556 return rtnl_log_create_error(r);
557
558 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
559 }
560
561 if (r < 0)
562 return rtnl_log_create_error(r);
563
564 r = sd_rtnl_call(rtnl, req, 0, &reply);
565 if (r < 0)
566 return log_error_errno(r, "Failed to query link: %m");
567
568 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
569 if (r < 0)
570 return rtnl_log_parse_error(r);
571
572 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
573 if (r < 0)
574 return rtnl_log_parse_error(r);
575
576 r = sd_rtnl_message_link_get_type(reply, &iftype);
577 if (r < 0)
578 return rtnl_log_parse_error(r);
579
580 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
581
582 if (have_mac) {
583 const uint8_t *p;
584 bool all_zeroes = true;
585
586 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
587 if (*p != 0) {
588 all_zeroes = false;
589 break;
590 }
591
592 if (all_zeroes)
593 have_mac = false;
594 }
595
596 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
597
598 sd_network_link_get_operational_state(ifindex, &operational_state);
599 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
600
601 sd_network_link_get_setup_state(ifindex, &setup_state);
602 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
603
604 sd_network_link_get_dns(ifindex, &dns);
605 sd_network_link_get_ntp(ifindex, &ntp);
606 sd_network_link_get_domains(ifindex, &domains);
607 r = sd_network_link_get_wildcard_domain(ifindex);
608 if (r > 0) {
609 char *wildcard;
610
611 wildcard = strdup("*");
612 if (!wildcard)
613 return log_oom();
614
615 if (strv_consume(&domains, wildcard) < 0)
616 return log_oom();
617 }
618
619 sprintf(devid, "n%i", ifindex);
620 d = udev_device_new_from_device_id(udev, devid);
621
622 link_get_type_string(iftype, d, &t);
623
624 if (d) {
625 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
626 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
627 path = udev_device_get_property_value(d, "ID_PATH");
628
629 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
630 if (!vendor)
631 vendor = udev_device_get_property_value(d, "ID_VENDOR");
632
633 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
634 if (!model)
635 model = udev_device_get_property_value(d, "ID_MODEL");
636 }
637
638 sd_network_link_get_network_file(ifindex, &network);
639
640 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
641
642 printf(" Link File: %s\n"
643 "Network File: %s\n"
644 " Type: %s\n"
645 " State: %s%s%s (%s%s%s)\n",
646 strna(link),
647 strna(network),
648 strna(t),
649 on_color_operational, strna(operational_state), off_color_operational,
650 on_color_setup, strna(setup_state), off_color_setup);
651
652 if (path)
653 printf(" Path: %s\n", path);
654 if (driver)
655 printf(" Driver: %s\n", driver);
656 if (vendor)
657 printf(" Vendor: %s\n", vendor);
658 if (model)
659 printf(" Model: %s\n", model);
660
661 if (have_mac) {
662 char ea[ETHER_ADDR_TO_STRING_MAX];
663 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
664 }
665
666 if (mtu > 0)
667 printf(" MTU: %u\n", mtu);
668
669 hwdb = udev_hwdb_new(udev);
670
671 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
672
673 dump_addresses(rtnl, " Address: ", ifindex);
674
675 if (!strv_isempty(dns))
676 dump_list(" DNS: ", dns);
677 if (!strv_isempty(domains))
678 dump_list(" Domain: ", domains);
679 if (!strv_isempty(ntp))
680 dump_list(" NTP: ", ntp);
681
682 return 0;
683 }
684
685 static int link_status(char **args, unsigned n) {
686 _cleanup_udev_unref_ struct udev *udev = NULL;
687 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
688 char **name;
689 int r;
690
691 r = sd_rtnl_open(&rtnl, 0);
692 if (r < 0)
693 return log_error_errno(r, "Failed to connect to netlink: %m");
694
695 udev = udev_new();
696 if (!udev)
697 return log_error_errno(errno, "Failed to connect to udev: %m");
698
699 if (n <= 1 && !arg_all) {
700 _cleanup_free_ char *operational_state = NULL;
701 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
702 _cleanup_free_ struct local_address *addresses = NULL;
703 const char *on_color_operational, *off_color_operational;
704 int i, c;
705
706 sd_network_get_operational_state(&operational_state);
707 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
708
709 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
710
711 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
712 for (i = 0; i < c; i++) {
713 _cleanup_free_ char *pretty = NULL;
714
715 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
716 if (r < 0)
717 return log_oom();
718
719 printf("%13s %s\n",
720 i > 0 ? "" : "Address:", pretty);
721 }
722
723 sd_network_get_dns(&dns);
724 if (!strv_isempty(dns))
725 dump_list(" DNS: ", dns);
726
727 sd_network_get_domains(&domains);
728 if (!strv_isempty(domains))
729 dump_list(" Domain: ", domains);
730
731 sd_network_get_ntp(&ntp);
732 if (!strv_isempty(ntp))
733 dump_list(" NTP: ", ntp);
734
735 return 0;
736 }
737
738 pager_open_if_enabled();
739
740 if (arg_all) {
741 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
742 _cleanup_free_ LinkInfo *links = NULL;
743 int c, i;
744
745 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
746 if (r < 0)
747 return rtnl_log_create_error(r);
748
749 r = sd_rtnl_message_request_dump(req, true);
750 if (r < 0)
751 return rtnl_log_create_error(r);
752
753 r = sd_rtnl_call(rtnl, req, 0, &reply);
754 if (r < 0)
755 return log_error_errno(r, "Failed to enumerate links: %m");
756
757 c = decode_and_sort_links(reply, &links);
758 if (c < 0)
759 return rtnl_log_parse_error(c);
760
761 for (i = 0; i < c; i++) {
762 if (i > 0)
763 fputc('\n', stdout);
764
765 link_status_one(rtnl, udev, links[i].name);
766 }
767 }
768
769 STRV_FOREACH(name, args + 1) {
770 if (name != args+1)
771 fputc('\n', stdout);
772
773 link_status_one(rtnl, udev, *name);
774 }
775
776 return 0;
777 }
778
779 static void help(void) {
780 printf("%s [OPTIONS...]\n\n"
781 "Query and control the networking subsystem.\n\n"
782 " -h --help Show this help\n"
783 " --version Show package version\n"
784 " --no-pager Do not pipe output into a pager\n"
785 " --no-legend Do not show the headers and footers\n"
786 " -a --all Show status for all links\n\n"
787 "Commands:\n"
788 " list List links\n"
789 " status LINK Show link status\n"
790 , program_invocation_short_name);
791 }
792
793 static int parse_argv(int argc, char *argv[]) {
794
795 enum {
796 ARG_VERSION = 0x100,
797 ARG_NO_PAGER,
798 ARG_NO_LEGEND,
799 };
800
801 static const struct option options[] = {
802 { "help", no_argument, NULL, 'h' },
803 { "version", no_argument, NULL, ARG_VERSION },
804 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
805 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
806 { "all", no_argument, NULL, 'a' },
807 {}
808 };
809
810 int c;
811
812 assert(argc >= 0);
813 assert(argv);
814
815 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
816
817 switch (c) {
818
819 case 'h':
820 help();
821 return 0;
822
823 case ARG_VERSION:
824 puts(PACKAGE_STRING);
825 puts(SYSTEMD_FEATURES);
826 return 0;
827
828 case ARG_NO_PAGER:
829 arg_no_pager = true;
830 break;
831
832 case ARG_NO_LEGEND:
833 arg_legend = false;
834 break;
835
836 case 'a':
837 arg_all = true;
838 break;
839
840 case '?':
841 return -EINVAL;
842
843 default:
844 assert_not_reached("Unhandled option");
845 }
846 }
847
848 return 1;
849 }
850
851 static int networkctl_main(int argc, char *argv[]) {
852
853 static const struct {
854 const char* verb;
855 const enum {
856 MORE,
857 LESS,
858 EQUAL
859 } argc_cmp;
860 const int argc;
861 int (* const dispatch)(char **args, unsigned n);
862 } verbs[] = {
863 { "list", LESS, 1, list_links },
864 { "status", MORE, 1, link_status },
865 };
866
867 int left;
868 unsigned i;
869
870 assert(argc >= 0);
871 assert(argv);
872
873 left = argc - optind;
874
875 if (left <= 0)
876 /* Special rule: no arguments means "list" */
877 i = 0;
878 else {
879 if (streq(argv[optind], "help")) {
880 help();
881 return 0;
882 }
883
884 for (i = 0; i < ELEMENTSOF(verbs); i++)
885 if (streq(argv[optind], verbs[i].verb))
886 break;
887
888 if (i >= ELEMENTSOF(verbs)) {
889 log_error("Unknown operation %s", argv[optind]);
890 return -EINVAL;
891 }
892 }
893
894 switch (verbs[i].argc_cmp) {
895
896 case EQUAL:
897 if (left != verbs[i].argc) {
898 log_error("Invalid number of arguments.");
899 return -EINVAL;
900 }
901
902 break;
903
904 case MORE:
905 if (left < verbs[i].argc) {
906 log_error("Too few arguments.");
907 return -EINVAL;
908 }
909
910 break;
911
912 case LESS:
913 if (left > verbs[i].argc) {
914 log_error("Too many arguments.");
915 return -EINVAL;
916 }
917
918 break;
919
920 default:
921 assert_not_reached("Unknown comparison operator.");
922 }
923
924 return verbs[i].dispatch(argv + optind, left);
925 }
926
927 int main(int argc, char* argv[]) {
928 int r;
929
930 log_parse_environment();
931 log_open();
932
933 r = parse_argv(argc, argv);
934 if (r <= 0)
935 goto finish;
936
937 r = networkctl_main(argc, argv);
938
939 finish:
940 pager_close();
941
942 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
943 }