sysupdate: add support for 'update --component-all'

Let's add a way to quickly update all components that are enabled.
This commit is contained in:
Lennart Poettering
2026-07-15 13:27:48 +02:00
parent dcf86d1529
commit 921f63447b
+102 -43
View File
@@ -2591,44 +2591,20 @@ typedef enum {
UPDATE_ACTION_INSTALL = 1 << 1,
} UpdateActionFlags;
static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flags) {
_cleanup_free_ char *booted_version = NULL;
static int context_update(
Context *c,
const char *version,
const char *booted_version,
UpdateActionFlags action_flags) {
UpdateSet *applied = NULL;
const char *version;
int r;
assert(argc <= 2);
version = argc >= 2 ? argv[1] : NULL;
_cleanup_(context_done) Context context = CONTEXT_NULL;
r = context_from_cmdline(&context);
if (r < 0)
return r;
if (context.feature_select != SELECT_EXPLICIT)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--feature-all/--feature-suggested is not supported for '%s'.", argv[0]);
if (context.component_select != SELECT_EXPLICIT)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--component-all/--component-suggested currently not supported for '%s'.", argv[0]);
if (context.instances_max < 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"The --instances-max= argument must be >= 2 while updating");
if (context.reboot) {
/* If automatic reboot on completion is requested, let's first determine the currently booted image */
r = parse_os_release(context.root, "IMAGE_VERSION", &booted_version);
if (r < 0)
return log_error_errno(r, "Failed to parse /etc/os-release: %m");
if (!booted_version)
return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION field.");
}
bool installed = false;
int ret = 0;
int r, ret = 0;
assert(c);
r = context_load_online(
&context,
c,
/* process_image_flags= */ 0,
READ_DEFINITIONS_REQUIRES_ENABLED_TRANSFERS|
READ_DEFINITIONS_REQUIRES_ANY_TRANSFERS|
@@ -2640,15 +2616,15 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag
/* No transfer files found. In that case, still do the installdb cleanup below */
RET_GATHER(ret, r);
} else {
if (action_flags & UPDATE_ACTION_ACQUIRE)
r = context_acquire(&context, version);
if (FLAGS_SET(action_flags, UPDATE_ACTION_ACQUIRE))
r = context_acquire(c, version);
else
r = context_process_partial_and_pending(&context, version);
r = context_process_partial_and_pending(c, version);
if (r < 0)
return r;
if (FLAGS_SET(action_flags, UPDATE_ACTION_INSTALL) && r > 0) { /* installation of update indicated */
r = context_install(&context, version, &applied);
r = context_install(c, version, &applied);
if (r < 0)
return r;
@@ -2658,22 +2634,22 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag
/* context_install() returns > 0 (and emits a notification) only if it actually applied an update. If
* nothing was applied but SYSTEMD_SYSUPDATE_FORCE_NOTIFY=1 is set, still notify subscribers (without a
* resource list), so e.g. a kernel/policy refresh can be triggered unconditionally. */
if ((action_flags & UPDATE_ACTION_INSTALL) && !installed) {
if (FLAGS_SET(action_flags, UPDATE_ACTION_INSTALL) && !installed) {
int f = secure_getenv_bool("SYSTEMD_SYSUPDATE_FORCE_NOTIFY");
if (f < 0 && f != -ENXIO)
log_debug_errno(f, "Failed to parse $SYSTEMD_SYSUPDATE_FORCE_NOTIFY, ignoring: %m");
if (f > 0)
(void) context_notify_subscribers(&context, /* us= */ NULL);
(void) context_notify_subscribers(c, /* us= */ NULL);
}
}
if (context.cleanup > 0)
RET_GATHER(ret, installdb_cleanup_component(&context));
if (c->cleanup > 0)
RET_GATHER(ret, installdb_cleanup_component(c));
if (installed) {
/* We installed something, yay */
if (context.reboot) {
if (c->reboot) {
assert(applied);
assert(booted_version);
@@ -2692,6 +2668,89 @@ static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flag
return ret;
}
static int verb_update_impl(int argc, char **argv, UpdateActionFlags action_flags) {
const char *version;
int r;
assert(argc <= 2);
version = argc >= 2 ? argv[1] : NULL;
_cleanup_(context_done) Context context = CONTEXT_NULL;
r = context_from_cmdline(&context);
if (r < 0)
return r;
if (context.feature_select != SELECT_EXPLICIT)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--feature-all/--feature-suggested is not supported for '%s'.", argv[0]);
if (!IN_SET(context.component_select, SELECT_EXPLICIT, SELECT_ALL))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--component-suggested currently not supported for '%s'.", argv[0]);
if (context.instances_max < 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"The --instances-max= argument must be >= 2 while updating");
_cleanup_free_ char *booted_version = NULL;
if (context.reboot) {
/* We already checked in parse_argv() that --component=/--component-all/--component-suggested
* are not combined with --reboot */
assert(!context.component);
assert(context.component_select == SELECT_EXPLICIT);
/* If automatic reboot on completion is requested, let's first determine the currently booted image */
r = parse_os_release(context.root, "IMAGE_VERSION", &booted_version);
if (r < 0)
return log_error_errno(r, "Failed to parse /etc/os-release: %m");
if (!booted_version)
return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION field.");
}
switch (context.component_select) {
case SELECT_EXPLICIT:
return context_update(&context, version, booted_version, action_flags);
case SELECT_ALL: {
int ret = 0;
/* Update the default, component-less installation first (if any). Running it before the
* enumeration below also ensures the image (if any) is mounted and context.root is set, so
* that we enumerate the components inside the image rather than on the host. A missing
* default installation (ENOENT) is not an error in this mode. */
r = context_update(&context, version, booted_version, action_flags);
if (r != -ENOENT)
RET_GATHER(ret, r);
_cleanup_strv_free_ char **component_names = NULL;
r = context_list_components(&context, &component_names, /* ret_has_default_component= */ NULL);
if (r < 0) {
RET_GATHER(ret, r);
return ret;
}
STRV_FOREACH(name, component_names) {
_cleanup_(context_done) Context cc = CONTEXT_NULL;
r = context_from_base_with_component(&context, *name, &cc);
if (r < 0) {
RET_GATHER(ret, r);
continue;
}
r = context_update(&cc, version, booted_version, action_flags);
if (r == -EHOSTDOWN) /* Component disabled → skip it in the "all" case. */
continue;
RET_GATHER(ret, r);
}
return ret;
}
default:
assert_not_reached();
}
}
VERB(verb_update, "update", "[VERSION]", VERB_ANY, 2, 0,
"Install new version now");
static int verb_update(int argc, char *argv[], uintptr_t _data, void *userdata) {