chiark / gitweb /
networkctl: print local domain name in status output
[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, **domains = 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         sd_network_link_get_domains(ifindex, &domains);
368
369         sprintf(devid, "n%i", ifindex);
370         d = udev_device_new_from_device_id(udev, devid);
371
372         link_get_type_string(iftype, d, &t);
373
374         if (d) {
375                 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
376                 path = udev_device_get_property_value(d, "ID_PATH");
377
378                 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
379                 if (!vendor)
380                         vendor = udev_device_get_property_value(d, "ID_VENDOR");
381
382                 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
383                 if (!model)
384                         model = udev_device_get_property_value(d, "ID_MODEL");
385         }
386
387
388         printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
389
390         printf("        Type: %s\n"
391                "       State: %s%s%s (%s%s%s)\n",
392                strna(t),
393                on_color_operational, strna(operational_state), off_color_operational,
394                on_color_setup, strna(setup_state), off_color_setup);
395
396         if (path)
397                 printf("        Path: %s\n", path);
398         if (driver)
399                 printf("      Driver: %s\n", driver);
400         if (vendor)
401                 printf("      Vendor: %s\n", vendor);
402         if (model)
403                 printf("       Model: %s\n", model);
404
405         if (have_mac)
406                 printf("  HW Address: %s\n", ether_ntoa(&e));
407
408         if (mtu > 0)
409                 printf("         MTU: %u\n", mtu);
410
411         dump_addresses(rtnl, "     Address: ", ifindex);
412
413         if (!strv_isempty(dns))
414                 dump_list("         DNS: ", dns);
415         if (!strv_isempty(domains))
416                 dump_list("     Domains: ", domains);
417         if (!strv_isempty(ntp))
418                 dump_list("         NTP: ", ntp);
419
420         return 0;
421 }
422
423 static int link_status(char **args, unsigned n) {
424         _cleanup_udev_unref_ struct udev *udev = NULL;
425         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
426         char **name;
427         int r;
428
429         r = sd_rtnl_open(&rtnl, 0);
430         if (r < 0) {
431                 log_error("Failed to connect to netlink: %s", strerror(-r));
432                 return r;
433         }
434
435         udev = udev_new();
436         if (!udev) {
437                 log_error("Failed to connect to udev: %m");
438                 return -errno;
439         }
440
441         if (n <= 1 && !arg_all) {
442                 _cleanup_free_ char *operational_state = NULL;
443                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
444                 _cleanup_free_ struct local_address *addresses = NULL;
445                 const char *on_color_operational, *off_color_operational;
446                 int i, c;
447
448                 sd_network_get_operational_state(&operational_state);
449                 operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
450
451                 printf("       State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
452
453                 c = local_addresses(rtnl, 0, &addresses);
454                 for (i = 0; i < c; i++) {
455                         _cleanup_free_ char *pretty = NULL;
456
457                         r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
458                         if (r < 0)
459                                 return log_oom();
460
461                         printf("%13s %s\n",
462                                i > 0 ? "" : "Address:", pretty);
463                 }
464
465                 sd_network_get_dns(&dns);
466                 if (!strv_isempty(dns))
467                         dump_list("         DNS: ", dns);
468
469                 sd_network_get_dns(&ntp);
470                 if (!strv_isempty(ntp))
471                         dump_list("         NTP: ", ntp);
472
473                 return 0;
474         }
475
476         pager_open_if_enabled();
477
478         if (arg_all) {
479                 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
480                 _cleanup_free_ LinkInfo *links = NULL;
481                 int c, i;
482
483                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
484                 if (r < 0)
485                         return rtnl_log_create_error(r);
486
487                 r = sd_rtnl_message_request_dump(req, true);
488                 if (r < 0)
489                         return rtnl_log_create_error(r);
490
491                 r = sd_rtnl_call(rtnl, req, 0, &reply);
492                 if (r < 0) {
493                         log_error("Failed to enumerate links: %s", strerror(-r));
494                         return r;
495                 }
496
497                 c = decode_and_sort_links(reply, &links);
498                 if (c < 0)
499                         return rtnl_log_parse_error(c);
500
501                 for (i = 0; i < c; i++) {
502                         if (i > 0)
503                                 fputc('\n', stdout);
504
505                         link_status_one(rtnl, udev, links[i].name);
506                 }
507         }
508
509         STRV_FOREACH(name, args + 1) {
510                 if (name != args+1)
511                         fputc('\n', stdout);
512
513                 link_status_one(rtnl, udev, *name);
514         }
515
516         return 0;
517 }
518
519 static void help(void) {
520         printf("%s [OPTIONS...]\n\n"
521                "Query and control the networking subsystem.\n\n"
522                "  -h --help             Show this help\n"
523                "     --version          Show package version\n"
524                "     --no-pager         Do not pipe output into a pager\n"
525                "     --no-legend        Do not show the headers and footers\n"
526                "  -a --all              Show status for all links\n\n"
527                "Commands:\n"
528                "  list                  List links\n"
529                "  status LINK           Show link status\n"
530                , program_invocation_short_name);
531 }
532
533 static int parse_argv(int argc, char *argv[]) {
534
535         enum {
536                 ARG_VERSION = 0x100,
537                 ARG_NO_PAGER,
538                 ARG_NO_LEGEND,
539         };
540
541         static const struct option options[] = {
542                 { "help",      no_argument,       NULL, 'h'           },
543                 { "version",   no_argument,       NULL, ARG_VERSION   },
544                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
545                 { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
546                 { "all",       no_argument,       NULL, 'a'           },
547                 {}
548         };
549
550         int c;
551
552         assert(argc >= 0);
553         assert(argv);
554
555         while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
556
557                 switch (c) {
558
559                 case 'h':
560                         help();
561                         return 0;
562
563                 case ARG_VERSION:
564                         puts(PACKAGE_STRING);
565                         puts(SYSTEMD_FEATURES);
566                         return 0;
567
568                 case ARG_NO_PAGER:
569                         arg_no_pager = true;
570                         break;
571
572                 case ARG_NO_LEGEND:
573                         arg_legend = false;
574                         break;
575
576                 case 'a':
577                         arg_all = true;
578                         break;
579
580                 case '?':
581                         return -EINVAL;
582
583                 default:
584                         assert_not_reached("Unhandled option");
585                 }
586         }
587
588         return 1;
589 }
590
591 static int networkctl_main(int argc, char *argv[]) {
592
593         static const struct {
594                 const char* verb;
595                 const enum {
596                         MORE,
597                         LESS,
598                         EQUAL
599                 } argc_cmp;
600                 const int argc;
601                 int (* const dispatch)(char **args, unsigned n);
602         } verbs[] = {
603                 { "list",   LESS, 1, list_links  },
604                 { "status", MORE, 1, link_status },
605         };
606
607         int left;
608         unsigned i;
609
610         assert(argc >= 0);
611         assert(argv);
612
613         left = argc - optind;
614
615         if (left <= 0)
616                 /* Special rule: no arguments means "list" */
617                 i = 0;
618         else {
619                 if (streq(argv[optind], "help")) {
620                         help();
621                         return 0;
622                 }
623
624                 for (i = 0; i < ELEMENTSOF(verbs); i++)
625                         if (streq(argv[optind], verbs[i].verb))
626                                 break;
627
628                 if (i >= ELEMENTSOF(verbs)) {
629                         log_error("Unknown operation %s", argv[optind]);
630                         return -EINVAL;
631                 }
632         }
633
634         switch (verbs[i].argc_cmp) {
635
636         case EQUAL:
637                 if (left != verbs[i].argc) {
638                         log_error("Invalid number of arguments.");
639                         return -EINVAL;
640                 }
641
642                 break;
643
644         case MORE:
645                 if (left < verbs[i].argc) {
646                         log_error("Too few arguments.");
647                         return -EINVAL;
648                 }
649
650                 break;
651
652         case LESS:
653                 if (left > verbs[i].argc) {
654                         log_error("Too many arguments.");
655                         return -EINVAL;
656                 }
657
658                 break;
659
660         default:
661                 assert_not_reached("Unknown comparison operator.");
662         }
663
664         return verbs[i].dispatch(argv + optind, left);
665 }
666
667 int main(int argc, char* argv[]) {
668         int r;
669
670         log_parse_environment();
671         log_open();
672
673         r = parse_argv(argc, argv);
674         if (r <= 0)
675                 goto finish;
676
677         r = networkctl_main(argc, argv);
678
679 finish:
680         pager_close();
681
682         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
683 }