1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

Merge pull request #30786 from yuwata/udev-net-link-property

udev/net: introduce [Link] Property= setting and friends
This commit is contained in:
Lennart Poettering 2024-01-10 15:56:29 +01:00 committed by GitHub
commit 9a70dc02c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 594 additions and 53 deletions

3
TODO
View File

@ -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.

View File

@ -361,7 +361,60 @@
<listitem>
<para>A description of the device.</para>
<xi:include href="version-info.xml" xpointer="v211"/>
<xi:include href="version-info.xml" xpointer="v211"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Property=</varname></term>
<listitem>
<para>Set specified udev properties. This takes space separated list of key-value pairs
concatenated with equal sign (<literal>=</literal>). Example:
<programlisting>Property=HOGE=foo BAR=baz</programlisting>
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.</para>
<para>This setting is useful to configure the <literal>ID_NET_MANAGED_BY=</literal> property which
declares which network management service shall manage the interface, which is respected by
systemd-networkd and others. Use
<programlisting>Property=ID_NET_MANAGED_BY=io.systemd.Network</programlisting>
to declare explicitly that <command>systemd-networkd</command> shall manage the interface, or set
the property to something else to declare explicitly it shall not do so. See
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details how this property is used to match interface names.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ImportProperty=</varname></term>
<listitem>
<para>Import specified udev properties from the saved database. This takes space separated list of
property names. Example: <programlisting>ImportProperty=HOGE BAR</programlisting>
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.</para>
<para>If the same property is also set in <varname>Property=</varname> in the above, then the
imported property value will be overridden by the value specified in <varname>Property=</varname>.
</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UnsetProperty=</varname></term>
<listitem>
<para>Unset specified udev properties. This takes space separated list of
property names. Example: <programlisting>ImportProperty=HOGE BAR</programlisting>
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.</para>
<para>This setting is applied after <varname>ImportProperty=</varname> and
<varname>Property=</varname> are applied. Hence, if the same property is specified in
<varname>ImportProperty=</varname> or <varname>Property=</varname>, then the imported or specified
property value will be ignored, and the property will be unset.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
@ -369,7 +422,7 @@
<listitem>
<para>The <varname>ifalias</varname> interface property is set to this value.</para>
<xi:include href="version-info.xml" xpointer="v211"/>
<xi:include href="version-info.xml" xpointer="v211"/>
</listitem>
</varlistentry>
<varlistentry>
@ -1260,6 +1313,47 @@
</variablelist>
</refsect1>
<refsect1>
<title>Specifiers</title>
<para>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:</para>
<table class='specifiers'>
<title>Specifiers available in unit files</title>
<tgroup cols='3' align='left' colsep='1' rowsep='1'>
<colspec colname="spec" />
<colspec colname="mean" />
<colspec colname="detail" />
<thead>
<row>
<entry>Specifier</entry>
<entry>Meaning</entry>
<entry>Details</entry>
</row>
</thead>
<tbody>
<xi:include href="standard-specifiers.xml" xpointer="a"/>
<xi:include href="standard-specifiers.xml" xpointer="A"/>
<xi:include href="standard-specifiers.xml" xpointer="b"/>
<xi:include href="standard-specifiers.xml" xpointer="B"/>
<xi:include href="standard-specifiers.xml" xpointer="H"/>
<xi:include href="standard-specifiers.xml" xpointer="l"/>
<xi:include href="standard-specifiers.xml" xpointer="m"/>
<xi:include href="standard-specifiers.xml" xpointer="M"/>
<xi:include href="standard-specifiers.xml" xpointer="o"/>
<xi:include href="standard-specifiers.xml" xpointer="q"/>
<xi:include href="standard-specifiers.xml" xpointer="T"/>
<xi:include href="standard-specifiers.xml" xpointer="v"/>
<xi:include href="standard-specifiers.xml" xpointer="V"/>
<xi:include href="standard-specifiers.xml" xpointer="w"/>
<xi:include href="standard-specifiers.xml" xpointer="W"/>
</tbody>
</tgroup>
</table>
</refsect1>
<refsect1>
<title>Examples</title>

View File

@ -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");
}

View File

@ -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)

View File

