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