chiark / gitweb /
dc0bb4da7c9c3bcd2d79fe4584fce0f8151ae866
[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_oper = "", *off_color_oper = "";
443                 int i, c;
444
445                 sd_network_get_operational_state(&operational_state);
446                 if (streq_ptr(operational_state, "routable")) {
447                         on_color_oper = ansi_highlight_green();
448                         off_color_oper = ansi_highlight_off();
449                 } else if (streq_ptr(operational_state, "degraded")) {
450                         on_color_oper = ansi_highlight_yellow();
451                         off_color_oper = ansi_highlight_off();
452                 }
453
454                 printf("       State: %s%s%s\n", on_color_oper, strna(operational_state), off_color_oper);
455
456                 c = local_addresses(rtnl, 0, &addresses);
457                 for (i = 0; i < c; i++) {
458                         _cleanup_free_ char *pretty = NULL;
459
460                         r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
461                         if (r < 0)
462                                 return log_oom();
463
464                         printf("%13s %s\n",
465                                i > 0 ? "" : "Address:", pretty);
466                 }
467
468                 sd_network_get_dns(&dns);
469                 if (!strv_isempty(dns))
470                         dump_list("         DNS: ", dns);
471
472                 sd_network_get_dns(&ntp);
473                 if (!strv_isempty(ntp))
474                         dump_list("         NTP: ", ntp);
475
476                 return 0;
477         }
478
479         pager_open_if_enabled();
480
481         if (arg_all) {
482                 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
483                 _cleanup_free_ LinkInfo *links = NULL;
484                 int c, i;
485
486                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
487                 if (r < 0)
488                         return rtnl_log_create_error(r);
489
490                 r = sd_rtnl_message_request_dump(req, true);
491                 if (r < 0)
492                         return rtnl_log_create_error(r);
493
494                 r = sd_rtnl_call(rtnl, req, 0, &reply);
495                 if (r < 0) {
496                         log_error("Failed to enumerate links: %s", strerror(-r));
497                         return r;
498                 }
499
500                 c = decode_and_sort_links(reply, &links);
501                 if (c < 0)
502                         return rtnl_log_parse_error(c);
503
504                 for (i = 0; i < c; i++) {
505                         if (i > 0)
506                                 fputc('\n', stdout);
507
508                         link_status_one(rtnl, udev, links[i].name);
509                 }
510         }
511
512         STRV_FOREACH(name, args + 1) {
513                 if (name != args+1)
514                         fputc('\n', stdout);
515
516                 link_status_one(rtnl, udev, *name);
517         }
518
519         return 0;
520 }
521
522 static void help(void) {
523         printf("%s [OPTIONS...]\n\n"
524                "Query and control the networking subsystem.\n\n"
525                "  -h --help             Show this help\n"
526                "     --version          Show package version\n"
527                "     --no-pager         Do not pipe output into a pager\n"
528                "     --no-legend        Do not show the headers and footers\n"
529                "  -a --all              Show status for all links\n\n"
530                "Commands:\n"
531                "  list                  List links\n"
532                "  status LINK           Show link status\n"
533                , program_invocation_short_name);
534 }
535
536 static int parse_argv(int argc, char *argv[]) {
537
538         enum {
539                 ARG_VERSION = 0x100,
540                 ARG_NO_PAGER,
541                 ARG_NO_LEGEND,
542         };
543
544         static const struct option options[] = {
545                 { "help",      no_argument,       NULL, 'h'           },
546                 { "version",   no_argument,       NULL, ARG_VERSION   },
547                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
548                 { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
549                 { "all",       no_argument,       NULL, 'a'           },
550                 {}
551         };
552
553         int c;
554
555         assert(argc >= 0);
556         assert(argv);
557
558         while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
559
560                 switch (c) {
561
562                 case 'h':
563                         help();
564                         return 0;
565
566                 case ARG_VERSION:
567                         puts(PACKAGE_STRING);
568                         puts(SYSTEMD_FEATURES);
569                         return 0;
570
571                 case ARG_NO_PAGER:
572                         arg_no_pager = true;
573                         break;
574
575                 case ARG_NO_LEGEND:
576                         arg_legend = false;
577                         break;
578
579                 case 'a':
580                         arg_all = true;
581                         break;
582
583                 case '?':
584                         return -EINVAL;
585
586                 default:
587                         assert_not_reached("Unhandled option");
588                 }
589         }
590
591         return 1;
592 }
593
594 static int networkctl_main(int argc, char *argv[]) {
595
596         static const struct {
597                 const char* verb;
598                 const enum {
599                         MORE,
600                         LESS,
601                         EQUAL
602                 } argc_cmp;
603                 const int argc;
604                 int (* const dispatch)(char **args, unsigned n);
605         } verbs[] = {
606                 { "list",   LESS, 1, list_links  },
607                 { "status", MORE, 1, link_status },
608         };
609
610         int left;
611         unsigned i;
612
613         assert(argc >= 0);
614         assert(argv);
615
616         left = argc - optind;
617
618         if (left <= 0)
619                 /* Special rule: no arguments means "list" */
620                 i = 0;
621         else {
622                 if (streq(argv[optind], "help")) {
623                         help();
624                         return 0;
625                 }
626
627                 for (i = 0; i < ELEMENTSOF(verbs); i++)
628                         if (streq(argv[optind], verbs[i].verb))
629                                 break;
630
631                 if (i >= ELEMENTSOF(verbs)) {
632                         log_error("Unknown operation %s", argv[optind]);
633                         return -EINVAL;
634                 }
635         }
636
637         switch (verbs[i].argc_cmp) {
638
639         case EQUAL:
640                 if (left != verbs[i].argc) {
641                         log_error("Invalid number of arguments.");
642                         return -EINVAL;
643                 }
644
645                 break;
646
647         case MORE:
648                 if (left < verbs[i].argc) {
649                         log_error("Too few arguments.");
650                         return -EINVAL;
651                 }
652
653                 break;
654
655         case LESS:
656                 if (left > verbs[i].argc) {
657                         log_error("Too many arguments.");
658                         return -EINVAL;
659                 }
660
661                 break;
662
663         default:
664                 assert_not_reached("Unknown comparison operator.");
665         }
666
667         return verbs[i].dispatch(argv + optind, left);
668 }
669
670 int main(int argc, char* argv[]) {
671         int r;
672
673         log_parse_environment();
674         log_open();
675
676         r = parse_argv(argc, argv);
677         if (r <= 0)
678                 goto finish;
679
680         r = networkctl_main(argc, argv);
681
682 finish:
683         pager_close();
684
685         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
686 }