@ -15,6 +15,8 @@
#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"
#include "fileio.h"
@ -30,12 +32,20 @@
#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"
#include "strv.h"
#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;
@ -53,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);
@ -363,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);
@ -383,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);
@ -921,21 +937,69 @@ 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);
/* 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;
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);
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;
@ -956,9 +1020,149 @@ 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;
}
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,

View File

@ -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,12 +99,12 @@ 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);
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_;
@ -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);

View File

@ -1,8 +1,8 @@
/* 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"
#include "link-config.h"
#include "log.h"
@ -15,13 +15,33 @@ 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)
return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
r = link_new(ctx, &event->rtnl, dev, &link);
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, event->dev_db_clone, &link);
if (r == -ENODEV) {
log_device_debug_errno(dev, r, "Link vanished while getting information, ignoring.");
return 0;
@ -39,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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -6,6 +6,8 @@
#include <stdio.h>
#include <stdlib.h>
#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);

View File

@ -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 <<EOF
[Match]
Kind=dummy
MACAddress=00:50:56:c0:00:19
[Link]
Name=test1
EOF
mkdir /run/systemd/network/10-test.link.d
cat >/run/systemd/network/10-test.link.d/10-override.conf <<EOF
[Link]
Property=HOGE=foo BAR=baz SHOULD_BE_UNSET=unset
UnsetProperty=SHOULD_BE_UNSET
EOF
udevadm control --reload
ip link add address 00:50:56:c0:00:19 type dummy
udevadm wait --settle --timeout=30 /sys/class/net/test1
output=$(udevadm info --query property /sys/class/net/test1)
assert_in "HOGE=foo" "$output"
assert_in "BAR=baz" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
cat >/run/systemd/network/10-test.link.d/11-override.conf <<EOF
[Link]
Property=
Property=HOGE2=foo2 BAR2=baz2 SHOULD_BE_UNSET=unset
ImportProperty=HOGE
EOF
udevadm control --reload
udevadm trigger --settle --action add /sys/class/net/test1
output=$(udevadm info --query property /sys/class/net/test1)
assert_in "HOGE=foo" "$output"
assert_in "HOGE2=foo2" "$output"
assert_not_in "BAR=" "$output"
assert_in "BAR2=baz2" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
# On change event, .link file will not be applied.
udevadm trigger --settle --action change /sys/class/net/test1
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_not_in "HOGE2=" "$output"
assert_not_in "BAR=" "$output"
assert_not_in "BAR2=" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
### testing with udevadm test-builtin
output=$(udevadm test-builtin --action add net_setup_link /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_in "HOGE2=foo2" "$output"
assert_not_in "BAR=" "$output"
assert_in "BAR2=baz2" "$output"
assert_in "SHOULD_BE_UNSET=" "$output" # this is expected, as an empty assignment is also logged.
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
# check that test-builtin command does not update udev database.
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_not_in "HOGE2=" "$output"
assert_not_in "BAR=" "$output"
assert_not_in "BAR2=" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
output=$(udevadm test-builtin --action change net_setup_link /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_not_in "HOGE2=" "$output"
assert_not_in "BAR=" "$output"
assert_not_in "BAR2=" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_not_in "HOGE2=" "$output"
assert_not_in "BAR=" "$output"
assert_not_in "BAR2=" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
### testing with udevadm test
output=$(udevadm test --action add /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_in "HOGE2=foo2" "$output"
assert_not_in "BAR=" "$output"
assert_in "BAR2=baz2" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
# check that test command _does_ update udev database.
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_in "HOGE2=foo2" "$output"
assert_not_in "BAR=" "$output"
assert_in "BAR2=baz2" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
output=$(udevadm test --action change /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_not_in "HOGE2=" "$output"
assert_not_in "BAR=" "$output"
assert_not_in "BAR2=" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "HOGE=" "$output"
assert_not_in "HOGE2=" "$output"
assert_not_in "BAR=" "$output"
assert_not_in "BAR2=" "$output"
assert_not_in "SHOULD_BE_UNSET=" "$output"
assert_in "ID_NET_LINK_FILE=/run/systemd/network/10-test.link" "$output"
assert_in "ID_NET_LINK_FILE_DROPINS=/run/systemd/network/10-test.link.d/10-override.conf:/run/systemd/network/10-test.link.d/11-override.conf" "$output"
assert_in "ID_NET_NAME=test1" "$output"
# test for specifiers
cat >/run/systemd/network/10-test.link.d/12-override.conf <<EOF
[Link]
Property=
Property=LINK_VERSION=%v
EOF
udevadm control --reload
output=$(udevadm test --action add /sys/class/net/test1)
assert_in "LINK_VERSION=$(uname -r)" "$output"
output=$(udevadm info --query property /sys/class/net/test1)
assert_in "LINK_VERSION=$(uname -r)" "$output"
# test for constant properties
cat >/run/systemd/network/10-test.link.d/13-override.conf <<EOF
[Link]
Property=
Property=ACTION=foo IFINDEX=bar
UnsetProperty=DEVPATH
EOF
udevadm control --reload
output=$(udevadm test --action add /sys/class/net/test1)
assert_in "ACTION=add" "$output"
assert_not_in "ACTION=foo" "$output"
assert_in "IFINDEX=" "$output"
assert_not_in "IFINDEX=bar" "$output"
assert_in "DEVPATH=" "$output"
output=$(udevadm info --query property /sys/class/net/test1)
assert_not_in "ACTION=foo" "$output"
assert_in "IFINDEX=" "$output"
assert_not_in "IFINDEX=bar" "$output"
assert_in "DEVPATH=" "$output"
# cleanup
ip link del dev test1
rm -f /run/systemd/network/10-test.link
rm -rf /run/systemd/network/10-test.link.d
udevadm control --reload --log-level=info
exit 0