udev: support reverting/serializing/deserializing configurations set by 'udevadm control' (#37067)
Previously, log level, properties, maximum number of worker processes, and so on set by 'udevadm control' are discarded on restart. This makes the configuration serialized on stop and deserialized in next invocation. Also, this introduces 'udevadm control --revert' to clear previous configurations.
This commit is contained in:
@@ -769,6 +769,16 @@
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>--revert</option></term>
|
||||
<listitem>
|
||||
<para>Revert settings previously set with <command>udevadm control</command> command. When
|
||||
specified, settings set with <option>-l/--log-level=</option>, <option>--trace</option>,
|
||||
<option>-m/--children-max=</option>, and <option>-p/--property=</option> will be cleared.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>-t</option></term>
|
||||
<term><option>--timeout=<replaceable>seconds</replaceable></option></term>
|
||||
|
||||
@@ -93,7 +93,7 @@ _udevadm() {
|
||||
-g --tag-match -y --sysname-match --name-match -b --parent-match
|
||||
--prioritized-subsystem'
|
||||
[SETTLE]='-t --timeout -E --exit-if-exists'
|
||||
[CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload --ping
|
||||
[CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload --ping --revert
|
||||
--load-credentials'
|
||||
[CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout --trace'
|
||||
[MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
|
||||
|
||||
@@ -67,6 +67,7 @@ _udevadm_control(){
|
||||
'(-p --property)'{-p,--property=}'[Set a global property for all events.]:KEY=VALUE' \
|
||||
'(-m --children-max=)'{-m,--children-max=}'[Set the maximum number of events.]:N' \
|
||||
'--trace=[Enable/disable trace logging.]:BOOL' \
|
||||
'--revert[Revert previously set configurations.]' \
|
||||
'(-t --timeout=)'{-t,--timeout=}'[The maximum number of seconds to wait for a reply from systemd-udevd.]:SECONDS'
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
static int notify_remove_fd_warn(const char *name) {
|
||||
int notify_remove_fd_warn(const char *name) {
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
@@ -23,6 +23,7 @@ static inline void notify_on_cleanup(const char **p) {
|
||||
(void) sd_notify(false, *p);
|
||||
}
|
||||
|
||||
int notify_remove_fd_warn(const char *name);
|
||||
int notify_remove_fd_warnf(const char *format, ...) _printf_(1, 2);
|
||||
int close_and_notify_warn(int fd, const char *name);
|
||||
int notify_push_fd(int fd, const char *name);
|
||||
|
||||
@@ -17,6 +17,8 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
SD_VARLINK_FIELD_COMMENT("An array of global udev property assignments. Each string must be in KEY=VALUE style."),
|
||||
SD_VARLINK_DEFINE_INPUT(assignments, SD_VARLINK_STRING, SD_VARLINK_ARRAY));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(Revert);
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(StartExecQueue);
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(StopExecQueue);
|
||||
@@ -34,6 +36,8 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||
SD_VARLINK_SYMBOL_COMMENT("Sets the global udev properties."),
|
||||
&vl_method_SetEnvironment,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Starts processing of queued events."),
|
||||
&vl_method_Revert,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Revert previously set configurations."),
|
||||
&vl_method_StartExecQueue,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Stops processing of queued events."),
|
||||
&vl_method_StopExecQueue,
|
||||
|
||||
+147
-9
@@ -5,10 +5,14 @@
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "daemon-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "limits-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "serialize.h"
|
||||
#include "signal-util.h"
|
||||
#include "syslog-util.h"
|
||||
#include "udev-config.h"
|
||||
@@ -463,6 +467,24 @@ int manager_load(Manager *manager, int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static UdevReloadFlags manager_needs_reload(Manager *manager, const UdevConfig *old) {
|
||||
assert(manager);
|
||||
assert(old);
|
||||
|
||||
if (manager->config.resolve_name_timing != old->resolve_name_timing)
|
||||
return UDEV_RELOAD_RULES | UDEV_RELOAD_KILL_WORKERS;
|
||||
|
||||
if (manager->config.log_level != old->log_level ||
|
||||
manager->config.exec_delay_usec != old->exec_delay_usec ||
|
||||
manager->config.timeout_usec != old->timeout_usec ||
|
||||
manager->config.timeout_signal != old->timeout_signal ||
|
||||
manager->config.blockdev_read_only != old->blockdev_read_only ||
|
||||
manager->config.trace != old->trace)
|
||||
return UDEV_RELOAD_KILL_WORKERS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UdevReloadFlags manager_reload_config(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
@@ -473,17 +495,133 @@ UdevReloadFlags manager_reload_config(Manager *manager) {
|
||||
manager_merge_config(manager);
|
||||
manager_adjust_config(&manager->config);
|
||||
|
||||
if (manager->config.resolve_name_timing != old.resolve_name_timing)
|
||||
return UDEV_RELOAD_RULES | UDEV_RELOAD_KILL_WORKERS;
|
||||
return manager_needs_reload(manager, &old);
|
||||
}
|
||||
|
||||
if (manager->config.log_level != old.log_level ||
|
||||
manager->config.exec_delay_usec != old.exec_delay_usec ||
|
||||
manager->config.timeout_usec != old.timeout_usec ||
|
||||
manager->config.timeout_signal != old.timeout_signal ||
|
||||
manager->config.blockdev_read_only != old.blockdev_read_only ||
|
||||
manager->config.trace != old.trace)
|
||||
return UDEV_RELOAD_KILL_WORKERS;
|
||||
UdevReloadFlags manager_revert_config(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
UdevReloadFlags flags = 0;
|
||||
if (!hashmap_isempty(manager->properties)) {
|
||||
flags |= UDEV_RELOAD_KILL_WORKERS;
|
||||
manager->properties = hashmap_free(manager->properties);
|
||||
}
|
||||
|
||||
UdevConfig old = manager->config;
|
||||
|
||||
manager->config_by_control = UDEV_CONFIG_INIT;
|
||||
manager_merge_config(manager);
|
||||
manager_adjust_config(&manager->config);
|
||||
|
||||
return flags | manager_needs_reload(manager, &old);
|
||||
}
|
||||
|
||||
int manager_serialize_config(Manager *manager) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
r = open_serialization_file("systemd-udevd", &f);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to open new serialization file: %m");
|
||||
|
||||
if (manager->config_by_control.log_level >= 0) {
|
||||
r = serialize_item_format(f, "log-level", "%i", manager->config_by_control.log_level);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (manager->config_by_control.children_max > 0) {
|
||||
r = serialize_item_format(f, "children-max", "%u", manager->config_by_control.children_max);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = serialize_bool_elide(f, "trace", manager->config_by_control.trace);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *k, *v;
|
||||
HASHMAP_FOREACH_KEY(v, k, manager->properties) {
|
||||
r = serialize_item_format(f, "property", "%s=%s", k, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = finish_serialization_file(f);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to finalize serialization file: %m");
|
||||
|
||||
/* Remove the previous serialization to make it replaced with the new one. */
|
||||
(void) notify_remove_fd_warn("config-serialization");
|
||||
|
||||
r = notify_push_fd(fileno(f), "config-serialization");
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to push serialization fd to service manager: %m");
|
||||
|
||||
log_debug("Serialized configurations.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_deserialize_config(Manager *manager, int *fd) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(fd);
|
||||
assert(*fd >= 0);
|
||||
|
||||
/* This may invalidate passed file descriptor even on failure. */
|
||||
|
||||
_cleanup_fclose_ FILE *f = take_fdopen(fd, "r");
|
||||
if (!f)
|
||||
return log_warning_errno(errno, "Failed to open serialization fd: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *l = NULL;
|
||||
const char *val;
|
||||
|
||||
r = deserialize_read_line(f, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* eof or end marker */
|
||||
break;
|
||||
|
||||
if ((val = startswith(l, "log-level="))) {
|
||||
r = log_level_from_string(val);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse serialized log level (%s), ignoring: %m", val);
|
||||
else
|
||||
manager->config_by_control.log_level = r;
|
||||
|
||||
} else if ((val = startswith(l, "children-max="))) {
|
||||
r = safe_atou(val, &manager->config_by_control.children_max);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse serialized children max (%s), ignoring: %m", val);
|
||||
|
||||
} else if ((val = startswith(l, "trace="))) {
|
||||
r = parse_boolean(val);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse serialized trace (%s), ignoring: %m", val);
|
||||
else
|
||||
manager->config_by_control.trace = r;
|
||||
|
||||
} else if ((val = startswith(l, "property="))) {
|
||||
if (!udev_property_assignment_is_valid(val))
|
||||
r = -EINVAL;
|
||||
else
|
||||
r = manager_set_environment_one(manager, val);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to deserialize property (%s), ignoring: %m", val);
|
||||
|
||||
} else
|
||||
log_debug("Unknown serialization item, ignoring: %s", l);
|
||||
}
|
||||
|
||||
manager_merge_config(manager);
|
||||
manager_adjust_config(&manager->config);
|
||||
|
||||
log_debug("Deserialized configurations.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,5 +34,9 @@ void manager_set_environment(Manager *manager, char * const *v);
|
||||
|
||||
int manager_load(Manager *manager, int argc, char *argv[]);
|
||||
UdevReloadFlags manager_reload_config(Manager *manager);
|
||||
UdevReloadFlags manager_revert_config(Manager *manager);
|
||||
|
||||
int manager_serialize_config(Manager *manager);
|
||||
int manager_deserialize_config(Manager *manager, int *fd);
|
||||
|
||||
usec_t manager_kill_worker_timeout(Manager *manager);
|
||||
|
||||
@@ -237,6 +237,7 @@ void manager_exit(Manager *manager) {
|
||||
/* close sources of new events and discard buffered events */
|
||||
manager->ctrl = udev_ctrl_unref(manager->ctrl);
|
||||
manager->varlink_server = sd_varlink_server_unref(manager->varlink_server);
|
||||
(void) manager_serialize_config(manager);
|
||||
|
||||
/* Disable the event source, but does not close the inotify fd here, as we may still receive
|
||||
* notification messages about requests to add or remove inotify watches. */
|
||||
@@ -311,6 +312,17 @@ void manager_reload(Manager *manager, bool force) {
|
||||
notify_ready(manager);
|
||||
}
|
||||
|
||||
void manager_revert(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
UdevReloadFlags flags = manager_revert_config(manager);
|
||||
if (flags == 0)
|
||||
return;
|
||||
|
||||
assert(flags == UDEV_RELOAD_KILL_WORKERS);
|
||||
manager_kill_workers(manager, SIGTERM);
|
||||
}
|
||||
|
||||
static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
|
||||
_cleanup_(worker_freep) Worker *worker = ASSERT_PTR(userdata);
|
||||
sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
|
||||
@@ -1167,6 +1179,8 @@ static int manager_listen_fds(Manager *manager) {
|
||||
r = manager_init_device_monitor(manager, fd);
|
||||
else if (streq(names[i], "inotify"))
|
||||
r = manager_init_inotify(manager, fd);
|
||||
else if (streq(names[i], "config-serialization"))
|
||||
r = manager_deserialize_config(manager, &fd);
|
||||
else
|
||||
r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received unexpected fd (%s), ignoring.", names[i]);
|
||||
|
||||
@@ -70,6 +70,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
int manager_main(Manager *manager);
|
||||
void manager_reload(Manager *manager, bool force);
|
||||
void manager_revert(Manager *manager);
|
||||
void manager_exit(Manager *manager);
|
||||
|
||||
void notify_ready(Manager *manager);
|
||||
|
||||
@@ -103,6 +103,21 @@ static int vl_method_set_environment(sd_varlink *link, sd_json_variant *paramete
|
||||
return sd_varlink_reply(link, NULL);
|
||||
}
|
||||
|
||||
static int vl_method_revert(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, /* dispatch_table = */ NULL, /* userdata = */ NULL);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
log_debug("Received io.systemd.Udev.Revert()");
|
||||
manager_revert(manager);
|
||||
return sd_varlink_reply(link, NULL);
|
||||
}
|
||||
|
||||
static int vl_method_start_stop_exec_queue(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
const char *method;
|
||||
@@ -183,6 +198,7 @@ int manager_start_varlink_server(Manager *manager) {
|
||||
"io.systemd.Udev.SetTrace", vl_method_set_trace,
|
||||
"io.systemd.Udev.SetChildrenMax", vl_method_set_children_max,
|
||||
"io.systemd.Udev.SetEnvironment", vl_method_set_environment,
|
||||
"io.systemd.Udev.Revert", vl_method_revert,
|
||||
"io.systemd.Udev.StartExecQueue", vl_method_start_stop_exec_queue,
|
||||
"io.systemd.Udev.StopExecQueue", vl_method_start_stop_exec_queue,
|
||||
"io.systemd.Udev.Exit", vl_method_exit);
|
||||
|
||||
@@ -32,6 +32,7 @@ static int arg_max_children = -1;
|
||||
static int arg_log_level = -1;
|
||||
static int arg_start_exec_queue = -1;
|
||||
static int arg_trace = -1;
|
||||
static bool arg_revert = false;
|
||||
static bool arg_load_credentials = false;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep);
|
||||
@@ -45,7 +46,8 @@ static bool arg_has_control_commands(void) {
|
||||
!strv_isempty(arg_env) ||
|
||||
arg_max_children >= 0 ||
|
||||
arg_ping ||
|
||||
arg_trace >= 0;
|
||||
arg_trace >= 0 ||
|
||||
arg_revert;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
@@ -62,6 +64,7 @@ static int help(void) {
|
||||
" -m --children-max=N Maximum number of children\n"
|
||||
" --ping Wait for udev to respond to a ping message\n"
|
||||
" --trace=BOOL Enable/disable trace logging\n"
|
||||
" --revert Revert previously set configurations\n"
|
||||
" -t --timeout=SECONDS Maximum time to block for a reply\n"
|
||||
" --load-credentials Load udev rules from credentials\n",
|
||||
program_invocation_short_name);
|
||||
@@ -73,6 +76,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_PING = 0x100,
|
||||
ARG_TRACE,
|
||||
ARG_REVERT,
|
||||
ARG_LOAD_CREDENTIALS,
|
||||
};
|
||||
|
||||
@@ -89,6 +93,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "children-max", required_argument, NULL, 'm' },
|
||||
{ "ping", no_argument, NULL, ARG_PING },
|
||||
{ "trace", required_argument, NULL, ARG_TRACE },
|
||||
{ "revert", no_argument, NULL, ARG_REVERT },
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "load-credentials", no_argument, NULL, ARG_LOAD_CREDENTIALS },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
@@ -157,6 +162,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_trace = r;
|
||||
break;
|
||||
|
||||
case ARG_REVERT:
|
||||
arg_revert = true;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
r = parse_sec(optarg, &arg_timeout);
|
||||
if (r < 0)
|
||||
@@ -270,6 +279,12 @@ static int send_control_commands(void) {
|
||||
if (arg_exit)
|
||||
return varlink_call_and_log(link, "io.systemd.Udev.Exit", /* parameters = */ NULL, /* reply = */ NULL);
|
||||
|
||||
if (arg_revert) {
|
||||
r = varlink_call_and_log(link, "io.systemd.Udev.Revert", /* parameters = */ NULL, /* reply = */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_log_level >= 0) {
|
||||
r = varlink_callbo_and_log(link, "io.systemd.service.SetLogLevel", /* reply = */ NULL,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("level", arg_log_level));
|
||||
|
||||
@@ -86,4 +86,33 @@ udevadm trigger --action change --settle /dev/null
|
||||
test_property /dev/null PROP_FOO foo
|
||||
test_not_property /dev/null PROP_BAR
|
||||
|
||||
: revert
|
||||
|
||||
udevadm control --revert
|
||||
udevadm trigger --action change --settle /dev/null
|
||||
test_not_property /dev/null PROP_FOO
|
||||
test_not_property /dev/null PROP_BAR
|
||||
|
||||
: set again, and restart
|
||||
|
||||
udevadm control -p FOO=foo -p BAR=bar
|
||||
udevadm trigger --action change --settle /dev/null
|
||||
test_property /dev/null PROP_FOO foo
|
||||
test_property /dev/null PROP_BAR bar
|
||||
systemctl restart systemd-udevd.service
|
||||
udevadm trigger --action change --settle /dev/null
|
||||
test_property /dev/null PROP_FOO foo
|
||||
test_property /dev/null PROP_BAR bar
|
||||
|
||||
: revert again, and restart
|
||||
|
||||
udevadm control --revert
|
||||
udevadm trigger --action change --settle /dev/null
|
||||
test_not_property /dev/null PROP_FOO
|
||||
test_not_property /dev/null PROP_BAR
|
||||
systemctl restart systemd-udevd.service
|
||||
udevadm trigger --action change --settle /dev/null
|
||||
test_not_property /dev/null PROP_FOO
|
||||
test_not_property /dev/null PROP_BAR
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -61,10 +61,13 @@ udevadm control -R
|
||||
udevadm control -p HELLO=world
|
||||
udevadm control -m 42
|
||||
udevadm control --ping -t 5
|
||||
udevadm control --trace yes
|
||||
udevadm control --trace no
|
||||
udevadm control --trace yes
|
||||
udevadm control --load-credentials
|
||||
udevadm control -h
|
||||
# Sanity check for serialization and deserialization
|
||||
systemctl restart systemd-udevd.service
|
||||
udevadm control --revert
|
||||
|
||||
udevadm info /dev/null
|
||||
udevadm info /sys/class/net/$netdev
|
||||
|
||||
Reference in New Issue
Block a user