]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev-varlink: introduce io.systemd.Udev varlink interface
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 21 Dec 2024 16:58:09 +0000 (01:58 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 7 Jan 2025 11:31:15 +0000 (20:31 +0900)
Co-authored-by: David Tardon <dtardon@redhat.com>
src/shared/meson.build
src/shared/varlink-io.systemd.Udev.c [new file with mode: 0644]
src/shared/varlink-io.systemd.Udev.h [new file with mode: 0644]
src/test/test-varlink-idl.c
src/udev/meson.build
src/udev/udev-manager.c
src/udev/udev-manager.h
src/udev/udev-varlink.c [new file with mode: 0644]
src/udev/udev-varlink.h [new file with mode: 0644]

index 29bf974a71075ada360cef2fbad953cfab831257..403c304338ddd697b0ed8800e8331e60abb12c6e 100644 (file)
@@ -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 (file)
index 0000000..fb34036
--- /dev/null
@@ -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 (file)
index 0000000..d56f613
--- /dev/null
@@ -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;
index 74c64d392abd2f4e337831f7c6e17ec51b67ad91..24267795051801173df519bd67966b85be71d908 100644 (file)
@@ -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);
 }
 
index b4af60255c02bed61b32084a459de85281c4cd49..5d5cc33a499f8c0d16f17787ca7892be2edda540 100644 (file)
@@ -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',
 )
index 1768da5a3884f9973e48a539cf54adee280e53e6..b7f2b185c99008bb55b47a1baba1b69ea41927c6 100644 (file)
@@ -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;
index 2690d59bf39c82445577863f4e34f0107c5f9778..808f75d3e155280cb7c71db06801d71927dae80d 100644 (file)
@@ -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 (file)
index 0000000..8a7b488
--- /dev/null
@@ -0,0 +1,165 @@
+/* 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;
+}
diff --git a/src/udev/udev-varlink.h b/src/udev/udev-varlink.h
new file mode 100644 (file)
index 0000000..c4e3251
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+typedef struct Manager Manager;
+
+int manager_start_varlink_server(Manager *manager);