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