From b0ea79c5b5e056cd61eb518308aa5a992f6c313d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 27 Jun 2025 15:08:44 +0200 Subject: [PATCH 1/5] network: Add varlink socket unit --- src/network/networkd-manager-varlink.c | 11 ++++++++--- src/network/networkd-manager-varlink.h | 2 +- src/network/networkd-manager.c | 20 ++++++++++++++------ units/meson.build | 4 ++++ units/systemd-networkd-varlink.socket | 25 +++++++++++++++++++++++++ units/systemd-networkd.service.in | 4 ++-- 6 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 units/systemd-networkd-varlink.socket diff --git a/src/network/networkd-manager-varlink.c b/src/network/networkd-manager-varlink.c index d443188709..1697efd771 100644 --- a/src/network/networkd-manager-varlink.c +++ b/src/network/networkd-manager-varlink.c @@ -259,8 +259,9 @@ static int vl_method_set_persistent_storage(sd_varlink *vlink, sd_json_variant * return sd_varlink_reply(vlink, NULL); } -int manager_connect_varlink(Manager *m) { +int manager_connect_varlink(Manager *m, int fd) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL; + _unused_ _cleanup_close_ int fd_close = fd; int r; assert(m); @@ -297,10 +298,15 @@ int manager_connect_varlink(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); - r = sd_varlink_server_listen_address(s, "/run/systemd/netif/io.systemd.Network", 0666); + if (fd < 0) + r = sd_varlink_server_listen_address(s, "/run/systemd/netif/io.systemd.Network", /* mode= */ 0666); + else + r = sd_varlink_server_listen_fd(s, fd); if (r < 0) return log_error_errno(r, "Failed to bind to varlink socket: %m"); + TAKE_FD(fd_close); + r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); @@ -313,5 +319,4 @@ void manager_varlink_done(Manager *m) { assert(m); m->varlink_server = sd_varlink_server_unref(m->varlink_server); - (void) unlink("/run/systemd/netif/io.systemd.Network"); } diff --git a/src/network/networkd-manager-varlink.h b/src/network/networkd-manager-varlink.h index 992e48839e..bd5cede404 100644 --- a/src/network/networkd-manager-varlink.h +++ b/src/network/networkd-manager-varlink.h @@ -3,5 +3,5 @@ #include "networkd-forward.h" -int manager_connect_varlink(Manager *m); +int manager_connect_varlink(Manager *m, int fd); void manager_varlink_done(Manager *m); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index a0455544cf..82aa1337fe 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -205,12 +205,13 @@ static int manager_connect_udev(Manager *m) { return 0; } -static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) { +static int manager_listen_fds(Manager *m, int *ret_rtnl_fd, int *ret_varlink_fd) { _cleanup_strv_free_ char **names = NULL; - int n, rtnl_fd = -EBADF; + int n, rtnl_fd = -EBADF, varlink_fd = -EBADF; assert(m); assert(ret_rtnl_fd); + assert(ret_varlink_fd); n = sd_listen_fds_with_names(/* unset_environment = */ true, &names); if (n < 0) @@ -221,7 +222,7 @@ static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) { if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { if (rtnl_fd >= 0) { - log_debug("Received multiple netlink socket, ignoring."); + log_debug("Received multiple netlink sockets, ignoring."); goto unused; } @@ -229,6 +230,11 @@ static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) { continue; } + if (streq(names[i], "varlink")) { + varlink_fd = fd; + continue; + } + if (manager_set_serialization_fd(m, fd, names[i]) >= 0) continue; @@ -243,6 +249,8 @@ static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) { } *ret_rtnl_fd = rtnl_fd; + *ret_varlink_fd = varlink_fd; + return 0; } @@ -513,7 +521,7 @@ static int manager_set_keep_configuration(Manager *m) { } int manager_setup(Manager *m) { - _cleanup_close_ int rtnl_fd = -EBADF; + _cleanup_close_ int rtnl_fd = -EBADF, varlink_fd = -EBADF; int r; assert(m); @@ -537,7 +545,7 @@ int manager_setup(Manager *m) { if (r < 0) return r; - r = manager_listen_fds(m, &rtnl_fd); + r = manager_listen_fds(m, &rtnl_fd, &varlink_fd); if (r < 0) return r; @@ -552,7 +560,7 @@ int manager_setup(Manager *m) { if (m->test_mode) return 0; - r = manager_connect_varlink(m); + r = manager_connect_varlink(m, TAKE_FD(varlink_fd)); if (r < 0) return r; diff --git a/units/meson.build b/units/meson.build index 8a3ce203a1..c595c1de5a 100644 --- a/units/meson.build +++ b/units/meson.build @@ -502,6 +502,10 @@ units = [ 'file' : 'systemd-networkd-persistent-storage.service', 'conditions' : ['ENABLE_NETWORKD'], }, + { + 'file' : 'systemd-networkd-varlink.socket', + 'conditions' : ['ENABLE_NETWORKD'], + }, { 'file' : 'systemd-networkd-wait-online.service.in', 'conditions' : ['ENABLE_NETWORKD'], diff --git a/units/systemd-networkd-varlink.socket b/units/systemd-networkd-varlink.socket new file mode 100644 index 0000000000..0f2304ff49 --- /dev/null +++ b/units/systemd-networkd-varlink.socket @@ -0,0 +1,25 @@ +# 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=Network Service Varlink Socket +Documentation=man:systemd-networkd.service(8) +ConditionCapability=CAP_NET_ADMIN +DefaultDependencies=no +Before=sockets.target shutdown.target +Conflicts=shutdown.target + +[Socket] +ListenStream=/run/systemd/netif/io.systemd.Network +FileDescriptorName=varlink +SocketMode=0666 +Service=systemd-networkd.service + +[Install] +WantedBy=sockets.target diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in index 6fb2b7e2db..c7bcf73a27 100644 --- a/units/systemd-networkd.service.in +++ b/units/systemd-networkd.service.in @@ -46,7 +46,7 @@ RestrictRealtime=yes RestrictSUIDSGID=yes RuntimeDirectory=systemd/netif RuntimeDirectoryPreserve=yes -Sockets=systemd-networkd.socket +Sockets=systemd-networkd.socket systemd-networkd-varlink.socket SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service bpf @@ -56,7 +56,7 @@ User=systemd-network [Install] WantedBy=multi-user.target -Also=systemd-networkd.socket +Also=systemd-networkd.socket systemd-networkd-varlink.socket Alias=dbus-org.freedesktop.network1.service # The output from this generator is used by udevd and networkd. Enable it by From 0eb59bd5ae45fbe368ca8f863d6e64652475764a Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 27 Jun 2025 16:14:33 +0200 Subject: [PATCH 2/5] login: Add missing newline between headers --- src/login/logind-varlink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c index ada491c6e2..40aa82f1f8 100644 --- a/src/login/logind-varlink.c +++ b/src/login/logind-varlink.c @@ -2,6 +2,7 @@ #include "sd-bus.h" #include "sd-event.h" + #include "alloc-util.h" #include "cgroup-util.h" #include "format-util.h" From 0470754f9d2c6298ba19d1b1219c25e979190001 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 27 Jun 2025 16:18:23 +0200 Subject: [PATCH 3/5] login: Add varlink socket unit --- src/login/logind-varlink.c | 11 +++++++-- src/login/logind-varlink.h | 2 +- src/login/logind.c | 35 ++++++++++++++++++++++++----- units/meson.build | 5 +++++ units/systemd-logind-varlink.socket | 18 +++++++++++++++ 5 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 units/systemd-logind-varlink.socket diff --git a/src/login/logind-varlink.c b/src/login/logind-varlink.c index 40aa82f1f8..757b4196fd 100644 --- a/src/login/logind-varlink.c +++ b/src/login/logind-varlink.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "cgroup-util.h" +#include "fd-util.h" #include "format-util.h" #include "hashmap.h" #include "json-util.h" @@ -327,8 +328,9 @@ static int vl_method_release_session(sd_varlink *link, sd_json_variant *paramete return sd_varlink_reply(link, NULL); } -int manager_varlink_init(Manager *m) { +int manager_varlink_init(Manager *m, int fd) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL; + _unused_ _cleanup_close_ int fd_close = fd; int r; assert(m); @@ -362,10 +364,15 @@ int manager_varlink_init(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register varlink methods: %m"); - r = sd_varlink_server_listen_address(s, "/run/systemd/io.systemd.Login", 0666); + if (fd < 0) + r = sd_varlink_server_listen_address(s, "/run/systemd/io.systemd.Login", /* mode= */ 0666); + else + r = sd_varlink_server_listen_fd(s, fd); if (r < 0) return log_error_errno(r, "Failed to bind to varlink socket: %m"); + TAKE_FD(fd_close); + r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); diff --git a/src/login/logind-varlink.h b/src/login/logind-varlink.h index d99513d210..b6b74093a5 100644 --- a/src/login/logind-varlink.h +++ b/src/login/logind-varlink.h @@ -3,7 +3,7 @@ #include "logind-forward.h" -int manager_varlink_init(Manager *m); +int manager_varlink_init(Manager *m, int fd); void manager_varlink_done(Manager *m); int session_send_create_reply_varlink(Session *s, const sd_bus_error *error); diff --git a/src/login/logind.c b/src/login/logind.c index adcf306509..c5819101e5 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -525,9 +525,8 @@ fail_close: } static int manager_enumerate_sessions(Manager *m) { - _cleanup_strv_free_ char **fdnames = NULL; _cleanup_closedir_ DIR *d = NULL; - int r = 0, n; + int r = 0; assert(m); @@ -560,6 +559,16 @@ static int manager_enumerate_sessions(Manager *m) { RET_GATHER(r, log_warning_errno(k, "Failed to deserialize session '%s', ignoring: %m", s->id)); } + return r; +} + +static int manager_enumerate_fds(Manager *m, int *ret_varlink_fd) { + _cleanup_strv_free_ char **fdnames = NULL; + int varlink_fd = -EBADF, n, r = 0; + + assert(m); + assert(ret_varlink_fd); + n = sd_listen_fds_with_names(/* unset_environment = */ true, &fdnames); if (n < 0) return log_error_errno(n, "Failed to acquire passed fd list: %m"); @@ -567,9 +576,18 @@ static int manager_enumerate_sessions(Manager *m) { for (int i = 0; i < n; i++) { int fd = SD_LISTEN_FDS_START + i; + if (streq(fdnames[i], "varlink")) { + assert(varlink_fd < 0); + varlink_fd = fd; + continue; + } + RET_GATHER(r, manager_attach_session_fd_one_consume(m, fdnames[i], fd)); } + if (r >= 0) + *ret_varlink_fd = varlink_fd; + return r; } @@ -1171,6 +1189,7 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa } static int manager_startup(Manager *m) { + _cleanup_close_ int varlink_fd = -EBADF; int r; Seat *seat; Session *session; @@ -1202,10 +1221,6 @@ static int manager_startup(Manager *m) { if (r < 0) return r; - r = manager_varlink_init(m); - if (r < 0) - return r; - /* Instantiate magic seat 0 */ r = manager_add_seat(m, "seat0", &m->seat0); if (r < 0) @@ -1232,6 +1247,10 @@ static int manager_startup(Manager *m) { if (r < 0) log_warning_errno(r, "Session enumeration failed: %m"); + r = manager_enumerate_fds(m, &varlink_fd); + if (r < 0) + log_warning_errno(r, "File descriptor enumeration failed: %m"); + r = manager_enumerate_inhibitors(m); if (r < 0) log_warning_errno(r, "Inhibitor enumeration failed: %m"); @@ -1240,6 +1259,10 @@ static int manager_startup(Manager *m) { if (r < 0) log_warning_errno(r, "Button enumeration failed: %m"); + r = manager_varlink_init(m, TAKE_FD(varlink_fd)); + if (r < 0) + return r; + manager_load_scheduled_shutdown(m); /* Remove stale objects before we start them */ diff --git a/units/meson.build b/units/meson.build index c595c1de5a..3f3b047f3c 100644 --- a/units/meson.build +++ b/units/meson.build @@ -472,6 +472,11 @@ units = [ 'conditions' : ['ENABLE_LOCALED'], 'symlinks' : ['dbus-org.freedesktop.locale1.service'], }, + { + 'file': 'systemd-logind-varlink.socket', + 'conditions': ['ENABLE_LOGIND'], + 'symlinks' : ['sockets.target.wants/'], + }, { 'file' : 'systemd-logind.service.in', 'conditions' : ['ENABLE_LOGIND'], diff --git a/units/systemd-logind-varlink.socket b/units/systemd-logind-varlink.socket new file mode 100644 index 0000000000..3978e8ab90 --- /dev/null +++ b/units/systemd-logind-varlink.socket @@ -0,0 +1,18 @@ +# 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=User Login Management Varlink Socket +Documentation=man:systemd-logind.service(8) + +[Socket] +ListenStream=/run/systemd/io.systemd.Login +FileDescriptorName=varlink +SocketMode=0666 +Service=systemd-logind.service From ce06fad9c54fca5f05521d6393f15eebfcffa772 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 30 Jun 2025 13:06:46 +0200 Subject: [PATCH 4/5] udev: Fix initializing varlink server from listen fd manager_listen_fds() instructs sd_listen_fds_with_names() to unset the environment which means that when sd_varlink_server_listen_auto() is called from manager_start_varlink_server(), when it eventually calls sd_listen_fds_with_names() it will return zero because the environment has already been unset in manager_listen_fds(). Fix the issue by not using sd_varlink_server_listen_auto() but instead keeping track of the varlink socket in manager_listen_fds() and returning it and passing it to manager_start_varlink_server(). --- src/udev/udev-manager.c | 18 ++++++++++++------ src/udev/udev-varlink.c | 18 ++++++++++-------- src/udev/udev-varlink.h | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 280626da9e..34032184c6 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -1405,11 +1405,13 @@ static int manager_setup_event(Manager *manager) { return 0; } -static int manager_listen_fds(Manager *manager) { +static int manager_listen_fds(Manager *manager, int *ret_varlink_fd) { _cleanup_strv_free_ char **names = NULL; + int varlink_fd = -EBADF; int r; assert(manager); + assert(ret_varlink_fd); int n = sd_listen_fds_with_names(/* unset_environment = */ true, &names); if (n < 0) @@ -1418,9 +1420,10 @@ static int manager_listen_fds(Manager *manager) { for (int i = 0; i < n; i++) { int fd = SD_LISTEN_FDS_START + i; - if (streq(names[i], "varlink")) - r = 0; /* The fd will be handled by sd_varlink_server_listen_auto(). */ - else if (streq(names[i], "systemd-udevd-control.socket")) + if (streq(names[i], "varlink")) { + varlink_fd = fd; + r = 0; + } else if (streq(names[i], "systemd-udevd-control.socket")) r = manager_init_ctrl(manager, fd); else if (streq(names[i], "systemd-udevd-kernel.socket")) r = manager_init_device_monitor(manager, fd); @@ -1437,10 +1440,13 @@ static int manager_listen_fds(Manager *manager) { close_and_notify_warn(fd, names[i]); } + *ret_varlink_fd = varlink_fd; + return 0; } int manager_main(Manager *manager) { + _cleanup_close_ int varlink_fd = -EBADF; int r; assert(manager); @@ -1458,7 +1464,7 @@ int manager_main(Manager *manager) { if (r < 0) return r; - r = manager_listen_fds(manager); + r = manager_listen_fds(manager, &varlink_fd); if (r < 0) return r; @@ -1466,7 +1472,7 @@ int manager_main(Manager *manager) { if (r < 0) return r; - r = manager_start_varlink_server(manager); + r = manager_start_varlink_server(manager, TAKE_FD(varlink_fd)); if (r < 0) return r; diff --git a/src/udev/udev-varlink.c b/src/udev/udev-varlink.c index 5228d3512c..9a887d9c7f 100644 --- a/src/udev/udev-varlink.c +++ b/src/udev/udev-varlink.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "fd-util.h" #include "json-util.h" #include "log.h" #include "string-util.h" @@ -165,8 +166,9 @@ static int vl_method_exit(sd_varlink *link, sd_json_variant *parameters, sd_varl return sd_varlink_reply(link, NULL); } -int manager_start_varlink_server(Manager *manager) { +int manager_start_varlink_server(Manager *manager, int fd) { _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *v = NULL; + _cleanup_close_ int fd_close = fd; int r; assert(manager); @@ -183,14 +185,14 @@ int manager_start_varlink_server(Manager *manager) { 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) { + if (fd < 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"); - } + else + r = sd_varlink_server_listen_fd(v, fd); + if (r < 0) + return log_error_errno(r, "Failed to bind to Varlink socket: %m"); + + TAKE_FD(fd_close); r = sd_varlink_server_add_interface_many( v, diff --git a/src/udev/udev-varlink.h b/src/udev/udev-varlink.h index 080f7c95a7..fb14ac711d 100644 --- a/src/udev/udev-varlink.h +++ b/src/udev/udev-varlink.h @@ -3,5 +3,5 @@ #include "udev-forward.h" -int manager_start_varlink_server(Manager *manager); +int manager_start_varlink_server(Manager *manager, int fd); int udev_varlink_connect(sd_varlink **ret, usec_t timeout); From 67b2cb77695eec98e905693147eb6bd014c1768d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 30 Jun 2025 13:41:04 +0200 Subject: [PATCH 5/5] test: Add tests for various varlink socket units Let's make sure these socket units work as expected by stopping the related services and making sure the services are started as expected when a client connects to the corresponding socket. --- test/units/TEST-74-AUX-UTILS.varlinkctl.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/units/TEST-74-AUX-UTILS.varlinkctl.sh b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh index 2e0bf5f7d6..481dc85d6f 100755 --- a/test/units/TEST-74-AUX-UTILS.varlinkctl.sh +++ b/test/units/TEST-74-AUX-UTILS.varlinkctl.sh @@ -66,6 +66,20 @@ if [[ -x /usr/lib/systemd/systemd-pcrextend ]]; then varlinkctl introspect /usr/lib/systemd/systemd-pcrextend fi +# Test various varlink socket units to make sure that we can still connect to the varlink sockets even if the +# services are currently stopped (or restarting). +systemctl stop \ + systemd-networkd.service \ + systemd-hostnamed.service \ + systemd-machined.service \ + systemd-udevd.service +varlinkctl introspect /run/systemd/netif/io.systemd.Network +varlinkctl introspect /run/systemd/io.systemd.Hostname +varlinkctl introspect /run/systemd/machine/io.systemd.Machine +if ! systemd-detect-virt -qc; then + varlinkctl introspect /run/udev/io.systemd.Udev +fi + # SSH transport SSHBINDIR="$(mktemp -d)"