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