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:
@@ -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:
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,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;
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+21
-14
@@ -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[];
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user