Skip to content

Commit

Permalink
finalize-staged: Ensure /boot and /sysroot automounts don't expire
Browse files Browse the repository at this point in the history
If `/boot` or `/sysroot` are automounts, then the unit will be stopped
as soon as the automounts expire. That's would defeat the purpose of
using systemd to delay finalizing the deployment until shutdown. This is
not uncommon as `systemd-gpt-auto-generator` will create an automount
unit for `/boot` when it's the EFI System Partition and there's no fstab
entry.

Instead of relying on systemd to run the command via `ExecStop` at the
appropriate time, have `finalize-staged` open `/boot` and `/sysroot` and
then block on `SIGTERM`. Having the directories open will prevent the
automounts from expiring, and then we presume that systemd will send
`SIGTERM` when it's time for the service to stop. Finalizing the
deployment still happens when the service is stopped. The difference is
that the process is already running.

In order to keep from blocking legitimate sysroot activity prior to
shutdown, the sysroot lock is only taken after the signal has been
received. Similarly, the sysroot is reloaded to ensure the state of the
deployments is current.

Fixes: ostreedev#2543
  • Loading branch information
dbnicholson committed Feb 17, 2022
1 parent 188b187 commit d1e8d2a
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 6 deletions.
5 changes: 2 additions & 3 deletions src/boot/ostree-finalize-staged.service
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
53 changes: 50 additions & 3 deletions src/ostree/ot-admin-builtin-finalize-staged.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@

#include "config.h"

#include "config.h"

#include <glib-unix.h>
#include <sched.h>
#include <signal.h>
#include <stdlib.h>

#include "ot-main.h"
Expand All @@ -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.
*/
Expand All @@ -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;

Expand Down

0 comments on commit d1e8d2a

Please sign in to comment.