diff --git a/Makefile-boot.am b/Makefile-boot.am
index ec10a0d649..93c57e28a5 100644
--- a/Makefile-boot.am
+++ b/Makefile-boot.am
@@ -40,6 +40,7 @@ systemdsystemunit_DATA = src/boot/ostree-prepare-root.service \
src/boot/ostree-remount.service \
src/boot/ostree-finalize-staged.service \
src/boot/ostree-finalize-staged.path \
+ src/boot/ostree-auto-cleanup.service \
$(NULL)
systemdtmpfilesdir = $(prefix)/lib/tmpfiles.d
dist_systemdtmpfiles_DATA = src/boot/ostree-tmpfiles.conf
@@ -68,6 +69,7 @@ EXTRA_DIST += src/boot/dracut/module-setup.sh \
src/boot/ostree-finalize-staged.path \
src/boot/ostree-remount.service \
src/boot/ostree-finalize-staged.service \
+ src/boot/ostree-auto-cleanup.service \
src/boot/grub2/grub2-15_ostree \
src/boot/grub2/ostree-grub-generator \
$(NULL)
diff --git a/Makefile-libostree.am b/Makefile-libostree.am
index c9511fe318..18b61e2846 100644
--- a/Makefile-libostree.am
+++ b/Makefile-libostree.am
@@ -171,9 +171,9 @@ endif # USE_GPGME
symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym
# Uncomment this include when adding new development symbols.
-# if BUILDOPT_IS_DEVEL_BUILD
-# symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
-# endif
+if BUILDOPT_IS_DEVEL_BUILD
+symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
+endif
# http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
wl_versionscript_arg = -Wl,--version-script=
diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt
index aa74c839b8..ad7a34d6d6 100644
--- a/apidoc/ostree-sections.txt
+++ b/apidoc/ostree-sections.txt
@@ -567,6 +567,7 @@ ostree_sysroot_get_deployment_directory
ostree_sysroot_get_deployment_dirpath
ostree_sysroot_get_deployment_origin_path
ostree_sysroot_cleanup
+ostree_sysroot_auto_cleanup
ostree_sysroot_prepare_cleanup
ostree_sysroot_cleanup_prune_repo
ostree_sysroot_repo
diff --git a/man/ostree-admin-cleanup.xml b/man/ostree-admin-cleanup.xml
index dba4b62b7b..8b11527362 100644
--- a/man/ostree-admin-cleanup.xml
+++ b/man/ostree-admin-cleanup.xml
@@ -48,9 +48,9 @@ License along with this library. If not, see .
-
- ostree admin cleanup
-
+
+ ostree admin cleanup OPTIONS
+
@@ -61,6 +61,25 @@ License along with this library. If not, see .
+
+ Options
+
+
+
+
+
+
+ Cleanup the sysroot only when needed. Whether to
+ cleanup or not is determined by the presence of the
+ .cleanup file in the sysroot.
+ When the .cleanup file exists,
+ the sysroot will be cleaned and the file will be
+ removed if it is successful.
+
+
+
+
+
Example
$ ostree admin cleanup
diff --git a/src/boot/ostree-auto-cleanup.service b/src/boot/ostree-auto-cleanup.service
new file mode 100644
index 0000000000..2d2eede520
--- /dev/null
+++ b/src/boot/ostree-auto-cleanup.service
@@ -0,0 +1,43 @@
+# Copyright © 2022 Endless OS Foundation LLC
+#
+# This library 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 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see .
+
+[Unit]
+Description=OSTree Automatic Sysroot Cleanup
+Documentation=man:ostree-admin-cleanup(1)
+ConditionPathExists=/sysroot/.cleanup
+
+# We want this to be triggered by multi-user.target but not block it via
+# the default After added to target units since pruning the repo can be
+# slow. See the Default Dependencies sections in systemd.service(5) and
+# systemd.target(5).
+DefaultDependencies=no
+Requires=sysinit.target
+After=sysinit.target basic.target
+Conflicts=shutdown.target
+Before=shutdown.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStop=/usr/bin/ostree admin cleanup --auto
+ProtectSystem=strict
+ReadWritePaths=/sysroot /boot
+
+# This will be allowed to run in the background, so try to make it less
+# disruptive while it prunes the repo.
+IOSchedulingClass=idle
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym
index 9168db734a..347510cdb3 100644
--- a/src/libostree/libostree-devel.sym
+++ b/src/libostree/libostree-devel.sym
@@ -20,12 +20,17 @@
- uncomment the include in Makefile-libostree.am
*/
+LIBOSTREE_2022.2 {
+global:
+ ostree_sysroot_auto_cleanup;
+} LIBOSTREE_2021.5;
+
/* Stub section for the stable release *after* this development one; don't
* edit this other than to update the year. This is just a copy/paste
* source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION
* with whatever the next version with new symbols will be.
-LIBOSTREE_2021.$NEWVERSION {
+LIBOSTREE_2022.$NEWVERSION {
global:
someostree_symbol_deleteme;
-} LIBOSTREE_2021.$LASTSTABLE;
+} LIBOSTREE_2022.$LASTSTABLE;
*/
diff --git a/src/libostree/ostree-impl-system-generator.c b/src/libostree/ostree-impl-system-generator.c
index 769f0cbdaf..8641f54cb3 100644
--- a/src/libostree/ostree-impl-system-generator.c
+++ b/src/libostree/ostree-impl-system-generator.c
@@ -134,6 +134,8 @@ require_internal_units (const char *normal_dir,
return FALSE;
if (symlinkat (SYSTEM_DATA_UNIT_PATH "/ostree-finalize-staged.path", normal_dir_dfd, "multi-user.target.wants/ostree-finalize-staged.path") < 0)
return glnx_throw_errno_prefix (error, "symlinkat");
+ if (symlinkat (SYSTEM_DATA_UNIT_PATH "/ostree-auto-cleanup.service", normal_dir_dfd, "multi-user.target.wants/ostree-auto-cleanup.service") < 0)
+ return glnx_throw_errno_prefix (error, "symlinkat");
return TRUE;
#else
diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c
index 3471cac72a..300f4bb726 100644
--- a/src/libostree/ostree-sysroot-cleanup.c
+++ b/src/libostree/ostree-sysroot-cleanup.c
@@ -555,6 +555,48 @@ ostree_sysroot_cleanup (OstreeSysroot *self,
return _ostree_sysroot_cleanup_internal (self, TRUE, cancellable, error);
}
+/**
+ * ostree_sysroot_auto_cleanup:
+ * @self: Sysroot
+ * @out_cleaned: (out): Whether a cleanup was performed
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Cleanup @self when needed. Whether to cleanup or not is determined by
+ * the presence of the .cleanup file in the sysroot. When the .cleanup
+ * file exists, ostree_sysroot_cleanup() will be called and the file
+ * will be removed if it is successful.
+ *
+ * Locking: exclusive
+ * Since: 2022.2
+ */
+gboolean
+ostree_sysroot_auto_cleanup (OstreeSysroot *self,
+ gboolean *out_cleaned,
+ GCancellable *cancellable,
+ GError **error)
+{
+ struct stat stbuf;
+ if (!glnx_fstatat_allow_noent (self->sysroot_fd, _OSTREE_SYSROOT_AUTOCLEANUP, &stbuf, AT_SYMLINK_NOFOLLOW, error))
+ return FALSE;
+ if (errno == ENOENT)
+ {
+ g_debug ("Did not find %s in sysroot; skipping auto cleanup", _OSTREE_SYSROOT_AUTOCLEANUP);
+ return TRUE;
+ }
+
+ if (!ostree_sysroot_cleanup (self, cancellable, error))
+ return FALSE;
+
+ if (!ot_ensure_unlinked_at (self->sysroot_fd, _OSTREE_SYSROOT_AUTOCLEANUP, error))
+ return FALSE;
+
+ if (out_cleaned != NULL)
+ *out_cleaned = TRUE;
+
+ return TRUE;
+}
+
/**
* ostree_sysroot_prepare_cleanup:
* @self: Sysroot
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index c4ae86d545..488745792a 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -3282,6 +3282,15 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self,
if (!ostree_sysroot_prepare_cleanup (self, cancellable, error))
return FALSE;
+ /* Touch the autocleanup file so the repo pruning can be run on the
+ * next boot, but ignore errors.
+ */
+ glnx_autofd int autocleanup_fd =
+ openat (self->sysroot_fd, _OSTREE_SYSROOT_AUTOCLEANUP, O_CREAT | O_WRONLY | O_NOCTTY | O_CLOEXEC, 0644);
+ if (autocleanup_fd == -1)
+ ot_journal_print (LOG_WARNING, "Failed to create sysroot autocleanup file: %s",
+ g_strerror (errno));
+
return TRUE;
}
diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h
index 9cc4023fa6..8cac5eb51f 100644
--- a/src/libostree/ostree-sysroot-private.h
+++ b/src/libostree/ostree-sysroot-private.h
@@ -79,6 +79,7 @@ struct OstreeSysroot {
};
#define OSTREE_SYSROOT_LOCKFILE "ostree/lock"
+#define _OSTREE_SYSROOT_AUTOCLEANUP ".cleanup"
/* We keep some transient state in /run */
#define _OSTREE_SYSROOT_RUNSTATE_STAGED "/run/ostree/staged-deployment"
#define _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED "/run/ostree/staged-deployment-locked"
diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h
index 41361716db..0c8bbd553d 100644
--- a/src/libostree/ostree-sysroot.h
+++ b/src/libostree/ostree-sysroot.h
@@ -129,6 +129,12 @@ gboolean ostree_sysroot_cleanup (OstreeSysroot *self,
GCancellable *cancellable,
GError **error);
+_OSTREE_PUBLIC
+gboolean ostree_sysroot_auto_cleanup (OstreeSysroot *self,
+ gboolean *out_cleaned,
+ GCancellable *cancellable,
+ GError **error);
+
_OSTREE_PUBLIC
gboolean ostree_sysroot_prepare_cleanup (OstreeSysroot *self,
GCancellable *cancellable,
diff --git a/src/ostree/ot-admin-builtin-cleanup.c b/src/ostree/ot-admin-builtin-cleanup.c
index 94d3224c1b..62b2bf50cf 100644
--- a/src/ostree/ot-admin-builtin-cleanup.c
+++ b/src/ostree/ot-admin-builtin-cleanup.c
@@ -28,7 +28,10 @@
#include
+static gboolean opt_auto;
+
static GOptionEntry options[] = {
+ { "auto", 0, 0, G_OPTION_ARG_NONE, &opt_auto, "Cleanup sysroot when needed", NULL },
{ NULL }
};
@@ -43,8 +46,19 @@ ot_admin_builtin_cleanup (int argc, char **argv, OstreeCommandInvocation *invoca
invocation, &sysroot, cancellable, error))
return FALSE;
- if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
- return FALSE;
+ if (opt_auto)
+ {
+ gboolean cleaned = FALSE;
+ if (!ostree_sysroot_auto_cleanup (sysroot, &cleaned, cancellable, error))
+ return FALSE;
+ if (!cleaned)
+ g_print ("No cleanup needed.\n");
+ }
+ else
+ {
+ if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
+ return FALSE;
+ }
return TRUE;
}
diff --git a/tests/inst/src/destructive.rs b/tests/inst/src/destructive.rs
index 2e2bd374b1..8bd55e6e90 100644
--- a/tests/inst/src/destructive.rs
+++ b/tests/inst/src/destructive.rs
@@ -202,7 +202,8 @@ fn upgrade_and_finalize() -> Result<()> {
bash!(
"rpm-ostree upgrade
systemctl start ostree-finalize-staged
- systemctl stop ostree-finalize-staged"
+ systemctl stop ostree-finalize-staged
+ test -f /sysroot/.cleanup"
)
.context("Upgrade and finalize failed")?;
Ok(())
@@ -465,6 +466,7 @@ fn impl_transaction_test>(
systemctl stop ostree-finalize-staged
ostree reset testrepo:{testref} {booted_commit}
rpm-ostree cleanup -pbrm
+ rm -f /sysroot/.cleanup
",
testref,
booted_commit,
diff --git a/tests/kolainst/destructive/staged-deploy.sh b/tests/kolainst/destructive/staged-deploy.sh
index baadb3d86e..c0c530f493 100755
--- a/tests/kolainst/destructive/staged-deploy.sh
+++ b/tests/kolainst/destructive/staged-deploy.sh
@@ -10,6 +10,7 @@ case "${AUTOPKGTEST_REBOOT_MARK:-}" in
"")
# Test our generator
test -f /run/systemd/generator/multi-user.target.wants/ostree-finalize-staged.path
+ test -f /run/systemd/generator/multi-user.target.wants/ostree-auto-cleanup.service
test -f /run/systemd/generator/local-fs.target.requires/ostree-remount.service
cat >/etc/systemd/system/sock-to-ignore.socket << 'EOF'
@@ -77,6 +78,12 @@ EOF
rm -f svc.txt
# And there should not be a staged deployment
test '!' -f /run/ostree/staged-deployment
+ # Check that auto cleanup ran
+ assert_not_has_file /sysroot/.cleanup
+ journalctl -b 0 -u ostree-auto-cleanup.service > svc.txt
+ assert_file_has_content svc.txt 'Starting ostree-auto-cleanup.service'
+ assert_not_file_has_content svc.txt 'No cleanup needed.'
+ rm -f svc.txt
# Upgrade with staging
test '!' -f /run/ostree/staged-deployment
diff --git a/tests/test-admin-deploy-2.sh b/tests/test-admin-deploy-2.sh
index 23645ded6e..6ea5a3be9d 100755
--- a/tests/test-admin-deploy-2.sh
+++ b/tests/test-admin-deploy-2.sh
@@ -24,7 +24,7 @@ set -euo pipefail
# Exports OSTREE_SYSROOT so --sysroot not needed.
setup_os_repository "archive" "syslinux"
-echo "1..8"
+echo "1..9"
${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmain/x86_64-runtime
rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmain/x86_64-runtime)
@@ -62,6 +62,17 @@ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-r
echo "ok manual cleanup"
+assert_not_has_file sysroot/.cleanup
+${CMD_PREFIX} ostree admin cleanup --auto > cleanup.txt
+assert_file_has_content cleanup.txt "No cleanup needed."
+assert_not_has_file sysroot/.cleanup
+touch sysroot/.cleanup
+${CMD_PREFIX} ostree admin cleanup --auto > cleanup.txt
+assert_not_file_has_content cleanup.txt "No cleanup needed."
+assert_not_has_file sysroot/.cleanup
+
+echo "ok auto cleanup"
+
# Commit + upgrade twice, so that we'll rotate out the original deployment
os_repository_new_commit "1"
${CMD_PREFIX} ostree admin upgrade --os=testos