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