chiark / gitweb /
9aec3f418a4fe0adf60d76fd60ad70194cd38b3d
[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 static int list_links(char **args, unsigned n) {
92         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
93         _cleanup_udev_unref_ struct udev *udev = NULL;
94         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
95         sd_rtnl_message *i;
96         unsigned c = 0;
97         int r;
98
99         pager_open_if_enabled();
100
101         r = sd_rtnl_open(&rtnl, 0);
102         if (r < 0) {
103                 log_error("Failed to connect to netlink: %s", strerror(-r));
104                 return r;
105         }
106
107         udev = udev_new();
108         if (!udev) {
109                 log_error("Failed to connect to udev: %m");
110                 return -errno;
111         }
112
113         r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
114         if (r < 0)
115                 return rtnl_log_create_error(r);
116
117         r = sd_rtnl_message_request_dump(req, true);
118         if (r < 0)
119                 return rtnl_log_create_error(r);
120
121         r = sd_rtnl_call(rtnl, req, 0, &reply);
122         if (r < 0) {
123                 log_error("Failed to enumerate links: %s", strerror(-r));
124                 return r;
125         }
126
127         if (arg_legend)
128                 printf("%3s %-16s %-10s %-10s %-10s\n", "IDX", "LINK", "TYPE", "ADMIN", "OPERATIONAL");
129
130         for (i = reply; i; i = sd_rtnl_message_next(i)) {
131                 _cleanup_free_ char *state = NULL, *operational_state = NULL;
132                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
133                 const char *on_color = "", *off_color = "";
134                  char devid[2 + DECIMAL_STR_MAX(int)];
135                 _cleanup_free_ char *t = NULL;
136                 const char *name;
137                 unsigned iftype;
138                 uint16_t type;
139                 int ifindex;
140
141                 r = sd_rtnl_message_get_type(i, &type);
142                 if (r < 0)
143                         return rtnl_log_parse_error(r);
144
145                 if (type != RTM_NEWLINK)
146                         continue;
147
148                 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
149                 if (r < 0)
150                         return rtnl_log_parse_error(r);
151
152                 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
153                 if (r < 0)
154                         return rtnl_log_parse_error(r);
155
156                 r = sd_rtnl_message_link_get_type(i, &iftype);
157                 if (r < 0)
158                         return rtnl_log_parse_error(r);
159
160                 sd_network_get_link_state(ifindex, &state);
161                 sd_network_get_link_operational_state(ifindex, &operational_state);
162
163                 sprintf(devid, "n%i", ifindex);
164                 d = udev_device_new_from_device_id(udev, devid);
165
166                 link_get_type_string(iftype, d, &t);
167
168                 if (streq_ptr(operational_state, "routable")) {
169                         on_color = ansi_highlight_green();
170                         off_color = ansi_highlight_off();
171                 } else if (streq_ptr(operational_state, "degraded")) {
172                         on_color = ansi_highlight_yellow();
173                         off_color = ansi_highlight_off();
174                 }
175
176                 printf("%3i %-16s %-10s %-10s %s%-10s%s\n", ifindex, name, strna(t), strna(state), on_color, strna(operational_state), off_color);
177                 c++;
178         }
179
180         if (arg_legend)
181                 printf("\n%u links listed.\n", c);
182
183         return 0;
184 }
185
186 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
187         _cleanup_free_ struct local_address *local = NULL;
188         int r, n, i;
189
190         n = local_addresses(rtnl, ifindex, &local);
191         if (n < 0)
192                 return n;
193
194         for (i = 0; i < n; i++) {
195                 _cleanup_free_ char *pretty = NULL;
196
197                 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
198                 if (r < 0)
199                         return r;
200
201                 printf("%*s%s\n",
202                        (int) strlen(prefix),
203                        i == 0 ? prefix : "",
204                        pretty);
205         }
206
207         return 0;
208 }
209
210 static void dump_list(const char *prefix, char **l) {
211         char **i;
212
213         STRV_FOREACH(i, l) {
214                 printf("%*s%s\n",
215                        (int) strlen(prefix),
216                        i == l ? prefix : "",
217                        *i);
218         }
219 }
220
221 static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
222         _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
223         _cleanup_free_ char *state = NULL, *operational_state = NULL;
224         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
225         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
226         char devid[2 + DECIMAL_STR_MAX(int)];
227         _cleanup_free_ char *t = NULL;
228         const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
229         const char *on_color = "", *off_color = "";
230         struct ether_addr e;
231         unsigned iftype;
232         int r, ifindex;
233         bool have_mac;
234         uint32_t mtu;
235
236         assert(rtnl);
237         assert(udev);
238         assert(name);
239
240         if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
241                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
242         else {
243                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
244                 if (r < 0)
245                         return rtnl_log_create_error(r);
246
247                 r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
248         }
249
250         if (r < 0)
251                 return rtnl_log_create_error(r);
252
253         r = sd_rtnl_call(rtnl, req, 0, &reply);
254         if (r < 0) {
255                 log_error("Failed to query link: %s", strerror(-r));
256                 return r;
257         }
258
259         r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
260         if (r < 0)
261                 return rtnl_log_parse_error(r);
262
263         r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
264         if (r < 0)
265                 return rtnl_log_parse_error(r);
266
267         r = sd_rtnl_message_link_get_type(reply, &iftype);
268         if (r < 0)
269                 return rtnl_log_parse_error(r);
270
271         have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
272
273         if (have_mac) {
274                 const uint8_t *p;
275                 bool all_zeroes = true;
276
277                 for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
278                         if (*p != 0) {
279                                 all_zeroes = false;
280                                 break;
281                         }
282
283                 if (all_zeroes)
284                         have_mac = false;
285         }
286
287         sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
288
289         sd_network_get_link_state(ifindex, &state);
290         sd_network_get_link_operational_state(ifindex, &operational_state);
291
292         sd_network_get_link_dns(ifindex, &dns);
293         sd_network_get_link_ntp(ifindex, &ntp);
294
295         sprintf(devid, "n%i", ifindex);
296         d = udev_device_new_from_device_id(udev, devid);
297
298         link_get_type_string(iftype, d, &t);
299
300         if (d) {
301                 driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
302                 path = udev_device_get_property_value(d, "ID_PATH");
303
304                 vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
305                 if (!vendor)
306                         vendor = udev_device_get_property_value(d, "ID_VENDOR");
307
308                 model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
309                 if (!model)
310                         model = udev_device_get_property_value(d, "ID_MODEL");
311         }
312
313         if (streq_ptr(operational_state, "routable")) {
314                 on_color = ansi_highlight_green();
315                 off_color = ansi_highlight_off();
316         } else if (streq_ptr(operational_state, "degraded")) {
317                 on_color = ansi_highlight_yellow();
318                 off_color = ansi_highlight_off();
319         }
320
321         printf("%s%s%s %i: %s\n", on_color, draw_special_char(DRAW_BLACK_CIRCLE), off_color, ifindex, name);
322
323         printf("        Type: %s\n"
324                "       State: %s%s%s (%s)\n",
325                strna(t),
326                on_color, strna(operational_state), off_color,
327                strna(state));
328
329         if (path)
330                 printf("        Path: %s\n", path);
331         if (driver)
332                 printf("      Driver: %s\n", driver);
333         if (vendor)
334                 printf("      Vendor: %s\n", vendor);
335         if (model)
336                 printf("       Model: %s\n", model);
337
338         if (have_mac)
339                 printf("  HW Address: %s\n", ether_ntoa(&e));
340
341         if (mtu > 0)
342                 printf("         MTU: %u\n", mtu);
343
344         dump_addresses(rtnl, "     Address: ", ifindex);
345
346         if (!strv_isempty(dns))
347                 dump_list("         DNS: ", dns);
348         if (!strv_isempty(ntp))
349                 dump_list("         NTP: ", ntp);
350
351         return 0;
352 }
353
354 static int link_status(char **args, unsigned n) {
355         _cleanup_udev_unref_ struct udev *udev = NULL;
356         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
357         char **name;
358         int r;
359
360         if (n <= 1 && !arg_all) {
361                 _cleanup_free_ char *operational_state = NULL;
362                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
363
364                 sd_network_get_operational_state(&operational_state);
365                 if (operational_state)
366                         printf("       State: %s\n", operational_state);
367
368                 sd_network_get_dns(&dns);
369                 if (!strv_isempty(dns))
370                         dump_list("         DNS: ", dns);
371
372                 sd_network_get_dns(&ntp);
373                 if (!strv_isempty(ntp))
374                         dump_list("         NTP: ", ntp);
375
376                 return 0;
377         }
378
379         pager_open_if_enabled();
380
381         r = sd_rtnl_open(&rtnl, 0);
382         if (r < 0) {
383                 log_error("Failed to connect to netlink: %s", strerror(-r));
384                 return r;
385         }
386
387         udev = udev_new();
388         if (!udev) {
389                 log_error("Failed to connect to udev: %m");
390                 return -errno;
391         }
392
393         if (arg_all) {
394                 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
395                 sd_rtnl_message *i;
396                 bool space = false;
397                 uint16_t type;
398
399                 r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
400                 if (r < 0)
401                         return rtnl_log_create_error(r);
402
403                 r = sd_rtnl_message_request_dump(req, true);
404                 if (r < 0)
405                         return rtnl_log_create_error(r);
406
407                 r = sd_rtnl_call(rtnl, req, 0, &reply);
408                 if (r < 0) {
409                         log_error("Failed to enumerate links: %s", strerror(-r));
410                         return r;
411                 }
412
413                 for (i = reply; i; i = sd_rtnl_message_next(i)) {
414                         const char *nn;
415
416                         r = sd_rtnl_message_get_type(i, &type);
417                         if (r < 0)
418                                 return rtnl_log_parse_error(r);
419
420                         if (type != RTM_NEWLINK)
421                                 continue;
422
423                         r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &nn);
424                         if (r < 0)
425                                 return rtnl_log_parse_error(r);
426
427                         if (space)
428                                 fputc('\n', stdout);
429
430                         link_status_one(rtnl, udev, nn);
431                         space = true;
432                 }
433         }
434
435         STRV_FOREACH(name, args + 1) {
436                 if (name != args+1)
437                         fputc('\n', stdout);
438
439                 link_status_one(rtnl, udev, *name);
440         }
441
442         return 0;
443 }
444
445 static void help(void) {
446         printf("%s [OPTIONS...]\n\n"
447                "Query and control the networking subsystem.\n\n"
448                "  -h --help             Show this help\n"
449                "     --version          Show package version\n"
450                "     --no-pager         Do not pipe output into a pager\n"
451                "     --no-legend        Do not show the headers and footers\n"
452                "  -a --all              Show status for all links\n\n"
453                "Commands:\n"
454                "  list                  List links\n"
455                "  status LINK           Show link status\n"
456                , program_invocation_short_name);
457 }
458
459 static int parse_argv(int argc, char *argv[]) {
460
461         enum {
462                 ARG_VERSION = 0x100,
463                 ARG_NO_PAGER,
464                 ARG_NO_LEGEND,
465         };
466
467         static const struct option options[] = {
468                 { "help",      no_argument,       NULL, 'h'           },
469                 { "version",   no_argument,       NULL, ARG_VERSION   },
470                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
471                 { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
472                 { "all",       no_argument,       NULL, 'a'           },
473                 {}
474         };
475
476         int c;
477
478         assert(argc >= 0);
479         assert(argv);
480
481         while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
482
483                 switch (c) {
484
485                 case 'h':
486                         help();
487                         return 0;
488
489                 case ARG_VERSION:
490                         puts(PACKAGE_STRING);
491                         puts(SYSTEMD_FEATURES);
492                         return 0;
493
494                 case ARG_NO_PAGER:
495                         arg_no_pager = true;
496                         break;
497
498                 case ARG_NO_LEGEND:
499                         arg_legend = false;
500                         break;
501
502                 case 'a':
503                         arg_all = true;
504                         break;
505
506                 case '?':
507                         return -EINVAL;
508
509                 default:
510                         assert_not_reached("Unhandled option");
511                 }
512         }
513
514         return 1;
515 }
516
517 static int networkctl_main(int argc, char *argv[]) {
518
519         static const struct {
520                 const char* verb;
521                 const enum {
522                         MORE,
523                         LESS,
524                         EQUAL
525                 } argc_cmp;
526                 const int argc;
527                 int (* const dispatch)(char **args, unsigned n);
528         } verbs[] = {
529                 { "list",   LESS, 1, list_links  },
530                 { "status", MORE, 1, link_status },
531         };
532
533         int left;
534         unsigned i;
535
536         assert(argc >= 0);
537         assert(argv);
538
539         left = argc - optind;
540
541         if (left <= 0)
542                 /* Special rule: no arguments means "list" */
543                 i = 0;
544         else {
545                 if (streq(argv[optind], "help")) {
546                         help();
547                         return 0;
548                 }
549
550                 for (i = 0; i < ELEMENTSOF(verbs); i++)
551                         if (streq(argv[optind], verbs[i].verb))
552                                 break;
553
554                 if (i >= ELEMENTSOF(verbs)) {
555                         log_error("Unknown operation %s", argv[optind]);
556                         return -EINVAL;
557                 }
558         }
559
560         switch (verbs[i].argc_cmp) {
561
562         case EQUAL:
563                 if (left != verbs[i].argc) {
564                         log_error("Invalid number of arguments.");
565                         return -EINVAL;
566                 }
567
568                 break;
569
570         case MORE:
571                 if (left < verbs[i].argc) {
572                         log_error("Too few arguments.");
573                         return -EINVAL;
574                 }
575
576                 break;
577
578         case LESS:
579                 if (left > verbs[i].argc) {
580                         log_error("Too many arguments.");
581                         return -EINVAL;
582                 }
583
584                 break;
585
586         default:
587                 assert_not_reached("Unknown comparison operator.");
588         }
589
590         return verbs[i].dispatch(argv + optind, left);
591 }
592
593 int main(int argc, char* argv[]) {
594         int r;
595
596         log_parse_environment();
597         log_open();
598
599         r = parse_argv(argc, argv);
600         if (r <= 0)
601                 goto finish;
602
603         r = networkctl_main(argc, argv);
604
605 finish:
606         pager_close();
607
608         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
609 }