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