]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
networkctl: show MAC address OUI vendor next to MAC addresses
[thirdparty/systemd.git] / src / network / networkctl.c
CommitLineData
ee8c4568
LP
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"
db73295a 36#include "socket-util.h"
d8500c53 37#include "ether-addr-util.h"
ee8c4568
LP
38
39static bool arg_no_pager = false;
40static bool arg_legend = true;
9085f64a 41static bool arg_all = false;
ee8c4568
LP
42
43static void pager_open_if_enabled(void) {
44
45 if (arg_no_pager)
46 return;
47
48 pager_open(false);
49}
50
51static 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
6d0c65ff
LP
93typedef struct LinkInfo {
94 const char *name;
95 int ifindex;
96 unsigned iftype;
97} LinkInfo;
98
99static 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
105static 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
a6a4f528 145 qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
6d0c65ff
LP
146
147 *ret = links;
148 links = NULL;
149
150 return (int) c;
151}
152
d57c365b
LP
153static 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
167static 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
ee8c4568
LP
184static 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;
6d0c65ff
LP
188 _cleanup_free_ LinkInfo *links = NULL;
189 int r, c, i;
ee8c4568
LP
190
191 pager_open_if_enabled();
192
193 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
194 if (r < 0)
195 return log_error_errno(r, "Failed to connect to netlink: %m");
ee8c4568
LP
196
197 udev = udev_new();
4a62c710
MS
198 if (!udev)
199 return log_error_errno(errno, "Failed to connect to udev: %m");
ee8c4568
LP
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);
f647962d
MS
210 if (r < 0)
211 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568
LP
212
213 if (arg_legend)
3e3db0ee 214 printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
ee8c4568 215
6d0c65ff
LP
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++) {
ab1525bc 221 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
ee8c4568 222 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
d57c365b
LP
223 const char *on_color_operational, *off_color_operational,
224 *on_color_setup, *off_color_setup;
df3fb561 225 char devid[2 + DECIMAL_STR_MAX(int)];
ee8c4568 226 _cleanup_free_ char *t = NULL;
ee8c4568 227
d6731e4c 228 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
d57c365b
LP
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);
ee8c4568 233
6d0c65ff 234 sprintf(devid, "n%i", links[i].ifindex);
ee8c4568
LP
235 d = udev_device_new_from_device_id(udev, devid);
236
6d0c65ff 237 link_get_type_string(links[i].iftype, d, &t);
ee8c4568 238
3e3db0ee 239 printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
d57c365b
LP
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);
ee8c4568
LP
243 }
244
245 if (arg_legend)
6d0c65ff 246 printf("\n%i links listed.\n", c);
ee8c4568
LP
247
248 return 0;
249}
250
c09da729
TG
251/* IEEE Organizationally Unique Identifier vendor string */
252static int ieee_oui(struct udev_hwdb *hwdb, struct ether_addr *mac, char **ret) {
253 struct udev_list_entry *entry;
254 char *description;
d8500c53 255 char str[strlen("OUI:XXYYXXYYXXYY") + 1];
c09da729 256
888943fc
LP
257 if (!hwdb)
258 return -EINVAL;
259
c09da729
TG
260 /* skip commonly misused 00:00:00 (Xerox) prefix */
261 if (memcmp(mac, "\0\0\0", 3) == 0)
262 return -EINVAL;
263
d8500c53 264 snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
c09da729
TG
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
279static 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) {
144232a8 339 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
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
380static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) {
b6a3ca6d
TG
381 _cleanup_free_ struct local_address *local = NULL;
382 int r, n, i;
c09da729 383
b6a3ca6d
TG
384 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
385 if (n < 0)
386 return n;
c09da729 387
b6a3ca6d
TG
388 for (i = 0; i < n; i++) {
389 _cleanup_free_ char *gateway = NULL, *description = NULL;
c09da729 390
b6a3ca6d 391 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 392 if (r < 0)
b6a3ca6d 393 return r;
c09da729 394
b6a3ca6d 395 r = get_gateway_description(rtnl, hwdb, ifindex, local[i].family, &local[i].address, &description);
c09da729 396 if (r < 0)
b6a3ca6d 397 log_debug_errno(r, "Could not get description of gateway: %m");
c09da729 398
b6a3ca6d 399 if (description)
c09da729
TG
400 printf("%*s%s (%s)\n",
401 (int) strlen(prefix),
b6a3ca6d
TG
402 i == 0 ? prefix : "",
403 gateway, description);
c09da729
TG
404 else
405 printf("%*s%s\n",
406 (int) strlen(prefix),
b6a3ca6d 407 i == 0 ? prefix : "",
c09da729 408 gateway);
c09da729
TG
409 }
410
411 return 0;
412}
413
ee8c4568
LP
414static 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
1d050e1e 418 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
ee8c4568
LP
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
438static 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
9085f64a 449static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
2301cb9f 450 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
6c03d27d 451 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
9085f64a
LP
452 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
453 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
c09da729 454 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
9085f64a 455 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 456 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 457 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
458 const char *on_color_operational, *off_color_operational,
459 *on_color_setup, *off_color_setup;
9085f64a
LP
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);
f647962d
MS
484 if (r < 0)
485 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
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
d6731e4c 517 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
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);
9085f64a 522
d6731e4c
TG
523 sd_network_link_get_dns(ifindex, &dns);
524 sd_network_link_get_ntp(ifindex, &ntp);
2301cb9f 525 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
526 r = sd_network_link_get_wildcard_domain(ifindex);
527 if (r > 0) {
528 char *wildcard;
529
530 wildcard = strdup("*");
1405434b
LP
531 if (!wildcard)
532 return log_oom();
533
534 if (strv_consume(&domains, wildcard) < 0)
535 return log_oom();
67272d15 536 }
9085f64a
LP
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) {
af5effc4 544 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
9085f64a
LP
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
373d9f17 557 sd_network_link_get_network_file(ifindex, &network);
df3fb561 558
d57c365b 559 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 560
af5effc4
TG
561 printf(" Link File: %s\n"
562 "Network File: %s\n"
373d9f17 563 " Type: %s\n"
d57c365b 564 " State: %s%s%s (%s%s%s)\n",
af5effc4 565 strna(link),
373d9f17 566 strna(network),
9085f64a 567 strna(t),
d57c365b
LP
568 on_color_operational, strna(operational_state), off_color_operational,
569 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
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
888943fc
LP
580 hwdb = udev_hwdb_new(udev);
581
db73295a 582 if (have_mac) {
888943fc 583 _cleanup_free_ char *description = NULL;
db73295a 584 char ea[ETHER_ADDR_TO_STRING_MAX];
888943fc
LP
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));
db73295a 592 }
9085f64a
LP
593
594 if (mtu > 0)
595 printf(" MTU: %u\n", mtu);
596
597 dump_addresses(rtnl, " Address: ", ifindex);
888943fc 598 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
9085f64a
LP
599
600 if (!strv_isempty(dns))
601 dump_list(" DNS: ", dns);
2301cb9f 602 if (!strv_isempty(domains))
c627729f 603 dump_list(" Domain: ", domains);
9085f64a
LP
604 if (!strv_isempty(ntp))
605 dump_list(" NTP: ", ntp);
606
607 return 0;
608}
609
ee8c4568
LP
610static 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
f7d68aa8 616 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
617 if (r < 0)
618 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8
LP
619
620 udev = udev_new();
4a62c710
MS
621 if (!udev)
622 return log_error_errno(errno, "Failed to connect to udev: %m");
f7d68aa8 623
9085f64a 624 if (n <= 1 && !arg_all) {
ee8c4568 625 _cleanup_free_ char *operational_state = NULL;
c627729f 626 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
f7d68aa8 627 _cleanup_free_ struct local_address *addresses = NULL;
e92da1e5 628 const char *on_color_operational, *off_color_operational;
f7d68aa8 629 int i, c;
ee8c4568 630
03cc0fd1 631 sd_network_get_operational_state(&operational_state);
e92da1e5 632 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 633
e92da1e5 634 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 635
1d050e1e 636 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
f7d68aa8
LP
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
03cc0fd1
LP
648 sd_network_get_dns(&dns);
649 if (!strv_isempty(dns))
650 dump_list(" DNS: ", dns);
651
c627729f
LP
652 sd_network_get_domains(&domains);
653 if (!strv_isempty(domains))
654 dump_list(" Domain: ", domains);
655
ddb7f7fc 656 sd_network_get_ntp(&ntp);
03cc0fd1
LP
657 if (!strv_isempty(ntp))
658 dump_list(" NTP: ", ntp);
ee8c4568 659
ee8c4568
LP
660 return 0;
661 }
662
663 pager_open_if_enabled();
664
9085f64a 665 if (arg_all) {
ee8c4568 666 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
667 _cleanup_free_ LinkInfo *links = NULL;
668 int c, i;
ee8c4568 669
9085f64a
LP
670 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
671 if (r < 0)
672 return rtnl_log_create_error(r);
ee8c4568 673
9085f64a 674 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
675 if (r < 0)
676 return rtnl_log_create_error(r);
677
678 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
679 if (r < 0)
680 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 681
6d0c65ff
LP
682 c = decode_and_sort_links(reply, &links);
683 if (c < 0)
684 return rtnl_log_parse_error(c);
ee8c4568 685
6d0c65ff
LP
686 for (i = 0; i < c; i++) {
687 if (i > 0)
9085f64a 688 fputc('\n', stdout);
ee8c4568 689
6d0c65ff 690 link_status_one(rtnl, udev, links[i].name);
ee8c4568 691 }
9085f64a 692 }
ee8c4568 693
9085f64a
LP
694 STRV_FOREACH(name, args + 1) {
695 if (name != args+1)
696 fputc('\n', stdout);
ee8c4568 697
9085f64a 698 link_status_one(rtnl, udev, *name);
ee8c4568
LP
699 }
700
701 return 0;
702}
703
704static 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"
9085f64a
LP
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"
ee8c4568
LP
712 "Commands:\n"
713 " list List links\n"
714 " status LINK Show link status\n"
715 , program_invocation_short_name);
716}
717
718static 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 },
9085f64a 731 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
732 {}
733 };
734
735 int c;
736
737 assert(argc >= 0);
738 assert(argv);
739
9085f64a 740 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
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
9085f64a
LP
761 case 'a':
762 arg_all = true;
763 break;
764
ee8c4568
LP
765 case '?':
766 return -EINVAL;
767
768 default:
769 assert_not_reached("Unhandled option");
770 }
771 }
772
773 return 1;
774}
775
776static 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
852int 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
864finish:
865 pager_close();
866
867 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
868}