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