]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
networkctl: use the shared functions for enumerating the local gateways
[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_free_ struct local_address *local = NULL;
379 int r, n, i;
380
381 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
382 if (n < 0)
383 return n;
384
385 for (i = 0; i < n; i++) {
386 _cleanup_free_ char *gateway = NULL, *description = NULL;
387
388 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
389 if (r < 0)
390 return r;
391
392 r = get_gateway_description(rtnl, hwdb, ifindex, local[i].family, &local[i].address, &description);
393 if (r < 0)
394 log_debug_errno(r, "Could not get description of gateway: %m");
395
396 if (description)
397 printf("%*s%s (%s)\n",
398 (int) strlen(prefix),
399 i == 0 ? prefix : "",
400 gateway, description);
401 else
402 printf("%*s%s\n",
403 (int) strlen(prefix),
404 i == 0 ? prefix : "",
405 gateway);
406 }
407
408 return 0;
409 }
410
411 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
412 _cleanup_free_ struct local_address *local = NULL;
413 int r, n, i;
414
415 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
416 if (n < 0)
417 return n;
418
419 for (i = 0; i < n; i++) {
420 _cleanup_free_ char *pretty = NULL;
421
422 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
423 if (r < 0)
424 return r;
425
426 printf("%*s%s\n",
427 (int) strlen(prefix),
428 i == 0 ? prefix : "",
429 pretty);
430 }
431
432 return 0;
433 }
434
435 static void dump_list(const char *prefix, char **l) {
436 char **i;
437
438 STRV_FOREACH(i, l) {
439 printf("%*s%s\n",
440 (int) strlen(prefix),
441 i == l ? prefix : "",
442 *i);
443 }
444 }
445
446 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
447 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
448 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
449 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
450 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
451 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
452 char devid[2 + DECIMAL_STR_MAX(int)];
453 _cleanup_free_ char *t = NULL, *network = NULL;
454 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
455 const char *on_color_operational, *off_color_operational,
456 *on_color_setup, *off_color_setup;
457 struct ether_addr e;
458 unsigned iftype;
459 int r, ifindex;
460 bool have_mac;
461 uint32_t mtu;
462
463 assert(rtnl);
464 assert(udev);
465 assert(name);
466
467 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
468 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
469 else {
470 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
471 if (r < 0)
472 return rtnl_log_create_error(r);
473
474 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
475 }
476
477 if (r < 0)
478 return rtnl_log_create_error(r);
479
480 r = sd_rtnl_call(rtnl, req, 0, &reply);
481 if (r < 0)
482 return log_error_errno(r, "Failed to query link: %m");
483
484 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
485 if (r < 0)
486 return rtnl_log_parse_error(r);
487
488 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
489 if (r < 0)
490 return rtnl_log_parse_error(r);
491
492 r = sd_rtnl_message_link_get_type(reply, &iftype);
493 if (r < 0)
494 return rtnl_log_parse_error(r);
495
496 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
497
498 if (have_mac) {
499 const uint8_t *p;
500 bool all_zeroes = true;
501
502 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
503 if (*p != 0) {
504 all_zeroes = false;
505 break;
506 }
507
508 if (all_zeroes)
509 have_mac = false;
510 }
511
512 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
513
514 sd_network_link_get_operational_state(ifindex, &operational_state);
515 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
516
517 sd_network_link_get_setup_state(ifindex, &setup_state);
518 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
519
520 sd_network_link_get_dns(ifindex, &dns);
521 sd_network_link_get_ntp(ifindex, &ntp);
522 sd_network_link_get_domains(ifindex, &domains);
523 r = sd_network_link_get_wildcard_domain(ifindex);
524 if (r > 0) {
525 char *wildcard;
526
527 wildcard = strdup("*");
528 if (!wildcard)
529 return log_oom();
530
531 if (strv_consume(&domains, wildcard) < 0)
532 return log_oom();
533 }
534
535 sprintf(devid, "n%i", ifindex);
536 d = udev_device_new_from_device_id(udev, devid);
537
538 link_get_type_string(iftype, d, &t);
539
540 if (d) {
541 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
542 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
543 path = udev_device_get_property_value(d, "ID_PATH");
544
545 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
546 if (!vendor)
547 vendor = udev_device_get_property_value(d, "ID_VENDOR");
548
549 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
550 if (!model)
551 model = udev_device_get_property_value(d, "ID_MODEL");
552 }
553
554 sd_network_link_get_network_file(ifindex, &network);
555
556 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
557
558 printf(" Link File: %s\n"
559 "Network File: %s\n"
560 " Type: %s\n"
561 " State: %s%s%s (%s%s%s)\n",
562 strna(link),
563 strna(network),
564 strna(t),
565 on_color_operational, strna(operational_state), off_color_operational,
566 on_color_setup, strna(setup_state), off_color_setup);
567
568 if (path)
569 printf(" Path: %s\n", path);
570 if (driver)
571 printf(" Driver: %s\n", driver);
572 if (vendor)
573 printf(" Vendor: %s\n", vendor);
574 if (model)
575 printf(" Model: %s\n", model);
576
577 if (have_mac) {
578 char ea[ETHER_ADDR_TO_STRING_MAX];
579 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
580 }
581
582 if (mtu > 0)
583 printf(" MTU: %u\n", mtu);
584
585 hwdb = udev_hwdb_new(udev);
586
587 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
588
589 dump_addresses(rtnl, " Address: ", ifindex);
590
591 if (!strv_isempty(dns))
592 dump_list(" DNS: ", dns);
593 if (!strv_isempty(domains))
594 dump_list(" Domain: ", domains);
595 if (!strv_isempty(ntp))
596 dump_list(" NTP: ", ntp);
597
598 return 0;
599 }
600
601 static int link_status(char **args, unsigned n) {
602 _cleanup_udev_unref_ struct udev *udev = NULL;
603 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
604 char **name;
605 int r;
606
607 r = sd_rtnl_open(&rtnl, 0);
608 if (r < 0)
609 return log_error_errno(r, "Failed to connect to netlink: %m");
610
611 udev = udev_new();
612 if (!udev)
613 return log_error_errno(errno, "Failed to connect to udev: %m");
614
615 if (n <= 1 && !arg_all) {
616 _cleanup_free_ char *operational_state = NULL;
617 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
618 _cleanup_free_ struct local_address *addresses = NULL;
619 const char *on_color_operational, *off_color_operational;
620 int i, c;
621
622 sd_network_get_operational_state(&operational_state);
623 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
624
625 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
626
627 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
628 for (i = 0; i < c; i++) {
629 _cleanup_free_ char *pretty = NULL;
630
631 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
632 if (r < 0)
633 return log_oom();
634
635 printf("%13s %s\n",
636 i > 0 ? "" : "Address:", pretty);
637 }
638
639 sd_network_get_dns(&dns);
640 if (!strv_isempty(dns))
641 dump_list(" DNS: ", dns);
642
643 sd_network_get_domains(&domains);
644 if (!strv_isempty(domains))
645 dump_list(" Domain: ", domains);
646
647 sd_network_get_ntp(&ntp);
648 if (!strv_isempty(ntp))
649 dump_list(" NTP: ", ntp);
650
651 return 0;
652 }
653
654 pager_open_if_enabled();
655
656 if (arg_all) {
657 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
658 _cleanup_free_ LinkInfo *links = NULL;
659 int c, i;
660
661 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
662 if (r < 0)
663 return rtnl_log_create_error(r);
664
665 r = sd_rtnl_message_request_dump(req, true);
666 if (r < 0)
667 return rtnl_log_create_error(r);
668
669 r = sd_rtnl_call(rtnl, req, 0, &reply);
670 if (r < 0)
671 return log_error_errno(r, "Failed to enumerate links: %m");
672
673 c = decode_and_sort_links(reply, &links);
674 if (c < 0)
675 return rtnl_log_parse_error(c);
676
677 for (i = 0; i < c; i++) {
678 if (i > 0)
679 fputc('\n', stdout);
680
681 link_status_one(rtnl, udev, links[i].name);
682 }
683 }
684
685 STRV_FOREACH(name, args + 1) {
686 if (name != args+1)
687 fputc('\n', stdout);
688
689 link_status_one(rtnl, udev, *name);
690 }
691
692 return 0;
693 }
694
695 static void help(void) {
696 printf("%s [OPTIONS...]\n\n"
697 "Query and control the networking subsystem.\n\n"
698 " -h --help Show this help\n"
699 " --version Show package version\n"
700 " --no-pager Do not pipe output into a pager\n"
701 " --no-legend Do not show the headers and footers\n"
702 " -a --all Show status for all links\n\n"
703 "Commands:\n"
704 " list List links\n"
705 " status LINK Show link status\n"
706 , program_invocation_short_name);
707 }
708
709 static int parse_argv(int argc, char *argv[]) {
710
711 enum {
712 ARG_VERSION = 0x100,
713 ARG_NO_PAGER,
714 ARG_NO_LEGEND,
715 };
716
717 static const struct option options[] = {
718 { "help", no_argument, NULL, 'h' },
719 { "version", no_argument, NULL, ARG_VERSION },
720 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
721 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
722 { "all", no_argument, NULL, 'a' },
723 {}
724 };
725
726 int c;
727
728 assert(argc >= 0);
729 assert(argv);
730
731 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
732
733 switch (c) {
734
735 case 'h':
736 help();
737 return 0;
738
739 case ARG_VERSION:
740 puts(PACKAGE_STRING);
741 puts(SYSTEMD_FEATURES);
742 return 0;
743
744 case ARG_NO_PAGER:
745 arg_no_pager = true;
746 break;
747
748 case ARG_NO_LEGEND:
749 arg_legend = false;
750 break;
751
752 case 'a':
753 arg_all = true;
754 break;
755
756 case '?':
757 return -EINVAL;
758
759 default:
760 assert_not_reached("Unhandled option");
761 }
762 }
763
764 return 1;
765 }
766
767 static int networkctl_main(int argc, char *argv[]) {
768
769 static const struct {
770 const char* verb;
771 const enum {
772 MORE,
773 LESS,
774 EQUAL
775 } argc_cmp;
776 const int argc;
777 int (* const dispatch)(char **args, unsigned n);
778 } verbs[] = {
779 { "list", LESS, 1, list_links },
780 { "status", MORE, 1, link_status },
781 };
782
783 int left;
784 unsigned i;
785
786 assert(argc >= 0);
787 assert(argv);
788
789 left = argc - optind;
790
791 if (left <= 0)
792 /* Special rule: no arguments means "list" */
793 i = 0;
794 else {
795 if (streq(argv[optind], "help")) {
796 help();
797 return 0;
798 }
799
800 for (i = 0; i < ELEMENTSOF(verbs); i++)
801 if (streq(argv[optind], verbs[i].verb))
802 break;
803
804 if (i >= ELEMENTSOF(verbs)) {
805 log_error("Unknown operation %s", argv[optind]);
806 return -EINVAL;
807 }
808 }
809
810 switch (verbs[i].argc_cmp) {
811
812 case EQUAL:
813 if (left != verbs[i].argc) {
814 log_error("Invalid number of arguments.");
815 return -EINVAL;
816 }
817
818 break;
819
820 case MORE:
821 if (left < verbs[i].argc) {
822 log_error("Too few arguments.");
823 return -EINVAL;
824 }
825
826 break;
827
828 case LESS:
829 if (left > verbs[i].argc) {
830 log_error("Too many arguments.");
831 return -EINVAL;
832 }
833
834 break;
835
836 default:
837 assert_not_reached("Unknown comparison operator.");
838 }
839
840 return verbs[i].dispatch(argv + optind, left);
841 }
842
843 int main(int argc, char* argv[]) {
844 int r;
845
846 log_parse_environment();
847 log_open();
848
849 r = parse_argv(argc, argv);
850 if (r <= 0)
851 goto finish;
852
853 r = networkctl_main(argc, argv);
854
855 finish:
856 pager_close();
857
858 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
859 }