chiark / gitweb /
networkd: add FDB support
[elogind.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 }