summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2025-01-07 16:49:37 +0100
committerGitHub <noreply@github.com>2025-01-07 16:49:37 +0100
commit0dfd89fa32b828e284de61c8e98bdf6148d18422 (patch)
tree36279d277eea5e07261b0b8516c40aecfc30fe26
parentresolve: fix typo in DNS_EDE_RCODE_PROHIBITIED constant name (diff)
parentunits: introduce systemd-udevd-varlink.socket (diff)
downloadsystemd-0dfd89fa32b828e284de61c8e98bdf6148d18422.tar.xz
systemd-0dfd89fa32b828e284de61c8e98bdf6148d18422.zip
udev: introduce io.systemd.Udev varlink interface (#35721)
Replaces #25523.
-rw-r--r--man/rules/meson.build3
-rw-r--r--man/systemd-udevd.service.xml2
-rw-r--r--man/udevadm.xml2
-rw-r--r--src/shared/meson.build1
-rw-r--r--src/shared/varlink-io.systemd.Udev.c34
-rw-r--r--src/shared/varlink-io.systemd.Udev.h7
-rw-r--r--src/test/test-varlink-idl.c3
-rw-r--r--src/udev/meson.build1
-rw-r--r--src/udev/udev-manager.c15
-rw-r--r--src/udev/udev-manager.h4
-rw-r--r--src/udev/udev-varlink.c185
-rw-r--r--src/udev/udev-varlink.h11
-rw-r--r--src/udev/udevadm-control.c63
-rw-r--r--src/udev/udevadm-settle.c22
-rw-r--r--src/udev/udevadm-trigger.c16
-rw-r--r--src/udev/udevadm-util.c46
-rw-r--r--src/udev/udevadm-util.h1
-rwxr-xr-xtest/units/TEST-17-UDEV.10.sh6
-rw-r--r--units/initrd-udevadm-cleanup-db.service4
-rw-r--r--units/meson.build4
-rw-r--r--units/systemd-udev-trigger.service2
-rw-r--r--units/systemd-udevd-varlink.socket22
-rw-r--r--units/systemd-udevd.service.in2
23 files changed, 415 insertions, 41 deletions
diff --git a/man/rules/meson.build b/man/rules/meson.build
index d281842396..4fe9ca17b8 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -1143,7 +1143,8 @@ manpages = [
'8',
['systemd-udevd',
'systemd-udevd-control.socket',
- 'systemd-udevd-kernel.socket'],
+ 'systemd-udevd-kernel.socket',
+ 'systemd-udevd-varlink.socket'],
''],
['systemd-update-done.service', '8', ['systemd-update-done'], ''],
['systemd-update-utmp.service',
diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml
index c781765e1c..ad0be5bdab 100644
--- a/man/systemd-udevd.service.xml
+++ b/man/systemd-udevd.service.xml
@@ -20,6 +20,7 @@
<refname>systemd-udevd.service</refname>
<refname>systemd-udevd-control.socket</refname>
<refname>systemd-udevd-kernel.socket</refname>
+ <refname>systemd-udevd-varlink.socket</refname>
<refname>systemd-udevd</refname>
<refpurpose>Device event managing daemon</refpurpose>
</refnamediv>
@@ -28,6 +29,7 @@
<para><filename>systemd-udevd.service</filename></para>
<para><filename>systemd-udevd-control.socket</filename></para>
<para><filename>systemd-udevd-kernel.socket</filename></para>
+ <para><filename>systemd-udevd-varlink.socket</filename></para>
<cmdsynopsis>
<command>/usr/lib/systemd/systemd-udevd</command>
diff --git a/man/udevadm.xml b/man/udevadm.xml
index 8923bc70fe..13cf106d87 100644
--- a/man/udevadm.xml
+++ b/man/udevadm.xml
@@ -677,7 +677,7 @@
Note that <filename>systemd-udevd.service</filename> contains
<option>Restart=always</option> and so as a result, this option restarts systemd-udevd.
If you want to stop <filename>systemd-udevd.service</filename>, please use the following:
- <programlisting>systemctl stop systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd.service</programlisting>
+ <programlisting>systemctl stop systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd-varlink.socket systemd-udevd.service</programlisting>
</para>
</listitem>
</varlistentry>
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 29bf974a71..403c304338 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -194,6 +194,7 @@ shared_sources = files(
'varlink-io.systemd.PCRLock.c',
'varlink-io.systemd.Resolve.c',
'varlink-io.systemd.Resolve.Monitor.c',
+ 'varlink-io.systemd.Udev.c',
'varlink-io.systemd.UserDatabase.c',
'varlink-io.systemd.oom.c',
'varlink-io.systemd.service.c',
diff --git a/src/shared/varlink-io.systemd.Udev.c b/src/shared/varlink-io.systemd.Udev.c
new file mode 100644
index 0000000000..fb34036b42
--- /dev/null
+++ b/src/shared/varlink-io.systemd.Udev.c
@@ -0,0 +1,34 @@
+/* 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);
diff --git a/src/shared/varlink-io.systemd.Udev.h b/src/shared/varlink-io.systemd.Udev.h
new file mode 100644
index 0000000000..d56f613eff
--- /dev/null
+++ b/src/shared/varlink-io.systemd.Udev.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#pragma once
+
+#include "sd-varlink.h"
+#include "sd-varlink-idl.h"
+
+extern const sd_varlink_interface vl_interface_io_systemd_Udev;
diff --git a/src/test/test-varlink-idl.c b/src/test/test-varlink-idl.c
index 74c64d392a..2426779505 100644
--- a/src/test/test-varlink-idl.c
+++ b/src/test/test-varlink-idl.c
@@ -25,6 +25,7 @@
#include "varlink-io.systemd.PCRLock.h"
#include "varlink-io.systemd.Resolve.h"
#include "varlink-io.systemd.Resolve.Monitor.h"
+#include "varlink-io.systemd.Udev.h"
#include "varlink-io.systemd.UserDatabase.h"
#include "varlink-io.systemd.oom.h"
#include "varlink-io.systemd.service.h"
@@ -197,6 +198,8 @@ TEST(parse_format) {
print_separator();
test_parse_format_one(&vl_interface_io_systemd_AskPassword);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_Udev);
+ print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}
diff --git a/src/udev/meson.build b/src/udev/meson.build
index b4af60255c..5d5cc33a49 100644
--- a/src/udev/meson.build
+++ b/src/udev/meson.build
@@ -37,6 +37,7 @@ libudevd_core_sources = files(
'udev-node.c',
'udev-rules.c',
'udev-spawn.c',
+ 'udev-varlink.c',
'udev-watch.c',
'udev-worker.c',
)
diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c
index 1768da5a38..b7f2b185c9 100644
--- a/src/udev/udev-manager.c
+++ b/src/udev/udev-manager.c
@@ -32,6 +32,7 @@
#include "udev-spawn.h"
#include "udev-trace.h"
#include "udev-util.h"
+#include "udev-varlink.h"
#include "udev-watch.h"
#include "udev-worker.h"
@@ -148,6 +149,7 @@ Manager* manager_free(Manager *manager) {
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);
@@ -213,7 +215,7 @@ void manager_kill_workers(Manager *manager, bool force) {
}
}
-static void manager_exit(Manager *manager) {
+void manager_exit(Manager *manager) {
assert(manager);
manager->exit = true;
@@ -222,7 +224,7 @@ static 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);
manager->inotify_event = sd_event_source_disable_unref(manager->inotify_event);
manager->inotify_fd = safe_close(manager->inotify_fd);
@@ -246,7 +248,7 @@ void notify_ready(Manager *manager) {
}
/* 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;
@@ -1150,6 +1152,9 @@ static int listen_fds(int *ret_ctrl, int *ret_netlink) {
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]);
@@ -1417,6 +1422,10 @@ int manager_main(Manager *manager) {
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;
diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h
index 2690d59bf3..808f75d3e1 100644
--- a/src/udev/udev-manager.h
+++ b/src/udev/udev-manager.h
@@ -5,6 +5,7 @@
#include "sd-device.h"
#include "sd-event.h"
+#include "sd-varlink.h"
#include "hashmap.h"
#include "macro.h"
@@ -28,6 +29,7 @@ typedef struct Manager {
sd_device_monitor *monitor;
UdevCtrl *ctrl;
+ sd_varlink_server *varlink_server;
int worker_notify_fd;
/* used by udev-watch */
@@ -54,6 +56,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
int manager_init(Manager *manager);
int manager_main(Manager *manager);
+void manager_reload(Manager *manager, bool force);
+void manager_exit(Manager *manager);
void notify_ready(Manager *manager);
diff --git a/src/udev/udev-varlink.c b/src/udev/udev-varlink.c
new file mode 100644
index 0000000000..8faadb8bcf
--- /dev/null
+++ b/src/udev/udev-varlink.c
@@ -0,0 +1,185 @@
+/* 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;
+}
+
+int udev_varlink_connect(sd_varlink **ret, usec_t timeout) {
+ _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *link = NULL;
+ int r;
+
+ assert(ret);
+
+ r = sd_varlink_connect_address(&link, UDEV_VARLINK_ADDRESS);
+ if (r < 0)
+ return r;
+
+ (void) sd_varlink_set_description(link, "udev");
+
+ r = sd_varlink_set_relative_timeout(link, timeout);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(link);
+ return 0;
+}
diff --git a/src/udev/udev-varlink.h b/src/udev/udev-varlink.h
new file mode 100644
index 0000000000..759a4460ab
--- /dev/null
+++ b/src/udev/udev-varlink.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-varlink.h"
+
+#include "time-util.h"
+
+typedef struct Manager Manager;
+
+int manager_start_varlink_server(Manager *manager);
+int udev_varlink_connect(sd_varlink **ret, usec_t timeout);
diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
index 29dc88330c..3ccc621357 100644
--- a/src/udev/udevadm-control.c
+++ b/src/udev/udevadm-control.c
@@ -9,6 +9,7 @@
#include <unistd.h>
#include "creds-util.h"
+#include "errno-util.h"
#include "parse-util.h"
#include "process-util.h"
#include "static-destruct.h"
@@ -17,6 +18,8 @@
#include "time-util.h"
#include "udevadm.h"
#include "udev-ctrl.h"
+#include "udev-varlink.h"
+#include "varlink-util.h"
#include "virt.h"
static char **arg_env = NULL;
@@ -174,7 +177,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int send_control_commands(void) {
+static int send_control_commands_via_ctrl(void) {
_cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
int r;
@@ -238,6 +241,64 @@ static int send_control_commands(void) {
return 0;
}
+static int send_control_commands(void) {
+ _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *link = NULL;
+ int r;
+
+ r = udev_varlink_connect(&link, arg_timeout);
+ if (ERRNO_IS_NEG_DISCONNECT(r) || r == -ENOENT) {
+ log_debug_errno(r, "Failed to connect to udev via varlink, falling back to use legacy control socket, ignoring: %m");
+ return send_control_commands_via_ctrl();
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to udev via varlink: %m");
+
+ if (arg_exit)
+ return varlink_call_and_log(link, "io.systemd.Udev.Exit", /* parameters = */ NULL, /* reply = */ NULL);
+
+ 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));
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_start_exec_queue >= 0) {
+ r = varlink_call_and_log(link, arg_start_exec_queue ? "io.systemd.Udev.StartExecQueue" : "io.systemd.Udev.StopExecQueue",
+ /* parameters = */ NULL, /* reply = */ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_reload) {
+ r = varlink_call_and_log(link, "io.systemd.service.Reload", /* parameters = */ NULL, /* reply = */ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ if (!strv_isempty(arg_env)) {
+ r = varlink_callbo_and_log(link, "io.systemd.Udev.SetEnvironment", /* reply = */ NULL,
+ SD_JSON_BUILD_PAIR_STRV("assignments", arg_env));
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_max_children >= 0) {
+ r = varlink_callbo_and_log(link, "io.systemd.Udev.SetChildrenMax", /* reply = */ NULL,
+ SD_JSON_BUILD_PAIR_UNSIGNED("number", arg_max_children));
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_ping) {
+ r = varlink_call_and_log(link, "io.systemd.service.Ping", /* parameters = */ NULL, /* reply = */ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int control_main(int argc, char *argv[], void *userdata) {
int r;
diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
index d47a9c3a07..72e09e0fc5 100644
--- a/src/udev/udevadm-settle.c
+++ b/src/udev/udevadm-settle.c
@@ -16,8 +16,8 @@
#include "path-util.h"
#include "strv.h"
#include "time-util.h"
-#include "udev-ctrl.h"
#include "udev-util.h"
+#include "udevadm-util.h"
#include "udevadm.h"
#include "unit-def.h"
#include "virt.h"
@@ -198,23 +198,9 @@ int settle_main(int argc, char *argv[], void *userdata) {
(void) emit_deprecation_warning();
if (getuid() == 0) {
- _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
-
- /* guarantee that the udev daemon isn't pre-processing */
-
- r = udev_ctrl_new(&uctrl);
- if (r < 0)
- return log_error_errno(r, "Failed to create control socket for udev daemon: %m");
-
- r = udev_ctrl_send_ping(uctrl);
- if (r < 0) {
- log_debug_errno(r, "Failed to connect to udev daemon, ignoring: %m");
- return 0;
- }
-
- r = udev_ctrl_wait(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout_usec));
- if (r < 0)
- return log_error_errno(r, "Failed to wait for daemon to reply: %m");
+ r = udev_ping(MAX(5 * USEC_PER_SEC, arg_timeout_usec), /* ignore_connection_failure = */ true);
+ if (r <= 0)
+ return r;
} else {
/* For non-privileged users, at least check if udevd is running. */
if (access("/run/udev/control", F_OK) < 0)
diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
index 695f440b8f..808c383e21 100644
--- a/src/udev/udevadm-trigger.c
+++ b/src/udev/udevadm-trigger.c
@@ -21,7 +21,6 @@
#include "strv.h"
#include "udevadm.h"
#include "udevadm-util.h"
-#include "udev-ctrl.h"
#include "virt.h"
static bool arg_verbose = false;
@@ -498,19 +497,10 @@ int trigger_main(int argc, char *argv[], void *userdata) {
}
if (ping) {
- _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
-
- r = udev_ctrl_new(&uctrl);
- if (r < 0)
- return log_error_errno(r, "Failed to initialize udev control: %m");
-
- r = udev_ctrl_send_ping(uctrl);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to udev daemon: %m");
-
- r = udev_ctrl_wait(uctrl, ping_timeout_usec);
+ r = udev_ping(ping_timeout_usec, /* ignore_connection_failure = */ false);
if (r < 0)
- return log_error_errno(r, "Failed to wait for daemon to reply: %m");
+ return r;
+ assert(r > 0);
}
for (; optind < argc; optind++) {
diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c
index 2447edad19..2a3e974d04 100644
--- a/src/udev/udevadm-util.c
+++ b/src/udev/udevadm-util.c
@@ -7,8 +7,11 @@
#include "bus-util.h"
#include "device-private.h"
#include "path-util.h"
+#include "udev-ctrl.h"
+#include "udev-varlink.h"
#include "udevadm-util.h"
#include "unit-name.h"
+#include "varlink-util.h"
static int find_device_from_unit(const char *unit_name, sd_device **ret) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -122,3 +125,46 @@ int parse_device_action(const char *str, sd_device_action_t *action) {
*action = a;
return 1;
}
+
+static int udev_ping_via_ctrl(usec_t timeout_usec, bool ignore_connection_failure) {
+ _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
+ int r;
+
+ r = udev_ctrl_new(&uctrl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize udev control: %m");
+
+ r = udev_ctrl_send_ping(uctrl);
+ if (r < 0) {
+ bool ignore = ignore_connection_failure && (ERRNO_IS_NEG_DISCONNECT(r) || r == -ENOENT);
+ log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
+ "Failed to connect to udev daemon%s: %m",
+ ignore ? ", ignoring" : "");
+ return ignore ? 0 : r;
+ }
+
+ r = udev_ctrl_wait(uctrl, timeout_usec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait for daemon to reply: %m");
+
+ return 1; /* received reply */
+}
+
+int udev_ping(usec_t timeout_usec, bool ignore_connection_failure) {
+ _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *link = NULL;
+ int r;
+
+ r = udev_varlink_connect(&link, timeout_usec);
+ if (ERRNO_IS_NEG_DISCONNECT(r) || r == -ENOENT) {
+ log_debug_errno(r, "Failed to connect to udev via varlink, falling back to use legacy control socket, ignoring: %m");
+ return udev_ping_via_ctrl(timeout_usec, ignore_connection_failure);
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to udev via varlink: %m");
+
+ r = varlink_call_and_log(link, "io.systemd.service.Ping", /* parameters = */ NULL, /* reply = */ NULL);
+ if (r < 0)
+ return r;
+
+ return 1; /* received reply */
+}
diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h
index 7fb4556413..4b58efbe97 100644
--- a/src/udev/udevadm-util.h
+++ b/src/udev/udevadm-util.h
@@ -6,3 +6,4 @@
int find_device(const char *id, const char *prefix, sd_device **ret);
int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret);
int parse_device_action(const char *str, sd_device_action_t *action);
+int udev_ping(usec_t timeout, bool ignore_connection_failure);
diff --git a/test/units/TEST-17-UDEV.10.sh b/test/units/TEST-17-UDEV.10.sh
index 7ca05f5287..afa05909c4 100755
--- a/test/units/TEST-17-UDEV.10.sh
+++ b/test/units/TEST-17-UDEV.10.sh
@@ -28,9 +28,15 @@ blk="$(mktemp)"
dd if=/dev/zero of="$blk" bs=1M count=1
loopdev="$(losetup --show -f "$blk")"
+# Wait for devices created in the above being processed.
+udevadm settle --timeout 30
+
udevadm -h
+INVOCATION_ID=$(systemctl show --property InvocationID --value systemd-udevd.service)
udevadm control -e
+# Wait for systemd-udevd.service being restarted.
+timeout 30 bash -ec "while [[ \"\$(systemctl show --property InvocationID --value systemd-udevd.service)\" == \"$INVOCATION_ID\" ]]; do sleep .5; done"
udevadm control -l emerg
udevadm control -l alert
udevadm control -l crit
diff --git a/units/initrd-udevadm-cleanup-db.service b/units/initrd-udevadm-cleanup-db.service
index bc444736fd..6c0a1e16ae 100644
--- a/units/initrd-udevadm-cleanup-db.service
+++ b/units/initrd-udevadm-cleanup-db.service
@@ -11,8 +11,8 @@
Description=Cleanup udev Database
DefaultDependencies=no
AssertPathExists=/etc/initrd-release
-Conflicts=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udev-trigger.service systemd-udev-settle.service
-After=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udev-trigger.service systemd-udev-settle.service
+Conflicts=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd-varlink.socket systemd-udev-trigger.service systemd-udev-settle.service
+After=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd-varlink.socket systemd-udev-trigger.service systemd-udev-settle.service
Before=initrd-switch-root.target
[Service]
diff --git a/units/meson.build b/units/meson.build
index f6d661da97..805991dfe4 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -733,6 +733,10 @@ units = [
'symlinks' : ['sockets.target.wants/'],
},
{
+ 'file' : 'systemd-udevd-varlink.socket',
+ 'symlinks' : ['sockets.target.wants/'],
+ },
+ {
'file' : 'systemd-udevd.service.in',
'symlinks' : ['sysinit.target.wants/'],
},
diff --git a/units/systemd-udev-trigger.service b/units/systemd-udev-trigger.service
index cb1e4f9fea..0ea1a4bd49 100644
--- a/units/systemd-udev-trigger.service
+++ b/units/systemd-udev-trigger.service
@@ -12,7 +12,7 @@ Description=Coldplug All udev Devices
Documentation=man:udev(7) man:systemd-udevd.service(8)
DefaultDependencies=no
Wants=systemd-udevd.service
-After=systemd-udevd-kernel.socket systemd-udevd-control.socket
+After=systemd-udevd-kernel.socket systemd-udevd-control.socket systemd-udevd-varlink.socket
Before=sysinit.target
ConditionPathIsReadWrite=/sys
diff --git a/units/systemd-udevd-varlink.socket b/units/systemd-udevd-varlink.socket
new file mode 100644
index 0000000000..94fee15b08
--- /dev/null
+++ b/units/systemd-udevd-varlink.socket
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=udev Varlink Socket
+Documentation=man:systemd-udevd-varlink.socket(8) man:udev(7)
+DefaultDependencies=no
+Before=sockets.target
+ConditionPathIsReadWrite=/sys
+
+[Socket]
+Service=systemd-udevd.service
+ListenStream=/run/udev/io.systemd.Udev
+FileDescriptorName=varlink
+SocketMode=0600
+RemoveOnStop=yes
diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in
index f4a4482088..13ec7088da 100644
--- a/units/systemd-udevd.service.in
+++ b/units/systemd-udevd.service.in
@@ -23,7 +23,7 @@ DelegateSubgroup=udev
Type=notify-reload
# Note that udev will reset the value internally for its workers
OOMScoreAdjust=-1000
-Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket
+Sockets=systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd-varlink.socket
Restart=always
RestartSec=0
ExecStart={{LIBEXECDIR}}/systemd-udevd