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