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:
commit
9a70dc02c6
3
TODO
3
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.
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
201
test/units/testsuite-17.link-property.sh
Executable file
201
test/units/testsuite-17.link-property.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user