From 2d201a107bea2a407fe35f8ad34cbdc6e1551faf Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 29 May 2026 13:37:16 +0100 Subject: [PATCH] sysupdate: Add basic varlink interface scaffolding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the scaffolding for being able to call sysupdate via varlink, but it doesn’t yet define or implement any methods. Those will come in following commits. The existing `systemd-sysupdate.service` and `systemd-sysupdate.timer` (which periodically ran `systemd-sysupdate update`) have been renamed to `systemd-sysupdate-update.{service,timer}` to make way for a new `systemd-sysupdate@.service` and `systemd-sysupdate.socket` file to handle varlink activation. Compatibility symlinks have been added for them. --- NEWS | 8 ++ man/rules/meson.build | 2 + man/systemd-sysupdate.xml | 15 ++-- src/libsystemd/sd-varlink/test-varlink-idl.c | 2 + src/shared/meson.build | 1 + src/shared/varlink-io.systemd.SysUpdate.c | 9 ++ src/shared/varlink-io.systemd.SysUpdate.h | 6 ++ src/sysupdate/sysupdate.c | 83 +++++++++++++++++++ units/meson.build | 15 +++- ...rvice => systemd-sysupdate-update.service} | 6 +- ...e.timer => systemd-sysupdate-update.timer} | 2 +- units/systemd-sysupdate.socket | 28 +++++++ units/systemd-sysupdate@.service | 31 +++++++ 13 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 src/shared/varlink-io.systemd.SysUpdate.c create mode 100644 src/shared/varlink-io.systemd.SysUpdate.h rename units/{systemd-sysupdate.service => systemd-sysupdate-update.service} (86%) rename units/{systemd-sysupdate.timer => systemd-sysupdate-update.timer} (94%) create mode 100644 units/systemd-sysupdate.socket create mode 100644 units/systemd-sysupdate@.service diff --git a/NEWS b/NEWS index e482c3f898..d34215377e 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,14 @@ CHANGES WITH 262: the symlink target. Previously, these were only expanded in the source. + * As part of changes to systemd-sysupdate, the existing + "systemd-sysupdate.service" and "systemd-sysupdate.timer" units – + which periodically ran "systemd-sysupdate" to update the host system – + have been renamed to "systemd-sysupdate-update.service" and + "systemd-sysupdate-update.timer" respectively. Compatibility symlinks + have been provided. This clears the way for a new + "systemd-sysupdate@.service" unit for varlink activation of sysupdate. + CHANGES WITH 261: Announcements of Future Feature Removals and Incompatible Changes: diff --git a/man/rules/meson.build b/man/rules/meson.build index 974d9eee4b..13025b8993 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -1267,6 +1267,8 @@ manpages = [ '8', ['systemd-sysupdate-reboot.service', 'systemd-sysupdate-reboot.timer', + 'systemd-sysupdate-update.service', + 'systemd-sysupdate-update.timer', 'systemd-sysupdate.service', 'systemd-sysupdate.timer'], 'ENABLE_SYSUPDATE'], diff --git a/man/systemd-sysupdate.xml b/man/systemd-sysupdate.xml index 7f4b089031..2855c650c8 100644 --- a/man/systemd-sysupdate.xml +++ b/man/systemd-sysupdate.xml @@ -18,10 +18,15 @@ systemd-sysupdate - systemd-sysupdate.service - systemd-sysupdate.timer + systemd-sysupdate-update.service + systemd-sysupdate-update.timer systemd-sysupdate-reboot.service systemd-sysupdate-reboot.timer + + + systemd-sysupdate.service + systemd-sysupdate.timer + Automatically Update OS or Other Resources @@ -31,7 +36,7 @@ OPTIONS - systemd-sysupdate.service + systemd-sysupdate-update.service @@ -73,9 +78,9 @@ embedded in the disk images. For the latter, see below. The latter is particularly interesting to update container images or portable service images. - The systemd-sysupdate.service system service will automatically update the + The systemd-sysupdate-update.service system service will automatically update the host OS based on the installed transfer files. It is triggered in regular intervals via - systemd-sysupdate.timer. The systemd-sysupdate-reboot.service + systemd-sysupdate-update.timer. The systemd-sysupdate-reboot.service will automatically reboot the system after a new version is installed. It is triggered via systemd-sysupdate-reboot.timer. The two services are separate from each other as it is typically advisable to download updates regularly while the system is up, but delay reboots until the diff --git a/src/libsystemd/sd-varlink/test-varlink-idl.c b/src/libsystemd/sd-varlink/test-varlink-idl.c index 0b3304a43c..a1ed9a0f42 100644 --- a/src/libsystemd/sd-varlink/test-varlink-idl.c +++ b/src/libsystemd/sd-varlink/test-varlink-idl.c @@ -51,6 +51,7 @@ #include "varlink-io.systemd.Resolve.Monitor.h" #include "varlink-io.systemd.Shutdown.h" #include "varlink-io.systemd.StorageProvider.h" +#include "varlink-io.systemd.SysUpdate.h" #include "varlink-io.systemd.SysUpdate.Notify.h" #include "varlink-io.systemd.Udev.h" #include "varlink-io.systemd.Unit.h" @@ -228,6 +229,7 @@ TEST(parse_format) { &vl_interface_io_systemd_Resolve_Monitor, &vl_interface_io_systemd_Shutdown, &vl_interface_io_systemd_StorageProvider, + &vl_interface_io_systemd_SysUpdate, &vl_interface_io_systemd_SysUpdate_Notify, &vl_interface_io_systemd_Udev, &vl_interface_io_systemd_Unit, diff --git a/src/shared/meson.build b/src/shared/meson.build index 6fa4216305..e2bef83bd2 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -251,6 +251,7 @@ shared_sources = files( 'varlink-io.systemd.Resolve.Monitor.c', 'varlink-io.systemd.Shutdown.c', 'varlink-io.systemd.StorageProvider.c', + 'varlink-io.systemd.SysUpdate.c', 'varlink-io.systemd.SysUpdate.Notify.c', 'varlink-io.systemd.Udev.c', 'varlink-io.systemd.Unit.c', diff --git a/src/shared/varlink-io.systemd.SysUpdate.c b/src/shared/varlink-io.systemd.SysUpdate.c new file mode 100644 index 0000000000..67595fe9ee --- /dev/null +++ b/src/shared/varlink-io.systemd.SysUpdate.c @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "bus-polkit.h" +#include "varlink-io.systemd.SysUpdate.h" + +SD_VARLINK_DEFINE_INTERFACE( + io_systemd_SysUpdate, + "io.systemd.SysUpdate", + SD_VARLINK_INTERFACE_COMMENT("APIs to manage system updates")); diff --git a/src/shared/varlink-io.systemd.SysUpdate.h b/src/shared/varlink-io.systemd.SysUpdate.h new file mode 100644 index 0000000000..8399cdfbc3 --- /dev/null +++ b/src/shared/varlink-io.systemd.SysUpdate.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-varlink-idl.h" + +extern const sd_varlink_interface vl_interface_io_systemd_SysUpdate; diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index f0f4ccefc1..f1ba327c5d 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -2,11 +2,13 @@ #include +#include "sd-bus.h" #include "sd-daemon.h" #include "sd-json.h" #include "sd-varlink.h" #include "build.h" +#include "bus-polkit.h" #include "conf-files.h" #include "constants.h" #include "dissect-image.h" @@ -19,6 +21,7 @@ #include "help-util.h" #include "hexdecoct.h" #include "image-policy.h" +#include "json-util.h" #include "loop-util.h" #include "main-func.h" #include "mount-util.h" @@ -39,6 +42,7 @@ #include "sysupdate-transfer.h" #include "sysupdate-update-set.h" #include "sysupdate-util.h" +#include "varlink-io.systemd.SysUpdate.h" #include "varlink-util.h" #include "verbs.h" @@ -58,6 +62,7 @@ static int arg_verify = -1; static ImagePolicy *arg_image_policy = NULL; static bool arg_offline = false; static char *arg_transfer_source = NULL; +static bool arg_varlink = false; STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); @@ -154,6 +159,25 @@ static int context_from_cmdline(Context *ret) { return 0; } +/* Stores any long-running server state which needs to persist between varlink calls, such as state for + * pending polkit requests */ +typedef struct Server { + sd_bus *system_bus; + Hashmap *polkit_registry; +} Server; + +#define SERVER_NULL \ + (Server) { \ + /* all fields fine with being initialised to NULL */ \ + } + +static void server_done(Server *s) { + assert(s); + + s->polkit_registry = hashmap_free(s->polkit_registry); + s->system_bus = sd_bus_flush_close_unref(s->system_bus); +} + static DEFINE_POINTER_ARRAY_FREE_FUNC(Transfer*, transfer_free); static int read_definitions( @@ -1413,6 +1437,29 @@ static int context_install( return 1; } +static int verify_polkit(Context *context, sd_varlink *link, const char *action, const char **details) { + int r; + Server *s = ASSERT_PTR(sd_varlink_get_userdata(ASSERT_PTR(link))); + + assert(context); + + if (!s->system_bus) { + r = sd_bus_open_system_with_description(&s->system_bus, "sysupdate-system"); + if (r < 0) + return log_error_errno(r, "Failed to get system bus connection: %m"); + + r = sd_bus_attach_event(s->system_bus, sd_varlink_get_event(link), SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return log_error_errno(r, "Failed to attach system bus to event loop: %m"); + } + + return varlink_verify_polkit_async(link, + s->system_bus, + action, + details, + &s->polkit_registry); +} + VERB(verb_list, "list", "[VERSION]", VERB_ANY, 2, VERB_DEFAULT, "Show installed and available versions"); static int verb_list(int argc, char *argv[], uintptr_t _data, void *userdata) { @@ -2220,10 +2267,43 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) { if (arg_definitions && arg_component) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --component= switches may not be combined."); + r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT); + if (r < 0) + return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m"); + if (r > 0) + arg_varlink = true; + *remaining_args = option_parser_get_args(&opts); return 1; } +static int vl_server(void) { + _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL; + _cleanup_(server_done) Server server = SERVER_NULL; + int r; + + r = varlink_server_new(&varlink_server, + SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA, + &server); + if (r < 0) + return log_error_errno(r, "Failed to allocate Varlink server: %m"); + + r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_SysUpdate); + if (r < 0) + return log_error_errno(r, "Failed to add Varlink interface: %m"); + + r = sd_varlink_server_bind_method_many( + varlink_server); + if (r < 0) + return log_error_errno(r, "Failed to bind Varlink method: %m"); + + r = sd_varlink_server_loop_auto(varlink_server); + if (r < 0) + return log_error_errno(r, "Failed to run Varlink event loop: %m"); + + return 0; +} + static int run(int argc, char *argv[]) { int r; @@ -2234,6 +2314,9 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; + if (arg_varlink) + return vl_server(); /* Invocation as Varlink service */ + return dispatch_verb(args, NULL); } diff --git a/units/meson.build b/units/meson.build index 83dae605fa..569aa2e2a2 100644 --- a/units/meson.build +++ b/units/meson.build @@ -849,12 +849,23 @@ units = [ 'conditions' : ['ENABLE_SYSUPDATE'], }, { - 'file' : 'systemd-sysupdate.service', + 'file' : 'systemd-sysupdate-update.service', + 'conditions' : ['ENABLE_SYSUPDATE'], + 'symlinks' : ['systemd-sysupdate.service'], # compatibility after rename + }, + { + 'file' : 'systemd-sysupdate@.service', 'conditions' : ['ENABLE_SYSUPDATE'], }, { - 'file' : 'systemd-sysupdate.timer', + 'file' : 'systemd-sysupdate.socket', 'conditions' : ['ENABLE_SYSUPDATE'], + 'symlinks' : ['sockets.target.wants/'], + }, + { + 'file' : 'systemd-sysupdate-update.timer', + 'conditions' : ['ENABLE_SYSUPDATE'], + 'symlinks' : ['systemd-sysupdate.timer'], # compatibility after rename }, { 'file' : 'systemd-sysupdated.service.in', diff --git a/units/systemd-sysupdate.service b/units/systemd-sysupdate-update.service similarity index 86% rename from units/systemd-sysupdate.service rename to units/systemd-sysupdate-update.service index 92ec7266e8..e8a634bfff 100644 --- a/units/systemd-sysupdate.service +++ b/units/systemd-sysupdate-update.service @@ -9,7 +9,7 @@ [Unit] Description=Automatic System Update -Documentation=man:systemd-sysupdate.service(8) +Documentation=man:systemd-sysupdate-update.service(8) Wants=network-online.target After=network-online.target ConditionVirtualization=!container @@ -18,6 +18,8 @@ ConditionVirtualization=!container Type=simple NotifyAccess=main ExecStart=systemd-sysupdate update --cleanup=yes + +# Keep this sandboxing synchronised with systemd-sysupdate@.service CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE CAP_LINUX_IMMUTABLE NoNewPrivileges=yes MemoryDenyWriteExecute=yes @@ -31,4 +33,4 @@ SystemCallArchitectures=native LockPersonality=yes [Install] -Also=systemd-sysupdate.timer +Also=systemd-sysupdate-update.timer diff --git a/units/systemd-sysupdate.timer b/units/systemd-sysupdate-update.timer similarity index 94% rename from units/systemd-sysupdate.timer rename to units/systemd-sysupdate-update.timer index b2c7cd4808..d055948c29 100644 --- a/units/systemd-sysupdate.timer +++ b/units/systemd-sysupdate-update.timer @@ -9,7 +9,7 @@ [Unit] Description=Automatic System Update -Documentation=man:systemd-sysupdate.service(8) +Documentation=man:systemd-sysupdate-update.service(8) # For containers we assume that the manager will handle updates. And we likely # can't even access our backing block device anyway. diff --git a/units/systemd-sysupdate.socket b/units/systemd-sysupdate.socket new file mode 100644 index 0000000000..d91ec793e7 --- /dev/null +++ b/units/systemd-sysupdate.socket @@ -0,0 +1,28 @@ +# 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=System Updates +Documentation=man:systemd-sysupdate(8) +DefaultDependencies=no +Before=sockets.target + +[Socket] +ListenStream=/run/systemd/io.systemd.SysUpdate +Symlinks=/run/varlink/registry/io.systemd.SysUpdate +FileDescriptorName=varlink +SocketMode=0666 +Accept=yes +MaxConnectionsPerSource=16 +XAttrEntryPoint=user.varlink=entrypoint +XAttrListen=user.varlink=listen +XAttrAccept=user.varlink=server + +[Install] +WantedBy=sockets.target diff --git a/units/systemd-sysupdate@.service b/units/systemd-sysupdate@.service new file mode 100644 index 0000000000..f8e86dd2c4 --- /dev/null +++ b/units/systemd-sysupdate@.service @@ -0,0 +1,31 @@ +# 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=System Updates +Documentation=man:systemd-sysupdate(8) +DefaultDependencies=no +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +ExecStart=-systemd-sysupdate + +# Keep this sandboxing synchronised with systemd-sysupdate-update.service +CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE CAP_LINUX_IMMUTABLE +NoNewPrivileges=yes +MemoryDenyWriteExecute=yes +ProtectHostname=yes +RestrictRealtime=yes +RestrictNamespaces=net +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +SystemCallFilter=@system-service @mount +SystemCallErrorNumber=EPERM +SystemCallArchitectures=native +LockPersonality=yes