From 49120a7a8fdf772483f907c0bc4111b141185ac2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Apr 2025 04:33:14 +0900 Subject: [PATCH] udev-config: serialize/deserialize dynamical configurations Previously, configurations set by 'udevadm control' such as log level, maximum number of childrens, global properties, and so on were discarded on restart. This makes udevd serialize those configurations on stop, and deserialize them in the next invocation. --- src/udev/udev-config.c | 113 ++++++++++++++++++++++++++++++++++++++++ src/udev/udev-config.h | 3 ++ src/udev/udev-manager.c | 3 ++ 3 files changed, 119 insertions(+) diff --git a/src/udev/udev-config.c b/src/udev/udev-config.c index d88c18ba15..52855145da 100644 --- a/src/udev/udev-config.c +++ b/src/udev/udev-config.c @@ -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" @@ -512,6 +516,115 @@ UdevReloadFlags manager_revert_config(Manager *manager) { 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; +} + static usec_t extra_timeout_usec(void) { static usec_t saved = 10 * USEC_PER_SEC; static bool parsed = false; diff --git a/src/udev/udev-config.h b/src/udev/udev-config.h index 72ac3920ab..127b3b9a1b 100644 --- a/src/udev/udev-config.h +++ b/src/udev/udev-config.h @@ -36,4 +36,7 @@ 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); diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 919314b546..d35100198b 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -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. */ @@ -1178,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]);