Skip to content

Commit

Permalink
finalize-staged: Ensure /boot automount doesn't expire
Browse files Browse the repository at this point in the history
If `/boot` is an automount, then the unit will be stopped as soon as the
automount expires. 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 then block on
`SIGTERM`. Having the directory open will prevent the automount 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 0bfeb40
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 0bfeb40

Please sign in to comment.