chiark / gitweb /
networkctl: name setup state variable setup_state
[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 typedef struct LinkInfo {
92         const char *name;
93         int ifindex;
94         unsigned iftype;
95 } LinkInfo;
96
97 static int link_info_compare(const void *a, const void *b) {
98         const LinkInfo *x = a, *y = b;
99
100         return x->ifindex - y->ifindex;
101 }
102
103 static int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
104         _cleanup_free_ LinkInfo *links = NULL;
105         size_t size = 0, c = 0;
106         sd_rtnl_message *i;
107         int r;
108
109         for (i = m; i; i = sd_rtnl_message_next(i)) {
110                 const char *name;
111                 unsigned iftype;
112                 uint16_t type;
113                 int ifindex;
114
115                 r = sd_rtnl_message_get_type(i, &type);
116                 if (r < 0)
117                         return r;
118
119                 if (type != RTM_NEWLINK)
120                         continue;
121
122                 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
123                 if (r < 0)
124                         return r;
125
126                 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
127                 if (r < 0)
128                         return r;
129
130                 r = sd_rtnl_message_link_get_type(i, &iftype);
131                 if (r < 0)
132                         return r;
133
134                 if (!GREEDY_REALLOC(links, size, c+1))
135                         return -ENOMEM;
136
137                 links[c].name = name;
138                 links[c].ifindex = ifindex;
139                 links[c].iftype = iftype;
140                 c++;
141         }
142
143         qsort(links, c, sizeof(LinkInfo), link_info_compare);
144
145         *ret = links;
146         links = NULL;
147
148         return (int) c;
149 }
150
151 static int list_links(char **args, unsigned n) {
152         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
153         _cleanup_udev_unref_ struct udev *udev = NULL;
154         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
155         _cleanup_free_ LinkInfo *links = NULL;
156         int r, c, i;
157
158         pager_open_if_enabled();
159
160         r = sd_rtnl_open(&rtnl, 0);
161         if (r < 0) {
162                 log_error("Failed to connect to netlink: %s", strerror(-r));
163                 return r;
164         }
165
166         udev = udev_new();
167         if (!udev) {
168                 log_error("Failed to connect to udev: %m");
169                 return -errno;
170         }
171
172         r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
173         if (r < 0)
174                 return rtnl_log_create_error(r);
175
176         r = sd_rtnl_message_request_dump(req, true);
177         if (r < 0)
178                 return rtnl_log_create_error(r);
179
180         r = sd_rtnl_call(rtnl, req, 0, &reply);
181         if (r < 0) {
182                 log_error("Failed to enumerate links: %s", strerror(-r));
183                 return r;
184         }
185
186         if (arg_legend)
187                 printf("%3s %-16s %-10s %-11s %-10s\n", "IDX", "LINK", "TYPE", "SETUP", "OPERATIONAL");
188
189         c = decode_and_sort_links(reply, &links);
190         if (c < 0)
191                 return rtnl_log_parse_error(c);
192
193         for (i = 0; i < c; i++) {
194                 _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
195                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
196                 const char *on_color_oper = "", *off_color_oper = "",
197                            *on_color = "", *off_color = "";
198                  char devid[2 + DECIMAL_STR_MAX(int)];
199                 _cleanup_free_ char *t = NULL;
200
201                 sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
202                 sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
203
204                 sprintf(devid, "n%i", links[i].ifindex);
205                 d = udev_device_new_from_device_id(udev, devid);
206
207                 link_get_type_string(links[i].iftype, d, &t);
208
209                 if (streq_ptr(operational_state, "routable")) {
210                         on_color_oper = ansi_highlight_green();
211                         off_color_oper = ansi_highlight_off();
212                 } else if (streq_ptr(operational_state, "degraded")) {
213                         on_color_oper = ansi_highlight_yellow();
214                         off_color_oper = ansi_highlight_off();
215                 }
216
217                 if (streq_ptr(setup_state, "configured")) {
218                         on_color = ansi_highlight_green();
219                         off_color = ansi_highlight_off();
220                 } else if (streq_ptr(setup_state, "configuring")) {
221                         on_color = ansi_highlight_yellow();
222                         off_color = ansi_highlight_off();
223                 } else if (streq_ptr(setup_state, "failed") ||
224                            streq_ptr(setup_state, "linger")) {
225                         on_color = ansi_highlight_red();
226                         off_color = ansi_highlight_off();
227                 }
228
229                 printf("%3i %-16s %-10s %s%-11s%s %s%-10s%s\n", links[i].ifindex,
230                        links[i].name, strna(t), on_color, strna(setup_state), off_color,
231                        on_color_oper, strna(operational_state), off_color_oper);
232         }
233
234         if (arg_legend)
235                 printf("\n%i links listed.\n", c);
236
237         return 0;
238 }
239
240 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
241         _cleanup_free_ struct local_address *local = NULL;
242         int r, n, i;
243
244         n = local_addresses(rtnl, ifindex, &local);
245         if (n < 0)
246                 return n;
247
248         for (i = 0; i < n; i++) {
249                 _cleanup_free_ char *pretty = NULL;
250
251                 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
252                 if (r < 0)
253                         return r;
254
255                 printf("%*s%s\n",
256                        (int) strlen(prefix),
257                        i == 0 ? prefix : "",
258                        pretty);
259         }
260
261         return 0;
262 }
263
264 static void dump_list(const char *prefix, char **l) {
265         char **i;
266
267         STRV_FOREACH(i, l) {
268                 printf("%*s%s\n",
269                        (int) strlen(prefix),
270                        i == l ? prefix : "",
271                        *i);
272         }
273 }
274
275 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
276         _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
277         _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
278         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
279         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
280         char devid[2 + DECIMAL_STR_MAX(int)];
281         _cleanup_free_ char *t = NULL;
282         const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
283         const char *on_color = "", *off_color = "";
284         struct ether_addr e;
285         unsigned iftype;
286         int r, ifindex;
287         bool have_mac;
288         uint32_t mtu;
289
290         assert(rtnl);
291         assert(udev);
292         assert(name);
293
294         if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
295                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
296         else {
297                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
298                 if (r < 0)
299                         return rtnl_log_create_error(r);
300
301                 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
302         }
303
304         if (r < 0)
305                 return rtnl_log_create_error(r);
306
307         r = sd_rtnl_call(rtnl, req, 0, &reply);
308         if (r < 0) {
309                 log_error("Failed to query link: %s", strerror(-r));
310                 return r;
311         }
312
313         r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
314         if (r < 0)
315                 return rtnl_log_parse_error(r);
316
317         r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
318         if (r < 0)
319                 return rtnl_log_parse_error(r);
320
321         r = sd_rtnl_message_link_get_type(reply, &iftype);
322         if (r < 0)
323                 return rtnl_log_parse_error(r);
324
325         have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
326
327         if (have_mac) {
328                 const uint8_t *p;
329                 bool all_zeroes = true;
330
331                 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
332                         if (*p != 0) {
333                                 all_zeroes = false;
334                                 break;
335                         }
336
337                 if (all_zeroes)
338                         have_mac = false;
339         }
340
341         sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
342
343         sd_network_link_get_setup_state(ifindex, &setup_state);
344         sd_network_link_get_operational_state(ifindex, &operational_state);
345
346         sd_network_link_get_dns(ifindex, &dns);
347         sd_network_link_get_ntp(ifindex, &ntp);
348
349         sprintf(devid, "n%i", ifindex);
350         d = udev_device_new_from_device_id(udev, devid);
351
352         link_get_type_string(iftype, d, &t);
353
354         if (d) {
355                 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
356                 path = udev_device_get_property_value(d, "ID_PATH");
357
358                 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
359                 if (!vendor)
360                         vendor = udev_device_get_property_value(d, "ID_VENDOR");
361
362                 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
363                 if (!model)
364                         model = udev_device_get_property_value(d, "ID_MODEL");
365         }
366
367         if (streq_ptr(operational_state, "routable")) {
368                 on_color = ansi_highlight_green();
369                 off_color = ansi_highlight_off();
370         } else if (streq_ptr(operational_state, "degraded")) {
371                 on_color = ansi_highlight_yellow();
372                 off_color = ansi_highlight_off();
373         }
374
375         printf("%s%s%s %i: %s\n", on_color, draw_special_char(DRAW_BLACK_CIRCLE), off_color, ifindex, name);
376
377         printf("        Type: %s\n"
378                "       State: %s%s%s (%s)\n",
379                strna(t),
380                on_color, strna(operational_state), off_color,
381                strna(setup_state));
382
383         if (path)
384                 printf("        Path: %s\n", path);
385         if (driver)
386                 printf("      Driver: %s\n", driver);
387         if (vendor)
388                 printf("      Vendor: %s\n", vendor);
389         if (model)
390                 printf("       Model: %s\n", model);
391
392         if (have_mac)
393                 printf("  HW Address: %s\n", ether_ntoa(&e));
394
395         if (mtu > 0)
396                 printf("         MTU: %u\n", mtu);
397
398         dump_addresses(rtnl, "     Address: ", ifindex);
399
400         if (!strv_isempty(dns))
401                 dump_list("         DNS: ", dns);
402         if (!strv_isempty(ntp))
403                 dump_list("         NTP: ", ntp);
404
405         return 0;
406 }
407
408 static int link_status(char **args, unsigned n) {
409         _cleanup_udev_unref_ struct udev *udev = NULL;
410         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
411         char **name;
412         int r;
413
414         r = sd_rtnl_open(&rtnl, 0);
415         if (r < 0) {
416                 log_error("Failed to connect to netlink: %s", strerror(-r));
417                 return r;
418         }
419
420         udev = udev_new();
421         if (!udev) {
422                 log_error("Failed to connect to udev: %m");
423                 return -errno;
424         }
425
426         if (n <= 1 && !arg_all) {
427                 _cleanup_free_ char *operational_state = NULL;
428                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
429                 _cleanup_free_ struct local_address *addresses = NULL;
430                 const char *on_color_oper = "", *off_color_oper = "";
431                 int i, c;
432
433                 sd_network_get_operational_state(&operational_state);
434                 if (streq_ptr(operational_state, "routable")) {
435                         on_color_oper = ansi_highlight_green();
436                         off_color_oper = ansi_highlight_off();
437                 } else if (streq_ptr(operational_state, "degraded")) {
438                         on_color_oper = ansi_highlight_yellow();
439                         off_color_oper = ansi_highlight_off();
440                 }
441
442                 printf("       State: %s%s%s\n", on_color_oper, strna(operational_state), off_color_oper);
443
444                 c = local_addresses(rtnl, 0, &addresses);
445                 for (i = 0; i < c; i++) {
446                         _cleanup_free_ char *pretty = NULL;
447
448                         r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
449                         if (r < 0)
450                                 return log_oom();
451
452                         printf("%13s %s\n",
453                                i > 0 ? "" : "Address:", pretty);
454                 }
455
456                 sd_network_get_dns(&dns);
457                 if (!strv_isempty(dns))
458                         dump_list("         DNS: ", dns);
459
460                 sd_network_get_dns(&ntp);
461                 if (!strv_isempty(ntp))
462                         dump_list("         NTP: ", ntp);
463
464                 return 0;
465         }
466
467         pager_open_if_enabled();
468
469         if (arg_all) {
470                 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
471                 _cleanup_free_ LinkInfo *links = NULL;
472                 int c, i;
473
474                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
475                 if (r < 0)
476                         return rtnl_log_create_error(r);
477
478                 r = sd_rtnl_message_request_dump(req, true);
479                 if (r < 0)
480                         return rtnl_log_create_error(r);
481
482                 r = sd_rtnl_call(rtnl, req, 0, &reply);
483                 if (r < 0) {
484                         log_error("Failed to enumerate links: %s", strerror(-r));
485                         return r;
486                 }
487
488                 c = decode_and_sort_links(reply, &links);
489                 if (c < 0)
490                         return rtnl_log_parse_error(c);
491
492                 for (i = 0; i < c; i++) {
493                         if (i > 0)
494                                 fputc('\n', stdout);
495
496                         link_status_one(rtnl, udev, links[i].name);
497                 }
498         }
499
500         STRV_FOREACH(name, args + 1) {
501                 if (name != args+1)
502                         fputc('\n', stdout);
503
504                 link_status_one(rtnl, udev, *name);
505         }
506
507         return 0;
508 }
509
510 static void help(void) {
511         printf("%s [OPTIONS...]\n\n"
512                "Query and control the networking subsystem.\n\n"
513                "  -h --help             Show this help\n"
514                "     --version          Show package version\n"
515                "     --no-pager         Do not pipe output into a pager\n"
516                "     --no-legend        Do not show the headers and footers\n"
517                "  -a --all              Show status for all links\n\n"
518                "Commands:\n"
519                "  list                  List links\n"
520                "  status LINK           Show link status\n"
521                , program_invocation_short_name);
522 }
523
524 static int parse_argv(int argc, char *argv[]) {
525
526         enum {
527                 ARG_VERSION = 0x100,
528                 ARG_NO_PAGER,
529                 ARG_NO_LEGEND,
530         };
531
532         static const struct option options[] = {
533                 { "help",      no_argument,       NULL, 'h'           },
534                 { "version",   no_argument,       NULL, ARG_VERSION   },
535                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
536                 { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
537                 { "all",       no_argument,       NULL, 'a'           },
538                 {}
539         };
540
541         int c;
542
543         assert(argc >= 0);
544         assert(argv);
545
546         while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
547
548                 switch (c) {
549
550                 case 'h':
551                         help();
552                         return 0;
553
554                 case ARG_VERSION:
555                         puts(PACKAGE_STRING);
556                         puts(SYSTEMD_FEATURES);
557                         return 0;
558
559                 case ARG_NO_PAGER:
560                         arg_no_pager = true;
561                         break;
562
563                 case ARG_NO_LEGEND:
564                         arg_legend = false;
565                         break;
566
567                 case 'a':
568                         arg_all = true;
569                         break;
570
571                 case '?':
572                         return -EINVAL;
573
574                 default:
575                         assert_not_reached("Unhandled option");
576                 }
577         }
578
579         return 1;
580 }
581
582 static int networkctl_main(int argc, char *argv[]) {
583
584         static const struct {
585                 const char* verb;
586                 const enum {
587                         MORE,
588                         LESS,
589                         EQUAL
590                 } argc_cmp;
591                 const int argc;
592                 int (* const dispatch)(char **args, unsigned n);
593         } verbs[] = {
594                 { "list",   LESS, 1, list_links  },
595                 { "status", MORE, 1, link_status },
596         };
597
598         int left;
599         unsigned i;
600
601         assert(argc >= 0);
602         assert(argv);
603
604         left = argc - optind;
605
606         if (left <= 0)
607                 /* Special rule: no arguments means "list" */
608                 i = 0;
609         else {
610                 if (streq(argv[optind], "help")) {
611                         help();
612                         return 0;
613                 }
614
615                 for (i = 0; i < ELEMENTSOF(verbs); i++)
616                         if (streq(argv[optind], verbs[i].verb))
617                                 break;
618
619                 if (i >= ELEMENTSOF(verbs)) {
620                         log_error("Unknown operation %s", argv[optind]);
621                         return -EINVAL;
622                 }
623         }
624
625         switch (verbs[i].argc_cmp) {
626
627         case EQUAL:
628                 if (left != verbs[i].argc) {
629                         log_error("Invalid number of arguments.");
630                         return -EINVAL;
631                 }
632
633                 break;
634
635         case MORE:
636                 if (left < verbs[i].argc) {
637                         log_error("Too few arguments.");
638                         return -EINVAL;
639                 }
640
641                 break;
642
643         case LESS:
644                 if (left > verbs[i].argc) {
645                         log_error("Too many arguments.");
646                         return -EINVAL;
647                 }
648
649                 break;
650
651         default:
652                 assert_not_reached("Unknown comparison operator.");
653         }
654
655         return verbs[i].dispatch(argv + optind, left);
656 }
657
658 int main(int argc, char* argv[]) {
659         int r;
660
661         log_parse_environment();
662         log_open();
663
664         r = parse_argv(argc, argv);
665         if (r <= 0)
666                 goto finish;
667
668         r = networkctl_main(argc, argv);
669
670 finish:
671         pager_close();
672
673         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
674 }