diff options
author | Colin Walters <walters@verbum.org> | 2018-02-22 15:27:59 -0500 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-04-12 14:55:12 +0000 |
commit | eb506c759c666af2461f1ba3dda4e31ea49ebc41 (patch) | |
tree | ec78dcc19982e3b14545ad2b22902a85c9a7e4ef /src/libostree/ostree-sysroot.c | |
parent | ff50495f67a95c9b2fef5f9b84bc91469a46eb27 (diff) | |
download | ostree-eb506c759c666af2461f1ba3dda4e31ea49ebc41.tar.gz |
Add concept of "staged" deployment
Add API to write a deployment state to `/run/ostree/staged-deployment`,
along with a systemd service which runs at shutdown time.
This is a big change to the ostree model for hosts,
but it closes a longstanding set of bugs; many, many people have
hit the "losing changes in /etc" problem. It also avoids
the other problem of racing with programs that modify `/etc`
such as LVM backups:
https://bugzilla.redhat.com/show_bug.cgi?id=1365297
We need this in particular to go to a full-on model for
automatically updated host systems where (like a dual-partition model)
everything is fully prepared and the reboot can be taken
asynchronously.
Closes: https://github.com/ostreedev/ostree/issues/545
Closes: #1503
Approved by: jlebon
Diffstat (limited to 'src/libostree/ostree-sysroot.c')
-rw-r--r-- | src/libostree/ostree-sysroot.c | 98 |
1 files changed, 89 insertions, 9 deletions
diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index f77d7703..f4a8eade 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -82,6 +82,8 @@ ostree_sysroot_finalize (GObject *object) g_clear_object (&self->repo); g_clear_pointer (&self->deployments, g_ptr_array_unref); g_clear_object (&self->booted_deployment); + g_clear_object (&self->staged_deployment); + g_clear_pointer (&self->staged_deployment_data, (GDestroyNotify)g_variant_unref); glnx_release_lock_file (&self->lock); @@ -584,14 +586,14 @@ parse_bootlink (const char *bootlink, return TRUE; } -static char * -get_unlocked_development_path (OstreeDeployment *deployment) +char * +_ostree_sysroot_get_runstate_path (OstreeDeployment *deployment, const char *key) { return g_strdup_printf ("%s%s.%d/%s", _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR, ostree_deployment_get_csum (deployment), ostree_deployment_get_deployserial (deployment), - _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT); + key); } static gboolean @@ -636,9 +638,10 @@ parse_deployment (OstreeSysroot *self, return FALSE; /* See if this is the booted deployment */ + const gboolean root_is_ostree_booted = + (self->ostree_booted && self->root_is_sysroot); const gboolean looking_for_booted_deployment = - (self->ostree_booted && self->root_is_sysroot && - !self->booted_deployment); + (root_is_ostree_booted && !self->booted_deployment); gboolean is_booted_deployment = FALSE; if (looking_for_booted_deployment) { @@ -665,7 +668,8 @@ parse_deployment (OstreeSysroot *self, ostree_deployment_set_origin (ret_deployment, origin); ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_NONE; - g_autofree char *unlocked_development_path = get_unlocked_development_path (ret_deployment); + g_autofree char *unlocked_development_path = + _ostree_sysroot_get_runstate_path (ret_deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT); struct stat stbuf; if (lstat (unlocked_development_path, &stbuf) == 0) ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT; @@ -789,6 +793,60 @@ ensure_repo (OstreeSysroot *self, return TRUE; } +/* Reload the staged deployment from the file in /run */ +gboolean +_ostree_sysroot_reload_staged (OstreeSysroot *self, + GError **error) +{ + GLNX_AUTO_PREFIX_ERROR ("Loading staged deployment", error); + const gboolean root_is_ostree_booted = + self->ostree_booted && self->root_is_sysroot; + if (!root_is_ostree_booted) + return TRUE; /* Note early return */ + + g_assert (self->booted_deployment); + + g_clear_object (&self->staged_deployment); + g_clear_pointer (&self->staged_deployment_data, (GDestroyNotify)g_variant_unref); + + /* Read the staged state from disk */ + glnx_autofd int fd = -1; + if (!ot_openat_ignore_enoent (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, &fd, error)) + return FALSE; + if (fd != -1) + { + g_autoptr(GBytes) contents = ot_fd_readall_or_mmap (fd, 0, error); + if (!contents) + return FALSE; + g_autoptr(GVariant) staged_deployment_data = + g_variant_new_from_bytes ((GVariantType*)"a{sv}", contents, TRUE); + g_autoptr(GVariantDict) staged_deployment_dict = + g_variant_dict_new (staged_deployment_data); + + /* Parse it */ + g_autoptr(GVariant) target = NULL; + g_autofree char **kargs = NULL; + g_variant_dict_lookup (staged_deployment_dict, "target", "@a{sv}", &target); + g_variant_dict_lookup (staged_deployment_dict, "kargs", "^a&s", &kargs); + if (target) + { + self->staged_deployment = + _ostree_sysroot_deserialize_deployment_from_variant (target, error); + if (!self->staged_deployment) + return FALSE; + _ostree_deployment_set_bootconfig_from_kargs (self->staged_deployment, kargs); + self->staged_deployment_data = g_steal_pointer (&staged_deployment_data); + /* We set this flag for ostree_deployment_is_staged() because that API + * doesn't have access to the sysroot, which currently has the + * canonical "staged_deployment" reference. + */ + self->staged_deployment->staged = TRUE; + } + } + + return TRUE; +} + gboolean ostree_sysroot_load_if_changed (OstreeSysroot *self, gboolean *out_changed, @@ -857,6 +915,7 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, g_clear_pointer (&self->deployments, g_ptr_array_unref); g_clear_object (&self->booted_deployment); + g_clear_object (&self->staged_deployment); self->bootversion = -1; self->subbootversion = -1; @@ -880,17 +939,23 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, } } - if (self->ostree_booted && self->root_is_sysroot - && !self->booted_deployment) + const gboolean root_is_ostree_booted = + self->ostree_booted && self->root_is_sysroot; + if (root_is_ostree_booted && !self->booted_deployment) return glnx_throw (error, "Unexpected state: /run/ostree-booted found and in / sysroot but not in a booted deployment"); + /* Ensure the entires are sorted */ g_ptr_array_sort (deployments, compare_deployments_by_boot_loader_version_reversed); + /* And then set their index variables */ for (guint i = 0; i < deployments->len; i++) { OstreeDeployment *deployment = deployments->pdata[i]; ostree_deployment_set_index (deployment, i); } + if (!_ostree_sysroot_reload_staged (self, error)) + return FALSE; + /* Determine whether we're "physical" or not, the first time we initialize */ if (!self->loaded) { @@ -950,6 +1015,20 @@ ostree_sysroot_get_booted_deployment (OstreeSysroot *self) } /** + * ostree_sysroot_get_staged_deployment: + * @self: Sysroot + * + * Returns: (transfer none): The currently staged deployment, or %NULL if none + */ +OstreeDeployment * +ostree_sysroot_get_staged_deployment (OstreeSysroot *self) +{ + g_return_val_if_fail (self->loaded, NULL); + + return self->staged_deployment; +} + +/** * ostree_sysroot_get_deployments: * @self: Sysroot * @@ -1769,7 +1848,8 @@ ostree_sysroot_deployment_unlock (OstreeSysroot *self, break; case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT: { - g_autofree char *devpath = get_unlocked_development_path (deployment); + g_autofree char *devpath = + _ostree_sysroot_get_runstate_path (deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT); g_autofree char *devpath_parent = dirname (g_strdup (devpath)); if (!glnx_shutil_mkdir_p_at (AT_FDCWD, devpath_parent, 0755, cancellable, error)) |