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
@@ -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);
}