sysroot-deploy: prepare for soft-reboot

This commit is contained in:
Mary Strodl 2025-02-27 14:19:07 -05:00
parent d5ea852ddc
commit 45f2550d50
No known key found for this signature in database
GPG Key ID: 23B7FC1590D8E30E
10 changed files with 180 additions and 2 deletions

View File

@ -621,6 +621,7 @@ ostree_sysroot_upgrader_get_origin
ostree_sysroot_upgrader_dup_origin
ostree_sysroot_upgrader_set_origin
ostree_sysroot_upgrader_get_origin_description
ostree_sysroot_upgrader_set_parent_mountns
ostree_sysroot_upgrader_check_timestamps
OstreeSysrootUpgraderPullFlags
ostree_sysroot_upgrader_pull

View File

@ -23,6 +23,7 @@
LIBOSTREE_2025.2 {
global:
ostree_sepolicy_set_null_log;
ostree_sysroot_upgrader_set_parent_mountns;
} LIBOSTREE_2025.1;
/* Stub section for the stable release *after* this development one; don't

View File

@ -4259,6 +4259,102 @@ ostree_sysroot_deployment_set_mutable (OstreeSysroot *self, OstreeDeployment *de
return TRUE;
}
/**
* ostree_sysroot_deployment_prepare_next_root
* @self: Sysroot
* @deployment: Deployment to prepare /run/nextroot for
* @cancellable: Cancellable
* @error: Error
*
* Prepare the specified deployment for a systemd soft-reboot by creating a new
* root with it at /run/nextroot
*
* Since: TODO
*/
gboolean
ostree_sysroot_deployment_prepare_next_root (OstreeSysroot *self, OstreeDeployment *deployment,
GCancellable *cancellable, GError **error)
{
GLNX_AUTO_PREFIX_ERROR ("Preparing /run/nextroot for a soft-reboot", error);
OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment);
const char *kargs_const = ostree_bootconfig_parser_get (bootconfig, "options");
g_autofree gchar *kargs = g_strdup (kargs_const);
gint exit_status;
g_autoptr (GPtrArray) args = g_ptr_array_new ();
/* We cannot just make a bind mount, because when the soft reboot happens, our
current root will be unmounted, and the bind mount will break. Therefore,
we have to recreate the mount at a different location. */
/* Read in mounted FSes */
g_autofree gchar *mounts_contents = NULL;
if (!g_file_get_contents ("/proc/self/mounts", &mounts_contents, NULL, error))
return FALSE;
g_auto (GStrv) mounts = g_strsplit (mounts_contents, "\n", -1);
for (char **iter = mounts; iter && *iter; iter++)
{
const gchar *mount_text = *iter;
g_auto (GStrv) mount_fields = g_strsplit (mount_text, " ", 6);
/* Validate all 6 tokens are present */
for (int i = 0; i < 6; ++i)
{
if (mount_fields[i] == NULL)
{
ot_journal_print (LOG_WARNING, "Mount %s is missing a field at %d!", mount_text, i);
continue;
}
}
/* Only care about /sysroot */
if (!g_str_equal (mount_fields[1], "/sysroot"))
continue;
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, "/run/nextroot", 0755, cancellable, error))
return FALSE;
if (mount (mount_fields[0], "/run/nextroot", mount_fields[2], MS_SILENT, NULL))
{
glnx_prefix_error (error, "failed to mount /run/nextroot");
goto err_unlink;
}
/* Found it, and mounted it! */
goto end_loop;
}
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Couldn't find mountpoint for /sysroot!");
return FALSE;
end_loop:
/* At this point, /sysroot is mounted at /run/nextroot, do the pivot from there: */
g_ptr_array_add (args, "/usr/lib/ostree/ostree-prepare-root");
g_ptr_array_add (args, "/run/nextroot");
g_ptr_array_add (args, kargs);
g_ptr_array_add (args, NULL);
if (!g_spawn_sync (NULL, (char **)args->pdata, NULL, 0, NULL, NULL, NULL, NULL, &exit_status,
error))
goto err_unlink;
if (!g_spawn_check_exit_status (exit_status, error))
{
glnx_prefix_error (error, "failed to prepare next root");
goto err_unlink;
}
return TRUE;
err_unlink:
/* We're already handling an error, not much we can do if this fails too... */
if (unlinkat (AT_FDCWD, "/run/nextroot", AT_REMOVEDIR) < 0)
ot_journal_print (LOG_WARNING,
"Couldn't remove /run/nextroot while cleaning up from failed next root");
return FALSE;
}
/**
* ostree_sysroot_deployment_kexec_load
* @self: Sysroot

View File

@ -25,6 +25,8 @@
#include "ostree-sysroot-upgrader.h"
#include "ostree.h"
#include <sys/wait.h>
/**
* SECTION:ostree-sysroot-upgrader
* @title: Simple upgrade class
@ -53,6 +55,8 @@ struct OstreeSysrootUpgrader
char *override_csum;
char *new_revision;
int parent_mountns;
};
enum
@ -139,6 +143,8 @@ ostree_sysroot_upgrader_initable_init (GInitable *initable, GCancellable *cancel
if (!parse_refspec (self, cancellable, error))
return FALSE;
self->parent_mountns = -1;
return TRUE;
}
@ -381,6 +387,21 @@ ostree_sysroot_upgrader_get_origin_description (OstreeSysrootUpgrader *self)
return g_key_file_get_string (self->origin, "origin", "refspec", NULL);
}
/**
* ostree_sysroot_upgrader_set_parent_mountns:
* @self: Sysroot Upgrader
* @mountns: FD of the parent mount NS
*
* Replace FD of the parent mountns
*/
gboolean
ostree_sysroot_upgrader_set_parent_mountns (OstreeSysrootUpgrader *self, int mountns)
{
self->parent_mountns = mountns;
return TRUE;
}
/**
* ostree_sysroot_upgrader_check_timestamps:
* @repo: Repo
@ -628,6 +649,36 @@ ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self, GCancellable *cance
error))
return FALSE;
}
if ((self->flags & OSTREE_SYSROOT_UPGRADER_FLAGS_SOFT_REBOOT) > 0)
{
/* Need to fork because setns will fail if we have threads (GIO loop) */
pid_t child_pid = fork ();
if (child_pid < 0)
{
return glnx_throw_errno_prefix (error, "fork() for parent mount ns");
}
/* Hop back into the main system's mount NS so we can mount /run/nextroot */
if (child_pid == 0)
{
if (self->parent_mountns >= 0 && setns (self->parent_mountns, CLONE_NEWNS))
{
return glnx_throw_errno_prefix (error, "Couldn't recover parent mount NS!");
}
if (!ostree_sysroot_deployment_prepare_next_root (self->sysroot, new_deployment,
cancellable, error))
return FALSE;
exit (0);
}
gint status;
if (waitpid (child_pid, &status, 0) < 0)
{
return glnx_throw_errno_prefix (error, "waitpid() for mount ns child");
}
}
}
return TRUE;
@ -645,6 +696,8 @@ ostree_sysroot_upgrader_flags_get_type (void)
"OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED", "ignore-unconfigured" },
{ OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE, "OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE", "stage" },
{ OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC, "OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC", "kexec" },
{ OSTREE_SYSROOT_UPGRADER_FLAGS_SOFT_REBOOT, "OSTREE_SYSROOT_UPGRADER_FLAGS_SOFT_REBOOT",
"soft_reboot" },
{ 0, NULL, NULL }
};
GType g_define_type_id

