From: Lennart Poettering Date: Tue, 30 Jun 2026 20:55:45 +0000 (+0200) Subject: sysupdate: add support for 'update --component-all' X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=921f63447bffef929d144982ffa2c54681fcf17b;p=thirdparty%2Fsystemd.git sysupdate: add support for 'update --component-all' Let's add a way to quickly update all components that are enabled. --- diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c index dadf307ff64..28ea65c0338 100644 --- a/src/sysupdate/sysupdate.c +++ b/src/sysupdate/sysupdate.c @@ -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; - 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."); - } +static int context_update( + Context *c, + const char *version, + const char *booted_version, + UpdateActionFlags action_flags) { + UpdateSet *applied = NULL; 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) {