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