--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "varlink-io.systemd.Udev.h"
+
+static SD_VARLINK_DEFINE_METHOD(
+ SetChildrenMax,
+ SD_VARLINK_FIELD_COMMENT("The maximum number of child processes. When 0 is specified, the maximum is determined based on the system resources."),
+ SD_VARLINK_DEFINE_INPUT(number, SD_VARLINK_INT, 0));
+
+static SD_VARLINK_DEFINE_METHOD(
+ SetEnvironment,
+ 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(StartExecQueue);
+
+static SD_VARLINK_DEFINE_METHOD(StopExecQueue);
+
+static SD_VARLINK_DEFINE_METHOD(Exit);
+
+SD_VARLINK_DEFINE_INTERFACE(
+ io_systemd_Udev,
+ "io.systemd.Udev",
+ SD_VARLINK_INTERFACE_COMMENT("An interface for controlling systemd-udevd."),
+ SD_VARLINK_SYMBOL_COMMENT("Sets the maximum number of child processes."),
+ &vl_method_SetChildrenMax,
+ SD_VARLINK_SYMBOL_COMMENT("Sets the global udev properties."),
+ &vl_method_SetEnvironment,
+ SD_VARLINK_SYMBOL_COMMENT("Starts processing of queued events."),
+ &vl_method_StartExecQueue,
+ SD_VARLINK_SYMBOL_COMMENT("Stops processing of queued events."),
+ &vl_method_StopExecQueue,
+ SD_VARLINK_SYMBOL_COMMENT("Terminates systemd-udevd. This exists for backward compatibility. Please consider to use 'systemctl stop systemd-udevd.service'."),
+ &vl_method_Exit);
#include "udev-spawn.h"
#include "udev-trace.h"
#include "udev-util.h"
+#include "udev-varlink.h"
#include "udev-watch.h"
#include "udev-worker.h"
sd_device_monitor_unref(manager->monitor);
udev_ctrl_unref(manager->ctrl);
+ sd_varlink_server_unref(manager->varlink_server);
sd_event_source_unref(manager->inotify_event);
sd_event_source_unref(manager->kill_workers_event);
}
}
-static void manager_exit(Manager *manager) {
+void manager_exit(Manager *manager) {
assert(manager);
manager->exit = true;
/* 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);
manager->inotify_event = sd_event_source_disable_unref(manager->inotify_event);
manager->inotify_fd = safe_close(manager->inotify_fd);
}
/* reload requested, HUP signal received, rules changed, builtin changed */
-static void manager_reload(Manager *manager, bool force) {
+void manager_reload(Manager *manager, bool force) {
_cleanup_(udev_rules_freep) UdevRules *rules = NULL;
usec_t now_usec;
int r;
for (int i = 0; i < n; i++) {
int fd = SD_LISTEN_FDS_START + i;
+ if (streq(names[i], "varlink"))
+ continue; /* The fd will be handled by sd_varlink_server_listen_auto(). */
+
if (sd_is_socket(fd, AF_UNIX, SOCK_SEQPACKET, -1) > 0) {
if (ctrl_fd >= 0) {
log_debug("Received multiple seqpacket socket (%s), ignoring.", names[i]);
if (r < 0)
return r;
+ r = manager_start_varlink_server(manager);
+ if (r < 0)
+ return r;
+
r = manager_start_device_monitor(manager);
if (r < 0)
return r;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "json-util.h"
+#include "strv.h"
+#include "udev-manager.h"
+#include "udev-varlink.h"
+#include "varlink-io.systemd.Udev.h"
+#include "varlink-io.systemd.service.h"
+#include "varlink-util.h"
+
+#define UDEV_VARLINK_ADDRESS "/run/udev/io.systemd.Udev"
+
+static int vl_method_reload(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ assert(link);
+
+ if (sd_json_variant_elements(parameters) > 0)
+ return sd_varlink_error_invalid_parameter(link, parameters);
+
+ log_debug("Received io.systemd.service.Reload()");
+ manager_reload(userdata, /* force = */ true);
+ return sd_varlink_reply(link, NULL);
+}
+
+static int vl_method_set_log_level(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ int r, level;
+
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "level", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_log_level, 0, SD_JSON_MANDATORY },
+ {}
+ };
+
+ assert(link);
+
+ r = sd_varlink_dispatch(link, parameters, dispatch_table, &level);
+ if (r != 0)
+ return r;
+
+ log_debug("Received io.systemd.service.SetLogLevel(%i)", level);
+ manager_set_log_level(userdata, level);
+ return sd_varlink_reply(link, NULL);
+}
+
+static int vl_method_set_children_max(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ unsigned n;
+ int r;
+
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "number", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, 0, SD_JSON_MANDATORY },
+ {}
+ };
+
+ assert(link);
+
+ r = sd_varlink_dispatch(link, parameters, dispatch_table, &n);
+ if (r != 0)
+ return r;
+
+ log_debug("Received io.systemd.Udev.SetChildrenMax(%u)", n);
+ manager_set_children_max(userdata, n);
+ return sd_varlink_reply(link, NULL);
+}
+
+static int vl_method_set_environment(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ int r;
+
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "assignments", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, 0, SD_JSON_MANDATORY },
+ {}
+ };
+
+ assert(link);
+
+ _cleanup_strv_free_ char **v = NULL;
+ r = sd_varlink_dispatch(link, parameters, dispatch_table, &v);
+ if (r != 0)
+ return r;
+
+ log_debug("Received io.systemd.Udev.SetEnvironment()");
+ manager_set_environment(userdata, v);
+ 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;
+ int r;
+
+ assert(link);
+
+ if (sd_json_variant_elements(parameters) > 0)
+ return sd_varlink_error_invalid_parameter(link, parameters);
+
+ r = sd_varlink_get_current_method(link, &method);
+ if (r < 0)
+ return r;
+
+ log_debug("Received %s()", method);
+ manager->stop_exec_queue = streq(method, "io.systemd.Udev.StopExecQueue");
+ return sd_varlink_reply(link, NULL);
+}
+
+static int vl_method_exit(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ assert(link);
+
+ if (sd_json_variant_elements(parameters) > 0)
+ return sd_varlink_error_invalid_parameter(link, parameters);
+
+ /* Refuse further connections. */
+ _unused_ _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *v = sd_varlink_ref(link);
+
+ log_debug("Received io.systemd.udev.Exit()");
+ manager_exit(userdata);
+ return sd_varlink_reply(link, NULL);
+}
+
+int manager_start_varlink_server(Manager *manager) {
+ _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *v = NULL;
+ int r;
+
+ assert(manager);
+ assert(manager->event);
+
+ r = varlink_server_new(&v, SD_VARLINK_SERVER_ROOT_ONLY | SD_VARLINK_SERVER_INHERIT_USERDATA, manager);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ /* This needs to be after the inotify and uevent handling, to make sure that the ping is send back
+ * after fully processing the pending uevents (including the synthetic ones we may create due to
+ * inotify events). */
+ r = sd_varlink_server_attach_event(v, manager->event, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach Varlink connection to event loop: %m");
+
+ r = sd_varlink_server_listen_auto(v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to passed Varlink socket: %m");
+ if (r == 0) {
+ r = sd_varlink_server_listen_address(v, UDEV_VARLINK_ADDRESS, 0600);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to Varlink socket: %m");
+ }
+
+ r = sd_varlink_server_add_interface_many(
+ v,
+ &vl_interface_io_systemd_service,
+ &vl_interface_io_systemd_Udev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+ r = sd_varlink_server_bind_method_many(
+ v,
+ "io.systemd.service.Ping", varlink_method_ping,
+ "io.systemd.service.Reload", vl_method_reload,
+ "io.systemd.service.SetLogLevel", vl_method_set_log_level,
+ "io.systemd.Udev.SetChildrenMax", vl_method_set_children_max,
+ "io.systemd.Udev.SetEnvironment", vl_method_set_environment,
+ "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);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind Varlink methods: %m");
+
+ manager->varlink_server = TAKE_PTR(v);
+ return 0;
+}