View File

@ -45,6 +45,7 @@ typedef enum
OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED = (1 << 1),
OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE = (1 << 2),
OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC = (1 << 3),
OSTREE_SYSROOT_UPGRADER_FLAGS_SOFT_REBOOT = (1 << 4),
} OstreeSysrootUpgraderFlags;
_OSTREE_PUBLIC
@ -80,6 +81,9 @@ gboolean ostree_sysroot_upgrader_set_origin (OstreeSysrootUpgrader *self, GKeyFi
_OSTREE_PUBLIC
char *ostree_sysroot_upgrader_get_origin_description (OstreeSysrootUpgrader *self);
_OSTREE_PUBLIC
gboolean ostree_sysroot_upgrader_set_parent_mountns (OstreeSysrootUpgrader *self, int mountns);
_OSTREE_PUBLIC
gboolean ostree_sysroot_upgrader_check_timestamps (OstreeRepo *repo, const char *from_rev,
const char *to_rev, GError **error);

View File

@ -268,6 +268,11 @@ gboolean ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, const c
OstreeSysrootSimpleWriteDeploymentFlags flags,
GCancellable *cancellable, GError **error);
_OSTREE_PUBLIC
gboolean ostree_sysroot_deployment_prepare_next_root (OstreeSysroot *self,
OstreeDeployment *deployment,
GCancellable *cancellable, GError **error);
_OSTREE_PUBLIC
gboolean ostree_sysroot_deployment_kexec_load (OstreeSysroot *self, OstreeDeployment *deployment,
GCancellable *cancellable, GError **error);

