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