]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
networkctl: show interface names next to IP addresses if we dump adresses from all...
[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
584 link_get_type_string(iftype, d, &t);
585
586 if (d) {
587 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
588 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
589 path = udev_device_get_property_value(d, "ID_PATH");
590
591 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
592 if (!vendor)
593 vendor = udev_device_get_property_value(d, "ID_VENDOR");
594
595 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
596 if (!model)
597 model = udev_device_get_property_value(d, "ID_MODEL");
598 }
599
600 sd_network_link_get_network_file(ifindex, &network);
601
602 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
603
604 printf(" Link File: %s\n"
605 "Network File: %s\n"
606 " Type: %s\n"
607 " State: %s%s%s (%s%s%s)\n",
608 strna(link),
609 strna(network),
610 strna(t),
611 on_color_operational, strna(operational_state), off_color_operational,
612 on_color_setup, strna(setup_state), off_color_setup);
613
614 if (path)
615 printf(" Path: %s\n", path);
616 if (driver)
617 printf(" Driver: %s\n", driver);
618 if (vendor)
619 printf(" Vendor: %s\n", vendor);
620 if (model)
621 printf(" Model: %s\n", model);
622
623 if (have_mac) {
624 _cleanup_free_ char *description = NULL;
625 char ea[ETHER_ADDR_TO_STRING_MAX];
626
627 ieee_oui(hwdb, &e, &description);
628
629 if (description)
630 printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description);
631 else
632 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
633 }
634
635 if (mtu > 0)
636 printf(" MTU: %u\n", mtu);
637
638 dump_addresses(rtnl, " Address: ", ifindex);
639 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
640
641 if (!strv_isempty(dns))
642 dump_list(" DNS: ", dns);
643 if (!strv_isempty(domains))
644 dump_list(" Domain: ", domains);
645 if (!strv_isempty(ntp))
646 dump_list(" NTP: ", ntp);
647
648 return 0;
649 }
650
651 static int link_status(char **args, unsigned n) {
652 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
653 _cleanup_udev_unref_ struct udev *udev = NULL;
654 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
655 char **name;
656 int r;
657
658 r = sd_rtnl_open(&rtnl, 0);
659 if (r < 0)
660 return log_error_errno(r, "Failed to connect to netlink: %m");
661
662 udev = udev_new();
663 if (!udev)
664 return log_error_errno(errno, "Failed to connect to udev: %m");
665
666 hwdb = udev_hwdb_new(udev);
667 if (!hwdb)
668 log_debug_errno(errno, "Failed to open hardware database: %m");
669
670 if (n <= 1 && !arg_all) {
671 _cleanup_free_ char *operational_state = NULL;
672 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
673 _cleanup_free_ struct local_address *addresses = NULL;
674 const char *on_color_operational, *off_color_operational;
675
676 sd_network_get_operational_state(&operational_state);
677 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
678
679 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
680
681 dump_addresses(rtnl, " Address: ", 0);
682 dump_gateways(rtnl, hwdb, " Gateway: ", 0);
683
684 sd_network_get_dns(&dns);
685 if (!strv_isempty(dns))
686 dump_list(" DNS: ", dns);
687
688 sd_network_get_domains(&domains);
689 if (!strv_isempty(domains))
690 dump_list(" Domain: ", domains);
691
692 sd_network_get_ntp(&ntp);
693 if (!strv_isempty(ntp))
694 dump_list(" NTP: ", ntp);
695
696 return 0;
697 }
698
699 pager_open_if_enabled();
700
701 if (arg_all) {
702 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
703 _cleanup_free_ LinkInfo *links = NULL;
704 int c, i;
705
706 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
707 if (r < 0)
708 return rtnl_log_create_error(r);
709
710 r = sd_rtnl_message_request_dump(req, true);
711 if (r < 0)
712 return rtnl_log_create_error(r);
713
714 r = sd_rtnl_call(rtnl, req, 0, &reply);
715 if (r < 0)
716 return log_error_errno(r, "Failed to enumerate links: %m");
717
718 c = decode_and_sort_links(reply, &links);
719 if (c < 0)
720 return rtnl_log_parse_error(c);
721
722 for (i = 0; i < c; i++) {
723 if (i > 0)
724 fputc('\n', stdout);
725
726 link_status_one(rtnl, udev, hwdb, links[i].name);
727 }
728 } else {
729 STRV_FOREACH(name, args + 1) {
730 if (name != args+1)
731 fputc('\n', stdout);
732
733 link_status_one(rtnl, udev, hwdb, *name);
734 }
735 }
736
737 return 0;
738 }
739
740 static void help(void) {
741 printf("%s [OPTIONS...]\n\n"
742 "Query and control the networking subsystem.\n\n"
743 " -h --help Show this help\n"
744 " --version Show package version\n"
745 " --no-pager Do not pipe output into a pager\n"
746 " --no-legend Do not show the headers and footers\n"
747 " -a --all Show status for all links\n\n"
748 "Commands:\n"
749 " list List links\n"
750 " status LINK Show link status\n"
751 , program_invocation_short_name);
752 }
753
754 static int parse_argv(int argc, char *argv[]) {
755
756 enum {
757 ARG_VERSION = 0x100,
758 ARG_NO_PAGER,
759 ARG_NO_LEGEND,
760 };
761
762 static const struct option options[] = {
763 { "help", no_argument, NULL, 'h' },
764 { "version", no_argument, NULL, ARG_VERSION },
765 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
766 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
767 { "all", no_argument, NULL, 'a' },
768 {}
769 };
770
771 int c;
772
773 assert(argc >= 0);
774 assert(argv);
775
776 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
777
778 switch (c) {
779
780 case 'h':
781 help();
782 return 0;
783
784 case ARG_VERSION:
785 puts(PACKAGE_STRING);
786 puts(SYSTEMD_FEATURES);
787 return 0;
788
789 case ARG_NO_PAGER:
790 arg_no_pager = true;
791 break;
792
793 case ARG_NO_LEGEND:
794 arg_legend = false;
795 break;
796
797 case 'a':
798 arg_all = true;
799 break;
800
801 case '?':
802 return -EINVAL;
803
804 default:
805 assert_not_reached("Unhandled option");
806 }
807 }
808
809 return 1;
810 }
811
812 static int networkctl_main(int argc, char *argv[]) {
813
814 static const struct {
815 const char* verb;
816 const enum {
817 MORE,
818 LESS,
819 EQUAL
820 } argc_cmp;
821 const int argc;
822 int (* const dispatch)(char **args, unsigned n);
823 } verbs[] = {
824 { "list", LESS, 1, list_links },
825 { "status", MORE, 1, link_status },
826 };
827
828 int left;
829 unsigned i;
830
831 assert(argc >= 0);
832 assert(argv);
833
834 left = argc - optind;
835
836 if (left <= 0)
837 /* Special rule: no arguments means "list" */
838 i = 0;
839 else {
840 if (streq(argv[optind], "help")) {
841 help();
842 return 0;
843 }
844
845 for (i = 0; i < ELEMENTSOF(verbs); i++)
846 if (streq(argv[optind], verbs[i].verb))
847 break;
848
849 if (i >= ELEMENTSOF(verbs)) {
850 log_error("Unknown operation %s", argv[optind]);
851 return -EINVAL;
852 }
853 }
854
855 switch (verbs[i].argc_cmp) {
856
857 case EQUAL:
858 if (left != verbs[i].argc) {
859 log_error("Invalid number of arguments.");
860 return -EINVAL;
861 }
862
863 break;
864
865 case MORE:
866 if (left < verbs[i].argc) {
867 log_error("Too few arguments.");
868 return -EINVAL;
869 }
870
871 break;
872
873 case LESS:
874 if (left > verbs[i].argc) {
875 log_error("Too many arguments.");
876 return -EINVAL;
877 }
878
879 break;
880
881 default:
882 assert_not_reached("Unknown comparison operator.");
883 }
884
885 return verbs[i].dispatch(argv + optind, left);
886 }
887
888 int main(int argc, char* argv[]) {
889 int r;
890
891 log_parse_environment();
892 log_open();
893
894 r = parse_argv(argc, argv);
895 if (r <= 0)
896 goto finish;
897
898 r = networkctl_main(argc, argv);
899
900 finish:
901 pager_close();
902
903 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
904 }