View File

@ -32,6 +32,7 @@
static gboolean opt_reboot;
static gboolean opt_kexec;
static gboolean opt_soft_reboot;
static gboolean opt_allow_downgrade;
static gboolean opt_pull_only;
static gboolean opt_deploy_only;
@ -44,6 +45,8 @@ static GOptionEntry options[] = {
"Use a different operating system root than the current one", "OSNAME" },
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Reboot after a successful upgrade", NULL },
{ "kexec", 'k', 0, G_OPTION_ARG_NONE, &opt_kexec, "Stage new kernel in kexec", NULL },
{ "soft-reboot", 'x', 0, G_OPTION_ARG_NONE, &opt_soft_reboot,
"Prepare nextroot for a systemd soft reboot", NULL },
{ "allow-downgrade", 0, 0, G_OPTION_ARG_NONE, &opt_allow_downgrade,
"Permit deployment of chronologically older trees", NULL },
{ "override-commit", 0, 0, G_OPTION_ARG_STRING, &opt_override_commit,
@ -86,12 +89,17 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeCommandInvocation *invoca
flags |= OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE;
if (opt_kexec)
flags |= OSTREE_SYSROOT_UPGRADER_FLAGS_KEXEC;
if (opt_soft_reboot)
flags |= OSTREE_SYSROOT_UPGRADER_FLAGS_SOFT_REBOOT;
g_autoptr (OstreeSysrootUpgrader) upgrader = ostree_sysroot_upgrader_new_for_os_with_flags (
sysroot, opt_osname, flags, cancellable, error);
if (!upgrader)
return FALSE;
if (!ostree_sysroot_upgrader_set_parent_mountns (upgrader, invocation->parent_mountns))
return FALSE;
g_autoptr (GKeyFile) origin = ostree_sysroot_upgrader_dup_origin (upgrader);
if (origin != NULL)
{

View File

@ -174,6 +174,7 @@ ostree_builtin_admin (int argc, char **argv, OstreeCommandInvocation *invocation
g_set_prgname (prgname);
}
OstreeCommandInvocation sub_invocation = { .command = subcommand };
OstreeCommandInvocation sub_invocation = *invocation;
sub_invocation.command = subcommand;
return subcommand->fn (argc, argv, &sub_invocation, cancellable, error);
}

View File

@ -288,6 +288,13 @@ ostree_run (int argc, char **argv, OstreeCommand *commands, GError **res_error)
command++;
}
int parent_mountns = open ("/proc/self/ns/mnt", O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (parent_mountns < 0)
{
glnx_throw_errno_prefix (&error, "open(mountns)");
goto out;
}
if (!command->fn)
{
g_autoptr (GOptionContext) context = ostree_option_context_new_with_commands (commands);
@ -315,7 +322,8 @@ ostree_run (int argc, char **argv, OstreeCommand *commands, GError **res_error)
prgname = g_strdup_printf ("%s %s", g_get_prgname (), command_name);
g_set_prgname (prgname);
#endif
OstreeCommandInvocation invocation = { .command = command };
OstreeCommandInvocation invocation = { .command = command, .parent_mountns = parent_mountns };
if (!command->fn (argc, argv, &invocation, cancellable, &error))
goto out;

View File

@ -61,6 +61,7 @@ typedef struct
struct OstreeCommandInvocation
{
OstreeCommand *command;
int parent_mountns;
};
int ostree_main (int argc, char **argv, OstreeCommand *commands);