diff --git a/src/boot/ostree-finalize-staged.service b/src/boot/ostree-finalize-staged.service index 2f28bbb706..997d7b71e5 100644 --- a/src/boot/ostree-finalize-staged.service +++ b/src/boot/ostree-finalize-staged.service @@ -30,9 +30,8 @@ After=systemd-journal-flush.service Conflicts=final.target [Service] -Type=oneshot -RemainAfterExit=yes -ExecStop=/usr/bin/ostree admin finalize-staged +Type=simple +ExecStart=/usr/bin/ostree admin finalize-staged # This is a quite long timeout intentionally; the failure mode # here is that people don't get an upgrade. We need to handle # cases with slow rotational media, etc. diff --git a/src/ostree/ot-admin-builtin-finalize-staged.c b/src/ostree/ot-admin-builtin-finalize-staged.c index 17b6a6255a..1acabfa320 100644 --- a/src/ostree/ot-admin-builtin-finalize-staged.c +++ b/src/ostree/ot-admin-builtin-finalize-staged.c @@ -19,8 +19,9 @@ #include "config.h" -#include "config.h" - +#include +#include +#include #include #include "ot-main.h" @@ -36,6 +37,16 @@ static GOptionEntry options[] = { { NULL } }; +static gboolean +sigterm_cb (gpointer user_data) +{ + gboolean *running = user_data; + g_print ("Received sigterm, finalizing deployment\n"); + *running = FALSE; + g_main_context_wakeup (NULL); + return G_SOURCE_REMOVE; +} + /* Called by ostree-finalize-staged.service, and in turn * invokes a cmdprivate function inside the shared library. */ @@ -51,10 +62,46 @@ ot_admin_builtin_finalize_staged (int argc, char **argv, OstreeCommandInvocation g_autoptr(GOptionContext) context = g_option_context_new (""); g_autoptr(OstreeSysroot) sysroot = NULL; if (!ostree_admin_option_context_parse (context, options, &argc, &argv, - OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, + OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED, invocation, &sysroot, cancellable, error)) return FALSE; + /* In case it's an automount, open /boot so that the automount doesn't expire + * until before this process exits. If it did expire and got unmounted, the + * service would be stopped and the deployment would be finalized earlier + * than expected. + */ + int sysroot_fd = ostree_sysroot_get_fd (sysroot); + glnx_autofd int boot_fd = -1; + g_debug ("Opening /boot directory"); + if (!glnx_opendirat (sysroot_fd, "boot", TRUE, &boot_fd, error)) + return FALSE; + + /* We want to wait until the system is shut down to actually finalize, so + * block on SIGTERM under the assumption that it will be received when + * systemd stops the unit. + */ + gboolean running = TRUE; + g_unix_signal_add (SIGTERM, sigterm_cb, &running); + g_print ("Waiting for SIGTERM\n"); + while (running) + g_main_context_iteration (NULL, TRUE); + + /* Since the sysroot was initially started unlocked, setup a mount namespace + * to allow /boot and /sysroot to be writable, lock the sysroot, and reload + * it to ensure the state of the deployments is current. + * + * FIXME: This overlaps with the mount namespace handling in + * ostree_admin_option_context_parse. That should be factored out. + */ + if (unshare (CLONE_NEWNS) < 0) + return glnx_throw_errno_prefix (error, "setting up mount namespace: unshare(CLONE_NEWNS)"); + ostree_sysroot_set_mount_namespace_in_use (sysroot); + if (!ot_admin_sysroot_lock (sysroot, error)) + return FALSE; + if (!ostree_sysroot_load (sysroot, cancellable, error)) + return FALSE; + if (!ostree_cmd__private__()->ostree_finalize_staged (sysroot, cancellable, error)) return FALSE;