chiark / gitweb /
networkctl: add new switch "-a" to "networkctl status" to show verbose status of...
[elogind.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 }