]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkctl.c
test-cap-list: always check libcap comes to the same names as we do, for the names...
[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
TG
256
257 /* skip commonly misused 00:00:00 (Xerox) prefix */
258 if (memcmp(mac, "\0\0\0", 3) == 0)
259 return -EINVAL;
260
d8500c53 261 snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac));
c09da729
TG
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
276static 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) {
144232a8 336 log_error_errno(r, "could not get ifindex: %m");
c09da729
TG
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
377static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) {
b6a3ca6d
TG
378 _cleanup_free_ struct local_address *local = NULL;
379 int r, n, i;
c09da729 380
b6a3ca6d
TG
381 n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local);
382 if (n < 0)
383 return n;
c09da729 384
b6a3ca6d
TG
385 for (i = 0; i < n; i++) {
386 _cleanup_free_ char *gateway = NULL, *description = NULL;
c09da729 387
b6a3ca6d 388 r = in_addr_to_string(local[i].family, &local[i].address, &gateway);
c09da729 389 if (r < 0)
b6a3ca6d 390 return r;
c09da729 391
b6a3ca6d 392 r = get_gateway_description(rtnl, hwdb, ifindex, local[i].family, &local[i].address, &description);
c09da729 393 if (r < 0)
b6a3ca6d 394 log_debug_errno(r, "Could not get description of gateway: %m");
c09da729 395
b6a3ca6d 396 if (description)
c09da729
TG
397 printf("%*s%s (%s)\n",
398 (int) strlen(prefix),
b6a3ca6d
TG
399 i == 0 ? prefix : "",
400 gateway, description);
c09da729
TG
401 else
402 printf("%*s%s\n",
403 (int) strlen(prefix),
b6a3ca6d 404 i == 0 ? prefix : "",
c09da729 405 gateway);
c09da729
TG
406 }
407
408 return 0;
409}
410
ee8c4568
LP
411static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
412 _cleanup_free_ struct local_address *local = NULL;
413 int r, n, i;
414
1d050e1e 415 n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local);
ee8c4568
LP
416 if (n < 0)
417 return n;
418
419 for (i = 0; i < n; i++) {
420 _cleanup_free_ char *pretty = NULL;
421
422 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
423 if (r < 0)
424 return r;
425
426 printf("%*s%s\n",
427 (int) strlen(prefix),
428 i == 0 ? prefix : "",
429 pretty);
430 }
431
432 return 0;
433}
434
435static void dump_list(const char *prefix, char **l) {
436 char **i;
437
438 STRV_FOREACH(i, l) {
439 printf("%*s%s\n",
440 (int) strlen(prefix),
441 i == l ? prefix : "",
442 *i);
443 }
444}
445
9085f64a 446static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
2301cb9f 447 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
6c03d27d 448 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
9085f64a
LP
449 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
450 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
c09da729 451 _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL;
9085f64a 452 char devid[2 + DECIMAL_STR_MAX(int)];
373d9f17 453 _cleanup_free_ char *t = NULL, *network = NULL;
af5effc4 454 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
d57c365b
LP
455 const char *on_color_operational, *off_color_operational,
456 *on_color_setup, *off_color_setup;
9085f64a
LP
457 struct ether_addr e;
458 unsigned iftype;
459 int r, ifindex;
460 bool have_mac;
461 uint32_t mtu;
462
463 assert(rtnl);
464 assert(udev);
465 assert(name);
466
467 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
468 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
469 else {
470 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
471 if (r < 0)
472 return rtnl_log_create_error(r);
473
474 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
475 }
476
477 if (r < 0)
478 return rtnl_log_create_error(r);
479
480 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
481 if (r < 0)
482 return log_error_errno(r, "Failed to query link: %m");
9085f64a
LP
483
484 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
485 if (r < 0)
486 return rtnl_log_parse_error(r);
487
488 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
489 if (r < 0)
490 return rtnl_log_parse_error(r);
491
492 r = sd_rtnl_message_link_get_type(reply, &iftype);
493 if (r < 0)
494 return rtnl_log_parse_error(r);
495
496 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
497
498 if (have_mac) {
499 const uint8_t *p;
500 bool all_zeroes = true;
501
502 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
503 if (*p != 0) {
504 all_zeroes = false;
505 break;
506 }
507
508 if (all_zeroes)
509 have_mac = false;
510 }
511
512 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
513
d6731e4c 514 sd_network_link_get_operational_state(ifindex, &operational_state);
d57c365b
LP
515 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
516
517 sd_network_link_get_setup_state(ifindex, &setup_state);
518 setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
9085f64a 519
d6731e4c
TG
520 sd_network_link_get_dns(ifindex, &dns);
521 sd_network_link_get_ntp(ifindex, &ntp);
2301cb9f 522 sd_network_link_get_domains(ifindex, &domains);
67272d15
TG
523 r = sd_network_link_get_wildcard_domain(ifindex);
524 if (r > 0) {
525 char *wildcard;
526
527 wildcard = strdup("*");
1405434b
LP
528 if (!wildcard)
529 return log_oom();
530
531 if (strv_consume(&domains, wildcard) < 0)
532 return log_oom();
67272d15 533 }
9085f64a
LP
534
535 sprintf(devid, "n%i", ifindex);
536 d = udev_device_new_from_device_id(udev, devid);
537
538 link_get_type_string(iftype, d, &t);
539
540 if (d) {
af5effc4 541 link = udev_device_get_property_value(d, "ID_NET_LINK_FILE");
9085f64a
LP
542 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
543 path = udev_device_get_property_value(d, "ID_PATH");
544
545 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
546 if (!vendor)
547 vendor = udev_device_get_property_value(d, "ID_VENDOR");
548
549 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
550 if (!model)
551 model = udev_device_get_property_value(d, "ID_MODEL");
552 }
553
373d9f17 554 sd_network_link_get_network_file(ifindex, &network);
df3fb561 555
d57c365b 556 printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
9085f64a 557
af5effc4
TG
558 printf(" Link File: %s\n"
559 "Network File: %s\n"
373d9f17 560 " Type: %s\n"
d57c365b 561 " State: %s%s%s (%s%s%s)\n",
af5effc4 562 strna(link),
373d9f17 563 strna(network),
9085f64a 564 strna(t),
d57c365b
LP
565 on_color_operational, strna(operational_state), off_color_operational,
566 on_color_setup, strna(setup_state), off_color_setup);
9085f64a
LP
567
568 if (path)
569 printf(" Path: %s\n", path);
570 if (driver)
571 printf(" Driver: %s\n", driver);
572 if (vendor)
573 printf(" Vendor: %s\n", vendor);
574 if (model)
575 printf(" Model: %s\n", model);
576
db73295a
LP
577 if (have_mac) {
578 char ea[ETHER_ADDR_TO_STRING_MAX];
579 printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
580 }
9085f64a
LP
581
582 if (mtu > 0)
583 printf(" MTU: %u\n", mtu);
584
c09da729
TG
585 hwdb = udev_hwdb_new(udev);
586
587 dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
588
9085f64a
LP
589 dump_addresses(rtnl, " Address: ", ifindex);
590
591 if (!strv_isempty(dns))
592 dump_list(" DNS: ", dns);
2301cb9f 593 if (!strv_isempty(domains))
c627729f 594 dump_list(" Domain: ", domains);
9085f64a
LP
595 if (!strv_isempty(ntp))
596 dump_list(" NTP: ", ntp);
597
598 return 0;
599}
600
ee8c4568
LP
601static int link_status(char **args, unsigned n) {
602 _cleanup_udev_unref_ struct udev *udev = NULL;
603 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
604 char **name;
605 int r;
606
f7d68aa8 607 r = sd_rtnl_open(&rtnl, 0);
f647962d
MS
608 if (r < 0)
609 return log_error_errno(r, "Failed to connect to netlink: %m");
f7d68aa8
LP
610
611 udev = udev_new();
4a62c710
MS
612 if (!udev)
613 return log_error_errno(errno, "Failed to connect to udev: %m");
f7d68aa8 614
9085f64a 615 if (n <= 1 && !arg_all) {
ee8c4568 616 _cleanup_free_ char *operational_state = NULL;
c627729f 617 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
f7d68aa8 618 _cleanup_free_ struct local_address *addresses = NULL;
e92da1e5 619 const char *on_color_operational, *off_color_operational;
f7d68aa8 620 int i, c;
ee8c4568 621
03cc0fd1 622 sd_network_get_operational_state(&operational_state);
e92da1e5 623 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
5323ead1 624
e92da1e5 625 printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
03cc0fd1 626
1d050e1e 627 c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses);
f7d68aa8
LP
628 for (i = 0; i < c; i++) {
629 _cleanup_free_ char *pretty = NULL;
630
631 r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
632 if (r < 0)
633 return log_oom();
634
635 printf("%13s %s\n",
636 i > 0 ? "" : "Address:", pretty);
637 }
638
03cc0fd1
LP
639 sd_network_get_dns(&dns);
640 if (!strv_isempty(dns))
641 dump_list(" DNS: ", dns);
642
c627729f
LP
643 sd_network_get_domains(&domains);
644 if (!strv_isempty(domains))
645 dump_list(" Domain: ", domains);
646
ddb7f7fc 647 sd_network_get_ntp(&ntp);
03cc0fd1
LP
648 if (!strv_isempty(ntp))
649 dump_list(" NTP: ", ntp);
ee8c4568 650
ee8c4568
LP
651 return 0;
652 }
653
654 pager_open_if_enabled();
655
9085f64a 656 if (arg_all) {
ee8c4568 657 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
6d0c65ff
LP
658 _cleanup_free_ LinkInfo *links = NULL;
659 int c, i;
ee8c4568 660
9085f64a
LP
661 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
662 if (r < 0)
663 return rtnl_log_create_error(r);
ee8c4568 664
9085f64a 665 r = sd_rtnl_message_request_dump(req, true);
ee8c4568
LP
666 if (r < 0)
667 return rtnl_log_create_error(r);
668
669 r = sd_rtnl_call(rtnl, req, 0, &reply);
f647962d
MS
670 if (r < 0)
671 return log_error_errno(r, "Failed to enumerate links: %m");
ee8c4568 672
6d0c65ff
LP
673 c = decode_and_sort_links(reply, &links);
674 if (c < 0)
675 return rtnl_log_parse_error(c);
ee8c4568 676
6d0c65ff
LP
677 for (i = 0; i < c; i++) {
678 if (i > 0)
9085f64a 679 fputc('\n', stdout);
ee8c4568 680
6d0c65ff 681 link_status_one(rtnl, udev, links[i].name);
ee8c4568 682 }
9085f64a 683 }
ee8c4568 684
9085f64a
LP
685 STRV_FOREACH(name, args + 1) {
686 if (name != args+1)
687 fputc('\n', stdout);
ee8c4568 688
9085f64a 689 link_status_one(rtnl, udev, *name);
ee8c4568
LP
690 }
691
692 return 0;
693}
694
695static void help(void) {
696 printf("%s [OPTIONS...]\n\n"
697 "Query and control the networking subsystem.\n\n"
698 " -h --help Show this help\n"
9085f64a
LP
699 " --version Show package version\n"
700 " --no-pager Do not pipe output into a pager\n"
701 " --no-legend Do not show the headers and footers\n"
702 " -a --all Show status for all links\n\n"
ee8c4568
LP
703 "Commands:\n"
704 " list List links\n"
705 " status LINK Show link status\n"
706 , program_invocation_short_name);
707}
708
709static int parse_argv(int argc, char *argv[]) {
710
711 enum {
712 ARG_VERSION = 0x100,
713 ARG_NO_PAGER,
714 ARG_NO_LEGEND,
715 };
716
717 static const struct option options[] = {
718 { "help", no_argument, NULL, 'h' },
719 { "version", no_argument, NULL, ARG_VERSION },
720 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
721 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
9085f64a 722 { "all", no_argument, NULL, 'a' },
ee8c4568
LP
723 {}
724 };
725
726 int c;
727
728 assert(argc >= 0);
729 assert(argv);
730
9085f64a 731 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
ee8c4568
LP
732
733 switch (c) {
734
735 case 'h':
736 help();
737 return 0;
738
739 case ARG_VERSION:
740 puts(PACKAGE_STRING);
741 puts(SYSTEMD_FEATURES);
742 return 0;
743
744 case ARG_NO_PAGER:
745 arg_no_pager = true;
746 break;
747
748 case ARG_NO_LEGEND:
749 arg_legend = false;
750 break;
751
9085f64a
LP
752 case 'a':
753 arg_all = true;
754 break;
755
ee8c4568
LP
756 case '?':
757 return -EINVAL;
758
759 default:
760 assert_not_reached("Unhandled option");
761 }
762 }
763
764 return 1;
765}
766
767static int networkctl_main(int argc, char *argv[]) {
768
769 static const struct {
770 const char* verb;
771 const enum {
772 MORE,
773 LESS,
774 EQUAL
775 } argc_cmp;
776 const int argc;
777 int (* const dispatch)(char **args, unsigned n);
778 } verbs[] = {
779 { "list", LESS, 1, list_links },
780 { "status", MORE, 1, link_status },
781 };
782
783 int left;
784 unsigned i;
785
786 assert(argc >= 0);
787 assert(argv);
788
789 left = argc - optind;
790
791 if (left <= 0)
792 /* Special rule: no arguments means "list" */
793 i = 0;
794 else {
795 if (streq(argv[optind], "help")) {
796 help();
797 return 0;
798 }
799
800 for (i = 0; i < ELEMENTSOF(verbs); i++)
801 if (streq(argv[optind], verbs[i].verb))
802 break;
803
804 if (i >= ELEMENTSOF(verbs)) {
805 log_error("Unknown operation %s", argv[optind]);
806 return -EINVAL;
807 }
808 }
809
810 switch (verbs[i].argc_cmp) {
811
812 case EQUAL:
813 if (left != verbs[i].argc) {
814 log_error("Invalid number of arguments.");
815 return -EINVAL;
816 }
817
818 break;
819
820 case MORE:
821 if (left < verbs[i].argc) {
822 log_error("Too few arguments.");
823 return -EINVAL;
824 }
825
826 break;
827
828 case LESS:
829 if (left > verbs[i].argc) {
830 log_error("Too many arguments.");
831 return -EINVAL;
832 }
833
834 break;
835
836 default:
837 assert_not_reached("Unknown comparison operator.");
838 }
839
840 return verbs[i].dispatch(argv + optind, left);
841}
842
843int main(int argc, char* argv[]) {
844 int r;
845
846 log_parse_environment();
847 log_open();
848
849 r = parse_argv(argc, argv);
850 if (r <= 0)
851 goto finish;
852
853 r = networkctl_main(argc, argv);
854
855finish:
856 pager_close();
857
858 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
859}