chiark / gitweb /
shared: add format helpers for printing MAC addresses
[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         /* skip commonly misused 00:00:00 (Xerox) prefix */
258         if (memcmp(mac, "\0\0\0", 3) == 0)
259                 return -EINVAL;
260
261         snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
262
263         udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, str, 0))
264                 if (strcmp(udev_list_entry_get_name(entry), "ID_OUI_FROM_DATABASE") == 0) {
265                         description = strdup(udev_list_entry_get_value(entry));
266                         if (!description)
267                                 return -ENOMEM;
268
269                         *ret = description;
270                         return 0;
271                 }
272
273         return -ENODATA;
274 }
275
276 static int get_gateway_description(sd_rtnl *rtnl, struct udev_hwdb *hwdb, int ifindex, int family,
277                                    union in_addr_union *gateway, char **gateway_description) {
278         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
279         sd_rtnl_message *m;
280         int r;
281
282         assert(rtnl);
283         assert(ifindex >= 0);
284         assert(family == AF_INET || family == AF_INET6);
285         assert(gateway);
286         assert(gateway_description);
287
288         r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family);
289         if (r < 0)
290                 return r;
291
292         r = sd_rtnl_message_request_dump(req, true);
293         if (r < 0)
294                 return r;
295
296         r = sd_rtnl_call(rtnl, req, 0, &reply);
297         if (r < 0)
298                 return r;
299
300         for (m = reply; m; m = sd_rtnl_message_next(m)) {
301                 union in_addr_union gw = {};
302                 struct ether_addr mac = {};
303                 uint16_t type;
304                 int ifi, fam;
305
306                 r = sd_rtnl_message_get_errno(m);
307                 if (r < 0) {
308                         log_error_errno(r, "got error: %m");
309                         continue;
310                 }
311
312                 r = sd_rtnl_message_get_type(m, &type);
313                 if (r < 0) {
314                         log_error_errno(r, "could not get type: %m");
315                         continue;
316                 }
317
318                 if (type != RTM_NEWNEIGH) {
319                         log_error("type is not RTM_NEWNEIGH");
320                         continue;
321                 }
322
323                 r = sd_rtnl_message_neigh_get_family(m, &fam);
324                 if (r < 0) {
325                         log_error_errno(r, "could not get family: %m");
326                         continue;
327                 }
328
329                 if (fam != family) {
330                         log_error("family is not correct");
331                         continue;
332                 }
333
334                 r = sd_rtnl_message_neigh_get_ifindex(m, &ifi);
335                 if (r < 0) {
336                         log_error_errno(r, "colud not get ifindex: %m");
337                         continue;
338                 }
339
340                 if (ifindex > 0 && ifi != ifindex)
341                         continue;
342
343                 switch (fam) {
344                 case AF_INET:
345                         r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in);
346                         if (r < 0)
347                                 continue;
348
349                         break;
350                 case AF_INET6:
351                         r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6);
352                         if (r < 0)
353                                 continue;
354
355                         break;
356                 default:
357                         continue;
358                 }
359
360                 if (!in_addr_equal(fam, &gw, gateway))
361                         continue;
362
363                 r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac);
364                 if (r < 0)
365                         continue;
366
367                 r = ieee_oui(hwdb, &mac, gateway_description);
368                 if (r < 0)
369                         continue;
370
371                 return 0;
372         }
373
374         return -ENODATA;
375 }
376
377 static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) {
378         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
379         sd_rtnl_message *m;
380         bool first = true;
381         int r;
382
383         assert(rtnl);
384         assert(ifindex >= 0);
385
386         r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, AF_UNSPEC, RTPROT_UNSPEC);
387         if (r < 0)
388                 return r;
389
390         r = sd_rtnl_message_request_dump(req, true);
391         if (r < 0)
392                 return r;
393
394         r = sd_rtnl_call(rtnl, req, 0, &reply);
395         if (r < 0)
396                 return r;
397
398         for (m = reply; m; m = sd_rtnl_message_next(m)) {
399                 _cleanup_free_ char *gateway = NULL, *gateway_description = NULL;
400                 union in_addr_union gw = {};
401                 uint16_t type;
402                 uint32_t ifi;
403                 int family;
404
405                 r = sd_rtnl_message_get_errno(m);
406                 if (r < 0) {
407                         log_error_errno(r, "got error: %m");
408                         continue;
409                 }
410
411                 r = sd_rtnl_message_get_type(m, &type);
412                 if (r < 0) {
413                         log_error_errno(r, "could not get type: %m");
414                         continue;
415                 }
416
417                 if (type != RTM_NEWROUTE) {
418                         log_error("type is not RTM_NEWROUTE");
419                         continue;
420                 }
421
422                 r = sd_rtnl_message_route_get_family(m, &family);
423                 if (r < 0) {
424                         log_error_errno(r, "could not get family: %m");
425                         continue;
426                 }
427
428                 r = sd_rtnl_message_read_u32(m, RTA_OIF, &ifi);
429                 if (r < 0) {
430                         log_error_errno(r, "colud not get RTA_OIF: %m");
431                         continue;
432                 }
433
434                 if (ifindex > 0 && ifi != (unsigned) ifindex)
435                         continue;
436
437                 switch (family) {
438                 case AF_INET:
439                         r = sd_rtnl_message_read_in_addr(m, RTA_GATEWAY, &gw.in);
440                         if (r < 0)
441                                 continue;
442
443                         r = sd_rtnl_message_read_in_addr(m, RTA_DST, NULL);
444                         if (r >= 0)
445                                 continue;
446
447                         r = sd_rtnl_message_read_in_addr(m, RTA_SRC, NULL);
448                         if (r >= 0)
449                                 continue;
450
451                         break;
452                 case AF_INET6:
453                         r = sd_rtnl_message_read_in6_addr(m, RTA_GATEWAY, &gw.in6);
454                         if (r < 0)
455                                 continue;
456
457                         r = sd_rtnl_message_read_in6_addr(m, RTA_DST, NULL);
458                         if (r >= 0)
459                                 continue;
460
461                         r = sd_rtnl_message_read_in6_addr(m, RTA_SRC, NULL);
462                         if (r >= 0)
463                                 continue;
464
465                         break;
466                 default:
467                         continue;
468                 }
469
470                 r = in_addr_to_string(family, &gw, &gateway);
471                 if (r < 0)
472                         continue;
473
474                 r = get_gateway_description(rtnl, hwdb, ifi, family, &gw, &gateway_description);
475                 if (r < 0)
476                         log_debug("could not get description of gateway: %s", strerror(-r));
477
478                 if (gateway_description)
479                         printf("%*s%s (%s)\n",
480                                (int) strlen(prefix),
481                                first ? prefix : "",
482                                gateway, gateway_description);
483                 else
484                         printf("%*s%s\n",
485                                (int) strlen(prefix),
486                                first ? prefix : "",
487                                gateway);
488
489                 first = false;
490         }
491
492         return 0;
493 }
494
495 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
496         _cleanup_free_ struct local_address *local = NULL;
497         int r, n, i;
498
499         n = local_addresses(rtnl, ifindex, &local);
500         if (n < 0)
501                 return n;
502
503         for (i = 0; i < n; i++) {
504                 _cleanup_free_ char *pretty = NULL;
505
506                 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
507                 if (r < 0)
508                         return r;
509
510                 printf("%*s%s\n",
511                        (int) strlen(prefix),
512                        i == 0 ? prefix : "",
513                        pretty);
514         }
515
516         return 0;
517 }
518
519 static void dump_list(const char *prefix, char **l) {
520         char **i;
521
522         STRV_FOREACH(i, l) {
523                 printf("%*s%s\n",
524                        (int) strlen(prefix),
525                        i == l ? prefix : "",
526                        *i);
527         }
528 }
529
530 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
531         _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
532         _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *gateway = NULL, *gateway_description = NULL,
533                             *gateway6 = NULL, *gateway6_description = NULL;
534         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
535         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
536         _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
537         char devid[2 + DECIMAL_STR_MAX(int)];
538         _cleanup_free_ char *t = NULL, *network = NULL;
539         const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
540         const char *on_color_operational, *off_color_operational,
541                    *on_color_setup, *off_color_setup;
542         struct ether_addr e;
543         unsigned iftype;
544         int r, ifindex;
545         bool have_mac;
546         uint32_t mtu;
547
548         assert(rtnl);
549         assert(udev);
550         assert(name);
551
552         if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
553                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
554         else {
555                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
556                 if (r < 0)
557                         return rtnl_log_create_error(r);
558
559                 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
560         }
561
562         if (r < 0)
563                 return rtnl_log_create_error(r);
564
565         r = sd_rtnl_call(rtnl, req, 0, &reply);
566         if (r < 0)
567                 return log_error_errno(r, "Failed to query link: %m");
568
569         r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
570         if (r < 0)
571                 return rtnl_log_parse_error(r);
572
573         r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
574         if (r < 0)
575                 return rtnl_log_parse_error(r);
576
577         r = sd_rtnl_message_link_get_type(reply, &iftype);
578         if (r < 0)
579                 return rtnl_log_parse_error(r);
580
581         have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
582
583         if (have_mac) {
584                 const uint8_t *p;
585                 bool all_zeroes = true;
586
587                 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
588                         if (*p != 0) {
589                                 all_zeroes = false;
590                                 break;
591                         }
592
593                 if (all_zeroes)
594                         have_mac = false;
595         }
596
597         sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
598
599         sd_network_link_get_operational_state(ifindex, &operational_state);
600         operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
601
602         sd_network_link_get_setup_state(ifindex, &setup_state);
603         setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
604
605         sd_network_link_get_dns(ifindex, &dns);
606         sd_network_link_get_ntp(ifindex, &ntp);
607         sd_network_link_get_domains(ifindex, &domains);
608         r = sd_network_link_get_wildcard_domain(ifindex);
609         if (r > 0) {
610                 char *wildcard;
611
612                 wildcard = strdup("*");
613                 if (!wildcard)
614                         return log_oom();
615
616                 if (strv_consume(&domains, wildcard) < 0)
617                         return log_oom();
618         }
619
620         sprintf(devid, "n%i", ifindex);
621         d = udev_device_new_from_device_id(udev, devid);
622
623         link_get_type_string(iftype, d, &t);
624
625         if (d) {
626                 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
627                 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
628                 path = udev_device_get_property_value(d, "ID_PATH");
629
630                 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
631                 if (!vendor)
632                         vendor = udev_device_get_property_value(d, "ID_VENDOR");
633
634                 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
635                 if (!model)
636                         model = udev_device_get_property_value(d, "ID_MODEL");
637         }
638
639         sd_network_link_get_network_file(ifindex, &network);
640
641         printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
642
643         printf("   Link File: %s\n"
644                "Network File: %s\n"
645                "        Type: %s\n"
646                "       State: %s%s%s (%s%s%s)\n",
647                strna(link),
648                strna(network),
649                strna(t),
650                on_color_operational, strna(operational_state), off_color_operational,
651                on_color_setup, strna(setup_state), off_color_setup);
652
653         if (path)
654                 printf("        Path: %s\n", path);
655         if (driver)
656                 printf("      Driver: %s\n", driver);
657         if (vendor)
658                 printf("      Vendor: %s\n", vendor);
659         if (model)
660                 printf("       Model: %s\n", model);
661
662         if (have_mac) {
663                 char ea[ETHER_ADDR_TO_STRING_MAX];
664                 printf("  HW Address: %s\n", ether_addr_to_string(&e, ea));
665         }
666
667         if (mtu > 0)
668                 printf("         MTU: %u\n", mtu);
669
670         hwdb = udev_hwdb_new(udev);
671
672         dump_gateways(rtnl, hwdb, "     Gateway: ", ifindex);
673
674         dump_addresses(rtnl, "     Address: ", ifindex);
675
676         if (!strv_isempty(dns))
677                 dump_list("         DNS: ", dns);
678         if (!strv_isempty(domains))
679                 dump_list("      Domain: ", domains);
680         if (!strv_isempty(ntp))
681                 dump_list("         NTP: ", ntp);
682
683         return 0;
684 }
685
686 static int link_status(char **args, unsigned n) {
687         _cleanup_udev_unref_ struct udev *udev = NULL;
688         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
689         char **name;
690         int r;
691
692         r = sd_rtnl_open(&rtnl, 0);
693         if (r < 0)
694                 return log_error_errno(r, "Failed to connect to netlink: %m");
695
696         udev = udev_new();
697         if (!udev)
698                 return log_error_errno(errno, "Failed to connect to udev: %m");
699
700         if (n <= 1 && !arg_all) {
701                 _cleanup_free_ char *operational_state = NULL;
702                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
703                 _cleanup_free_ struct local_address *addresses = NULL;
704                 const char *on_color_operational, *off_color_operational;
705                 int i, c;
706
707                 sd_network_get_operational_state(&operational_state);
708                 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
709
710                 printf("       State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
711
712                 c = local_addresses(rtnl, 0, &addresses);
713                 for (i = 0; i < c; i++) {
714                         _cleanup_free_ char *pretty = NULL;
715
716                         r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
717                         if (r < 0)
718                                 return log_oom();
719
720                         printf("%13s %s\n",
721                                i > 0 ? "" : "Address:", pretty);
722                 }
723
724                 sd_network_get_dns(&dns);
725                 if (!strv_isempty(dns))
726                         dump_list("         DNS: ", dns);
727
728                 sd_network_get_domains(&domains);
729                 if (!strv_isempty(domains))
730                         dump_list("      Domain: ", domains);
731
732                 sd_network_get_ntp(&ntp);
733                 if (!strv_isempty(ntp))
734                         dump_list("         NTP: ", ntp);
735
736                 return 0;
737         }
738
739         pager_open_if_enabled();
740
741         if (arg_all) {
742                 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
743                 _cleanup_free_ LinkInfo *links = NULL;
744                 int c, i;
745
746                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
747                 if (r < 0)
748                         return rtnl_log_create_error(r);
749
750                 r = sd_rtnl_message_request_dump(req, true);
751                 if (r < 0)
752                         return rtnl_log_create_error(r);
753
754                 r = sd_rtnl_call(rtnl, req, 0, &reply);
755                 if (r < 0)
756                         return log_error_errno(r, "Failed to enumerate links: %m");
757
758                 c = decode_and_sort_links(reply, &links);
759                 if (c < 0)
760                         return rtnl_log_parse_error(c);
761
762                 for (i = 0; i < c; i++) {
763                         if (i > 0)
764                                 fputc('\n', stdout);
765
766                         link_status_one(rtnl, udev, links[i].name);
767                 }
768         }
769
770         STRV_FOREACH(name, args + 1) {
771                 if (name != args+1)
772                         fputc('\n', stdout);
773
774                 link_status_one(rtnl, udev, *name);
775         }
776
777         return 0;
778 }
779
780 static void help(void) {
781         printf("%s [OPTIONS...]\n\n"
782                "Query and control the networking subsystem.\n\n"
783                "  -h --help             Show this help\n"
784                "     --version          Show package version\n"
785                "     --no-pager         Do not pipe output into a pager\n"
786                "     --no-legend        Do not show the headers and footers\n"
787                "  -a --all              Show status for all links\n\n"
788                "Commands:\n"
789                "  list                  List links\n"
790                "  status LINK           Show link status\n"
791                , program_invocation_short_name);
792 }
793
794 static int parse_argv(int argc, char *argv[]) {
795
796         enum {
797                 ARG_VERSION = 0x100,
798                 ARG_NO_PAGER,
799                 ARG_NO_LEGEND,
800         };
801
802         static const struct option options[] = {
803                 { "help",      no_argument,       NULL, 'h'           },
804                 { "version",   no_argument,       NULL, ARG_VERSION   },
805                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
806                 { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
807                 { "all",       no_argument,       NULL, 'a'           },
808                 {}
809         };
810
811         int c;
812
813         assert(argc >= 0);
814         assert(argv);
815
816         while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
817
818                 switch (c) {
819
820                 case 'h':
821                         help();
822                         return 0;
823
824                 case ARG_VERSION:
825                         puts(PACKAGE_STRING);
826                         puts(SYSTEMD_FEATURES);
827                         return 0;
828
829                 case ARG_NO_PAGER:
830                         arg_no_pager = true;
831                         break;
832
833                 case ARG_NO_LEGEND:
834                         arg_legend = false;
835                         break;
836
837                 case 'a':
838                         arg_all = true;
839                         break;
840
841                 case '?':
842                         return -EINVAL;
843
844                 default:
845                         assert_not_reached("Unhandled option");
846                 }
847         }
848
849         return 1;
850 }
851
852 static int networkctl_main(int argc, char *argv[]) {
853
854         static const struct {
855                 const char* verb;
856                 const enum {
857                         MORE,
858                         LESS,
859                         EQUAL
860                 } argc_cmp;
861                 const int argc;
862                 int (* const dispatch)(char **args, unsigned n);
863         } verbs[] = {
864                 { "list",   LESS, 1, list_links  },
865                 { "status", MORE, 1, link_status },
866         };
867
868         int left;
869         unsigned i;
870
871         assert(argc >= 0);
872         assert(argv);
873
874         left = argc - optind;
875
876         if (left <= 0)
877                 /* Special rule: no arguments means "list" */
878                 i = 0;
879         else {
880                 if (streq(argv[optind], "help")) {
881                         help();
882                         return 0;
883                 }
884
885                 for (i = 0; i < ELEMENTSOF(verbs); i++)
886                         if (streq(argv[optind], verbs[i].verb))
887                                 break;
888
889                 if (i >= ELEMENTSOF(verbs)) {
890                         log_error("Unknown operation %s", argv[optind]);
891                         return -EINVAL;
892                 }
893         }
894
895         switch (verbs[i].argc_cmp) {
896
897         case EQUAL:
898                 if (left != verbs[i].argc) {
899                         log_error("Invalid number of arguments.");
900                         return -EINVAL;
901                 }
902
903                 break;
904
905         case MORE:
906                 if (left < verbs[i].argc) {
907                         log_error("Too few arguments.");
908                         return -EINVAL;
909                 }
910
911                 break;
912
913         case LESS:
914                 if (left > verbs[i].argc) {
915                         log_error("Too many arguments.");
916                         return -EINVAL;
917                 }
918
919                 break;
920
921         default:
922                 assert_not_reached("Unknown comparison operator.");
923         }
924
925         return verbs[i].dispatch(argv + optind, left);
926 }
927
928 int main(int argc, char* argv[]) {
929         int r;
930
931         log_parse_environment();
932         log_open();
933
934         r = parse_argv(argc, argv);
935         if (r <= 0)
936                 goto finish;
937
938         r = networkctl_main(argc, argv);
939
940 finish:
941         pager_close();
942
943         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
944 }