sysupdate: Add a stub varlink interface and implement CheckNew (#42422)

This puts the scaffolding in place for a varlink interface, but so far
it only adds a `io.systemd.Sysupdate.CheckNew()` method. Varlinkifying
the other verbs on `systemd-sysupdate` will happen in follow-up PRs, but
I thought I’d try and land this one early to:
 * Get review of the overall varlinkification scaffolding
* Lower the chance of big merge conflicts with others’ work by getting
the more invasive changes out of the way
* Get the scaffolding in place so others can start to build on it if
they wish (although I am currently working on porting the other existing
verbs)

It rearranges how the `Context` struct is allocated so that it’ll be
easier to add per-method/verb context structs which contain it in
future. It also changes all the `sysupdate.c` code to use arguments from
`Context` rather than `arg_*` globals, allowing them to be specified as
varlink parameters in future.

It also moves the existing `systemd-sysupdate.{timer,service}` units
(which periodically run `systemd-sysupdate update`) to
`systemd-sysupdate-update.{timer,service}` to clear space for a
`systemd-sysupdate@.service` and `systemd-sysupdate.socket` to act as a
varlink entry point.

/cc @AdrianVovk
This commit is contained in:
Lennart Poettering
2026-06-26 17:31:30 +02:00
committed by GitHub
28 changed files with 1038 additions and 357 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
+1 -1
View File
@@ -5359,7 +5359,7 @@ _public_ int sd_json_dispatch_full(
for (const sd_json_dispatch_field *p = table; p && p->name; p++) {
sd_json_dispatch_flags_t merged_flags = p->flags | flags;
if ((merged_flags & SD_JSON_MANDATORY) && !found[p-table]) {
if ((p->flags & SD_JSON_MANDATORY) && !found[p-table]) {
json_log(v, merged_flags, 0, "Missing object field '%s'.", p->name);
if ((merged_flags & SD_JSON_PERMISSIVE))
@@ -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,
+72
View File
@@ -452,6 +452,78 @@ TEST(invalid_parameter) {
ASSERT_OK(sd_event_loop(e));
}
typedef struct MandatoryTypeWithOptionalField {
const char *mandatory;
const char *optional;
} MandatoryTypeWithOptionalField;
static int dispatch_mandatory_type_with_optional_field(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
MandatoryTypeWithOptionalField *m = ASSERT_PTR(userdata);
static const sd_json_dispatch_field dispatch[] = {
{ "mandatory", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(*m, mandatory), SD_JSON_MANDATORY },
{ "optional", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(*m, optional), 0 },
{}
};
return sd_json_dispatch(variant, dispatch, flags, m);
}
static int method_mandatory_type_with_optional_field(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
MandatoryTypeWithOptionalField m = { NULL, NULL };
int r;
sd_json_dispatch_field table[] = {
{ "mandatoryField", SD_JSON_VARIANT_OBJECT, dispatch_mandatory_type_with_optional_field, 0, SD_JSON_MANDATORY },
{}
};
r = sd_varlink_dispatch(link, parameters, table, &m);
if (r != 0)
return r;
_cleanup_free_ char *sum = strjoin(m.mandatory, "+", m.optional);
if (!sum)
return log_oom();
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_STRING("result", sum));
}
static int reply_valid_hi(sd_varlink *link, sd_json_variant *parameters, const char *error_id, sd_varlink_reply_flags_t flags, void *userdata) {
ASSERT_NULL(error_id);
ASSERT_STREQ(sd_json_variant_string(sd_json_variant_by_key(parameters, "result")), "hi+");
ASSERT_OK(sd_event_exit(sd_varlink_get_event(link), EXIT_SUCCESS));
return 0;
}
TEST(mandatory_type_with_optional_field) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
ASSERT_OK(sd_event_default(&e));
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
ASSERT_OK(sd_varlink_server_new(&s, 0));
ASSERT_OK(sd_varlink_server_attach_event(s, e, 0));
ASSERT_OK(sd_varlink_server_bind_method(s, "foo.mytest.MandatoryTypeWithOptionalField", method_mandatory_type_with_optional_field));
int connfd[2];
ASSERT_OK_ERRNO(socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, connfd));
ASSERT_OK(sd_varlink_server_add_connection(s, connfd[0], /* ret= */ NULL));
_cleanup_(sd_varlink_unrefp) sd_varlink *c = NULL;
ASSERT_OK(sd_varlink_connect_fd(&c, connfd[1]));
ASSERT_OK(sd_varlink_attach_event(c, e, 0));
ASSERT_OK(sd_varlink_bind_reply(c, reply_valid_hi));
ASSERT_OK(sd_varlink_invokebo(c, "foo.mytest.MandatoryTypeWithOptionalField",
SD_JSON_BUILD_PAIR_OBJECT("mandatoryField",
SD_JSON_BUILD_PAIR_STRING("mandatory", "hi"))));
ASSERT_OK(sd_event_loop(e));
}
static int method_with_error_sentinel(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
/* Set an error sentinel and return without sending a reply. The sentinel error should be sent automatically. */
ASSERT_OK(sd_varlink_set_sentinel(link, "io.test.SentinelError"));
+6
View File
@@ -948,6 +948,12 @@ ImagePolicy* image_policy_free(ImagePolicy *p) {
return mfree(p);
}
ImagePolicy* image_policy_copy(const ImagePolicy *p) {
assert(p);
return memdup(p, offsetof(ImagePolicy, policies) + sizeof(PartitionPolicy) * p->n_policies);
}
int image_policy_ignore_designators(const ImagePolicy *p, const PartitionDesignator table[], size_t n_table, ImagePolicy **ret) {
assert(p);
assert(table || n_table == 0);
+2
View File
@@ -119,6 +119,8 @@ ImagePolicy* image_policy_free(ImagePolicy *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(ImagePolicy*, image_policy_free);
ImagePolicy* image_policy_copy(const ImagePolicy *p);
CONFIG_PARSER_PROTOTYPE(config_parse_image_policy);
int parse_image_policy_argument(const char *s, ImagePolicy **policy);
+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',
+58
View File
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-polkit.h"
#include "varlink-io.systemd.SysUpdate.h"
static SD_VARLINK_DEFINE_ENUM_TYPE(
TargetClass,
SD_VARLINK_FIELD_COMMENT("Container or machine managed by systemd-machined.service(8)."),
SD_VARLINK_DEFINE_ENUM_VALUE(machine),
SD_VARLINK_FIELD_COMMENT("Portable service."),
SD_VARLINK_DEFINE_ENUM_VALUE(portable),
SD_VARLINK_FIELD_COMMENT("System extension managed by systemd-sysext.service(8)."),
SD_VARLINK_DEFINE_ENUM_VALUE(sysext),
SD_VARLINK_FIELD_COMMENT("Configuration extension managed by systemd-confext.service(8)."),
SD_VARLINK_DEFINE_ENUM_VALUE(confext),
SD_VARLINK_FIELD_COMMENT("Host system."),
SD_VARLINK_DEFINE_ENUM_VALUE(host),
SD_VARLINK_FIELD_COMMENT("Component managed by systemd-sysupdate.service(8)."),
SD_VARLINK_DEFINE_ENUM_VALUE(component));
static SD_VARLINK_DEFINE_STRUCT_TYPE(
TargetIdentifier,
SD_VARLINK_FIELD_COMMENT("Where the target was enumerated."),
SD_VARLINK_DEFINE_FIELD_BY_TYPE(class, TargetClass, 0),
SD_VARLINK_FIELD_COMMENT("Name of the target, unique within a class."),
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
CheckNew,
SD_VARLINK_FIELD_COMMENT("Target to check for updates for."),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(target, TargetIdentifier, 0),
VARLINK_DEFINE_POLKIT_INPUT,
SD_VARLINK_FIELD_COMMENT("The version string for the new version which is available."),
SD_VARLINK_DEFINE_OUTPUT(available, SD_VARLINK_STRING, 0));
static SD_VARLINK_DEFINE_ERROR(NoUpdateNeeded);
static SD_VARLINK_DEFINE_ERROR(NoSuchTarget);
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_SysUpdate,
"io.systemd.SysUpdate",
SD_VARLINK_INTERFACE_COMMENT("APIs to manage system updates"),
/* Methods */
SD_VARLINK_SYMBOL_COMMENT("Check if there’s a new version available"),
&vl_method_CheckNew,
/* Types */
SD_VARLINK_SYMBOL_COMMENT("Class of a Target."),
&vl_type_TargetClass,
SD_VARLINK_SYMBOL_COMMENT("Identifier for a component of the system (i.e. the host itself, a sysext, a confext, etc.) that can be updated by systemd-sysupdate(8)."),
&vl_type_TargetIdentifier,
/* Errors */
SD_VARLINK_SYMBOL_COMMENT("Error indicating that no update is currently available to update to."),
&vl_error_NoUpdateNeeded,
SD_VARLINK_SYMBOL_COMMENT("Error indicating the specified target doesn’t exist"),
&vl_error_NoSuchTarget);
@@ -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;
+2 -1
View File
@@ -8,6 +8,7 @@ systemd_sysupdate_sources = files(
'sysupdate-partition.c',
'sysupdate-pattern.c',
'sysupdate-resource.c',
'sysupdate-target.c',
'sysupdate-transfer.c',
'sysupdate-update-set.c',
'sysupdate.c',
@@ -37,7 +38,7 @@ executables += [
'name' : 'systemd-sysupdated',
'dbus' : true,
'conditions' : ['ENABLE_SYSUPDATED'],
'sources' : files('sysupdated.c'),
'sources' : files('sysupdated.c', 'sysupdate-target.c'),
},
executable_template + {
'name' : 'updatectl',
+13 -19
View File
@@ -57,7 +57,7 @@ static int context_installdb_acquire_fd(Context *c, bool make) {
assert(c);
if (c->installdb_fd >= 0)
return 0;
return 1;
_cleanup_free_ char *j = NULL;
const char *p;
@@ -77,14 +77,14 @@ static int context_installdb_acquire_fd(Context *c, bool make) {
c->installdb_fd = chase_and_open(
p,
arg_root,
c->root,
flags,
O_DIRECTORY|O_CLOEXEC|(make ? O_CREAT : 0),
/* ret_path= */ NULL);
if (c->installdb_fd == -ENOENT && !make)
return 0;
if (c->installdb_fd < 0)
return log_error_errno(c->installdb_fd, "Failed to open install database '%s%s': %m", empty_or_root(arg_root) ? "" : arg_root, p);
return log_error_errno(c->installdb_fd, "Failed to open install database '%s%s': %m", empty_or_root(c->root) ? "" : c->root, p);
return 1;
}
@@ -128,8 +128,8 @@ int context_installdb_record(
if (strv_isempty(patterns))
return 0;
/* The provided path comes with arg_root prefixed. Strip it here again */
const char *p = arg_root ? ASSERT_PTR(path_startswith(path, arg_root)) : path;
/* The provided path comes with c->root prefixed. Strip it here again */
const char *p = c->root ? ASSERT_PTR(path_startswith(path, c->root)) : path;
r = context_installdb_acquire_fd(c, /* make= */ true);
if (r < 0)
@@ -336,7 +336,7 @@ static int context_installdb_process_entry(
/* NB: We set CHASE_PROHIBIT_SYMLINKS because the path was normalized by the writer of the entry
* already, and if it isn't anymore, then something is fishy. */
_cleanup_close_ int dir_fd = chase_and_open(path, arg_root, CHASE_MUST_BE_DIRECTORY|CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, O_DIRECTORY|O_CLOEXEC, /* ret_path= */ NULL);
_cleanup_close_ int dir_fd = chase_and_open(path, c->root, CHASE_MUST_BE_DIRECTORY|CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, O_DIRECTORY|O_CLOEXEC, /* ret_path= */ NULL);
if (dir_fd == -ENOENT) {
log_debug("Install database path '%s' does not exist, expunging database entry.", path);
return 0;
@@ -352,30 +352,23 @@ static int context_installdb_process_entry(
return context_installdb_process_directory(c, path, /* relpath= */ NULL, dir_fd, de, pattern);
}
int installdb_cleanup_component(const char *node, const char *component) {
int installdb_cleanup_component(Context *context) {
int r;
_cleanup_(context_freep) Context* context = NULL;
r = context_make_offline(
&context,
node,
component,
/* read_definitions_flags= */ 0);
if (r < 0)
return r;
assert(context);
r = context_installdb_acquire_fd(context, /* make= */ false);
if (r < 0)
return r;
if (r == 0) {
log_debug("Not cleaning up component '%s', install database is empty.", strna(component));
log_debug("Not cleaning up component '%s', install database is empty.", strna(context->component));
return 0;
}
_cleanup_free_ DirectoryEntries *de = NULL;
r = readdir_all(context->installdb_fd, RECURSE_DIR_ENSURE_TYPE|RECURSE_DIR_MUST_BE_SYMLINK, &de);
if (r < 0)
return log_error_errno(r, "Failed to enumerate install database for component '%s': %m", strna(component));
return log_error_errno(r, "Failed to enumerate install database for component '%s': %m", strna(context->component));
int ret = 0;
FOREACH_ARRAY(_d, de->entries, de->n_entries) {
@@ -406,14 +399,15 @@ int installdb_cleanup_component(const char *node, const char *component) {
return ret;
}
int installdb_list_components(char ***ret) {
int installdb_list_components(Context *context, char ***ret) {
int r;
assert(context);
assert(ret);
_cleanup_close_ int dir_fd = chase_and_open(
"/var/lib/systemd/sysupdate",
arg_root,
context->root,
CHASE_MUST_BE_DIRECTORY|CHASE_PREFIX_ROOT,
O_DIRECTORY|O_CLOEXEC,
/* ret_path= */ NULL);
+2 -2
View File
@@ -5,5 +5,5 @@
int context_installdb_record(Context *c, const char *path, char **patterns);
int installdb_cleanup_component(const char *node, const char *component);
int installdb_list_components(char ***ret);
int installdb_cleanup_component(Context *context);
int installdb_list_components(Context *context, char ***ret);
+7 -6
View File
@@ -54,6 +54,7 @@ static int config_parse_url_specifiers(
void *data,
void *userdata) {
char **s = ASSERT_PTR(data);
const char *root = ASSERT_PTR(userdata);
_cleanup_free_ char *resolved = NULL;
int r;
@@ -64,7 +65,7 @@ static int config_parse_url_specifiers(
return 0;
}
r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in %s=, ignoring: %s", lvalue, rvalue);
@@ -80,7 +81,7 @@ static int config_parse_url_specifiers(
return free_and_replace(*s, resolved);
}
int feature_read_definition(Feature *f, const char *path, const char *const *dirs) {
int feature_read_definition(Feature *f, const char *root, const char *path, const char *const *dirs) {
assert(f);
ConfigTableItem table[] = {
@@ -105,14 +106,14 @@ int feature_read_definition(Feature *f, const char *path, const char *const *dir
STRV_MAKE_CONST(path),
dirs,
strjoina(filename, ".d"),
arg_root,
root,
/* root_fd= */ -EBADF,
"Feature\0",
config_item_table_lookup, table,
CONFIG_PARSE_WARN,
/* userdata= */ NULL,
/* stats_by_path= */ NULL,
/* drop_in_files= */ NULL);
(void *) root,
/* ret_stats_by_path= */ NULL,
/* ret_drop_in_files= */ NULL);
if (r < 0)
return r;
+1 -1
View File
@@ -22,4 +22,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Feature*, feature_unref);
extern const struct hash_ops feature_hash_ops;
int feature_read_definition(Feature *f, const char *path, const char *const *conf_file_dirs);
int feature_read_definition(Feature *f, const char *root, const char *path, const char *const *conf_file_dirs);
+22
View File
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "string-table.h"
#include "sysupdate-target.h"
static const char* const target_class_table[_TARGET_CLASS_MAX] = {
[TARGET_MACHINE] = "machine",
[TARGET_PORTABLE] = "portable",
[TARGET_SYSEXT] = "sysext",
[TARGET_CONFEXT] = "confext",
[TARGET_COMPONENT] = "component",
[TARGET_HOST] = "host",
};
DEFINE_STRING_TABLE_LOOKUP(target_class, TargetClass);
void target_identifier_done(TargetIdentifier *t) {
assert(t);
t->class = _TARGET_CLASS_INVALID;
t->name = mfree(t->name);
}
+32
View File
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "os-util.h"
typedef enum TargetClass {
/* These should try to match ImageClass from src/basic/os-util.h */
TARGET_MACHINE = IMAGE_MACHINE,
TARGET_PORTABLE = IMAGE_PORTABLE,
TARGET_SYSEXT = IMAGE_SYSEXT,
TARGET_CONFEXT = IMAGE_CONFEXT,
_TARGET_CLASS_IS_IMAGE_CLASS_MAX,
/* sysupdate-specific classes */
TARGET_HOST = _TARGET_CLASS_IS_IMAGE_CLASS_MAX,
TARGET_COMPONENT,
_TARGET_CLASS_MAX,
_TARGET_CLASS_INVALID = -EINVAL,
} TargetClass;
/* Let's ensure when the number of classes is updated things are updated here too */
assert_cc((int) _IMAGE_CLASS_MAX == (int) _TARGET_CLASS_IS_IMAGE_CLASS_MAX);
DECLARE_STRING_TABLE_LOOKUP(target_class, TargetClass);
typedef struct TargetIdentifier {
TargetClass class;
char *name;
} TargetIdentifier;
void target_identifier_done(TargetIdentifier *t);
+27 -21
View File
@@ -122,11 +122,12 @@ static int config_parse_protect_version(
_cleanup_free_ char *resolved = NULL;
char ***protected_versions = ASSERT_PTR(data);
Transfer *t = ASSERT_PTR(userdata);
int r;
assert(rvalue);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, t->context->root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in ProtectVersion=, ignoring: %s", rvalue);
@@ -160,11 +161,12 @@ static int config_parse_min_version(
_cleanup_free_ char *resolved = NULL;
char **version = ASSERT_PTR(data);
Transfer *t = ASSERT_PTR(userdata);
int r;
assert(rvalue);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, t->context->root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in MinVersion=, ignoring: %s", rvalue);
@@ -192,6 +194,7 @@ static int config_parse_url_specifiers(
void *data,
void *userdata) {
char ***s = ASSERT_PTR(data);
Transfer *t = ASSERT_PTR(userdata);
_cleanup_free_ char *resolved = NULL;
int r;
@@ -202,7 +205,7 @@ static int config_parse_url_specifiers(
return 0;
}
r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, t->context->root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in %s=, ignoring: %s", lvalue, rvalue);
@@ -236,11 +239,12 @@ static int config_parse_current_symlink(
_cleanup_free_ char *resolved = NULL;
char **current_symlink = ASSERT_PTR(data);
Transfer *t = ASSERT_PTR(userdata);
int r;
assert(rvalue);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
r = specifier_printf(rvalue, NAME_MAX, specifier_table, t->context->root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in CurrentSymlink=, ignoring: %s", rvalue);
@@ -307,6 +311,7 @@ static int config_parse_resource_pattern(
void *userdata) {
char ***patterns = ASSERT_PTR(data);
Transfer *t = ASSERT_PTR(userdata);
int r;
assert(rvalue);
@@ -328,7 +333,7 @@ static int config_parse_resource_pattern(
if (r == 0)
break;
r = specifier_printf(word, NAME_MAX, specifier_table, arg_root, NULL, &resolved);
r = specifier_printf(word, NAME_MAX, specifier_table, t->context->root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in MatchPattern=, ignoring: %s", rvalue);
@@ -361,6 +366,7 @@ static int config_parse_resource_path(
void *userdata) {
_cleanup_free_ char *resolved = NULL;
Resource *rr = ASSERT_PTR(data);
Transfer *t = ASSERT_PTR(userdata);
int r;
assert(rvalue);
@@ -371,7 +377,7 @@ static int config_parse_resource_path(
return 0;
}
r = specifier_printf(rvalue, PATH_MAX-1, specifier_table, arg_root, NULL, &resolved);
r = specifier_printf(rvalue, PATH_MAX-1, specifier_table, t->context->root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in Path=, ignoring: %s", rvalue);
@@ -549,16 +555,16 @@ int transfer_read_definition(Transfer *t, const char *path, const char **dirs, H
STRV_MAKE_CONST(path),
dirs,
strjoina(filename, ".d"),
arg_root,
t->context->root,
/* root_fd= */ -EBADF,
"Transfer\0"
"Source\0"
"Target\0",
config_item_table_lookup, table,
CONFIG_PARSE_WARN,
/* userdata= */ NULL,
/* stats_by_path= */ NULL,
/* drop_in_files= */ NULL);
t,
/* ret_stats_by_path= */ NULL,
/* ret_drop_in_files= */ NULL);
if (r < 0)
return r;
@@ -613,7 +619,7 @@ int transfer_read_definition(Transfer *t, const char *path, const char **dirs, H
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Source specification lacks Path=.");
if (t->source.path_relative_to == PATH_RELATIVE_TO_EXPLICIT && !arg_transfer_source)
if (t->source.path_relative_to == PATH_RELATIVE_TO_EXPLICIT && !t->context->transfer_source)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"PathRelativeTo=explicit requires --transfer-source= to be specified.");
@@ -682,7 +688,7 @@ int transfer_resolve_paths(
assert(t);
r = resource_resolve_path(&t->source, root, arg_transfer_source, node);
r = resource_resolve_path(&t->source, root, t->context->transfer_source, node);
if (r < 0)
return r;
@@ -849,7 +855,7 @@ int transfer_vacuum(
/* Second, calculate how many instances to keep, based on the instance limit — but keep at least one */
instances_max = arg_instances_max != UINT64_MAX ? arg_instances_max : t->instances_max;
instances_max = t->context->instances_max != UINT64_MAX ? t->context->instances_max : t->instances_max;
assert(instances_max >= 1);
if (instances_max == UINT64_MAX) /* Keep infinite instances? */
limit = UINT64_MAX;
@@ -1369,7 +1375,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
SYSTEMD_IMPORT_PATH,
"raw",
"--direct", /* just copy/unpack the specified file, don't do anything else */
arg_sync ? "--sync=yes" : "--sync=no",
t->context->sync ? "--sync=yes" : "--sync=no",
i->path,
t->temporary_partial_path),
t, i, cb, userdata);
@@ -1386,7 +1392,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
"--direct", /* just copy/unpack the specified file, don't do anything else */
"--offset", offset,
"--size-max", max_size,
arg_sync ? "--sync=yes" : "--sync=no",
t->context->sync ? "--sync=yes" : "--sync=no",
i->path,
t->target.path),
t, i, cb, userdata);
@@ -1409,7 +1415,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
SYSTEMD_IMPORT_FS_PATH,
"run",
"--direct", /* just untar the specified file, don't do anything else */
arg_sync ? "--sync=yes" : "--sync=no",
t->context->sync ? "--sync=yes" : "--sync=no",
t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
i->path,
t->temporary_partial_path),
@@ -1426,7 +1432,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
SYSTEMD_IMPORT_PATH,
"tar",
"--direct", /* just untar the specified file, don't do anything else */
arg_sync ? "--sync=yes" : "--sync=no",
t->context->sync ? "--sync=yes" : "--sync=no",
t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
i->path,
t->temporary_partial_path),
@@ -1447,7 +1453,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
"raw",
"--direct", /* just download the specified URL, don't download anything else */
"--verify", digest, /* validate by explicit SHA256 sum */
arg_sync ? "--sync=yes" : "--sync=no",
t->context->sync ? "--sync=yes" : "--sync=no",
i->path,
t->temporary_partial_path),
t, i, cb, userdata);
@@ -1465,7 +1471,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
"--verify", digest, /* validate by explicit SHA256 sum */
"--offset", offset,
"--size-max", max_size,
arg_sync ? "--sync=yes" : "--sync=no",
t->context->sync ? "--sync=yes" : "--sync=no",
i->path,
t->target.path),
t, i, cb, userdata);
@@ -1487,7 +1493,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
"--direct", /* just download the specified URL, don't download anything else */
"--verify", digest, /* validate by explicit SHA256 sum */
t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no",
arg_sync ? "--sync=yes" : "--sync=no",
t->context->sync ? "--sync=yes" : "--sync=no",
i->path,
t->temporary_partial_path),
t, i, cb, userdata);
@@ -1528,7 +1534,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
}
/* Synchronize */
if (arg_sync && need_sync) {
if (t->context->sync && need_sync) {
if (t->target.type == RESOURCE_REGULAR_FILE)
r = fsync_path_and_parent_at(AT_FDCWD, t->temporary_partial_path);
else {
+595 -231
View File
File diff suppressed because it is too large Load Diff
+21 -14
View File
@@ -3,9 +3,27 @@
#include "specifier.h"
#include "sysupdate-forward.h"
#include "sysupdate-target.h"
typedef struct Context {
/* Parameters/Command line arguments: */
char *definitions;
bool sync;
uint64_t instances_max;
char *root;
char *image;
bool reboot;
int cleanup;
char *component;
bool component_all;
int verify;
ImagePolicy *image_policy;
bool offline;
char *transfer_source;
/* Loaded state: */
LoopDevice *loop_device;
char *mounted_dir;
Transfer **transfers;
size_t n_transfers;
@@ -23,21 +41,10 @@ typedef struct Context {
Hashmap *web_cache; /* Cache for downloaded resources, keyed by URL */
int installdb_fd;
TargetIdentifier target_identifier;
} Context;
Context* context_free(Context *c);
DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free);
typedef enum ReadDefinitionsFlags {
READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS = 1 << 0,
READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS = 1 << 1,
} ReadDefinitionsFlags;
int context_make_offline(Context **ret, const char *node, const char *component, ReadDefinitionsFlags read_definitions_flags);
extern bool arg_sync;
extern uint64_t arg_instances_max;
extern char *arg_root;
extern char *arg_transfer_source;
void context_done(Context *c);
extern const Specifier specifier_table[];
+1 -30
View File
@@ -40,6 +40,7 @@
#include "signal-util.h"
#include "string-table.h"
#include "strv.h"
#include "sysupdate-target.h"
#include "sysupdate-util.h"
#include "utf8.h"
@@ -64,25 +65,6 @@ typedef struct Manager {
/* Forward declare so that jobs can call it on exit */
static void manager_check_idle(Manager *m);
typedef enum TargetClass {
/* These should try to match ImageClass from src/basic/os-util.h */
TARGET_MACHINE = IMAGE_MACHINE,
TARGET_PORTABLE = IMAGE_PORTABLE,
TARGET_SYSEXT = IMAGE_SYSEXT,
TARGET_CONFEXT = IMAGE_CONFEXT,
_TARGET_CLASS_IS_IMAGE_CLASS_MAX,
/* sysupdate-specific classes */
TARGET_HOST = _TARGET_CLASS_IS_IMAGE_CLASS_MAX,
TARGET_COMPONENT,
_TARGET_CLASS_MAX,
_TARGET_CLASS_INVALID = -EINVAL,
} TargetClass;
/* Let's ensure when the number of classes is updated things are updated here too */
assert_cc((int) _IMAGE_CLASS_MAX == (int) _TARGET_CLASS_IS_IMAGE_CLASS_MAX);
typedef struct Target {
Manager *manager;
@@ -138,17 +120,6 @@ struct Job {
JobReady detach_cb; /* Callback called when job has started. Detaches the job to run in the background */
};
static const char* const target_class_table[_TARGET_CLASS_MAX] = {
[TARGET_MACHINE] = "machine",
[TARGET_PORTABLE] = "portable",
[TARGET_SYSEXT] = "sysext",
[TARGET_CONFEXT] = "confext",
[TARGET_COMPONENT] = "component",
[TARGET_HOST] = "host",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(target_class, TargetClass);
static const char* const job_type_table[_JOB_TYPE_MAX] = {
[JOB_LIST] = "list",
[JOB_DESCRIBE] = "describe",
+70 -21
View File
@@ -8,6 +8,7 @@ set -o pipefail
SYSUPDATE=/usr/bin/systemd-sysupdate
SYSUPDATED=/lib/systemd/systemd-sysupdated
UPDATECTL=""
VARLINK_SOCKET=/run/systemd/io.systemd.SysUpdate
SECTOR_SIZES=(512 4096)
WORKDIR="$(mktemp -d /var/tmp/test-72-XXXXXX)"
CONFIGDIR="/run/sysupdate.d"
@@ -35,8 +36,8 @@ if [[ ! -e /dev/loop-control ]]; then
SECTOR_SIZES=(512)
fi
# Set up sysupdated drop-in pointing at the correct definitions and setting
# no verification of images.
# Set up sysupdated and varlink drop-ins pointing at the correct definitions and
# setting no verification of images.
mkdir -p /run/systemd/system/systemd-sysupdated.service.d
cat >/run/systemd/system/systemd-sysupdated.service.d/override.conf<<EOF
[Service]
@@ -44,6 +45,15 @@ Environment=SYSTEMD_SYSUPDATE_NO_VERIFY=1
Environment=SYSTEMD_ESP_PATH=${SYSTEMD_ESP_PATH}
Environment=SYSTEMD_XBOOTLDR_PATH=${SYSTEMD_XBOOTLDR_PATH}
EOF
mkdir -p /run/systemd/system/systemd-sysupdate@.service.d
cat >/run/systemd/system/systemd-sysupdate@.service.d/override.conf<<EOF
[Service]
Environment=SYSTEMD_SYSUPDATE_NO_VERIFY=1
Environment=SYSTEMD_ESP_PATH=${SYSTEMD_ESP_PATH}
Environment=SYSTEMD_XBOOTLDR_PATH=${SYSTEMD_XBOOTLDR_PATH}
EOF
systemctl daemon-reload
at_exit() {
@@ -121,9 +131,34 @@ new_version() {
fi
}
check_no_new_update_available() {
local client="${1:?}"
if [[ "$client" == "sysupdate-cli" ]]; then
(! "$SYSUPDATE" --verify=no check-new)
elif [[ "$client" == "varlink" ]]; then
(! varlinkctl call "$VARLINK_SOCKET" io.systemd.SysUpdate.CheckNew '{"target":{"class":"host"}}') |& grep io.systemd.SysUpdate.NoUpdateNeeded >/dev/null
else
exit 1
fi
}
check_new_update_available() {
local client="${1:?}"
if [[ "$client" == "sysupdate-cli" ]]; then
"$SYSUPDATE" --verify=no check-new
elif [[ "$client" == "varlink" ]]; then
varlinkctl call "$VARLINK_SOCKET" io.systemd.SysUpdate.CheckNew '{"target":{"class":"host"}}' | grep available
else
exit 1
fi
}
update_now() {
local update_type="${1:?}"
local checks="${2:-}"
local client="${2:?}"
local checks="${3:-}"
# Update to newest version. First there should be an update ready, then we
# do the update, and then there should not be any ready anymore
@@ -135,7 +170,7 @@ update_now() {
# repairing an installation), so that can be overridden via the local.
if [[ "$checks" != "no-checks" ]]; then
"$SYSUPDATE" --verify=no check-new
check_new_update_available "$client"
fi
if [[ "$update_type" == "monolithic" ]]; then
@@ -159,7 +194,7 @@ update_now() {
fi
if [[ "$checks" != "no-checks" ]]; then
(! "$SYSUPDATE" --verify=no check-new)
check_no_new_update_available "$client"
fi
}
@@ -208,6 +243,7 @@ verify_object_fields() {
}
for sector_size in "${SECTOR_SIZES[@]}"; do
for client in sysupdate-cli varlink; do
for update_type in monolithic split-offline split updatectl; do
# Disk size of:
# - 1MB for GPT
@@ -371,18 +407,18 @@ EOF
# Install initial version and verify
new_version "$sector_size" v1
update_now "$update_type"
update_now "$update_type" "$client"
verify_version_current "$blockdev" "$sector_size" v1 1
# Create second version, update and verify that it is added
new_version "$sector_size" v2
update_now "$update_type"
update_now "$update_type" "$client"
verify_version "$blockdev" "$sector_size" v1 1
verify_version_current "$blockdev" "$sector_size" v2 2
# Create third version, update and verify it replaced the first version
new_version "$sector_size" v3
update_now "$update_type"
update_now "$update_type" "$client"
verify_version_current "$blockdev" "$sector_size" v3 1
verify_version "$blockdev" "$sector_size" v2 2
test ! -f "$WORKDIR/xbootldr/EFI/Linux/uki_v1+3-0.efi"
@@ -394,12 +430,12 @@ EOF
new_version "$sector_size" v4
rm "$WORKDIR/source/uki-extra-v4.efi"
update_checksums
(! "$SYSUPDATE" --verify=no check-new)
check_no_new_update_available "$client"
# Create a fifth version, that's complete on the server side. We should
# completely skip the incomplete v4 and install v5 instead.
new_version "$sector_size" v5
update_now "$update_type"
update_now "$update_type" "$client"
verify_version "$blockdev" "$sector_size" v3 1
verify_version_current "$blockdev" "$sector_size" v5 2
@@ -409,7 +445,7 @@ EOF
# Always do this as a monolithic update for the repair to work.
rm -r "$WORKDIR/xbootldr/EFI/Linux/uki_v5.efi.extra.d"
"$SYSUPDATE" --offline list v5 | grep "incomplete" >/dev/null
update_now "monolithic"
update_now "monolithic" "$client"
"$SYSUPDATE" --offline list v5 | grep -v "incomplete" >/dev/null
verify_version "$blockdev" "$sector_size" v3 1
verify_version_current "$blockdev" "$sector_size" v5 2
@@ -421,7 +457,7 @@ EOF
mkdir "$CONFIGDIR/optional.feature.d"
echo -e "[Feature]\nEnabled=true" > "$CONFIGDIR/optional.feature.d/enable.conf"
"$SYSUPDATE" --offline list v5 | grep "incomplete" >/dev/null
update_now "$update_type"
update_now "$update_type" "$client"
"$SYSUPDATE" --offline list v5 | grep -v "incomplete" >/dev/null
verify_version "$blockdev" "$sector_size" v3 1
verify_version_current "$blockdev" "$sector_size" v5 2
@@ -429,7 +465,7 @@ EOF
# And now let's disable it and make sure it gets cleaned up
rm -r "$CONFIGDIR/optional.feature.d"
(! "$SYSUPDATE" --verify=no check-new)
check_no_new_update_available "$client"
"$SYSUPDATE" vacuum
"$SYSUPDATE" --offline list v5 | grep -v "incomplete" >/dev/null
verify_version "$blockdev" "$sector_size" v3 1
@@ -441,17 +477,17 @@ EOF
new_version "$sector_size" v6
if [[ -x "$UPDATECTL" ]]; then
systemctl start systemd-sysupdated
"$SYSUPDATE" --verify=no check-new
check_new_update_available "$client"
"$UPDATECTL" update |& tee "$WORKDIR"/updatectl-update-6
grep "Done" "$WORKDIR"/updatectl-update-6
(! grep "Already up-to-date" "$WORKDIR"/updatectl-update-6)
else
# If no updatectl, gracefully fall back to systemd-sysupdate
update_now "$update_type"
update_now "$update_type" "$client"
fi
# User-facing updatectl returns 0 if there's no updates, so use the low-level
# utility to make sure we did upgrade
(! "$SYSUPDATE" --verify=no check-new )
check_no_new_update_available "$client"
verify_version_current "$blockdev" "$sector_size" v6 1
verify_version "$blockdev" "$sector_size" v5 2
@@ -504,7 +540,7 @@ MatchPattern=dir-@v
InstancesMax=3
EOF
update_now "$update_type"
update_now "$update_type" "$client"
verify_version "$blockdev" "$sector_size" v6 1
verify_version_current "$blockdev" "$sector_size" v7 2
@@ -527,7 +563,7 @@ EOF
# (what .transfer files were called before v257)
for i in "$CONFIGDIR/"*.conf; do echo mv "$i" "${i%.conf}.transfer"; done
new_version "$sector_size" v8
update_now "$update_type"
update_now "$update_type" "$client"
verify_version_current "$blockdev" "$sector_size" v8 1
verify_version "$blockdev" "$sector_size" v7 2
@@ -537,11 +573,11 @@ EOF
# Vacuum the partial version, regenerate it on the server, try updating
# again and it should succeed.
new_version "$sector_size" v9 "corrupt-checksum"
(! update_now "$update_type")
(! update_now "$update_type" "$client")
"$SYSUPDATE" --offline list v9 | grep "partial" >/dev/null
verify_version_current "$blockdev" "$sector_size" v8 1
# don’t verify the other part of the block device as it’s in an indeterminate state
(! update_now "$update_type" "no-checks") |& tee "$WORKDIR"/update_now-9
(! update_now "$update_type" "$client" "no-checks") |& tee "$WORKDIR"/update_now-9
cat "$WORKDIR"/update_now-9
grep "is already acquired and partially installed. Vacuum it to try installing again." "$WORKDIR"/update_now-9
"$SYSUPDATE" --offline vacuum |& grep "Removing old partial" >/dev/null
@@ -549,15 +585,28 @@ EOF
# don’t verify the other part of the block device as it’s in an indeterminate state
"$SYSUPDATE" --verify=no list v9 | grep "candidate" >/dev/null
new_version "$sector_size" v9
update_now "$update_type"
update_now "$update_type" "$client"
verify_version "$blockdev" "$sector_size" v8 1
verify_version_current "$blockdev" "$sector_size" v9 2
# Test that checking for an update on a non-existent target fails
# (for backwards compatibility reasons, the validation in sysupdate-cli is
# less strict)
if [[ "$client" == "sysupdate-cli" ]]; then
(! "$SYSUPDATE" --verify=no check-new --component=../) |& grep "Component name invalid" >/dev/null
elif [[ "$client" == "varlink" ]]; then
(! varlinkctl call "$VARLINK_SOCKET" io.systemd.SysUpdate.CheckNew '{"target":{"class":"component","name":"../"}}') |& grep org.varlink.service.InvalidParameter >/dev/null
(! varlinkctl call "$VARLINK_SOCKET" io.systemd.SysUpdate.CheckNew '{"target":{"class":"component","name":"doesnotexist"}}') |& grep io.systemd.SysUpdate.NoSuchTarget >/dev/null
else
exit 1
fi
# Cleanup
[[ -b "$blockdev" ]] && losetup --detach "$blockdev"
rm "$BACKING_FILE"
done
done
done
# Regression test for https://github.com/systemd/systemd/issues/41501 — check
# that a ‘default’ component is only listed by sysupdate if it’s fully configured
+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