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