networkctl.c revision ddb7f7fc07a31937989afad53edb87b50e2cad72
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen/***
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen This file is part of systemd.
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen Copyright 2014 Lennart Poettering
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen systemd is free software; you can redistribute it and/or modify it
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen under the terms of the GNU Lesser General Public License as published by
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen the Free Software Foundation; either version 2.1 of the License, or
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen (at your option) any later version.
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen systemd is distributed in the hope that it will be useful, but
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen WITHOUT ANY WARRANTY; without even the implied warranty of
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen Lesser General Public License for more details.
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen You should have received a copy of the GNU Lesser General Public License
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen along with systemd; If not, see <http://www.gnu.org/licenses/>.
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen***/
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include <stdbool.h>
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include <getopt.h>
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "sd-network.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "sd-rtnl.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "libudev.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "build.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "util.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "pager.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "rtnl-util.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "udev-util.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "arphrd-list.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "local-addresses.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen#include "socket-util.h"
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic bool arg_no_pager = false;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic bool arg_legend = true;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic bool arg_all = false;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic void pager_open_if_enabled(void) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (arg_no_pager)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen pager_open(false);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen}
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic int link_get_type_string(int iftype, struct udev_device *d, char **ret) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen const char *t;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen char *p;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (iftype == ARPHRD_ETHER && d) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen const char *devtype, *id = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen /* WLANs have iftype ARPHRD_ETHER, but we want
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen * to show a more useful type string for
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen * them */
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen devtype = udev_device_get_devtype(d);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (streq_ptr(devtype, "wlan"))
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen id = "wlan";
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen else if (streq_ptr(devtype, "wwan"))
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen id = "wwan";
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (id) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen p = strdup(id);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (!p)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return -ENOMEM;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *ret = p;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return 1;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen t = arphrd_to_name(iftype);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (!t) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *ret = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return 0;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen p = strdup(t);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (!p)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return -ENOMEM;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen ascii_strlower(p);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *ret = p;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return 0;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen}
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelementypedef struct LinkInfo {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen const char *name;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen int ifindex;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen unsigned iftype;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen} LinkInfo;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic int link_info_compare(const void *a, const void *b) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen const LinkInfo *x = a, *y = b;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return x->ifindex - y->ifindex;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen}
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic int decode_and_sort_links(sd_rtnl_message *m, LinkInfo **ret) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_free_ LinkInfo *links = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen size_t size = 0, c = 0;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen sd_rtnl_message *i;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen int r;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen for (i = m; i; i = sd_rtnl_message_next(i)) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen const char *name;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen unsigned iftype;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen uint16_t type;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen int ifindex;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_message_get_type(i, &type);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return r;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (type != RTM_NEWLINK)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen continue;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return r;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_message_read_string(i, IFLA_IFNAME, &name);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return r;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_message_link_get_type(i, &iftype);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return r;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (!GREEDY_REALLOC(links, size, c+1))
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return -ENOMEM;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen links[c].name = name;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen links[c].ifindex = ifindex;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen links[c].iftype = iftype;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen c++;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen qsort_safe(links, c, sizeof(LinkInfo), link_info_compare);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *ret = links;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen links = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return (int) c;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen}
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic void operational_state_to_color(const char *state, const char **on, const char **off) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen assert(on);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen assert(off);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (streq_ptr(state, "routable")) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on = ansi_highlight_green();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *off = ansi_highlight_off();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen } else if (streq_ptr(state, "degraded")) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on = ansi_highlight_yellow();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *off = ansi_highlight_off();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen } else
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on = *off = "";
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen}
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic void setup_state_to_color(const char *state, const char **on, const char **off) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen assert(on);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen assert(off);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (streq_ptr(state, "configured")) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on = ansi_highlight_green();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *off = ansi_highlight_off();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen } else if (streq_ptr(state, "configuring")) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on = ansi_highlight_yellow();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *off = ansi_highlight_off();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on = ansi_highlight_red();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *off = ansi_highlight_off();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen } else
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on = *off = "";
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen}
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic int list_links(char **args, unsigned n) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_udev_unref_ struct udev *udev = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_free_ LinkInfo *links = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen int r, c, i;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen pager_open_if_enabled();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_open(&rtnl, 0);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen log_error("Failed to connect to netlink: %s", strerror(-r));
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return r;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen udev = udev_new();
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (!udev) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen log_error("Failed to connect to udev: %m");
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return -errno;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return rtnl_log_create_error(r);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_message_request_dump(req, true);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return rtnl_log_create_error(r);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen r = sd_rtnl_call(rtnl, req, 0, &reply);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (r < 0) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen log_error("Failed to enumerate links: %s", strerror(-r));
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return r;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (arg_legend)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP");
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen c = decode_and_sort_links(reply, &links);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (c < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return rtnl_log_parse_error(c);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen for (i = 0; i < c; i++) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_udev_device_unref_ struct udev_device *d = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen const char *on_color_operational, *off_color_operational,
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen *on_color_setup, *off_color_setup;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen char devid[2 + DECIMAL_STR_MAX(int)];
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_free_ char *t = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen sprintf(devid, "n%i", links[i].ifindex);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen d = udev_device_new_from_device_id(udev, devid);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen link_get_type_string(links[i].iftype, d, &t);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n",
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen links[i].ifindex, links[i].name, strna(t),
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen on_color_operational, strna(operational_state), off_color_operational,
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen on_color_setup, strna(setup_state), off_color_setup);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen }
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (arg_legend)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen printf("\n%i links listed.\n", c);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return 0;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen}
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemenstatic int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) {
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen _cleanup_free_ struct local_address *local = NULL;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen int r, n, i;
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen n = local_addresses(rtnl, ifindex, &local);
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen if (n < 0)
dc0c531b98e470892518019fb538aa687e2fa056Gabor Kelemen return n;
for (i = 0; i < n; i++) {
_cleanup_free_ char *pretty = NULL;
r = in_addr_to_string(local[i].family, &local[i].address, &pretty);
if (r < 0)
return r;
printf("%*s%s\n",
(int) strlen(prefix),
i == 0 ? prefix : "",
pretty);
}
return 0;
}
static void dump_list(const char *prefix, char **l) {
char **i;
STRV_FOREACH(i, l) {
printf("%*s%s\n",
(int) strlen(prefix),
i == l ? prefix : "",
*i);
}
}
static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) {
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
char devid[2 + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *t = NULL;
const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL;
const char *on_color_operational, *off_color_operational,
*on_color_setup, *off_color_setup;
struct ether_addr e;
unsigned iftype;
int r, ifindex;
bool have_mac;
uint32_t mtu;
assert(rtnl);
assert(udev);
assert(name);
if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0)
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex);
else {
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_message_append_string(req, IFLA_IFNAME, name);
}
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_call(rtnl, req, 0, &reply);
if (r < 0) {
log_error("Failed to query link: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
if (r < 0)
return rtnl_log_parse_error(r);
r = sd_rtnl_message_read_string(reply, IFLA_IFNAME, &name);
if (r < 0)
return rtnl_log_parse_error(r);
r = sd_rtnl_message_link_get_type(reply, &iftype);
if (r < 0)
return rtnl_log_parse_error(r);
have_mac = sd_rtnl_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
if (have_mac) {
const uint8_t *p;
bool all_zeroes = true;
for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++)
if (*p != 0) {
all_zeroes = false;
break;
}
if (all_zeroes)
have_mac = false;
}
sd_rtnl_message_read_u32(reply, IFLA_MTU, &mtu);
sd_network_link_get_operational_state(ifindex, &operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
sd_network_link_get_setup_state(ifindex, &setup_state);
setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
sd_network_link_get_dns(ifindex, &dns);
sd_network_link_get_ntp(ifindex, &ntp);
sd_network_link_get_domains(ifindex, &domains);
r = sd_network_link_get_wildcard_domain(ifindex);
if (r > 0) {
char *wildcard;
wildcard = strdup("*");
if (!wildcard)
return log_oom();
if (strv_consume(&domains, wildcard) < 0)
return log_oom();
}
sprintf(devid, "n%i", ifindex);
d = udev_device_new_from_device_id(udev, devid);
link_get_type_string(iftype, d, &t);
if (d) {
driver = udev_device_get_property_value(d, "ID_NET_DRIVER");
path = udev_device_get_property_value(d, "ID_PATH");
vendor = udev_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE");
if (!vendor)
vendor = udev_device_get_property_value(d, "ID_VENDOR");
model = udev_device_get_property_value(d, "ID_MODEL_FROM_DATABASE");
if (!model)
model = udev_device_get_property_value(d, "ID_MODEL");
}
printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name);
printf(" Type: %s\n"
" State: %s%s%s (%s%s%s)\n",
strna(t),
on_color_operational, strna(operational_state), off_color_operational,
on_color_setup, strna(setup_state), off_color_setup);
if (path)
printf(" Path: %s\n", path);
if (driver)
printf(" Driver: %s\n", driver);
if (vendor)
printf(" Vendor: %s\n", vendor);
if (model)
printf(" Model: %s\n", model);
if (have_mac) {
char ea[ETHER_ADDR_TO_STRING_MAX];
printf(" HW Address: %s\n", ether_addr_to_string(&e, ea));
}
if (mtu > 0)
printf(" MTU: %u\n", mtu);
dump_addresses(rtnl, " Address: ", ifindex);
if (!strv_isempty(dns))
dump_list(" DNS: ", dns);
if (!strv_isempty(domains))
dump_list(" Domain: ", domains);
if (!strv_isempty(ntp))
dump_list(" NTP: ", ntp);
return 0;
}
static int link_status(char **args, unsigned n) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
char **name;
int r;
r = sd_rtnl_open(&rtnl, 0);
if (r < 0) {
log_error("Failed to connect to netlink: %s", strerror(-r));
return r;
}
udev = udev_new();
if (!udev) {
log_error("Failed to connect to udev: %m");
return -errno;
}
if (n <= 1 && !arg_all) {
_cleanup_free_ char *operational_state = NULL;
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
_cleanup_free_ struct local_address *addresses = NULL;
const char *on_color_operational, *off_color_operational;
int i, c;
sd_network_get_operational_state(&operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational);
c = local_addresses(rtnl, 0, &addresses);
for (i = 0; i < c; i++) {
_cleanup_free_ char *pretty = NULL;
r = in_addr_to_string(addresses[i].family, &addresses[i].address, &pretty);
if (r < 0)
return log_oom();
printf("%13s %s\n",
i > 0 ? "" : "Address:", pretty);
}
sd_network_get_dns(&dns);
if (!strv_isempty(dns))
dump_list(" DNS: ", dns);
sd_network_get_domains(&domains);
if (!strv_isempty(domains))
dump_list(" Domain: ", domains);
sd_network_get_ntp(&ntp);
if (!strv_isempty(ntp))
dump_list(" NTP: ", ntp);
return 0;
}
pager_open_if_enabled();
if (arg_all) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
_cleanup_free_ LinkInfo *links = NULL;
int c, i;
r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_message_request_dump(req, true);
if (r < 0)
return rtnl_log_create_error(r);
r = sd_rtnl_call(rtnl, req, 0, &reply);
if (r < 0) {
log_error("Failed to enumerate links: %s", strerror(-r));
return r;
}
c = decode_and_sort_links(reply, &links);
if (c < 0)
return rtnl_log_parse_error(c);
for (i = 0; i < c; i++) {
if (i > 0)
fputc('\n', stdout);
link_status_one(rtnl, udev, links[i].name);
}
}
STRV_FOREACH(name, args + 1) {
if (name != args+1)
fputc('\n', stdout);
link_status_one(rtnl, udev, *name);
}
return 0;
}
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Query and control the networking subsystem.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" -a --all Show status for all links\n\n"
"Commands:\n"
" list List links\n"
" status LINK Show link status\n"
, program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "all", no_argument, NULL, 'a' },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) {
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case 'a':
arg_all = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
}
return 1;
}
static int networkctl_main(int argc, char *argv[]) {
static const struct {
const char* verb;
const enum {
MORE,
LESS,
EQUAL
} argc_cmp;
const int argc;
int (* const dispatch)(char **args, unsigned n);
} verbs[] = {
{ "list", LESS, 1, list_links },
{ "status", MORE, 1, link_status },
};
int left;
unsigned i;
assert(argc >= 0);
assert(argv);
left = argc - optind;
if (left <= 0)
/* Special rule: no arguments means "list" */
i = 0;
else {
if (streq(argv[optind], "help")) {
help();
return 0;
}
for (i = 0; i < ELEMENTSOF(verbs); i++)
if (streq(argv[optind], verbs[i].verb))
break;
if (i >= ELEMENTSOF(verbs)) {
log_error("Unknown operation %s", argv[optind]);
return -EINVAL;
}
}
switch (verbs[i].argc_cmp) {
case EQUAL:
if (left != verbs[i].argc) {
log_error("Invalid number of arguments.");
return -EINVAL;
}
break;
case MORE:
if (left < verbs[i].argc) {
log_error("Too few arguments.");
return -EINVAL;
}
break;
case LESS:
if (left > verbs[i].argc) {
log_error("Too many arguments.");
return -EINVAL;
}
break;
default:
assert_not_reached("Unknown comparison operator.");
}
return verbs[i].dispatch(argv + optind, left);
}
int main(int argc, char* argv[]) {
int r;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
r = networkctl_main(argc, argv);
finish:
pager_close();
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}