From 12b1e04073530c1f8fd455f4352737ba152f71ee Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 5 Jan 2024 20:07:23 +0900 Subject: [PATCH 1/9] man: fix indentation --- man/systemd.link.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 4d99cd88f6..1f50506bfb 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -361,7 +361,7 @@ A description of the device. - + @@ -369,7 +369,7 @@ The ifalias interface property is set to this value. - + From 50a0379d5d5fd19bfe0efa5a4e32cb9cd5f48724 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 9 Jan 2024 01:12:20 +0900 Subject: [PATCH 2/9] udev/builtin: introduce udev_builtin_import_property() helper function This is not used in this commit, but will be used later commits. --- src/udev/udev-builtin.c | 23 +++++++++++++++++++++++ src/udev/udev-builtin.h | 1 + 2 files changed, 24 insertions(+) diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index bcc2018c6f..6caea8ecce 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -154,3 +154,26 @@ int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const return udev_builtin_add_property(dev, test, key, val); } + +int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, const char *key) { + const char *val; + int r; + + assert(dev); + assert(key); + + if (!src) + return 0; + + r = sd_device_get_property_value(src, key, &val); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_device_debug_errno(src, r, "Failed to get property \"%s\", ignoring: %m", key); + + r = udev_builtin_add_property(dev, test, key, val); + if (r < 0) + return r; + + return 1; +} diff --git a/src/udev/udev-builtin.h b/src/udev/udev-builtin.h index fcd41d615d..c7a48b0201 100644 --- a/src/udev/udev-builtin.h +++ b/src/udev/udev-builtin.h @@ -84,5 +84,6 @@ void udev_builtin_list(void); bool udev_builtin_should_reload(void); int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val); int udev_builtin_add_propertyf(sd_device *dev, bool test, const char *key, const char *valf, ...) _printf_(4, 5); +int udev_builtin_import_property(sd_device *dev, sd_device *src, bool test, const char *key); int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias, const char *filter, bool test); From ac364062779b605574ca06273ae26f9e03a25d5a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 10 Jan 2024 04:12:45 +0900 Subject: [PATCH 3/9] udev-builtin-path_id: drop redundant debugging logs The function udev_builtin_add_property() internally logs the failure. --- src/udev/udev-builtin-path_id.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index ebeadc3f3d..c4057d63c3 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -644,7 +644,6 @@ static int find_real_nvme_parent(sd_device *dev, sd_device **ret) { static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) { char *p; - int r; assert(dev); assert(path); @@ -660,9 +659,7 @@ static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) { if (p[1] != '-') return; - r = udev_builtin_add_property(dev, test, "ID_PATH_WITH_USB_REVISION", path); - if (r < 0) - log_device_debug_errno(dev, r, "Failed to add ID_PATH_WITH_USB_REVISION property, ignoring: %m"); + (void) udev_builtin_add_property(dev, test, "ID_PATH_WITH_USB_REVISION", path); /* Drop the USB revision specifier for backward compatibility. */ memmove(p - 1, p + 1, strlen(p + 1) + 1); @@ -671,7 +668,6 @@ static void add_id_with_usb_revision(sd_device *dev, bool test, char *path) { static void add_id_tag(sd_device *dev, bool test, const char *path) { char tag[UDEV_NAME_SIZE]; size_t i = 0; - int r; /* compose valid udev tag name */ for (const char *p = path; *p; p++) { @@ -697,9 +693,7 @@ static void add_id_tag(sd_device *dev, bool test, const char *path) { i--; tag[i] = '\0'; - r = udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); - if (r < 0) - log_device_debug_errno(dev, r, "Failed to add ID_PATH_TAG property, ignoring: %m"); + (void) udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); } static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test) { @@ -859,9 +853,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test) add_id_with_usb_revision(dev, test, path); - r = udev_builtin_add_property(dev, test, "ID_PATH", path); - if (r < 0) - log_device_debug_errno(dev, r, "Failed to add ID_PATH property, ignoring: %m"); + (void) udev_builtin_add_property(dev, test, "ID_PATH", path); add_id_tag(dev, test, path); @@ -871,7 +863,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test) * ID_PATH_ATA_COMPAT */ if (compat_path) - udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path); + (void) udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path); return 0; } From 0c3af33e19b5f74dbb893aca12946a4d4935a917 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 5 Jan 2024 20:36:33 +0900 Subject: [PATCH 4/9] udev/net: do not set unapplied .link file name to ID_NET_LINK_FILE Instead, import already assigned value from the udev database. Then, return the builtin earlier. --- src/udev/net/link-config.c | 8 -------- src/udev/udev-builtin-net_setup_link.c | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 910ec2709e..8689630f4b 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -928,14 +928,6 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) { assert(rtnl); assert(link); - if (!IN_SET(link->action, SD_DEVICE_ADD, SD_DEVICE_BIND, SD_DEVICE_MOVE)) { - log_link_debug(link, "Skipping to apply .link settings on '%s' uevent.", - device_action_to_string(link->action)); - - link->new_name = link->ifname; - return 0; - } - r = link_apply_ethtool_settings(link, &ctx->ethtool_fd); if (r < 0) return r; diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c index a308a211fb..ad73b8e1c4 100644 --- a/src/udev/udev-builtin-net_setup_link.c +++ b/src/udev/udev-builtin-net_setup_link.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "device-private.h" #include "device-util.h" #include "escape.h" #include "errno-util.h" @@ -21,6 +22,27 @@ static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool if (argc > 1) return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments."); + sd_device_action_t action; + r = sd_device_get_action(dev, &action); + if (r < 0) + return log_device_error_errno(dev, r, "Failed to get action: %m"); + + if (!IN_SET(action, SD_DEVICE_ADD, SD_DEVICE_BIND, SD_DEVICE_MOVE)) { + log_device_debug(dev, "Skipping to apply .link settings on '%s' uevent.", + device_action_to_string(action)); + + /* Import previously assigned .link file name. */ + (void) udev_builtin_import_property(dev, event->dev_db_clone, test, "ID_NET_LINK_FILE"); + (void) udev_builtin_import_property(dev, event->dev_db_clone, test, "ID_NET_LINK_FILE_DROPINS"); + + /* Set ID_NET_NAME= with the current interface name. */ + const char *value; + if (sd_device_get_sysname(dev, &value) >= 0) + (void) udev_builtin_add_property(dev, test, "ID_NET_NAME", value); + + return 0; + } + r = link_new(ctx, &event->rtnl, dev, &link); if (r == -ENODEV) { log_device_debug_errno(dev, r, "Link vanished while getting information, ignoring."); From 3e00171dcd94c84e1779b8243f483ead2c4e81fb Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 5 Jan 2024 20:36:52 +0900 Subject: [PATCH 5/9] udev/net: split out udev property assignment logic No functional change, just refactoring. --- src/udev/net/link-config.c | 39 +++++++++++++++++++++++++- src/udev/net/link-config.h | 2 +- src/udev/udev-builtin-net_setup_link.c | 21 +------------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 8689630f4b..c9c4e9c927 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -15,6 +15,7 @@ #include "creds-util.h" #include "device-private.h" #include "device-util.h" +#include "escape.h" #include "ethtool-util.h" #include "fd-util.h" #include "fileio.h" @@ -34,6 +35,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "udev-builtin.h" #include "utf8.h" struct LinkConfigContext { @@ -921,7 +923,38 @@ static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) { return 0; } -int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) { +static int link_apply_udev_properties(Link *link, bool test) { + LinkConfig *config; + sd_device *device; + + assert(link); + + config = ASSERT_PTR(link->config); + device = ASSERT_PTR(link->device); + + (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE", config->filename); + + _cleanup_free_ char *joined = NULL; + STRV_FOREACH(d, config->dropins) { + _cleanup_free_ char *escaped = NULL; + + escaped = xescape(*d, ":"); + if (!escaped) + return log_oom(); + + if (!strextend_with_separator(&joined, ":", escaped)) + return log_oom(); + } + + (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE_DROPINS", joined); + + if (link->new_name) + (void) udev_builtin_add_property(device, test, "ID_NET_NAME", link->new_name); + + return 0; +} + +int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, bool test) { int r; assert(ctx); @@ -948,6 +981,10 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) { if (r < 0) return r; + r = link_apply_udev_properties(link, test); + if (r < 0) + return r; + return 0; } diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index bab9d12970..06666a23ba 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -100,7 +100,7 @@ Link *link_free(Link *link); DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); int link_get_config(LinkConfigContext *ctx, Link *link); -int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link); +int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, bool test); const char *mac_address_policy_to_string(MACAddressPolicy p) _const_; MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_; diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c index ad73b8e1c4..59a0043422 100644 --- a/src/udev/udev-builtin-net_setup_link.c +++ b/src/udev/udev-builtin-net_setup_link.c @@ -3,7 +3,6 @@ #include "alloc-util.h" #include "device-private.h" #include "device-util.h" -#include "escape.h" #include "errno-util.h" #include "link-config.h" #include "log.h" @@ -16,7 +15,6 @@ static LinkConfigContext *ctx = NULL; static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool test) { sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); _cleanup_(link_freep) Link *link = NULL; - _cleanup_free_ char *joined = NULL; int r; if (argc > 1) @@ -61,31 +59,14 @@ static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool return log_device_error_errno(dev, r, "Failed to get link config: %m"); } - r = link_apply_config(ctx, &event->rtnl, link); + r = link_apply_config(ctx, &event->rtnl, link, test); if (r == -ENODEV) log_device_debug_errno(dev, r, "Link vanished while applying configuration, ignoring."); else if (r < 0) log_device_warning_errno(dev, r, "Could not apply link configuration, ignoring: %m"); - udev_builtin_add_property(dev, test, "ID_NET_LINK_FILE", link->config->filename); - if (link->new_name) - udev_builtin_add_property(dev, test, "ID_NET_NAME", link->new_name); - event->altnames = TAKE_PTR(link->altnames); - 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; } From 513ca8b6f084a3d189039765c304b9addcaa41f6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 9 Jan 2024 02:54:43 +0900 Subject: [PATCH 6/9] udev: split out check that udev property can be updated --- src/libsystemd/sd-device/device-util.h | 8 ++++++++ src/udev/udev-rules.c | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h index a9a9b7ad98..534a296715 100644 --- a/src/libsystemd/sd-device/device-util.h +++ b/src/libsystemd/sd-device/device-util.h @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "log.h" #include "macro.h" +#include "strv.h" #define device_unref_and_replace(a, b) \ unref_and_replace_full(a, b, sd_device_ref, sd_device_unref) @@ -105,3 +106,10 @@ char** device_make_log_fields(sd_device *device); bool device_in_subsystem(sd_device *device, const char *subsystem); bool device_is_devtype(sd_device *device, const char *devtype); + +static inline bool device_property_can_set(const char *property) { + return property && + !STR_IN_SET(property, + "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER", + "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS"); +} diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 9d01e5866c..3ec675746b 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -691,9 +691,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude } if (!is_match) { - if (STR_IN_SET(attr, - "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER", - "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS")) + if (!device_property_can_set(attr)) return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL), "Invalid ENV attribute. '%s' cannot be set.", attr); From 046286e863f20b2d0fa7eb9a5a7ac26aa399b3fe Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 5 Jan 2024 20:08:26 +0900 Subject: [PATCH 7/9] udev/net: introduce [Link] Property=, ImportProperty=, and UnsetProperty= settings The applied order is equivalent to Environment=, PassEnvironment=, and UnsetEnvironment= for [Service] or so. --- man/systemd.link.xml | 94 +++++++++++++ src/udev/net/link-config-gperf.gperf | 3 + src/udev/net/link-config.c | 177 ++++++++++++++++++++++++- src/udev/net/link-config.h | 8 +- src/udev/udev-builtin-net_setup_link.c | 2 +- src/udev/udevadm-test-builtin.c | 11 ++ 6 files changed, 292 insertions(+), 3 deletions(-) diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 1f50506bfb..8869d9589f 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -364,6 +364,59 @@ + + Property= + + Set specified udev properties. This takes space separated list of key-value pairs + concatenated with equal sign (=). Example: + Property=HOGE=foo BAR=baz + This option supports simple specifier expansion, see the Specifiers section below. + This option can be specified multiple times. If an empty string is assigned, then the all previous + assignments are cleared. + + This setting is useful to configure the ID_NET_MANAGED_BY= property which + declares which network management service shall manage the interface, which is respected by + systemd-networkd and others. Use + Property=ID_NET_MANAGED_BY=io.systemd.Network + to declare explicitly that systemd-networkd shall manage the interface, or set + the property to something else to declare explicitly it shall not do so. See + systemd.network5 + for details how this property is used to match interface names. + + + + + + ImportProperty= + + Import specified udev properties from the saved database. This takes space separated list of + property names. Example: ImportProperty=HOGE BAR + This option supports simple specifier expansion, see the Specifiers section below. + This option can be specified multiple times. If an empty string is assigned, then the all previous + assignments are cleared. + If the same property is also set in Property= in the above, then the + imported property value will be overridden by the value specified in Property=. + + + + + + + UnsetProperty= + + Unset specified udev properties. This takes space separated list of + property names. Example: ImportProperty=HOGE BAR + This option supports simple specifier expansion, see the Specifiers section below. + This option can be specified multiple times. If an empty string is assigned, then the all previous + assignments are cleared. + This setting is applied after ImportProperty= and + Property= are applied. Hence, if the same property is specified in + ImportProperty= or Property=, then the imported or specified + property value will be ignored, and the property will be unset. + + + + Alias= @@ -1260,6 +1313,47 @@ + + Specifiers + + Some settings resolve specifiers which may be used to write generic unit files referring to runtime + or unit parameters that are replaced when the unit files are loaded. Specifiers must be known and + resolvable for the setting to be valid. The following specifiers are understood: + + + Specifiers available in unit files + + + + + + + Specifier + Meaning + Details + + + + + + + + + + + + + + + + + + + + +
+
+ Examples diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 240f16e251..42d7cc7ee2 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -38,6 +38,9 @@ Match.Credential, config_parse_net_condition, Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions) Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(LinkConfig, conditions) Link.Description, config_parse_string, 0, offsetof(LinkConfig, description) +Link.Property, config_parse_udev_property, 0, offsetof(LinkConfig, properties) +Link.ImportProperty, config_parse_udev_property_name, 0, offsetof(LinkConfig, import_properties) +Link.UnsetProperty, config_parse_udev_property_name, 0, offsetof(LinkConfig, unset_properties) Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy) Link.MACAddress, config_parse_hw_addr, 0, offsetof(LinkConfig, hw_addr) Link.NamePolicy, config_parse_name_policy, 0, offsetof(LinkConfig, name_policy) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index c9c4e9c927..a8b2cc23a2 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -15,6 +15,7 @@ #include "creds-util.h" #include "device-private.h" #include "device-util.h" +#include "env-util.h" #include "escape.h" #include "ethtool-util.h" #include "fd-util.h" @@ -31,6 +32,7 @@ #include "path-util.h" #include "proc-cmdline.h" #include "random-util.h" +#include "specifier.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" @@ -38,6 +40,12 @@ #include "udev-builtin.h" #include "utf8.h" +static const Specifier link_specifier_table[] = { + COMMON_SYSTEM_SPECIFIERS, + COMMON_TMP_SPECIFIERS, + {} +}; + struct LinkConfigContext { LIST_HEAD(LinkConfig, configs); int ethtool_fd; @@ -55,6 +63,9 @@ static LinkConfig* link_config_free(LinkConfig *config) { condition_free_list(config->conditions); free(config->description); + strv_free(config->properties); + strv_free(config->import_properties); + strv_free(config->unset_properties); free(config->name_policy); free(config->name); strv_free(config->alternative_names); @@ -365,18 +376,20 @@ Link *link_free(Link *link) { return NULL; sd_device_unref(link->device); + sd_device_unref(link->device_db_clone); free(link->kind); strv_free(link->altnames); return mfree(link); } -int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link **ret) { +int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, sd_device *device_db_clone, Link **ret) { _cleanup_(link_freep) Link *link = NULL; int r; assert(ctx); assert(rtnl); assert(device); + assert(device_db_clone); assert(ret); link = new(Link, 1); @@ -385,6 +398,7 @@ int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link *link = (Link) { .device = sd_device_ref(device), + .device_db_clone = sd_device_ref(device_db_clone), }; r = sd_device_get_sysname(device, &link->ifname); @@ -932,6 +946,31 @@ static int link_apply_udev_properties(Link *link, bool test) { config = ASSERT_PTR(link->config); device = ASSERT_PTR(link->device); + /* 1. apply ImportProperty=. */ + STRV_FOREACH(p, config->import_properties) + (void) udev_builtin_import_property(device, link->device_db_clone, test, *p); + + /* 2. apply Property=. */ + STRV_FOREACH(p, config->properties) { + _cleanup_free_ char *key = NULL; + const char *eq; + + eq = strchr(*p, '='); + if (!eq) + continue; + + key = strndup(*p, eq - *p); + if (!key) + return log_oom(); + + (void) udev_builtin_add_property(device, test, key, eq + 1); + } + + /* 3. apply UnsetProperty=. */ + STRV_FOREACH(p, config->unset_properties) + (void) udev_builtin_add_property(device, test, *p, NULL); + + /* 4. set the default properties. */ (void) udev_builtin_add_property(device, test, "ID_NET_LINK_FILE", config->filename); _cleanup_free_ char *joined = NULL; @@ -988,6 +1027,142 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link, boo return 0; } +int config_parse_udev_property( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char ***properties = ASSERT_PTR(data); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *properties = strv_free(*properties); + return 0; + } + + for (const char *p = rvalue;; ) { + _cleanup_free_ char *word = NULL, *resolved = NULL, *key = NULL; + const char *eq; + + r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring assignment: %s", rvalue); + return 0; + } + if (r == 0) + return 0; + + r = specifier_printf(word, SIZE_MAX, link_specifier_table, NULL, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve specifiers in %s, ignoring assignment: %m", word); + continue; + } + + /* The restriction for udev property is not clear. Let's apply the one for environment variable here. */ + if (!env_assignment_is_valid(resolved)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid udev property, ignoring assignment: %s", word); + continue; + } + + assert_se(eq = strchr(resolved, '=')); + key = strndup(resolved, eq - resolved); + if (!key) + return log_oom(); + + if (!device_property_can_set(key)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid udev property name '%s', ignoring assignment: %s", key, resolved); + continue; + } + + r = strv_env_replace_consume(properties, TAKE_PTR(resolved)); + if (r < 0) + return log_error_errno(r, "Failed to update properties: %m"); + } +} + +int config_parse_udev_property_name( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char ***properties = ASSERT_PTR(data); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + /* Empty assignment resets the list */ + *properties = strv_free(*properties); + return 0; + } + + for (const char *p = rvalue;; ) { + _cleanup_free_ char *word = NULL, *resolved = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring assignment: %s", rvalue); + return 0; + } + if (r == 0) + return 0; + + r = specifier_printf(word, SIZE_MAX, link_specifier_table, NULL, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve specifiers in %s, ignoring assignment: %m", word); + continue; + } + + /* The restriction for udev property is not clear. Let's apply the one for environment variable here. */ + if (!env_name_is_valid(resolved)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid udev property name, ignoring assignment: %s", resolved); + continue; + } + + if (!device_property_can_set(resolved)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid udev property name, ignoring assignment: %s", resolved); + continue; + } + + r = strv_consume(properties, TAKE_PTR(resolved)); + if (r < 0) + return log_error_errno(r, "Failed to update properties: %m"); + } +} + int config_parse_ifalias( const char *unit, const char *filename, diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 06666a23ba..98cadc212e 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -31,6 +31,7 @@ typedef struct Link { LinkConfig *config; sd_device *device; + sd_device *device_db_clone; sd_device_action_t action; char *kind; @@ -51,6 +52,9 @@ struct LinkConfig { LIST_HEAD(Condition, conditions); char *description; + char **properties; + char **import_properties; + char **unset_properties; struct hw_addr_data hw_addr; MACAddressPolicy mac_address_policy; NamePolicy *name_policy; @@ -95,7 +99,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename); int link_config_load(LinkConfigContext *ctx); bool link_config_should_reload(LinkConfigContext *ctx); -int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link **ret); +int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, sd_device *device_db_clone, Link **ret); Link *link_free(Link *link); DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free); @@ -108,6 +112,8 @@ MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_; /* gperf lookup function */ const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN_TYPE length); +CONFIG_PARSER_PROTOTYPE(config_parse_udev_property); +CONFIG_PARSER_PROTOTYPE(config_parse_udev_property_name); CONFIG_PARSER_PROTOTYPE(config_parse_ifalias); CONFIG_PARSER_PROTOTYPE(config_parse_rx_tx_queues); CONFIG_PARSER_PROTOTYPE(config_parse_txqueuelen); diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c index 59a0043422..fc614443ef 100644 --- a/src/udev/udev-builtin-net_setup_link.c +++ b/src/udev/udev-builtin-net_setup_link.c @@ -41,7 +41,7 @@ static int builtin_net_setup_link(UdevEvent *event, int argc, char **argv, bool return 0; } - r = link_new(ctx, &event->rtnl, dev, &link); + r = link_new(ctx, &event->rtnl, dev, event->dev_db_clone, &link); if (r == -ENODEV) { log_device_debug_errno(dev, r, "Link vanished while getting information, ignoring."); return 0; diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c index ed71eaa3fa..fdbdb6f59a 100644 --- a/src/udev/udevadm-test-builtin.c +++ b/src/udev/udevadm-test-builtin.c @@ -6,6 +6,8 @@ #include #include +#include "device-private.h" +#include "device-util.h" #include "log.h" #include "udev-builtin.h" #include "udevadm.h" @@ -104,6 +106,15 @@ int builtin_main(int argc, char *argv[], void *userdata) { goto finish; } + if (arg_action != SD_DEVICE_REMOVE) { + /* For net_setup_link */ + r = device_clone_with_db(dev, &event->dev_db_clone); + if (r < 0) { + log_device_error_errno(dev, r, "Failed to clone device: %m"); + goto finish; + } + } + r = udev_builtin_run(event, cmd, arg_command, true); if (r < 0) { log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command); From d26319be925635003cc0ff46cf44985e5f80dfd6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 5 Jan 2024 22:41:06 +0900 Subject: [PATCH 8/9] test: add test for [Link] Property= and friends --- test/units/testsuite-17.link-property.sh | 201 +++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100755 test/units/testsuite-17.link-property.sh diff --git a/test/units/testsuite-17.link-property.sh b/test/units/testsuite-17.link-property.sh new file mode 100755 index 0000000000..a43ad22b43 --- /dev/null +++ b/test/units/testsuite-17.link-property.sh @@ -0,0 +1,201 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +udevadm control --log-level=debug + +mkdir -p /run/systemd/network/ +cat >/run/systemd/network/10-test.link </run/systemd/network/10-test.link.d/10-override.conf </run/systemd/network/10-test.link.d/11-override.conf </run/systemd/network/10-test.link.d/12-override.conf </run/systemd/network/10-test.link.d/13-override.conf < Date: Wed, 10 Jan 2024 03:52:02 +0900 Subject: [PATCH 9/9] TODO: drop implemented feature --- TODO | 3 --- 1 file changed, 3 deletions(-) diff --git a/TODO b/TODO index 130da3e8d0..9b6a52f5cf 100644 --- a/TODO +++ b/TODO @@ -137,9 +137,6 @@ Features: to read them from. This way the data doesn't remain in the SMBIOS blob during runtime, but only in the credentials fs. -* In .link files add support for setting ID_NET_MANAGED_BY= udev field via some - high-level setting. Possibly also add setting to add arbitrary udev fields. - * add a new ExecStart= flag that inserts the configured user's shell as first word in the command line. (maybe use character '.'). Usecase: tool such as uid0 can use that to spawn the target user's default shell.