networkctl: use io.systemd.Network.Link.Describe() Varlink method
This makes networkctl fetch bit-rate statistics and offered DHCP leases via Link.Describe() method instead of D-Bus. Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com> Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,13 +2,10 @@
|
||||
|
||||
#include <linux/if_tunnel.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-netlink.h"
|
||||
#include "sd-json.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-util.h"
|
||||
#include "device-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "glob-util.h"
|
||||
@@ -28,6 +25,7 @@
|
||||
LinkInfo* link_info_array_free(LinkInfo *array) {
|
||||
for (unsigned i = 0; array && array[i].needs_freeing; i++) {
|
||||
sd_device_unref(array[i].sd_device);
|
||||
sd_json_variant_unref(array[i].description);
|
||||
free(array[i].netdev_kind);
|
||||
free(array[i].ssid);
|
||||
free(array[i].qdisc);
|
||||
@@ -280,33 +278,31 @@ static int decode_link(
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
static int acquire_link_bitrates(LinkInfo *link) {
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(link);
|
||||
|
||||
r = link_get_property(bus, link->ifindex, &error, &reply, "org.freedesktop.network1.Link", "BitRates", "(tt)");
|
||||
if (r < 0) {
|
||||
bool quiet = sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY,
|
||||
BUS_ERROR_SPEED_METER_INACTIVE);
|
||||
|
||||
return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
|
||||
r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
|
||||
sd_json_variant *v;
|
||||
r = json_variant_find_object(link->description, STRV_MAKE("Interface", "BitRates"), &v);
|
||||
if (r == -ENODATA)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "TxBitRate", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LinkInfo, tx_bitrate), SD_JSON_MANDATORY },
|
||||
{ "RxBitRate", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LinkInfo, rx_bitrate), SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(v, dispatch_table,
|
||||
SD_JSON_LOG | SD_JSON_WARNING | SD_JSON_ALLOW_EXTENSIONS,
|
||||
link);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
link->has_bitrates = link->tx_bitrate != UINT64_MAX && link->rx_bitrate != UINT64_MAX;
|
||||
return r;
|
||||
|
||||
link->has_bitrates = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -356,7 +352,7 @@ static void acquire_wlan_link_info(LinkInfo *link) {
|
||||
link->has_wlan_link_info = r > 0 || k > 0;
|
||||
}
|
||||
|
||||
int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret) {
|
||||
int acquire_link_info(sd_varlink *vl, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
||||
_cleanup_(link_info_array_freep) LinkInfo *links = NULL;
|
||||
_cleanup_free_ bool *matched_patterns = NULL;
|
||||
@@ -402,6 +398,10 @@ int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, Lin
|
||||
acquire_ether_link_info(&fd, &links[c]);
|
||||
acquire_wlan_link_info(&links[c]);
|
||||
|
||||
if (vl)
|
||||
(void) acquire_link_description(vl, links[c].ifindex, &links[c].description);
|
||||
(void) acquire_link_bitrates(&links[c]);
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
@@ -421,10 +421,6 @@ int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, Lin
|
||||
|
||||
typesafe_qsort(links, c, link_info_compare);
|
||||
|
||||
if (bus)
|
||||
FOREACH_ARRAY(link, links, c)
|
||||
(void) acquire_link_bitrates(bus, link);
|
||||
|
||||
*ret = TAKE_PTR(links);
|
||||
|
||||
if (patterns && c == 0)
|
||||
|
||||
@@ -35,6 +35,7 @@ typedef struct LinkInfo {
|
||||
char name[IFNAMSIZ+1];
|
||||
char *netdev_kind;
|
||||
sd_device *sd_device;
|
||||
sd_json_variant *description;
|
||||
int ifindex;
|
||||
unsigned short iftype;
|
||||
struct hw_addr_data hw_address;
|
||||
@@ -133,4 +134,4 @@ typedef struct LinkInfo {
|
||||
LinkInfo* link_info_array_free(LinkInfo *array);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(LinkInfo*, link_info_array_free);
|
||||
|
||||
int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret);
|
||||
int acquire_link_info(sd_varlink *vl, sd_netlink *rtnl, char * const *patterns, LinkInfo **ret);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-device.h"
|
||||
#include "sd-dhcp-client-id.h"
|
||||
#include "sd-dhcp-lease.h"
|
||||
@@ -12,8 +11,6 @@
|
||||
#include "alloc-util.h"
|
||||
#include "bond-util.h"
|
||||
#include "bridge-util.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "extract-word.h"
|
||||
@@ -21,7 +18,9 @@
|
||||
#include "format-util.h"
|
||||
#include "geneve-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "ipvlan-util.h"
|
||||
#include "json-util.h"
|
||||
#include "macvlan-util.h"
|
||||
#include "netif-util.h"
|
||||
#include "network-internal.h"
|
||||
@@ -40,87 +39,58 @@
|
||||
#include "time-util.h"
|
||||
#include "udev-util.h"
|
||||
|
||||
static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) {
|
||||
typedef struct LeaseInfo {
|
||||
const char *address;
|
||||
struct iovec client_id;
|
||||
} LeaseInfo;
|
||||
|
||||
static void lease_info_done(LeaseInfo *p) {
|
||||
assert(p);
|
||||
|
||||
iovec_done(&p->client_id);
|
||||
}
|
||||
|
||||
static int dump_dhcp_leases(Table *table, const char *prefix, const LinkInfo *link) {
|
||||
_cleanup_strv_free_ char **buf = NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int r;
|
||||
|
||||
assert(table);
|
||||
assert(prefix);
|
||||
assert(bus);
|
||||
assert(link);
|
||||
|
||||
r = link_get_property(bus, link->ifindex, &error, &reply, "org.freedesktop.network1.DHCPServer", "Leases", "a(uayayayayt)");
|
||||
if (r < 0) {
|
||||
bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY);
|
||||
|
||||
log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
|
||||
r, "Failed to query link DHCP leases: %s", bus_error_message(&error, r));
|
||||
sd_json_variant *leases;
|
||||
r = json_variant_find_object(link->description, STRV_MAKE("Interface", "DHCPServer", "Leases"), &leases);
|
||||
if (r == -ENODATA)
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_bus_message_enter_container(reply, 'a', "(uayayayayt)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
return r;
|
||||
|
||||
while ((r = sd_bus_message_enter_container(reply, 'r', "uayayayayt")) > 0) {
|
||||
_cleanup_free_ char *id = NULL, *ip = NULL;
|
||||
const void *client_id, *addr, *gtw, *hwaddr;
|
||||
size_t client_id_sz, sz;
|
||||
uint64_t expiration;
|
||||
uint32_t family;
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "AddressString", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LeaseInfo, address), SD_JSON_MANDATORY },
|
||||
{ "ClientId", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(LeaseInfo, client_id), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_bus_message_read(reply, "u", &family);
|
||||
sd_json_variant *lease;
|
||||
JSON_VARIANT_ARRAY_FOREACH(lease, leases) {
|
||||
_cleanup_(lease_info_done) LeaseInfo info = {};
|
||||
_cleanup_free_ char *client_id = NULL;
|
||||
|
||||
r = sd_json_dispatch(lease, dispatch_table, SD_JSON_LOG | SD_JSON_WARNING | SD_JSON_ALLOW_EXTENSIONS, &info);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
continue;
|
||||
|
||||
r = sd_bus_message_read_array(reply, 'y', &client_id, &client_id_sz);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
if (info.client_id.iov_len > 0)
|
||||
(void) sd_dhcp_client_id_to_string_from_raw(info.client_id.iov_base, info.client_id.iov_len, &client_id);
|
||||
|
||||
r = sd_bus_message_read_array(reply, 'y', &addr, &sz);
|
||||
if (r < 0 || sz != 4)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_read_array(reply, 'y', >w, &sz);
|
||||
if (r < 0 || sz != 4)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_read_array(reply, 'y', &hwaddr, &sz);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_read_basic(reply, 't', &expiration);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_dhcp_client_id_to_string_from_raw(client_id, client_id_sz, &id);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = in_addr_to_string(family, addr, &ip);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = strv_extendf(&buf, "%s (to %s)", ip, id);
|
||||
r = strv_extendf(&buf, "%s%s%s%s",
|
||||
info.address,
|
||||
client_id ? " (to " : "",
|
||||
strempty(client_id),
|
||||
client_id ? ")" : "");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
}
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (strv_isempty(buf)) {
|
||||
r = strv_extendf(&buf, "none");
|
||||
@@ -259,7 +229,6 @@ static int format_config_files(char ***files, const char *main_config) {
|
||||
}
|
||||
|
||||
static int link_status_one(
|
||||
sd_bus *bus,
|
||||
sd_netlink *rtnl,
|
||||
sd_hwdb *hwdb,
|
||||
sd_varlink *vl,
|
||||
@@ -276,7 +245,6 @@ static int link_status_one(
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(rtnl);
|
||||
assert(vl);
|
||||
assert(info);
|
||||
@@ -919,7 +887,7 @@ static int link_status_one(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dump_dhcp_leases(table, "Offered DHCP leases", bus, info);
|
||||
r = dump_dhcp_leases(table, "Offered DHCP leases", info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -940,7 +908,6 @@ static int link_status_one(
|
||||
}
|
||||
|
||||
int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
||||
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
|
||||
_cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL;
|
||||
@@ -951,10 +918,6 @@ int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = acquire_bus(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
r = sd_netlink_open(&rtnl);
|
||||
@@ -970,11 +933,11 @@ int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
return r;
|
||||
|
||||
if (arg_all)
|
||||
c = acquire_link_info(bus, rtnl, NULL, &links);
|
||||
c = acquire_link_info(vl, rtnl, NULL, &links);
|
||||
else if (argc <= 1)
|
||||
return system_status(rtnl, hwdb);
|
||||
else
|
||||
c = acquire_link_info(bus, rtnl, argv + 1, &links);
|
||||
c = acquire_link_info(vl, rtnl, argv + 1, &links);
|
||||
if (c < 0)
|
||||
return c;
|
||||
|
||||
@@ -985,7 +948,7 @@ int verb_link_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
if (!first)
|
||||
putchar('\n');
|
||||
|
||||
RET_GATHER(r, link_status_one(bus, rtnl, hwdb, vl, i));
|
||||
RET_GATHER(r, link_status_one(rtnl, hwdb, vl, i));
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
@@ -232,3 +232,40 @@ void online_state_to_color(const char *state, const char **on, const char **off)
|
||||
*off = "";
|
||||
}
|
||||
}
|
||||
|
||||
int acquire_link_description(sd_varlink *vl, int ifindex, sd_json_variant **ret) {
|
||||
int r;
|
||||
|
||||
assert(vl);
|
||||
assert(ifindex > 0);
|
||||
assert(ret);
|
||||
|
||||
sd_json_variant *v; /* borrowed from vl, do not unref */
|
||||
r = varlink_callbo_and_log(
|
||||
vl,
|
||||
"io.systemd.Network.Link.Describe",
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("InterfaceIndex", ifindex));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = sd_json_variant_ref(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_variant_find_object(sd_json_variant *v, char * const *object_names, sd_json_variant **ret) {
|
||||
assert(object_names);
|
||||
assert(ret);
|
||||
|
||||
if (!v || sd_json_variant_is_null(v))
|
||||
return -ENODATA;
|
||||
|
||||
STRV_FOREACH(name, object_names) {
|
||||
v = sd_json_variant_by_key(v, *name);
|
||||
if (!v || sd_json_variant_is_null(v))
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
*ret = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,3 +20,6 @@ int link_get_property(
|
||||
void operational_state_to_color(const char *name, const char *state, const char **on, const char **off);
|
||||
void setup_state_to_color(const char *state, const char **on, const char **off);
|
||||
void online_state_to_color(const char *state, const char **on, const char **off);
|
||||
|
||||
int acquire_link_description(sd_varlink *vl, int ifindex, sd_json_variant **ret);
|
||||
int json_variant_find_object(sd_json_variant *v, char * const *object_names, sd_json_variant **ret);
|
||||
|
||||
Reference in New Issue
Block a user