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