chiark / gitweb /
networkd: add minimal client tool "networkd" to query network status
[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
220                 r = sd_network_get_operational_state(&operational_state);
221                 if (r < 0) {
222                         log_error("Failed to get operational state: %s", strerror(-r));
223                         return r;
224                 }
225
226                 printf("State: %s\n", operational_state);
227                 return 0;
228         }
229
230         pager_open_if_enabled();
231
232         r = sd_rtnl_open(&rtnl, 0);
233         if (r < 0) {
234                 log_error("Failed to connect to netlink: %s", strerror(-r));
235                 return r;
236         }
237
238         udev = udev_new();
239         if (!udev) {
240                 log_error("Failed to connect to udev: %m");
241                 return -errno;
242         }
243
244         STRV_FOREACH(name, args + 1) {
245                 _cleanup_strv_free_ char **dns = NULL, **ntp = NULL;
246                 _cleanup_free_ char *state = NULL, *operational_state = NULL;
247                 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
248                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
249                 const char *canonical_name = NULL;
250                 char devid[2 + DECIMAL_STR_MAX(int)];
251                 int ifindex, canonical_ifindex;
252                 _cleanup_free_ char *t = NULL;
253                 const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
254                 struct ether_addr e;
255                 unsigned iftype;
256                 bool have_mac;
257                 uint32_t mtu;
258
259                 if (name != args+1)
260                         printf("\n");
261
262                 if (safe_atoi(*name, &ifindex) >= 0 && ifindex > 0)
263                         r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
264                 else {
265                         r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
266                         if (r < 0)
267                                 return rtnl_log_create_error(r);
268
269                         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, *name);
270                 }
271
272                 if (r < 0)
273                         return rtnl_log_create_error(r);
274
275                 r = sd_rtnl_call(rtnl, req, 0, &reply);
276                 if (r < 0) {
277                         log_error("Failed to query link: %s", strerror(-r));
278                         continue;
279                 }
280
281                 r = sd_rtnl_message_link_get_ifindex(reply, &canonical_ifindex);
282                 if (r < 0)
283                         return rtnl_log_parse_error(r);
284
285                 r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &canonical_name);
286                 if (r < 0)
287                         return rtnl_log_parse_error(r);
288
289                 r = sd_rtnl_message_link_get_type(reply, &iftype);
290                 if (r < 0)
291                         return rtnl_log_parse_error(r);
292
293                 have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
294
295                 if (have_mac) {
296                         const uint8_t *p;
297                         bool all_zeroes = true;
298
299                         for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
300                                 if (*p != 0) {
301                                         all_zeroes = false;
302                                         break;
303                                 }
304
305                         if (all_zeroes)
306                                 have_mac = false;
307                 }
308
309                 sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
310
311                 sd_network_get_link_state(canonical_ifindex, &state);
312                 sd_network_get_link_operational_state(canonical_ifindex, &operational_state);
313
314                 sd_network_get_link_dns(canonical_ifindex, &dns);
315                 sd_network_get_link_dns(canonical_ifindex, &ntp);
316
317                 sprintf(devid, "n%i", canonical_ifindex);
318                 d = udev_device_new_from_device_id(udev, devid);
319
320                 link_get_type_string(iftype, d, &t);
321
322                 if (d) {
323                         driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
324                         path = udev_device_get_property_value(d, "ID_PATH");
325
326                         vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
327                         if (!vendor)
328                                 vendor = udev_device_get_property_value(d, "ID_VENDOR");
329
330                         model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
331                         if (!model)
332                                 model = udev_device_get_property_value(d, "ID_MODEL");
333                 }
334
335                 printf("%i: %s\n", canonical_ifindex, canonical_name);
336
337                 printf("        Type: %s\n"
338                        "       State: %s (%s)\n",
339                        strna(t),
340                        strna(operational_state),
341                        strna(state));
342
343                 if (path)
344                         printf("        Path: %s\n", path);
345                 if (driver)
346                         printf("      Driver: %s\n", driver);
347                 if (vendor)
348                         printf("      Vendor: %s\n", vendor);
349                 if (model)
350                         printf("       Model: %s\n", model);
351
352                 if (have_mac) {
353                         _cleanup_free_ char *h = NULL;
354
355                         h = hexmem(&e, sizeof(e));
356                         if (!h)
357                                 return log_oom();
358
359                         printf("  HW Address: %s\n", h);
360                 }
361
362                 if (mtu > 0)
363                         printf("         MTU: %u\n", mtu);
364
365                 dump_addresses(rtnl, "     Address: ", canonical_ifindex);
366
367                 if (!strv_isempty(dns))
368                         dump_list("         DNS: ", dns);
369                 if (!strv_isempty(ntp))
370                         dump_list("         NTP: ", ntp);
371         }
372
373         return 0;
374 }
375
376 static void help(void) {
377         printf("%s [OPTIONS...]\n\n"
378                "Query and control the networking subsystem.\n\n"
379                "  -h --help             Show this help\n"
380                "     --version          Show package version\n\n"
381                "Commands:\n"
382                "  list                  List links\n"
383                "  status LINK           Show link status\n"
384                , program_invocation_short_name);
385 }
386
387 static int parse_argv(int argc, char *argv[]) {
388
389         enum {
390                 ARG_VERSION = 0x100,
391                 ARG_NO_PAGER,
392                 ARG_NO_LEGEND,
393         };
394
395         static const struct option options[] = {
396                 { "help",      no_argument,       NULL, 'h'           },
397                 { "version",   no_argument,       NULL, ARG_VERSION   },
398                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
399                 { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
400                 {}
401         };
402
403         int c;
404
405         assert(argc >= 0);
406         assert(argv);
407
408         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
409
410                 switch (c) {
411
412                 case 'h':
413                         help();
414                         return 0;
415
416                 case ARG_VERSION:
417                         puts(PACKAGE_STRING);
418                         puts(SYSTEMD_FEATURES);
419                         return 0;
420
421                 case ARG_NO_PAGER:
422                         arg_no_pager = true;
423                         break;
424
425                 case ARG_NO_LEGEND:
426                         arg_legend = false;
427                         break;
428
429                 case '?':
430                         return -EINVAL;
431
432                 default:
433                         assert_not_reached("Unhandled option");
434                 }
435         }
436
437         return 1;
438 }
439
440 static int networkctl_main(int argc, char *argv[]) {
441
442         static const struct {
443                 const char* verb;
444                 const enum {
445                         MORE,
446                         LESS,
447                         EQUAL
448                 } argc_cmp;
449                 const int argc;
450                 int (* const dispatch)(char **args, unsigned n);
451         } verbs[] = {
452                 { "list",   LESS, 1, list_links  },
453                 { "status", MORE, 1, link_status },
454         };
455
456         int left;
457         unsigned i;
458
459         assert(argc >= 0);
460         assert(argv);
461
462         left = argc - optind;
463
464         if (left <= 0)
465                 /* Special rule: no arguments means "list" */
466                 i = 0;
467         else {
468                 if (streq(argv[optind], "help")) {
469                         help();
470                         return 0;
471                 }
472
473                 for (i = 0; i < ELEMENTSOF(verbs); i++)
474                         if (streq(argv[optind], verbs[i].verb))
475                                 break;
476
477                 if (i >= ELEMENTSOF(verbs)) {
478                         log_error("Unknown operation %s", argv[optind]);
479                         return -EINVAL;
480                 }
481         }
482
483         switch (verbs[i].argc_cmp) {
484
485         case EQUAL:
486                 if (left != verbs[i].argc) {
487                         log_error("Invalid number of arguments.");
488                         return -EINVAL;
489                 }
490
491                 break;
492
493         case MORE:
494                 if (left < verbs[i].argc) {
495                         log_error("Too few arguments.");
496                         return -EINVAL;
497                 }
498
499                 break;
500
501         case LESS:
502                 if (left > verbs[i].argc) {
503                         log_error("Too many arguments.");
504                         return -EINVAL;
505                 }
506
507                 break;
508
509         default:
510                 assert_not_reached("Unknown comparison operator.");
511         }
512
513         return verbs[i].dispatch(argv + optind, left);
514 }
515
516 int main(int argc, char* argv[]) {
517         int r;
518
519         log_parse_environment();
520         log_open();
521
522         r = parse_argv(argc, argv);
523         if (r <= 0)
524                 goto finish;
525
526         r = networkctl_main(argc, argv);
527
528 finish:
529         pager_close();
530
531         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
532 }