From 0be7ef439c9785da6eff93a63517a0f51f23361b Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Thu, 6 Jan 2022 11:18:46 -0700 Subject: [PATCH 1/4] sysroot: Add auto cleanup API This is a wrapper around `ostree_sysroot_cleanup` that only performs the cleanup when the `.cleanup` file exists. When the cleanup has completed, the file is deleted. The purpose of this API is to allow other routines to indicate that cleanup is needed when they're not in a good position to initiate the cleanup themselves. This API can then be run at a more opportune time without actually initiating the expensive cleanup operations unless they were needed. --- Makefile-libostree.am | 6 ++-- apidoc/ostree-sections.txt | 1 + src/libostree/libostree-devel.sym | 9 ++++-- src/libostree/ostree-sysroot-cleanup.c | 42 ++++++++++++++++++++++++++ src/libostree/ostree-sysroot-private.h | 1 + src/libostree/ostree-sysroot.h | 6 ++++ 6 files changed, 60 insertions(+), 5 deletions(-) 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/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-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-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, From d32dd29f1dd9283c25525ff77ff0556274a08e3c Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Thu, 6 Jan 2022 11:44:06 -0700 Subject: [PATCH 2/4] admin/cleanup: Add --auto option This provides a convenient method to run `ostree_sysroot_auto_cleanup` rather than `ostree_sysroot_cleanup`. --- man/ostree-admin-cleanup.xml | 25 ++++++++++++++++++++++--- src/ostree/ot-admin-builtin-cleanup.c | 18 ++++++++++++++++-- tests/test-admin-deploy-2.sh | 13 ++++++++++++- 3 files changed, 50 insertions(+), 6 deletions(-) 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/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/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 From 85dc35d4576dfe048ca47a2f99f237100beec8f6 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Thu, 6 Jan 2022 11:37:56 -0700 Subject: [PATCH 3/4] deploy: Touch auto cleanup file when finalizing staged deployment Currently finalizing a staged deployment skips the pruning that typically happens when writing out a deployment since it would block the shutdown path. In order to leave an indication that a cleanup should be run at a more opportune time, touch the `.cleanup` file after the staged deployment is written out. The `ostree_sysroot_auto_cleanup` API can then be used to perform the pruning at a better time without fear that it will initiate an unnecessary prune. --- src/libostree/ostree-sysroot-deploy.c | 9 +++++++++ tests/inst/src/destructive.rs | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) 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/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, From 951d5d730198907783dbadec63e5d4799dde2421 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Thu, 6 Jan 2022 13:30:32 -0700 Subject: [PATCH 4/4] boot: Add systemd unit to cleanup sysroot automatically This unit makes use of the `--auto` option of `ostree admin cleanup` to cleanup the sysroot when the `/sysroot/.cleanup` file is left behind when finalizing a staged deployment. The purpose of this service is to restore pruning of deleted deployments when a staged deployment is written out during shutdown of the previous boot. The unit is optional as the cleanup can be run at another time or not at all. Cleaning the sysroot will prune the repo, and this can be a slow and IO intensive operation. To keep the system from blocking, the default `Before` dependency on `multi-user.target` has been removed. Since the service will then run in the background, the IO scheduling class has been lowered to `idle` to keep the system's primary tasks more responsive. --- Makefile-boot.am | 2 + src/boot/ostree-auto-cleanup.service | 43 ++++++++++++++++++++ src/libostree/ostree-impl-system-generator.c | 2 + tests/kolainst/destructive/staged-deploy.sh | 7 ++++ 4 files changed, 54 insertions(+) create mode 100644 src/boot/ostree-auto-cleanup.service 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/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/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/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