char *arg_root = NULL;
static char *arg_image = NULL;
static bool arg_reboot = false;
+static int arg_cleanup = -1;
static char *arg_component = NULL;
static bool arg_component_all = false;
static int arg_verify = -1;
if (r < 0)
return r;
+ const char *node = loop_device ? loop_device->node : NULL;
+ bool installed = false;
+ int ret = 0;
+
r = context_make_online(
&context,
- loop_device ? loop_device->node : NULL,
+ node,
arg_component);
- if (r < 0)
- return r;
-
- if (action_flags & UPDATE_ACTION_ACQUIRE)
- r = context_acquire(context, version);
- else
- r = context_process_partial_and_pending(context, version);
- if (r < 0)
- return r; /* error */
+ if (r < 0) {
+ if (r != -ENOENT)
+ return r;
- if (action_flags & UPDATE_ACTION_INSTALL && r > 0) /* update needed */
- r = context_install(context, version, &applied);
- if (r < 0)
- return r;
+ /* 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);
+ else
+ r = context_process_partial_and_pending(context, version);
+ if (r < 0)
+ return r;
- if (r > 0 && arg_reboot) {
- assert(applied);
- assert(booted_version);
+ if (FLAGS_SET(action_flags, UPDATE_ACTION_INSTALL) && r > 0) { /* installation of update indicated */
+ r = context_install(context, version, &applied);
+ if (r < 0)
+ return r;
- if (strverscmp_improved(applied->version, booted_version) > 0) {
- log_notice("Newly installed version is newer than booted version, rebooting.");
- return reboot_now();
+ installed = r > 0;
}
+ }
- if (strverscmp_improved(applied->version, booted_version) == 0 &&
- FLAGS_SET(applied->flags, UPDATE_INCOMPLETE)) {
- log_notice("Currently booted version was incomplete and has been repaired, rebooting.");
- return reboot_now();
+ if (arg_cleanup > 0)
+ RET_GATHER(ret, installdb_cleanup_component(node, arg_component));
+
+ if (installed) {
+ /* We installed something, yay */
+
+ if (arg_reboot) {
+ assert(applied);
+ assert(booted_version);
+
+ if (strverscmp_improved(applied->version, booted_version) > 0) {
+ log_notice("Newly installed version is newer than booted version, rebooting.");
+ RET_GATHER(ret, reboot_now());
+ } else if (strverscmp_improved(applied->version, booted_version) == 0 &&
+ FLAGS_SET(applied->flags, UPDATE_INCOMPLETE)) {
+ log_notice("Currently booted version was incomplete and has been repaired, rebooting.");
+ RET_GATHER(ret, reboot_now());
+ } else
+ log_info("Booted version is newer or identical to newly installed version, not rebooting.");
}
-
- log_info("Booted version is newer or identical to newly installed version, not rebooting.");
}
- return 0;
+ return ret;
}
VERB(verb_update, "update", "[VERSION]", VERB_ANY, 2, 0,
assert(argc <= 1);
+ if (arg_cleanup == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invocation of 'cleanup' with --cleanup=no is contradictory, refusing.");
+
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
r = process_image(/* ro= */ false, &mounted_dir, &loop_device);
arg_offline = true;
break;
+ OPTION_LONG("cleanup", "BOOL", "Clean up orphaned files after completing update"): {
+ bool b;
+
+ r = parse_boolean_argument("--cleanup=", opts.arg, &b);
+ if (r < 0)
+ return r;
+
+ arg_cleanup = b;
+ break;
+ }
+
OPTION_COMMON_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
rm -rf "$COMPALL" /run/sysupdate.comp-a.d /run/sysupdate.comp-b.d \
/var/lib/systemd/sysupdate/installdb.comp-a /var/lib/systemd/sysupdate/installdb.comp-b
+# Check the "--cleanup=" switch of the "update" verb. With "--cleanup=yes" a
+# successful update must, after installing the new version, run the equivalent of
+# the "cleanup" verb and remove any resources that are no longer owned by a
+# currently defined transfer file. Reuse the "alpha"/"beta" helpers from above.
+rm -rf "$CONFIGDIR" "$INSTALLDB" "$CLEANUP"
+mkdir -p "$CONFIGDIR" "$CLEANUP/source" "$CLEANUP/target"
+
+cat >"$CONFIGDIR/01-alpha.transfer" <<EOF
+[Source]
+Type=regular-file
+Path=$CLEANUP/source
+MatchPattern=alpha-@v.bin
+
+[Target]
+Type=regular-file
+Path=$CLEANUP/target
+MatchPattern=alpha-@v.bin
+InstancesMax=2
+EOF
+
+cat >"$CONFIGDIR/02-beta.transfer" <<EOF
+[Source]
+Type=directory
+Path=$CLEANUP/source
+MatchPattern=beta-@v
+
+[Target]
+Type=directory
+Path=$CLEANUP/target
+MatchPattern=beta-@v
+InstancesMax=2
+EOF
+
+# Install a first version with both transfers in place.
+cleanup_new_version v1
+"$SYSUPDATE" --verify=no update --cleanup=yes
+test -f "$CLEANUP/target/alpha-v1.bin"
+verify_beta_synced v1
+[[ "$(installdb_count)" -eq 2 ]]
+assert_installdb_covers_target
+
+# Now drop the "beta" transfer file and install a second version with
+# "--cleanup=yes". The new alpha resource must be installed, and the now-orphaned
+# beta directory (and its install database entry) must be removed as part of the
+# same invocation, without a separate "cleanup" call.
+rm "$CONFIGDIR/02-beta.transfer"
+cleanup_new_version v2
+"$SYSUPDATE" --verify=no update --cleanup=yes
+test -f "$CLEANUP/target/alpha-v1.bin"
+test -f "$CLEANUP/target/alpha-v2.bin"
+test ! -e "$CLEANUP/target/beta-v1"
+[[ "$(installdb_count)" -eq 1 ]]
+assert_installdb_covers_target
+
+# With "--cleanup=no" (the default) orphaned resources must be left in place.
+# Redefine the "alpha" transfer so its patterns no longer match the already
+# installed alpha files (turning them into orphans), while keeping a valid
+# transfer definition in place. Updating with "--cleanup=no" must then install
+# nothing new (there's no matching source) and leave the now-orphaned alpha files
+# and their install database entry untouched.
+cat >"$CONFIGDIR/01-alpha.transfer" <<EOF
+[Source]
+Type=regular-file
+Path=$CLEANUP/source
+MatchPattern=gamma-@v.bin
+
+[Target]
+Type=regular-file
+Path=$CLEANUP/target
+MatchPattern=gamma-@v.bin
+InstancesMax=2
+EOF
+"$SYSUPDATE" --verify=no update --cleanup=no
+test -f "$CLEANUP/target/alpha-v1.bin"
+test -f "$CLEANUP/target/alpha-v2.bin"
+[[ "$(installdb_count)" -eq 1 ]]
+
+# Invoking the "cleanup" verb with "--cleanup=no" is contradictory and must be
+# refused.
+(! "$SYSUPDATE" --cleanup=no cleanup) |& grep "contradictory" >/dev/null
+
+# A plain "cleanup" must still remove the orphaned alpha files.
+"$SYSUPDATE" cleanup
+test ! -f "$CLEANUP/target/alpha-v1.bin"
+test ! -f "$CLEANUP/target/alpha-v2.bin"
+[[ "$(installdb_count)" -eq 0 ]]
+
+rm -rf "$CONFIGDIR" "$INSTALLDB" "$CLEANUP"
+
touch /testok