udev/net: introduce [Link] Property=, ImportProperty=, and UnsetProperty= settings

The applied order is equivalent to Environment=, PassEnvironment=, and
UnsetEnvironment= for [Service] or so.
This commit is contained in:
Yu Watanabe
2024-01-10 04:33:51 +09:00
parent 513ca8b6f0
commit 046286e863
6 changed files with 292 additions and 3 deletions
+3
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)
+176 -1
View File
@@ -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,
+7 -1
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,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);
+1 -1
View File
@@ -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;
+11
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);