diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index 56de3f965c..dd440a5d17 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -159,6 +159,25 @@ int sd_network_link_get_network_file(int ifindex, char **ret) { return network_link_get_string(ifindex, "NETWORK_FILE", ret); } +int sd_network_link_get_network_file_dropins(int ifindex, char ***ret) { + _cleanup_free_ char **sv = NULL, *joined = NULL; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + r = network_link_get_string(ifindex, "NETWORK_FILE_DROPINS", &joined); + if (r < 0) + return r; + + r = strv_split_full(&sv, joined, ":", EXTRACT_CUNESCAPE); + if (r < 0) + return r; + + *ret = TAKE_PTR(sv); + return 0; +} + int sd_network_link_get_operational_state(int ifindex, char **ret) { return network_link_get_string(ifindex, "OPER_STATE", ret); } diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 357d38f5ea..65015b132f 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -1536,13 +1536,29 @@ static int table_add_string_line(Table *table, const char *key, const char *valu return 0; } +static int format_dropins(char **dropins) { + STRV_FOREACH(d, dropins) { + _cleanup_free_ char *s = NULL; + int glyph = *(d + 1) == NULL ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH; + + s = strjoin(special_glyph(glyph), *d); + if (!s) + return log_oom(); + + free_and_replace(*d, s); + } + + return 0; +} + static int link_status_one( sd_bus *bus, sd_netlink *rtnl, sd_hwdb *hwdb, const LinkInfo *info) { - _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL; + _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, + **route_domains = NULL, **link_dropins = NULL, **network_dropins = NULL; _cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL, *setup_state = NULL, *operational_state = NULL, *online_state = NULL, *activation_policy = NULL; const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL, @@ -1571,12 +1587,22 @@ static int link_status_one( (void) sd_network_link_get_ntp(info->ifindex, &ntp); (void) sd_network_link_get_sip(info->ifindex, &sip); (void) sd_network_link_get_network_file(info->ifindex, &network); + (void) sd_network_link_get_network_file_dropins(info->ifindex, &network_dropins); (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to); (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by); (void) sd_network_link_get_activation_policy(info->ifindex, &activation_policy); if (info->sd_device) { + const char *joined; + (void) sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE", &link); + + if (sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE_DROPINS", &joined) >= 0) { + r = strv_split_full(&link_dropins, joined, ":", EXTRACT_CUNESCAPE); + if (r < 0) + return r; + } + (void) sd_device_get_property_value(info->sd_device, "ID_NET_DRIVER", &driver); (void) sd_device_get_property_value(info->sd_device, "ID_PATH", &path); @@ -1596,6 +1622,20 @@ static int link_status_one( (void) dhcp_lease_load(&lease, lease_file); + r = format_dropins(network_dropins); + if (r < 0) + return r; + + if (strv_prepend(&network_dropins, network) < 0) + return log_oom(); + + r = format_dropins(link_dropins); + if (r < 0) + return r; + + if (strv_prepend(&link_dropins, link) < 0) + return log_oom(); + table = table_new("dot", "key", "value"); if (!table) return log_oom(); @@ -1631,10 +1671,10 @@ static int link_status_one( TABLE_EMPTY, TABLE_STRING, "Link File:", TABLE_SET_ALIGN_PERCENT, 100, - TABLE_STRING, strna(link), + TABLE_STRV, link_dropins ?: STRV_MAKE("n/a"), TABLE_EMPTY, TABLE_STRING, "Network File:", - TABLE_STRING, strna(network), + TABLE_STRV, network_dropins ?: STRV_MAKE("n/a"), TABLE_EMPTY, TABLE_STRING, "State:"); if (r < 0) diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index 9e2ff33d97..c37f734d61 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -464,6 +464,7 @@ static int network_build_json(Network *network, JsonVariant **ret) { return json_build(ret, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING("NetworkFile", network->filename), + JSON_BUILD_PAIR_STRV("NetworkFileDropins", network->dropins), JSON_BUILD_PAIR_BOOLEAN("RequiredForOnline", network->required_for_online), JSON_BUILD_PAIR("RequiredOperationalStateForOnline", JSON_BUILD_ARRAY(JSON_BUILD_STRING(link_operstate_to_string(network->required_operstate_for_online.min)), @@ -475,7 +476,9 @@ static int network_build_json(Network *network, JsonVariant **ret) { } static int device_build_json(sd_device *device, JsonVariant **ret) { - const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL; + _cleanup_strv_free_ char **link_dropins = NULL; + const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL, *joined; + int r; assert(ret); @@ -485,6 +488,13 @@ static int device_build_json(sd_device *device, JsonVariant **ret) { } (void) sd_device_get_property_value(device, "ID_NET_LINK_FILE", &link); + + if (sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &joined) >= 0) { + r = strv_split_full(&link_dropins, joined, ":", EXTRACT_CUNESCAPE); + if (r < 0) + return r; + } + (void) sd_device_get_property_value(device, "ID_PATH", &path); if (sd_device_get_property_value(device, "ID_VENDOR_FROM_DATABASE", &vendor) < 0) @@ -495,6 +505,7 @@ static int device_build_json(sd_device *device, JsonVariant **ret) { return json_build(ret, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_STRING_NON_EMPTY("LinkFile", link), + JSON_BUILD_PAIR_STRV_NON_EMPTY("LinkFileDropins", link_dropins), JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", path), JSON_BUILD_PAIR_STRING_NON_EMPTY("Vendor", vendor), JSON_BUILD_PAIR_STRING_NON_EMPTY("Model", model))); diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index d516918c78..d881889316 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -552,7 +552,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi CONFIG_PARSE_WARN, network, &network->stats_by_path, - NULL); + &network->dropins); if (r < 0) return r; /* config_parse_many() logs internally. */ @@ -670,6 +670,7 @@ static Network *network_free(Network *network) { free(network->name); free(network->filename); free(network->description); + strv_free(network->dropins); hashmap_free(network->stats_by_path); /* conditions */ diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 09f0e9beea..fbeec6072e 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -65,6 +65,7 @@ struct Network { char *name; char *filename; + char **dropins; Hashmap *stats_by_path; char *description; diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c index 0800e91202..6e962c03f6 100644 --- a/src/network/networkd-state-file.c +++ b/src/network/networkd-state-file.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -515,7 +516,7 @@ int link_save(Link *link) { if (link->network) { const char *online_state; - bool space; + bool space = false; online_state = link_online_state_to_string(link->online_state); if (online_state) @@ -538,6 +539,18 @@ int link_save(Link *link) { fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); + fputs("NETWORK_FILE_DROPINS=\"", f); + STRV_FOREACH(d, link->network->dropins) { + _cleanup_free_ char *escaped = NULL; + + escaped = xescape(*d, ":"); + if (!escaped) + return -ENOMEM; + + fputs_with_space(f, escaped, ":", &space); + } + fputs("\"\n", f); + /************************************************************/ fputs("DNS=", f); diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index b89e035fd6..9cc2cbaa6e 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -119,6 +119,9 @@ int sd_network_link_get_activation_policy(int ifindex, char **ret); /* Get path to .network file applied to link */ int sd_network_link_get_network_file(int ifindex, char **ret); +/* Get paths to .network file dropins applied to link */ +int sd_network_link_get_network_file_dropins(int ifindex, char ***ret); + /* Get DNS entries for a given link. These are string representations of * IP addresses */ int sd_network_link_get_dns(int ifindex, char ***ret); diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 5f18ba35fc..9efdb6000a 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -47,6 +47,7 @@ static LinkConfig* link_config_free(LinkConfig *config) { return NULL; free(config->filename); + strv_free(config->dropins); net_match_clear(&config->match); condition_free_list(config->conditions); @@ -264,7 +265,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { "SR-IOV\0", config_item_perf_lookup, link_config_gperf_lookup, CONFIG_PARSE_WARN, config, &stats_by_path, - NULL); + &config->dropins); if (r < 0) return r; /* config_parse_many() logs internally. */ diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index ea9f560f45..ed0896e9de 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -44,6 +44,7 @@ typedef struct Link { struct LinkConfig { char *filename; + char **dropins; NetMatch match; LIST_HEAD(Condition, conditions); diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c index ea7b1c5f60..18450536b5 100644 --- a/src/udev/udev-builtin-net_setup_link.c +++ b/src/udev/udev-builtin-net_setup_link.c @@ -2,16 +2,19 @@ #include "alloc-util.h" #include "device-util.h" +#include "escape.h" #include "errno-util.h" #include "link-config.h" #include "log.h" #include "string-util.h" +#include "strv.h" #include "udev-builtin.h" static LinkConfigContext *ctx = NULL; static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, char **argv, bool test) { _cleanup_(link_freep) Link *link = NULL; + _cleanup_free_ char *joined = NULL; int r; if (argc > 1) @@ -48,6 +51,19 @@ static int builtin_net_setup_link(sd_device *dev, sd_netlink **rtnl, int argc, c if (link->new_name) udev_builtin_add_property(dev, test, "ID_NET_NAME", link->new_name); + STRV_FOREACH(d, link->config->dropins) { + _cleanup_free_ char *escaped = NULL; + + escaped = xescape(*d, ":"); + if (!escaped) + return log_oom(); + + if (!strextend_with_separator(&joined, ":", escaped)) + return log_oom(); + } + + udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE_DROPINS", joined); + return 0; } diff --git a/test/networkd-test.py b/test/networkd-test.py index 0a5ba11f89..9e7233d39b 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -992,6 +992,9 @@ DNS=127.0.0.1 self.assertIn('nameserver 192.168.42.1\n', contents) self.assertIn('nameserver 127.0.0.1\n', contents) + out = subprocess.check_output(['networkctl', 'status', 'dummy0']) + self.assertIn(b'test.network.d/dns.conf', out) + def test_dhcp_timezone(self): '''networkd sets time zone from DHCP'''