chiark / gitweb /
sd-network: add API to output all collected system-wide NTP and DNS servers
[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
40 static void pager_open_if_enabled(void) {
41
42         if (arg_no_pager)
43                 return;
44
45         pager_open(false);
46 }
47
48 static int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
49         const char *t;
50         char *p;
51
52         if (iftype == ARPHRD_ETHER && d) {
53                 const char *devtype, *id = NULL;
54                 /* WLANs have iftype ARPHRD_ETHER, but we want
55                  * to show a more useful type string for
56                  * them */
57
58                 devtype = udev_device_get_devtype(d);
59                 if (streq_ptr(devtype, "wlan"))
60                         id = "wlan";
61                 else if (streq_ptr(devtype, "wwan"))
62                         id = "wwan";
63
64                 if (id) {
65                         p = strdup(id);
66                         if (!p)
67                                 return -ENOMEM;
68
69                         *ret = p;
70                         return 1;
71                 }
72         }
73
74         t = arphrd_to_name(iftype);
75         if (!t) {
76                 *ret = NULL;
77                 return 0;
78         }
79
80         p = strdup(t);
81         if (!p)
82                 return -ENOMEM;
83
84         ascii_strlower(p);
85         *ret = p;
86
87         return 0;
88 }
89
90 static int list_links(char **args, unsigned n) {
91         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
92         _cleanup_udev_unref_ struct udev *udev = NULL;
93         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
94         sd_rtnl_message *i;
95         unsigned c = 0;
96         int r;
97
98         pager_open_if_enabled();
99
100         r = sd_rtnl_open(&rtnl, 0);
101         if (r < 0) {
102                 log_error("Failed to connect to netlink: %s", strerror(-r));
103                 return r;
104         }
105
106         udev = udev_new();
107         if (!udev) {
108                 log_error("Failed to connect to udev: %m");
109                 return -errno;
110         }
111
112         r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
113         if (r < 0)
114                 return rtnl_log_create_error(r);
115
116         r = sd_rtnl_message_request_dump(req, true);
117         if (r < 0)
118                 return rtnl_log_create_error(r);
119
120         r = sd_rtnl_call(rtnl, req, 0, &reply);
121         if (r < 0) {
122                 log_error("Failed to enumerate links: %s", strerror(-r));
123                 return r;
124         }
125
126         if (arg_legend)
127                 printf("%3s %-16s %-10s %-10s %-10s\n", "IDX", "LINK", "TYPE", "STATE", "OPERATIONAL");
128
129         for (i = reply; i; i = sd_rtnl_message_next(i)) {
130                 _cleanup_free_ char *state = NULL, *operational_state = NULL;
131                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
132                 char devid[2 + DECIMAL_STR_MAX(int)];
133                 _cleanup_free_ char *t = NULL;
134                 const char *name;
135                 unsigned iftype;
136                 uint16_t type;
137                 int ifindex;
138
139                 r = sd_rtnl_message_get_type(i, &type);
140                 if (r < 0)
141                         return rtnl_log_parse_error(r);
142
143                 if (type != RTM_NEWLINK)
144                         continue;
145
146                 r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
147                 if (r < 0)
148                         return rtnl_log_parse_error(r);
149
150                 r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
151                 if (r < 0)
152                         return rtnl_log_parse_error(r);
153
154                 r = sd_rtnl_message_link_get_type(i, &iftype);
155                 if (r < 0)
156                         return rtnl_log_parse_error(r);
157
158                 sd_network_get_link_state(ifindex, &state);
159                 sd_network_get_link_operational_state(ifindex, &operational_state);
160
161                 sprintf(devid, "n%i", ifindex);
162                 d = udev_device_new_from_device_id(udev, devid);
163
164                 link_get_type_string(iftype, d, &t);
165
166                 printf("%3i %-16s %-10s %-10s %-10s\n", ifindex, name, strna(t), strna(state), strna(operational_state));
167                 c++;
168         }
169
170         if (arg_legend)
171                 printf("\n%u links listed.\n", c);
172
173         return 0;
174 }
175
176 static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
177         _cleanup_free_ struct local_address *local = NULL;
178         int r, n, i;
179
180         n = local_addresses(rtnl, ifindex, &local);
181         if (n < 0)
182                 return n;
183
184         for (i = 0; i < n; i++) {
185                 _cleanup_free_ char *pretty = NULL;
186
187                 r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
188                 if (r < 0)
189                         return r;
190
191                 printf("%*s%s\n",
192                        (int) strlen(prefix),
193                        i == 0 ? prefix : "",
194                        pretty);
195         }
196
197         return 0;
198 }
199
200 static void dump_list(const char *prefix, char **l) {
201         char **i;
202
203         STRV_FOREACH(i, l) {
204                 printf("%*s%s\n",
205                        (int) strlen(prefix),
206                        i == l ? prefix : "",
207                        *i);
208         }
209 }
210
211 static int link_status(char **args, unsigned n) {
212         _cleanup_udev_unref_ struct udev *udev = NULL;
213         _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
214         char **name;
215         int r;
216
217         if (n <= 1) {
218                 _cleanup_free_ char *operational_state = NULL;
219                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
220
221                 sd_network_get_operational_state(&operational_state);
222                 if (operational_state)
223                         printf("       State: %s\n", operational_state);
224
225                 sd_network_get_dns(&dns);
226                 if (!strv_isempty(dns))
227                         dump_list("         DNS: ", dns);
228
229                 sd_network_get_dns(&ntp);
230                 if (!strv_isempty(ntp))
231                         dump_list("         NTP: ", ntp);
232
233                 return 0;
234         }
235
236         pager_open_if_enabled();
237
238         r = sd_rtnl_open(&rtnl, 0);
239         if (r < 0) {
240                 log_error("Failed to connect to netlink: %s", strerror(-r));
241                 return r;
242         }
243
244         udev = udev_new();
245         if (!udev) {
246                 log_error("Failed to connect to udev: %m");
247                 return -errno;
248         }
249
250         STRV_FOREACH(name, args + 1) {
251                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
252                 _cleanup_free_ char *state = NULL, *operational_state = NULL;
253                 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
254                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
255                 const char *canonical_name = NULL;
256                 char devid[2 + DECIMAL_STR_MAX(int)];
257                 int ifindex, canonical_ifindex;
258                 _cleanup_free_ char *t = NULL;
259                 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
260                 struct ether_addr e;
261                 unsigned iftype;
262                 bool have_mac;
263                 uint32_t mtu;
264
265                 if (name != args+1)
266                         printf("\n");
267
268                 if (safe_atoi(*name, &ifindex) >= 0 && ifindex > 0)
269                         r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
270                 else {
271                         r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
272                         if (r < 0)
273                                 return rtnl_log_create_error(r);
274
275                         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, *name);
276                 }
277
278                 if (r < 0)
279                         return rtnl_log_create_error(r);
280
281                 r = sd_rtnl_call(rtnl, req, 0, &reply);
282                 if (r < 0) {
283                         log_error("Failed to query link: %s", strerror(-r));
284                         continue;
285                 }
286
287                 r = sd_rtnl_message_link_get_ifindex(reply, &canonical_ifindex);
288                 if (r < 0)
289                         return rtnl_log_parse_error(r);
290
291                 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &canonical_name);
292                 if (r < 0)
293                         return rtnl_log_parse_error(r);
294
295                 r = sd_rtnl_message_link_get_type(reply, &iftype);
296                 if (r < 0)
297                         return rtnl_log_parse_error(r);
298
299                 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
300
301                 if (have_mac) {
302                         const uint8_t *p;
303                         bool all_zeroes = true;
304
305                         for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
306                                 if (*p != 0) {
307                                         all_zeroes = false;
308                                         break;
309                                 }
310
311                         if (all_zeroes)
312                                 have_mac = false;
313                 }
314
315                 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
316
317                 sd_network_get_link_state(canonical_ifindex, &state);
318                 sd_network_get_link_operational_state(canonical_ifindex, &operational_state);
319
320                 sd_network_get_link_dns(canonical_ifindex, &dns);
321                 sd_network_get_link_ntp(canonical_ifindex, &ntp);
322
323                 sprintf(devid, "n%i", canonical_ifindex);
324                 d = udev_device_new_from_device_id(udev, devid);
325
326                 link_get_type_string(iftype, d, &t);
327
328                 if (d) {
329                         driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
330                         path = udev_device_get_property_value(d, "ID_PATH");
331
332                         vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
333                         if (!vendor)
334                                 vendor = udev_device_get_property_value(d, "ID_VENDOR");
335
336                         model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
337                         if (!model)
338                                 model = udev_device_get_property_value(d, "ID_MODEL");
339                 }
340
341                 printf("%i: %s\n", canonical_ifindex, canonical_name);
342
343                 printf("        Type: %s\n"
344                        "       State: %s (%s)\n",
345                        strna(t),
346                        strna(operational_state),
347                        strna(state));
348
349                 if (path)
350                         printf("        Path: %s\n", path);
351                 if (driver)
352                         printf("      Driver: %s\n", driver);
353                 if (vendor)
354                         printf("      Vendor: %s\n", vendor);
355                 if (model)
356                         printf("       Model: %s\n", model);
357
358                 if (have_mac) {
359                         _cleanup_free_ char *h = NULL;
360
361                         h = hexmem(&e, sizeof(e));
362                         if (!h)
363                                 return log_oom();
364
365                         printf("  HW Address: %s\n", h);
366                 }
367
368                 if (mtu > 0)
369                         printf("         MTU: %u\n", mtu);
370
371                 dump_addresses(rtnl, "     Address: ", canonical_ifindex);
372
373                 if (!strv_isempty(dns))
374                         dump_list("         DNS: ", dns);
375                 if (!strv_isempty(ntp))
376                         dump_list("         NTP: ", ntp);
377         }
378
379         return 0;
380 }
381
382 static void help(void) {
383         printf("%s [OPTIONS...]\n\n"
384                "Query and control the networking subsystem.\n\n"
385                "  -h --help             Show this help\n"
386                "     --version          Show package version\n\n"
387                "Commands:\n"
388                "  list                  List links\n"
389                "  status LINK           Show link status\n"
390                , program_invocation_short_name);
391 }
392
393 static int parse_argv(int argc, char *argv[]) {
394
395         enum {
396                 ARG_VERSION = 0x100,
397                 ARG_NO_PAGER,
398                 ARG_NO_LEGEND,
399         };
400
401         static const struct option options[] = {
402                 { "help",      no_argument,       NULL, 'h'           },
403                 { "version",   no_argument,       NULL, ARG_VERSION   },
404                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
405                 { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
406                 {}
407         };
408
409         int c;
410
411         assert(argc >= 0);
412         assert(argv);
413
414         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
415
416                 switch (c) {
417
418                 case 'h':
419                         help();
420                         return 0;
421
422                 case ARG_VERSION:
423                         puts(PACKAGE_STRING);
424                         puts(SYSTEMD_FEATURES);
425                         return 0;
426
427                 case ARG_NO_PAGER:
428                         arg_no_pager = true;
429                         break;
430
431                 case ARG_NO_LEGEND:
432                         arg_legend = false;
433                         break;
434
435                 case '?':
436                         return -EINVAL;
437
438                 default:
439                         assert_not_reached("Unhandled option");
440                 }
441         }
442
443         return 1;
444 }
445
446 static int networkctl_main(int argc, char *argv[]) {
447
448         static const struct {
449                 const char* verb;
450                 const enum {
451                         MORE,
452                         LESS,
453                         EQUAL
454                 } argc_cmp;
455                 const int argc;
456                 int (* const dispatch)(char **args, unsigned n);
457         } verbs[] = {
458                 { "list",   LESS, 1, list_links  },
459                 { "status", MORE, 1, link_status },
460         };
461
462         int left;
463         unsigned i;
464
465         assert(argc >= 0);
466         assert(argv);
467
468         left = argc - optind;
469
470         if (left <= 0)
471                 /* Special rule: no arguments means "list" */
472                 i = 0;
473         else {
474                 if (streq(argv[optind], "help")) {
475                         help();
476                         return 0;
477                 }
478
479                 for (i = 0; i < ELEMENTSOF(verbs); i++)
480                         if (streq(argv[optind], verbs[i].verb))
481                                 break;
482
483                 if (i >= ELEMENTSOF(verbs)) {
484                         log_error("Unknown operation %s", argv[optind]);
485                         return -EINVAL;
486                 }
487         }
488
489         switch (verbs[i].argc_cmp) {
490
491         case EQUAL:
492                 if (left != verbs[i].argc) {
493                         log_error("Invalid number of arguments.");
494                         return -EINVAL;
495                 }
496
497                 break;
498
499         case MORE:
500                 if (left < verbs[i].argc) {
501                         log_error("Too few arguments.");
502                         return -EINVAL;
503                 }
504
505                 break;
506
507         case LESS:
508                 if (left > verbs[i].argc) {
509                         log_error("Too many arguments.");
510                         return -EINVAL;
511                 }
512
513                 break;
514
515         default:
516                 assert_not_reached("Unknown comparison operator.");
517         }
518
519         return verbs[i].dispatch(argv + optind, left);
520 }
521
522 int main(int argc, char* argv[]) {
523         int r;
524
525         log_parse_environment();
526         log_open();
527
528         r = parse_argv(argc, argv);
529         if (r <= 0)
530                 goto finish;
531
532         r = networkctl_main(argc, argv);
533
534 finish:
535         pager_close();
536
537         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
538 }