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