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