finalize-staged: Ensure /boot automount doesn't expire
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. To ensure that systemd doesn't stop the service early when the `/boot` automount expires, introduce a new unit that holds `/boot` open until it's sent `SIGTERM`. This uses a new `--hold` option for `finalize-staged` that loads but doesn't lock the sysroot. A separate unit is used since we want the process to remain active throughout the finalization run in `ExecStop`. That wouldn't work if it was specified in `ExecStart` in the same unit since it would be killed before the `ExecStop` action was run. Fixes: #2543
This commit is contained in:
1 parent
e30a3b6b17
commit
f3db79e7fa
6 files changed
+193
-7
No files matched your search
@@ -0,0 +1,35 @@
|
||||
# Copyright (C) 2018 Red Hat, Inc.
|
||||
# Copyright (C) 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
# See https://github.com/ostreedev/ostree/pull/2543 for background.
|
||||
[Unit]
|
||||
Description=Hold /boot Open for OSTree Finalize Staged Deployment
|
||||
Documentation=man:ostree(1)
|
||||
ConditionPathExists=/run/ostree-booted
|
||||
DefaultDependencies=no
|
||||
|
||||
RequiresMountsFor=/sysroot /boot
|
||||
After=local-fs.target
|
||||
Before=basic.target final.target
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
|
||||
# This is explicitly run in the root namespace to ensure an automounted
|
||||
# /boot doesn't time out since autofs doesn't handle mount namespaces.
|
||||
#
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2056090
|
||||
ExecStart=+/usr/bin/ostree admin finalize-staged --hold
|
||||
@@ -29,6 +29,11 @@ Before=basic.target final.target
|
||||
After=systemd-journal-flush.service
|
||||
Conflicts=final.target
|
||||
|
||||
# Start the hold unit and ensure it stays active throughout this
|
||||
# service.
|
||||
Wants=ostree-finalize-staged-hold.service
|
||||
After=ostree-finalize-staged-hold.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc.
|
||||
* Copyright (C) 2022 Endless OS Foundation LLC
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
@@ -19,8 +20,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib-unix.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ot-main.h"
|
||||
@@ -32,10 +34,23 @@
|
||||
#include "ostree-cmd-private.h"
|
||||
#include "ostree.h"
|
||||
|
||||
static gboolean opt_hold;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "hold", 0, 0, G_OPTION_ARG_NONE, &opt_hold, "Hold /boot open during finalization", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gboolean
|
||||
sigterm_cb (gpointer user_data)
|
||||
{
|
||||
gboolean *running = user_data;
|
||||
g_print ("Received SIGTERM, exiting\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.
|
||||
*/
|
||||
@@ -50,13 +65,56 @@ ot_admin_builtin_finalize_staged (int argc, char **argv, OstreeCommandInvocation
|
||||
|
||||
g_autoptr(GOptionContext) context = g_option_context_new ("");
|
||||
g_autoptr(OstreeSysroot) sysroot = NULL;
|
||||
|
||||
/* First parse the args without loading the sysroot to see what options are
|
||||
* set. */
|
||||
if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_NO_LOAD,
|
||||
invocation, &sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_cmd__private__()->ostree_finalize_staged (sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
if (opt_hold)
|
||||
{
|
||||
/* Load the sysroot unlocked so that a separate namespace isn't
|
||||
* created. */
|
||||
if (!ostree_admin_sysroot_load (sysroot,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER | OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED,
|
||||
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 keep /boot open until the deployment is finalized during
|
||||
* system shutdown, 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Load the sysroot with the normal flags and actually finalize the
|
||||
* deployment. */
|
||||
if (!ostree_admin_sysroot_load (sysroot,
|
||||
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_cmd__private__()->ostree_finalize_staged (sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
Reference in new issue
Block a user