From: Philip Withnall Date: Fri, 29 May 2026 12:37:16 +0000 (+0100) Subject: sysupdate: Add basic varlink interface scaffolding X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d201a107bea2a407fe35f8ad34cbdc6e1551faf;p=thirdparty%2Fsystemd.git sysupdate: Add basic varlink interface scaffolding 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. --- diff --git a/NEWS b/NEWS index e482c3f898f..d34215377eb 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 974d9eee4ba..13025b8993c 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 7f4b0890315..2855c650c88 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 0b3304a43c8..a1ed9a0f429 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 6fa42163056..e2bef83bd26 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 00000000000..67595fe9ee2 --- /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 00000000000..8399cdfbc39 --- /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 f0f4ccefc1d..f1ba327c5db 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 83dae605fad..569aa2e2a2c 100644 --- a/units/meson.build +++ b/units/meson.build @@ -849,13 +849,24 @@ 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.timer', + 'file' : 'systemd-sysupdate@.service', 'conditions' : ['ENABLE_SYSUPDATE'], }, + { + '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', 'conditions' : ['ENABLE_SYSUPDATED'], 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 92ec7266e8c..e8a634bfff5 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 b2c7cd48087..d055948c297 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 00000000000..d91ec793e70 --- /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 00000000000..f8e86dd2c4a --- /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