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