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