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.
This commit is contained in:
Philip Withnall
2026-06-26 13:01:31 +01:00
parent d0fbca06c5
commit 2d201a107b
13 changed files with 198 additions and 10 deletions
+8
View File
@@ -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:
+2
View File
@@ -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'],
+10 -5
View File
@@ -18,10 +18,15 @@
<refnamediv>
<refname>systemd-sysupdate</refname>
<refname>systemd-sysupdate.service</refname>
<refname>systemd-sysupdate.timer</refname>
<refname>systemd-sysupdate-update.service</refname>
<refname>systemd-sysupdate-update.timer</refname>
<refname>systemd-sysupdate-reboot.service</refname>
<refname>systemd-sysupdate-reboot.timer</refname>
<!-- Compatibility symlinks for old names of systemd-sysupdate-update.{service,timer} -->
<refname>systemd-sysupdate.service</refname>
<refname>systemd-sysupdate.timer</refname>
<refpurpose>Automatically Update OS or Other Resources</refpurpose>
</refnamediv>
@@ -31,7 +36,7 @@
<arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
<para><filename>systemd-sysupdate.service</filename></para>
<para><filename>systemd-sysupdate-update.service</filename></para>
</refsynopsisdiv>
<refsect1>
@@ -73,9 +78,9 @@
embedded in the disk images. For the latter, see <option>--image=</option> below. The latter is
particularly interesting to update container images or portable service images.</para>
<para>The <filename>systemd-sysupdate.service</filename> system service will automatically update the
<para>The <filename>systemd-sysupdate-update.service</filename> system service will automatically update the
host OS based on the installed transfer files. It is triggered in regular intervals via
<filename>systemd-sysupdate.timer</filename>. The <filename>systemd-sysupdate-reboot.service</filename>
<filename>systemd-sysupdate-update.timer</filename>. The <filename>systemd-sysupdate-reboot.service</filename>
will automatically reboot the system after a new version is installed. It is triggered via
<filename>systemd-sysupdate-reboot.timer</filename>. 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
@@ -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,
+1
View File
@@ -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',
@@ -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"));
@@ -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;
+83
View File
@@ -2,11 +2,13 @@
#include <unistd.h>
#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);
}
+13 -2
View File
@@ -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',
@@ -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
@@ -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.
+28
View File
@@ -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
+31
View File
@@ -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