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