]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkctl.c
networkctl: add new switch "-a" to "networkctl status" to show verbose status of...
[thirdparty/systemd.git] / src / network / networkctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdbool.h>
23 #include <getopt.h>
24
25 #include "sd-network.h"
26 #include "sd-rtnl.h"
27 #include "libudev.h"
28
29 #include "build.h"
30 #include "util.h"
31 #include "pager.h"
32 #include "rtnl-util.h"
33 #include "udev-util.h"
34 #include "arphrd-list.h"
35 #include "local-addresses.h"
36
37 static bool arg_no_pager = false;
38 static bool arg_legend = true;
39 static bool arg_all = false;
40
41 static void pager_open_if_enabled(void) {
42
43 if (arg_no_pager)
44 return;
45
46 pager_open(false);
47 }
48
49 static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
50 const char *t;
51 char *p;
52
53 if (iftype == ARPHRD_ETHER && d) {
54 const char *devtype, *id = NULL;
55 /* WLANs have iftype ARPHRD_ETHER, but we want
56 * to show a more useful type string for
57 * them */
58
59 devtype = udev_device_get_devtype(d);
60 if (streq_ptr(devtype, "wlan"))
61 id = "wlan";
62 else if (streq_ptr(devtype, "wwan"))
63 id = "wwan";
64
65 if (id) {
66 p = strdup(id);
67 if (!p)
68 return -ENOMEM;
69
70 *ret = p;
71 return 1;
72 }
73 }
74
75 t = arphrd_to_name(iftype);
76 if (!t) {
77 *ret = NULL;
78 return 0;
79 }
80
81 p = strdup(t);
82 if (!p)
83 return -ENOMEM;
84
85 ascii_strlower(p);
86 *ret = p;
87
88 return 0;
89 }
90
91 static int list_links(char **args, unsigned n) {
92 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
93 _cleanup_udev_unref_ struct udev *udev = NULL;
94 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
95 sd_rtnl_message *i;
96 unsigned c = 0;
97 int r;
98
99 pager_open_if_enabled();
100
101 r = sd_rtnl_open(&rtnl, 0);
102 if (r < 0) {
103 log_error("Failed to connect to netlink: %s", strerror(-r));
104 return r;
105 }
106
107 udev = udev_new();
108 if (!udev) {
109 log_error("Failed to connect to udev: %m");
110 return -errno;
111 }
112
113 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
114 if (r < 0)
115 return rtnl_log_create_error(r);
116
117 r = sd_rtnl_message_request_dump(req, true);
118 if (r < 0)
119 return rtnl_log_create_error(r);
120
121 r = sd_rtnl_call(rtnl, req, 0, &reply);
122 if (r < 0) {
123 log_error("Failed to enumerate links: %s", strerror(-r));
124 return r;
125 }
126
127 if (arg_legend)
128 printf("%3s %-16s %-10s %-10s %-10s\n", "IDX", "LINK", "TYPE", "STATE", "OPERATIONAL");
129
130 for (i = reply; i; i = sd_rtnl_message_next(i)) {
131 _cleanup_free_ char *state = NULL, *operational_state = NULL;
132 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
133 char devid[2 + DECIMAL_STR_MAX(int)];
134 _cleanup_free_ char *t = NULL;
135 const char *name;
136 unsigned iftype;
137 uint16_t type;
138 int ifindex;
139
140 r = sd_rtnl_message_get_type(i, &type);
141 if (r < 0)
142 return rtnl_log_parse_error(r);
143
144 if (type != RTM_NEWLINK)
145 continue;
146
147 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
148 if (r < 0)
149 return rtnl_log_parse_error(r);
150
151 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
152 if (r < 0)
153 return rtnl_log_parse_error(r);
154
155 r = sd_rtnl_message_link_get_type(i, &iftype);
156 if (r < 0)
157 return rtnl_log_parse_error(r);
158
159 sd_network_get_link_state(ifindex, &state);
160 sd_network_get_link_operational_state(ifindex, &operational_state);
161
162 sprintf(devid, "n%i", ifindex);
163 d = udev_device_new_from_device_id(udev, devid);
164
165 link_get_type_string(iftype, d, &t);
166
167 printf("%3i %-16s %-10s %-10s %-10s\n", ifindex, name, strna(t), strna(state), strna(operational_state));
168 c++;
169 }
170
171 if (arg_legend)
172 printf("\n%u links listed.\n", c);
173
174 return 0;
175 }
176
177 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
178 _cleanup_free_ struct local_address *local = NULL;
179 int r, n, i;
180
181 n = local_addresses(rtnl, ifindex, &local);
182 if (n < 0)
183 return n;
184
185 for (i = 0; i < n; i++) {
186 _cleanup_free_ char *pretty = NULL;
187
188 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
189 if (r < 0)
190 return r;
191
192 printf("%*s%s\n",
193 (int) strlen(prefix),
194 i == 0 ? prefix : "",
195 pretty);
196 }
197
198 return 0;
199 }
200
201 static void dump_list(const char *prefix, char **l) {
202 char **i;
203
204 STRV_FOREACH(i, l) {
205 printf("%*s%s\n",
206 (int) strlen(prefix),
207 i == l ? prefix : "",
208 *i);
209 }
210 }
211
212 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
213 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
214 _cleanup_free_ char *state = NULL, *operational_state = NULL;
215 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
216 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
217 char devid[2 + DECIMAL_STR_MAX(int)];
218 _cleanup_free_ char *t = NULL;
219 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
220 struct ether_addr e;
221 unsigned iftype;
222 int r, ifindex;
223 bool have_mac;
224 uint32_t mtu;
225
226 assert(rtnl);
227 assert(udev);
228 assert(name);
229
230 if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
231 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
232 else {
233 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
234 if (r < 0)
235 return rtnl_log_create_error(r);
236
237 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
238 }
239
240 if (r < 0)
241 return rtnl_log_create_error(r);
242
243 r = sd_rtnl_call(rtnl, req, 0, &reply);
244 if (r < 0) {
245 log_error("Failed to query link: %s", strerror(-r));
246 return r;
247 }
248
249 r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
250 if (r < 0)
251 return rtnl_log_parse_error(r);
252
253 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
254 if (r < 0)
255 return rtnl_log_parse_error(r);
256
257 r = sd_rtnl_message_link_get_type(reply, &iftype);
258 if (r < 0)
259 return rtnl_log_parse_error(r);
260
261 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
262
263 if (have_mac) {
264 const uint8_t *p;
265 bool all_zeroes = true;
266
267 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
268 if (*p != 0) {
269 all_zeroes = false;
270 break;
271 }
272
273 if (all_zeroes)
274 have_mac = false;
275 }
276
277 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
278
279 sd_network_get_link_state(ifindex, &state);
280 sd_network_get_link_operational_state(ifindex, &operational_state);
281
282 sd_network_get_link_dns(ifindex, &dns);
283 sd_network_get_link_ntp(ifindex, &ntp);
284
285 sprintf(devid, "n%i", ifindex);
286 d = udev_device_new_from_device_id(udev, devid);
287
288 link_get_type_string(iftype, d, &t);
289
290 if (d) {
291 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
292 path = udev_device_get_property_value(d, "ID_PATH");
293
294 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
295 if (!vendor)
296 vendor = udev_device_get_property_value(d, "ID_VENDOR");
297
298 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
299 if (!model)
300 model = udev_device_get_property_value(d, "ID_MODEL");
301 }
302
303 printf("%i: %s\n", ifindex, name);
304
305 printf(" Type: %s\n"
306 " State: %s (%s)\n",
307 strna(t),
308 strna(operational_state),
309 strna(state));
310
311 if (path)
312 printf(" Path: %s\n", path);
313 if (driver)
314 printf(" Driver: %s\n", driver);
315 if (vendor)
316 printf(" Vendor: %s\n", vendor);
317 if (model)
318 printf(" Model: %s\n", model);
319
320 if (have_mac)
321 printf(" HW Address: %s\n", ether_ntoa(&e));
322
323 if (mtu > 0)
324 printf(" MTU: %u\n", mtu);
325
326 dump_addresses(rtnl, " Address: ", ifindex);
327
328 if (!strv_isempty(dns))
329 dump_list(" DNS: ", dns);
330 if (!strv_isempty(ntp))
331 dump_list(" NTP: ", ntp);
332
333 return 0;
334 }
335
336 static int link_status(char **args, unsigned n) {
337 _cleanup_udev_unref_ struct udev *udev = NULL;
338 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
339 char **name;
340 int r;
341
342 if (n <= 1 && !arg_all) {
343 _cleanup_free_ char *operational_state = NULL;
344 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
345
346 sd_network_get_operational_state(&operational_state);
347 if (operational_state)
348 printf(" State: %s\n", operational_state);
349
350 sd_network_get_dns(&dns);
351 if (!strv_isempty(dns))
352 dump_list(" DNS: ", dns);
353
354 sd_network_get_dns(&ntp);
355 if (!strv_isempty(ntp))
356 dump_list(" NTP: ", ntp);
357
358 return 0;
359 }
360
361 pager_open_if_enabled();
362
363 r = sd_rtnl_open(&rtnl, 0);
364 if (r < 0) {
365 log_error("Failed to connect to netlink: %s", strerror(-r));
366 return r;
367 }
368
369 udev = udev_new();
370 if (!udev) {
371 log_error("Failed to connect to udev: %m");
372 return -errno;
373 }
374
375 if (arg_all) {
376 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
377 sd_rtnl_message *i;
378 bool space = false;
379 uint16_t type;
380
381 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
382 if (r < 0)
383 return rtnl_log_create_error(r);
384
385 r = sd_rtnl_message_request_dump(req, true);
386 if (r < 0)
387 return rtnl_log_create_error(r);
388
389 r = sd_rtnl_call(rtnl, req, 0, &reply);
390 if (r < 0) {
391 log_error("Failed to enumerate links: %s", strerror(-r));
392 return r;
393 }
394
395 for (i = reply; i; i = sd_rtnl_message_next(i)) {
396 const char *nn;
397
398 r = sd_rtnl_message_get_type(i, &type);
399 if (r < 0)
400 return rtnl_log_parse_error(r);
401
402 if (type != RTM_NEWLINK)
403 continue;
404
405 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &nn);
406 if (r < 0)
407 return rtnl_log_parse_error(r);
408
409 if (space)
410 fputc('\n', stdout);
411
412 link_status_one(rtnl, udev, nn);
413 space = true;
414 }
415 }
416
417 STRV_FOREACH(name, args + 1) {
418 if (name != args+1)
419 fputc('\n', stdout);
420
421 link_status_one(rtnl, udev, *name);
422 }
423
424 return 0;
425 }
426
427 static void help(void) {
428 printf("%s [OPTIONS...]\n\n"
429 "Query and control the networking subsystem.\n\n"
430 " -h --help Show this help\n"
431 " --version Show package version\n"
432 " --no-pager Do not pipe output into a pager\n"
433 " --no-legend Do not show the headers and footers\n"
434 " -a --all Show status for all links\n\n"
435 "Commands:\n"
436 " list List links\n"
437 " status LINK Show link status\n"
438 , program_invocation_short_name);
439 }
440
441 static int parse_argv(int argc, char *argv[]) {
442
443 enum {
444 ARG_VERSION = 0x100,
445 ARG_NO_PAGER,
446 ARG_NO_LEGEND,
447 };
448
449 static const struct option options[] = {
450 { "help", no_argument, NULL, 'h' },
451 { "version", no_argument, NULL, ARG_VERSION },
452 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
453 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
454 { "all", no_argument, NULL, 'a' },
455 {}
456 };
457
458 int c;
459
460 assert(argc >= 0);
461 assert(argv);
462
463 while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
464
465 switch (c) {
466
467 case 'h':
468 help();
469 return 0;
470
471 case ARG_VERSION:
472 puts(PACKAGE_STRING);
473 puts(SYSTEMD_FEATURES);
474 return 0;
475
476 case ARG_NO_PAGER:
477 arg_no_pager = true;
478 break;
479
480 case ARG_NO_LEGEND:
481 arg_legend = false;
482 break;
483
484 case 'a':
485 arg_all = true;
486 break;
487
488 case '?':
489 return -EINVAL;
490
491 default:
492 assert_not_reached("Unhandled option");
493 }
494 }
495
496 return 1;
497 }
498
499 static int networkctl_main(int argc, char *argv[]) {
500
501 static const struct {
502 const char* verb;
503 const enum {
504 MORE,
505 LESS,
506 EQUAL
507 } argc_cmp;
508 const int argc;
509 int (* const dispatch)(char **args, unsigned n);
510 } verbs[] = {
511 { "list", LESS, 1, list_links },
512 { "status", MORE, 1, link_status },
513 };
514
515 int left;
516 unsigned i;
517
518 assert(argc >= 0);
519 assert(argv);
520
521 left = argc - optind;
522
523 if (left <= 0)
524 /* Special rule: no arguments means "list" */
525 i = 0;
526 else {
527 if (streq(argv[optind], "help")) {
528 help();
529 return 0;
530 }
531
532 for (i = 0; i < ELEMENTSOF(verbs); i++)
533 if (streq(argv[optind], verbs[i].verb))
534 break;
535
536 if (i >= ELEMENTSOF(verbs)) {
537 log_error("Unknown operation %s", argv[optind]);
538 return -EINVAL;
539 }
540 }
541
542 switch (verbs[i].argc_cmp) {
543
544 case EQUAL:
545 if (left != verbs[i].argc) {
546 log_error("Invalid number of arguments.");
547 return -EINVAL;
548 }
549
550 break;
551
552 case MORE:
553 if (left < verbs[i].argc) {
554 log_error("Too few arguments.");
555 return -EINVAL;
556 }
557
558 break;
559
560 case LESS:
561 if (left > verbs[i].argc) {
562 log_error("Too many arguments.");
563 return -EINVAL;
564 }
565
566 break;
567
568 default:
569 assert_not_reached("Unknown comparison operator.");
570 }
571
572 return verbs[i].dispatch(argv + optind, left);
573 }
574
575 int main(int argc, char* argv[]) {
576 int r;
577
578 log_parse_environment();
579 log_open();
580
581 r = parse_argv(argc, argv);
582 if (r <= 0)
583 goto finish;
584
585 r = networkctl_main(argc, argv);
586
587 finish:
588 pager_close();
589
590 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
591 }