mirror of
https://github.com/ostreedev/ostree.git
synced 2024-12-22 17:35:55 +03:00
[INCOMPATIBLE CHANGE] Implement new deployment model
See https://wiki.gnome.org/OSTree/DeploymentModel2 This is a major rework of the on-disk filesystem layout, and the boot process. OSTree now explicitly supports upgrading kernels, and these upgrades are also atomic. The core concept of the new model is the "deployment list", which is an ordered list of bootable operating system trees. The deployment list is reflected in the bootloader configuration; which has a kernel argument that tells the initramfs (dracut) which operating system root to use. Invidiual notable changes that come along with this: 1) Operating systems should now come with their etc in usr/etc; OSTree will perform a 3-way merge at deployment time, and place etc in the actual root. This avoids the need for a bind mount, and is just a lot cleaner. 2) OSTree no longer bind mounts /root, /home, and /tmp. It is expected that the the OS/ has these as symbolic links into /var. At the moment, OSTree only supports managing syslinux; other bootloader backends will follow.
This commit is contained in:
parent
ecb3f0de03
commit
bb6eedfb25
@ -56,15 +56,26 @@ ostree_SOURCES += \
|
||||
src/ostree/ot-admin-builtin-diff.c \
|
||||
src/ostree/ot-admin-builtin-deploy.c \
|
||||
src/ostree/ot-admin-builtin-prune.c \
|
||||
src/ostree/ot-admin-builtin-pull-deploy.c \
|
||||
src/ostree/ot-admin-builtin-os-init.c \
|
||||
src/ostree/ot-admin-builtin-install.c \
|
||||
src/ostree/ot-admin-builtin-status.c \
|
||||
src/ostree/ot-admin-builtin-run-triggers.c \
|
||||
src/ostree/ot-admin-builtin-upgrade.c \
|
||||
src/ostree/ot-admin-builtin-update-kernel.c \
|
||||
src/ostree/ot-admin-builtins.h \
|
||||
src/ostree/ot-admin-functions.h \
|
||||
src/ostree/ot-admin-functions.c \
|
||||
src/ostree/ot-admin-deploy.h \
|
||||
src/ostree/ot-admin-deploy.c \
|
||||
src/ostree/ot-bootloader.h \
|
||||
src/ostree/ot-bootloader.c \
|
||||
src/ostree/ot-bootloader-syslinux.h \
|
||||
src/ostree/ot-bootloader-syslinux.c \
|
||||
src/ostree/ot-config-parser.h \
|
||||
src/ostree/ot-config-parser.c \
|
||||
src/ostree/ot-deployment.h \
|
||||
src/ostree/ot-deployment.c \
|
||||
src/ostree/ot-ordered-hash.h \
|
||||
src/ostree/ot-ordered-hash.c \
|
||||
$(NULL)
|
||||
|
||||
ostree_bin_shared_cflags = $(AM_CFLAGS) -I$(srcdir)/src/libgsystem -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(srcdir)/src/ostree -DLOCALEDIR=\"$(datadir)/locale\"
|
||||
|
@ -16,7 +16,6 @@
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
if !TRIGGERS_ONLY
|
||||
sbin_PROGRAMS += ostree-switch-root
|
||||
if BUILDOPT_DRACUT
|
||||
sbin_PROGRAMS += ostree-prepare-root
|
||||
sbin_PROGRAMS += ostree-remount
|
||||
@ -33,10 +32,6 @@ ostree_prepare_root_SOURCES = src/switchroot/ostree-prepare-root.c
|
||||
ostree_prepare_root_LDADD = libswitchroot-mountutil.la
|
||||
ostree_prepare_root_CFLAGS = $(AM_CFLAGS) -Isrc/switchroot
|
||||
|
||||
ostree_switch_root_SOURCES = src/switchroot/ostree-switch-root.c
|
||||
ostree_switch_root_LDADD = libswitchroot-mountutil.la
|
||||
ostree_switch_root_CFLAGS = $(AM_CFLAGS) -Isrc/switchroot
|
||||
|
||||
ostree_remount_SOURCES = src/switchroot/ostree-remount.c
|
||||
ostree_remount_LDADD = libswitchroot-mountutil.la
|
||||
ostree_remount_CFLAGS = $(AM_CFLAGS) -Isrc/switchroot
|
||||
|
@ -28,6 +28,7 @@ testfiles = t0000-basic \
|
||||
t0005-corruption \
|
||||
t0006-libarchive \
|
||||
t0011-pull-archive-z \
|
||||
t0015-admin-deploy \
|
||||
$(NULL)
|
||||
insttest_SCRIPTS = $(addprefix tests/,$(testfiles:=.sh))
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2012 Colin Walters <walters@verbum.org>
|
||||
* Copyright (C) 2012,2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -24,701 +24,109 @@
|
||||
|
||||
#include "ot-admin-builtins.h"
|
||||
#include "ot-admin-functions.h"
|
||||
#include "ot-admin-deploy.h"
|
||||
#include "ot-ordered-hash.h"
|
||||
#include "ostree.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
typedef struct {
|
||||
OstreeRepo *repo;
|
||||
OtAdminBuiltinOpts *admin_opts;
|
||||
GFile *ostree_dir;
|
||||
char *osname;
|
||||
GFile *osname_dir;
|
||||
|
||||
char *current_deployment_ref;
|
||||
char *previous_deployment_ref;
|
||||
char *resolved_commit;
|
||||
char *resolved_previous_commit;
|
||||
|
||||
char *previous_deployment_revision;
|
||||
GFile *deploy_target_path;
|
||||
GFile *previous_deployment;
|
||||
} OtAdminDeploy;
|
||||
|
||||
static gboolean opt_no_kernel;
|
||||
static gboolean opt_force;
|
||||
static gboolean opt_no_bootloader;
|
||||
static gboolean opt_retain;
|
||||
static char **opt_kernel_argv;
|
||||
static char *opt_osname;
|
||||
static char *opt_origin_path;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "no-kernel", 0, 0, G_OPTION_ARG_NONE, &opt_no_kernel, "Don't update kernel related config (initramfs, bootloader)", NULL },
|
||||
{ "force", 0, 0, G_OPTION_ARG_NONE, &opt_force, "Overwrite any existing deployment", NULL },
|
||||
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Specify operating system root to use", NULL },
|
||||
{ "origin-file", 0, 0, G_OPTION_ARG_FILENAME, &opt_origin_path, "Specify origin file", NULL },
|
||||
{ "no-bootloader", 0, 0, G_OPTION_ARG_NONE, &opt_no_bootloader, "Don't update bootloader", NULL },
|
||||
{ "retain", 0, 0, G_OPTION_ARG_NONE, &opt_retain, "Do not delete previous deployment", NULL },
|
||||
{ "karg", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv, "Set kernel argument, like --karg=root=/dev/sda1", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/**
|
||||
* update_current:
|
||||
*
|
||||
* Atomically swap the /ostree/current symbolic link to point to a new
|
||||
* path. If successful, the old current will be saved as
|
||||
* /ostree/previous, and /ostree/current-etc will be a link to the
|
||||
* current /etc subdirectory.
|
||||
*
|
||||
* Unless the new-current equals current, in which case, do nothing.
|
||||
*/
|
||||
static gboolean
|
||||
update_current (OtAdminDeploy *self,
|
||||
GFile *current_deployment,
|
||||
GFile *deploy_target,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
ot_lobj GFile *current_path = NULL;
|
||||
ot_lobj GFile *current_etc_path = NULL;
|
||||
ot_lobj GFile *previous_path = NULL;
|
||||
ot_lobj GFile *tmp_current_path = NULL;
|
||||
ot_lobj GFile *tmp_current_etc_path = NULL;
|
||||
ot_lobj GFile *tmp_previous_path = NULL;
|
||||
ot_lobj GFileInfo *previous_info = NULL;
|
||||
ot_lfree char *relative_current = NULL;
|
||||
ot_lfree char *relative_current_etc = NULL;
|
||||
ot_lfree char *relative_previous = NULL;
|
||||
|
||||
current_path = g_file_get_child (self->osname_dir, "current");
|
||||
current_etc_path = g_file_get_child (self->osname_dir, "current-etc");
|
||||
previous_path = g_file_get_child (self->osname_dir, "previous");
|
||||
|
||||
relative_current = g_file_get_relative_path (self->osname_dir, deploy_target);
|
||||
g_assert (relative_current);
|
||||
relative_current_etc = g_strconcat (relative_current, "-etc", NULL);
|
||||
|
||||
if (current_deployment)
|
||||
{
|
||||
ot_lfree char *relative_previous = NULL;
|
||||
|
||||
if (g_file_equal (current_deployment, deploy_target))
|
||||
{
|
||||
g_print ("ostadmin: %s already points to %s\n", gs_file_get_path_cached (current_path),
|
||||
relative_current);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
tmp_previous_path = g_file_get_child (self->osname_dir, "tmp-previous");
|
||||
(void) gs_file_unlink (tmp_previous_path, NULL, NULL);
|
||||
|
||||
relative_previous = g_file_get_relative_path (self->osname_dir, current_deployment);
|
||||
g_assert (relative_previous);
|
||||
if (symlink (relative_previous, gs_file_get_path_cached (tmp_previous_path)) < 0)
|
||||
{
|
||||
ot_util_set_error_from_errno (error, errno);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
tmp_current_path = g_file_get_child (self->osname_dir, "tmp-current");
|
||||
(void) gs_file_unlink (tmp_current_path, NULL, NULL);
|
||||
|
||||
if (symlink (relative_current, gs_file_get_path_cached (tmp_current_path)) < 0)
|
||||
{
|
||||
ot_util_set_error_from_errno (error, errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp_current_etc_path = g_file_get_child (self->osname_dir, "tmp-current-etc");
|
||||
(void) gs_file_unlink (tmp_current_etc_path, NULL, NULL);
|
||||
if (symlink (relative_current_etc, gs_file_get_path_cached (tmp_current_etc_path)) < 0)
|
||||
{
|
||||
ot_util_set_error_from_errno (error, errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gs_file_rename (tmp_current_path, current_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (!gs_file_rename (tmp_current_etc_path, current_etc_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (tmp_previous_path)
|
||||
{
|
||||
if (!gs_file_rename (tmp_previous_path, previous_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_print ("ostadmin: %s set to %s\n", gs_file_get_path_cached (current_path),
|
||||
relative_current);
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GError **error;
|
||||
gboolean caught_error;
|
||||
|
||||
GMainLoop *loop;
|
||||
} ProcessOneCheckoutData;
|
||||
|
||||
static void
|
||||
on_checkout_complete (GObject *object,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
ProcessOneCheckoutData *data = user_data;
|
||||
GError *local_error = NULL;
|
||||
|
||||
if (!ostree_repo_checkout_tree_finish ((OstreeRepo*)object, result,
|
||||
&local_error))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (local_error)
|
||||
{
|
||||
data->caught_error = TRUE;
|
||||
g_propagate_error (data->error, local_error);
|
||||
}
|
||||
g_main_loop_quit (data->loop);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ensure_unlinked:
|
||||
*
|
||||
* Like gs_file_unlink(), but return successfully if the file doesn't
|
||||
* exist.
|
||||
*/
|
||||
static gboolean
|
||||
ensure_unlinked (GFile *path,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GError *temp_error = NULL;
|
||||
|
||||
if (!gs_file_unlink (path, cancellable, &temp_error))
|
||||
{
|
||||
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_clear_error (&temp_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, temp_error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy_one_config_file:
|
||||
*
|
||||
* Copy @file from @modified_etc to @new_etc, overwriting any existing
|
||||
* file there.
|
||||
*/
|
||||
static gboolean
|
||||
copy_one_config_file (OtAdminDeploy *self,
|
||||
GFile *orig_etc,
|
||||
GFile *modified_etc,
|
||||
GFile *new_etc,
|
||||
GFile *src,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
ot_lobj GFileInfo *src_info = NULL;
|
||||
ot_lobj GFile *dest = NULL;
|
||||
ot_lobj GFile *parent = NULL;
|
||||
ot_lfree char *relative_path = NULL;
|
||||
|
||||
relative_path = g_file_get_relative_path (modified_etc, src);
|
||||
g_assert (relative_path);
|
||||
dest = g_file_resolve_relative_path (new_etc, relative_path);
|
||||
|
||||
src_info = g_file_query_info (src, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!src_info)
|
||||
goto out;
|
||||
|
||||
if (g_file_info_get_file_type (src_info) == G_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
ot_lobj GFileEnumerator *src_enum = NULL;
|
||||
ot_lobj GFileInfo *child_info = NULL;
|
||||
GError *temp_error = NULL;
|
||||
|
||||
/* FIXME actually we need to copy permissions and xattrs */
|
||||
if (!gs_file_ensure_directory (dest, TRUE, cancellable, error))
|
||||
goto out;
|
||||
|
||||
src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
|
||||
while ((child_info = g_file_enumerator_next_file (src_enum, cancellable, error)) != NULL)
|
||||
{
|
||||
ot_lobj GFile *child = g_file_get_child (src, g_file_info_get_name (child_info));
|
||||
|
||||
if (!copy_one_config_file (self, orig_etc, modified_etc, new_etc, child,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
g_clear_object (&child_info);
|
||||
if (temp_error != NULL)
|
||||
{
|
||||
g_propagate_error (error, temp_error);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent = g_file_get_parent (dest);
|
||||
|
||||
/* FIXME actually we need to copy permissions and xattrs */
|
||||
if (!gs_file_ensure_directory (parent, TRUE, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!g_file_copy (src, dest, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
|
||||
cancellable, NULL, NULL, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* merge_etc_changes:
|
||||
*
|
||||
* Compute the difference between @orig_etc and @modified_etc,
|
||||
* and apply that to @new_etc.
|
||||
*
|
||||
* The algorithm for computing the difference is pretty simple; it's
|
||||
* approximately equivalent to "diff -unR orig_etc modified_etc",
|
||||
* except that rather than attempting a 3-way merge if a file is also
|
||||
* changed in @new_etc, the modified version always wins.
|
||||
*/
|
||||
static gboolean
|
||||
merge_etc_changes (OtAdminDeploy *self,
|
||||
GFile *orig_etc,
|
||||
GFile *modified_etc,
|
||||
GFile *new_etc,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
ot_lobj GFile *ostree_etc = NULL;
|
||||
ot_lobj GFile *tmp_etc = NULL;
|
||||
ot_lptrarray GPtrArray *modified = NULL;
|
||||
ot_lptrarray GPtrArray *removed = NULL;
|
||||
ot_lptrarray GPtrArray *added = NULL;
|
||||
guint i;
|
||||
|
||||
modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
|
||||
removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
||||
added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
||||
|
||||
if (!ostree_diff_dirs (orig_etc, modified_etc, modified, removed, added,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "While computing configuration diff: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (modified->len > 0 || removed->len > 0 || added->len > 0)
|
||||
g_print ("ostadmin: Processing config: %u modified, %u removed, %u added\n",
|
||||
modified->len,
|
||||
removed->len,
|
||||
added->len);
|
||||
else
|
||||
g_print ("ostadmin: No modified configuration\n");
|
||||
|
||||
for (i = 0; i < removed->len; i++)
|
||||
{
|
||||
GFile *file = removed->pdata[i];
|
||||
ot_lobj GFile *target_file = NULL;
|
||||
ot_lfree char *path = NULL;
|
||||
|
||||
path = g_file_get_relative_path (orig_etc, file);
|
||||
g_assert (path);
|
||||
target_file = g_file_resolve_relative_path (new_etc, path);
|
||||
|
||||
if (!ensure_unlinked (target_file, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < modified->len; i++)
|
||||
{
|
||||
OstreeDiffItem *diff = modified->pdata[i];
|
||||
|
||||
if (!copy_one_config_file (self, orig_etc, modified_etc, new_etc, diff->target,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < added->len; i++)
|
||||
{
|
||||
GFile *file = added->pdata[i];
|
||||
|
||||
if (!copy_one_config_file (self, orig_etc, modified_etc, new_etc, file,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* deploy_tree:
|
||||
*
|
||||
* Look up @revision in the repository, and check it out in
|
||||
* OSTREE_DIR/deploy/OS/DEPLOY_TARGET.
|
||||
*
|
||||
* Merge configuration changes from the old deployment, if any.
|
||||
*
|
||||
* Update the OSTREE_DIR/current{,-etc} and OSTREE_DIR/previous symbolic
|
||||
* links.
|
||||
*/
|
||||
static gboolean
|
||||
deploy_tree (OtAdminDeploy *self,
|
||||
const char *deploy_target,
|
||||
const char *revision,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
ot_lfree char *deploy_target_fullname = NULL;
|
||||
ot_lfree char *deploy_target_fullname_tmp = NULL;
|
||||
ot_lobj GFile *deploy_target_path_tmp = NULL;
|
||||
ot_lfree char *deploy_target_etc_name = NULL;
|
||||
ot_lobj GFile *deploy_target_etc_path = NULL;
|
||||
ot_lobj GFile *deploy_target_default_etc_path = NULL;
|
||||
ot_lobj GFile *deploy_parent = NULL;
|
||||
ot_lobj GFile *previous_deployment_etc = NULL;
|
||||
ot_lobj GFile *previous_deployment_etc_default = NULL;
|
||||
ot_lobj OstreeRepoFile *root = NULL;
|
||||
ot_lobj GFileInfo *file_info = NULL;
|
||||
ot_lobj GFileInfo *existing_checkout_info = NULL;
|
||||
ot_lfree char *checkout_target_name = NULL;
|
||||
ot_lfree char *checkout_target_tmp_name = NULL;
|
||||
GError *temp_error = NULL;
|
||||
gboolean skip_checkout;
|
||||
|
||||
if (!revision)
|
||||
revision = deploy_target;
|
||||
|
||||
if (!g_file_query_exists (self->osname_dir, cancellable))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No OS \"%s\" found in \"%s\"", self->osname,
|
||||
gs_file_get_path_cached (self->osname_dir));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ostree_repo_resolve_rev (self->repo, revision, FALSE, &self->resolved_commit, error))
|
||||
goto out;
|
||||
if (!ostree_repo_resolve_rev (self->repo, revision, TRUE, &self->resolved_previous_commit, error))
|
||||
goto out;
|
||||
|
||||
root = (OstreeRepoFile*)ostree_repo_file_new_root (self->repo, self->resolved_commit);
|
||||
if (!ostree_repo_file_ensure_resolved (root, error))
|
||||
goto out;
|
||||
|
||||
file_info = g_file_query_info ((GFile*)root, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!file_info)
|
||||
goto out;
|
||||
|
||||
deploy_target_fullname = g_strconcat (deploy_target, "-", self->resolved_commit, NULL);
|
||||
self->deploy_target_path = g_file_resolve_relative_path (self->osname_dir, deploy_target_fullname);
|
||||
|
||||
deploy_target_fullname_tmp = g_strconcat (deploy_target_fullname, ".tmp", NULL);
|
||||
deploy_target_path_tmp = g_file_resolve_relative_path (self->osname_dir, deploy_target_fullname_tmp);
|
||||
|
||||
deploy_parent = g_file_get_parent (self->deploy_target_path);
|
||||
if (!gs_file_ensure_directory (deploy_parent, TRUE, cancellable, error))
|
||||
goto out;
|
||||
|
||||
deploy_target_etc_name = g_strconcat (deploy_target, "-", self->resolved_commit, "-etc", NULL);
|
||||
deploy_target_etc_path = g_file_resolve_relative_path (self->osname_dir, deploy_target_etc_name);
|
||||
|
||||
/* Delete any previous temporary data */
|
||||
if (!gs_shutil_rm_rf (deploy_target_path_tmp, cancellable, error))
|
||||
goto out;
|
||||
|
||||
existing_checkout_info = g_file_query_info (self->deploy_target_path, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, &temp_error);
|
||||
if (existing_checkout_info)
|
||||
{
|
||||
if (opt_force)
|
||||
{
|
||||
if (!gs_shutil_rm_rf (self->deploy_target_path, cancellable, error))
|
||||
goto out;
|
||||
if (!gs_shutil_rm_rf (deploy_target_etc_path, cancellable, error))
|
||||
goto out;
|
||||
|
||||
skip_checkout = FALSE;
|
||||
}
|
||||
else
|
||||
skip_checkout = TRUE;
|
||||
}
|
||||
else if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
{
|
||||
g_clear_error (&temp_error);
|
||||
skip_checkout = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, temp_error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ot_admin_get_current_deployment (self->ostree_dir, self->osname, &self->previous_deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (self->previous_deployment)
|
||||
{
|
||||
ot_lfree char *etc_name;
|
||||
ot_lobj GFile *parent;
|
||||
|
||||
etc_name = g_strconcat (gs_file_get_basename_cached (self->previous_deployment), "-etc", NULL);
|
||||
parent = g_file_get_parent (self->previous_deployment);
|
||||
|
||||
previous_deployment_etc = g_file_get_child (parent, etc_name);
|
||||
|
||||
if (!g_file_query_exists (previous_deployment_etc, cancellable)
|
||||
|| g_file_equal (self->previous_deployment, self->deploy_target_path))
|
||||
g_clear_object (&previous_deployment_etc);
|
||||
else
|
||||
previous_deployment_etc_default = g_file_get_child (self->previous_deployment, "etc");
|
||||
|
||||
if (!ostree_repo_resolve_rev (self->repo, self->current_deployment_ref, TRUE,
|
||||
&self->previous_deployment_revision, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
if (!skip_checkout)
|
||||
{
|
||||
ProcessOneCheckoutData checkout_data;
|
||||
ot_lobj GFile *triggers_run_path = NULL;
|
||||
gs_unref_object GFile *usr_etc_path = NULL;
|
||||
|
||||
g_print ("ostadmin: Creating deployment %s\n",
|
||||
gs_file_get_path_cached (self->deploy_target_path));
|
||||
|
||||
memset (&checkout_data, 0, sizeof (checkout_data));
|
||||
checkout_data.loop = g_main_loop_new (NULL, TRUE);
|
||||
checkout_data.error = error;
|
||||
|
||||
ostree_repo_checkout_tree_async (self->repo, 0, 0, deploy_target_path_tmp, root,
|
||||
file_info, cancellable,
|
||||
on_checkout_complete, &checkout_data);
|
||||
|
||||
g_main_loop_run (checkout_data.loop);
|
||||
|
||||
g_main_loop_unref (checkout_data.loop);
|
||||
|
||||
if (checkout_data.caught_error)
|
||||
goto out;
|
||||
|
||||
usr_etc_path = g_file_resolve_relative_path (deploy_target_path_tmp, "usr/etc");
|
||||
if (g_file_query_exists (usr_etc_path, NULL))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Error: This tree contains usr/etc; it is likely an OS in version 2.0 format, and this version of OSTree does not support it");
|
||||
goto out;
|
||||
}
|
||||
|
||||
triggers_run_path = g_file_resolve_relative_path (deploy_target_path_tmp, "usr/share/ostree/triggers-run");
|
||||
|
||||
if (!g_file_query_exists (triggers_run_path, NULL))
|
||||
{
|
||||
if (!ostree_run_triggers_in_root (deploy_target_path_tmp, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
deploy_target_default_etc_path = ot_gfile_get_child_strconcat (deploy_target_path_tmp, "etc", NULL);
|
||||
|
||||
if (!gs_shutil_rm_rf (deploy_target_etc_path, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!gs_shutil_cp_a (deploy_target_default_etc_path, deploy_target_etc_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deploy_target_etc_path));
|
||||
|
||||
if (previous_deployment_etc)
|
||||
{
|
||||
if (!merge_etc_changes (self, previous_deployment_etc_default,
|
||||
previous_deployment_etc, deploy_target_etc_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
g_print ("ostadmin: No previous deployment; therefore, no configuration changes to merge\n");
|
||||
|
||||
if (!gs_file_rename (deploy_target_path_tmp, self->deploy_target_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* do_update_kernel:
|
||||
*
|
||||
* Ensure we have a GRUB entry, initramfs set up, etc.
|
||||
*/
|
||||
static gboolean
|
||||
do_update_kernel (OtAdminDeploy *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_object GSSubprocess *proc = NULL;
|
||||
gs_unref_ptrarray GPtrArray *args = NULL;
|
||||
|
||||
args = g_ptr_array_new ();
|
||||
ot_ptrarray_add_many (args, "ostree", "admin",
|
||||
"--ostree-dir", gs_file_get_path_cached (self->ostree_dir),
|
||||
"--boot-dir", gs_file_get_path_cached (self->admin_opts->boot_dir),
|
||||
"update-kernel",
|
||||
self->osname,
|
||||
gs_file_get_path_cached (self->deploy_target_path), NULL);
|
||||
g_ptr_array_add (args, NULL);
|
||||
|
||||
proc = gs_subprocess_new_simple_argv ((char**)args->pdata,
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error);
|
||||
if (!proc)
|
||||
goto out;
|
||||
if (!gs_subprocess_wait_sync_check (proc, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
complete_deployment (OtAdminDeploy *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
/* Write out a ref so that any "ostree prune" on the raw repo
|
||||
* doesn't GC the currently deployed tree.
|
||||
*/
|
||||
if (!ostree_repo_write_ref (self->repo, NULL, self->current_deployment_ref,
|
||||
self->resolved_commit, error))
|
||||
goto out;
|
||||
/* Only overwrite previous if it's different from what we're deploying now.
|
||||
*/
|
||||
if (self->resolved_previous_commit != NULL
|
||||
&& strcmp (self->resolved_previous_commit, self->resolved_commit) != 0)
|
||||
{
|
||||
if (!ostree_repo_write_ref (self->repo, NULL, self->previous_deployment_ref,
|
||||
self->previous_deployment_revision, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!update_current (self, self->previous_deployment, self->deploy_target_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error)
|
||||
{
|
||||
GOptionContext *context;
|
||||
OtAdminDeploy self_data;
|
||||
OtAdminDeploy *self = &self_data;
|
||||
gboolean ret = FALSE;
|
||||
ot_lobj GFile *repo_path = NULL;
|
||||
ot_lobj GFile *deploy_path = NULL;
|
||||
const char *osname = NULL;
|
||||
const char *deploy_target = NULL;
|
||||
const char *revision = NULL;
|
||||
__attribute__((unused)) GCancellable *cancellable = NULL;
|
||||
const char *refspec;
|
||||
GOptionContext *context;
|
||||
GFile *sysroot = admin_opts->sysroot;
|
||||
GKeyFile *origin = NULL;
|
||||
int current_bootversion;
|
||||
int new_bootversion;
|
||||
gs_unref_object OstreeRepo *repo = NULL;
|
||||
gs_unref_ptrarray GPtrArray *current_deployments = NULL;
|
||||
gs_unref_ptrarray GPtrArray *new_deployments = NULL;
|
||||
gs_unref_object OtDeployment *new_deployment = NULL;
|
||||
gs_unref_object OtDeployment *booted_deployment = NULL;
|
||||
gs_free char *revision = NULL;
|
||||
|
||||
memset (self, 0, sizeof (*self));
|
||||
|
||||
context = g_option_context_new ("OSNAME TREENAME [REVISION] - In operating system OS, check out revision TREENAME (or REVISION as TREENAME)");
|
||||
context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment");
|
||||
|
||||
g_option_context_add_main_entries (context, options, NULL);
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, error))
|
||||
goto out;
|
||||
|
||||
if (argc < 3)
|
||||
if (argc < 2)
|
||||
{
|
||||
ot_util_usage_error (context, "OSNAME and TREENAME must be specified", error);
|
||||
ot_util_usage_error (context, "REF/REV must be specified", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
self->admin_opts = admin_opts;
|
||||
self->ostree_dir = g_object_ref (admin_opts->ostree_dir);
|
||||
refspec = argv[1];
|
||||
|
||||
if (!ot_admin_ensure_initialized (self->ostree_dir, cancellable, error))
|
||||
if (!ot_admin_get_repo (sysroot, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
repo_path = g_file_get_child (self->ostree_dir, "repo");
|
||||
self->repo = ostree_repo_new (repo_path);
|
||||
if (!ostree_repo_check (self->repo, error))
|
||||
goto out;
|
||||
|
||||
osname = argv[1];
|
||||
deploy_target = argv[2];
|
||||
if (argc > 3)
|
||||
revision = argv[3];
|
||||
|
||||
self->osname = g_strdup (osname);
|
||||
self->osname_dir = ot_gfile_get_child_build_path (self->ostree_dir, "deploy", osname, NULL);
|
||||
self->current_deployment_ref = g_strdup_printf ("deployment/%s/current", self->osname);
|
||||
self->previous_deployment_ref = g_strdup_printf ("deployment/%s/previous", self->osname);
|
||||
|
||||
if (!deploy_tree (self, deploy_target, revision, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!opt_no_kernel)
|
||||
if (!ot_admin_list_deployments (sysroot, ¤t_bootversion, ¤t_deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
if (!do_update_kernel (self, cancellable, error))
|
||||
goto out;
|
||||
g_prefix_error (error, "While listing deployments: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!complete_deployment (self, cancellable, error))
|
||||
/* Find the currently booted deployment, if any; we will ensure it
|
||||
* is present in the new deployment list.
|
||||
*/
|
||||
if (!ot_admin_require_deployment_or_osname (sysroot, current_deployments,
|
||||
opt_osname,
|
||||
&booted_deployment,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Looking for booted deployment: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opt_origin_path)
|
||||
{
|
||||
origin = g_key_file_new ();
|
||||
|
||||
if (!g_key_file_load_from_file (origin, opt_origin_path, 0, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = ot_origin_new_from_refspec (refspec);
|
||||
}
|
||||
|
||||
if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_admin_deploy (sysroot, current_bootversion, current_deployments,
|
||||
opt_osname, revision, origin,
|
||||
opt_kernel_argv, opt_retain,
|
||||
booted_deployment, NULL,
|
||||
&new_deployment, &new_bootversion, &new_deployments,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_clear_object (&self->repo);
|
||||
g_free (self->osname);
|
||||
g_free (self->current_deployment_ref);
|
||||
g_free (self->previous_deployment_ref);
|
||||
g_free (self->resolved_commit);
|
||||
g_free (self->resolved_previous_commit);
|
||||
g_free (self->previous_deployment_revision);
|
||||
g_clear_object (&self->previous_deployment);
|
||||
g_clear_object (&self->ostree_dir);
|
||||
g_clear_object (&self->osname_dir);
|
||||
if (origin)
|
||||
g_key_file_unref (origin);
|
||||
if (context)
|
||||
g_option_context_free (context);
|
||||
return ret;
|
||||
|
@ -28,7 +28,10 @@
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static char *opt_osname;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Specify operating system root to use", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -37,57 +40,55 @@ ot_admin_builtin_diff (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GE
|
||||
{
|
||||
GOptionContext *context;
|
||||
gboolean ret = FALSE;
|
||||
const char *osname;
|
||||
GFile *ostree_dir = admin_opts->ostree_dir;
|
||||
gs_free char *booted_osname = NULL;
|
||||
ot_lobj GFile *repo_path = NULL;
|
||||
ot_lobj GFile *deployment = NULL;
|
||||
gs_unref_object OtDeployment *deployment = NULL;
|
||||
gs_unref_object GFile *deployment_dir = NULL;
|
||||
ot_lobj GFile *deploy_parent = NULL;
|
||||
ot_lptrarray GPtrArray *modified = NULL;
|
||||
ot_lptrarray GPtrArray *removed = NULL;
|
||||
ot_lptrarray GPtrArray *added = NULL;
|
||||
gs_unref_ptrarray GPtrArray *deployments = NULL;
|
||||
ot_lobj GFile *orig_etc_path = NULL;
|
||||
ot_lobj GFile *new_etc_path = NULL;
|
||||
__attribute__((unused)) GCancellable *cancellable = NULL;
|
||||
int bootversion;
|
||||
|
||||
context = g_option_context_new ("OSNAME [REVISION] - Diff configuration for OSNAME");
|
||||
context = g_option_context_new ("Diff current /etc configuration versus default");
|
||||
|
||||
g_option_context_add_main_entries (context, options, NULL);
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, error))
|
||||
goto out;
|
||||
|
||||
repo_path = g_file_get_child (ostree_dir, "repo");
|
||||
repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
|
||||
|
||||
if (argc < 2)
|
||||
if (!ot_admin_list_deployments (admin_opts->sysroot, &bootversion, &deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
ot_util_usage_error (context, "OSNAME must be specified", error);
|
||||
g_prefix_error (error, "While listing deployments: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
osname = argv[1];
|
||||
|
||||
if (argc > 2)
|
||||
if (!ot_admin_require_deployment_or_osname (admin_opts->sysroot, deployments,
|
||||
opt_osname, &deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (deployment != NULL)
|
||||
opt_osname = (char*)ot_deployment_get_osname (deployment);
|
||||
if (deployment == NULL)
|
||||
deployment = ot_admin_get_merge_deployment (deployments, opt_osname, deployment, NULL);
|
||||
if (deployment == NULL)
|
||||
{
|
||||
deployment = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, argv[2], NULL);
|
||||
if (!g_file_query_exists (deployment, NULL))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Deployment %s doesn't exist", gs_file_get_path_cached (deployment));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ot_admin_get_current_deployment (ostree_dir, osname, &deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||
"No deployment for OS '%s'", opt_osname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
orig_etc_path = g_file_resolve_relative_path (deployment, "etc");
|
||||
deploy_parent = g_file_get_parent (deployment);
|
||||
new_etc_path = ot_gfile_get_child_strconcat (deploy_parent,
|
||||
gs_file_get_basename_cached (deployment),
|
||||
"-etc", NULL);
|
||||
deployment_dir = ot_admin_get_deployment_directory (admin_opts->sysroot, deployment);
|
||||
|
||||
orig_etc_path = g_file_resolve_relative_path (deployment_dir, "usr/etc");
|
||||
new_etc_path = g_file_resolve_relative_path (deployment_dir, "etc");
|
||||
|
||||
modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
|
||||
removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
|
||||
|
@ -75,8 +75,7 @@ ot_admin_builtin_init_fs (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
goto out;
|
||||
g_clear_object (&child);
|
||||
|
||||
child = g_file_get_child (dir, "ostree");
|
||||
if (!ot_admin_ensure_initialized (child, cancellable, error))
|
||||
if (!ot_admin_ensure_initialized (dir, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
|
@ -70,7 +70,6 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
gboolean ret = FALSE;
|
||||
const char *keyfile_arg = NULL;
|
||||
const char *treename_arg = NULL;
|
||||
GFile *ostree_dir = admin_opts->ostree_dir;
|
||||
ot_lobj GFile *deploy_dir = NULL;
|
||||
ot_lobj GFile *osdir = NULL;
|
||||
ot_lobj GFile *dest_osconfig_path = NULL;
|
||||
@ -96,14 +95,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (admin_opts->ostree_dir == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No existing /ostree found; use --ostree-dir");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ot_admin_ensure_initialized (admin_opts->ostree_dir, cancellable, error))
|
||||
if (!ot_admin_ensure_initialized (admin_opts->sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
self->loop = g_main_loop_new (NULL, TRUE);
|
||||
@ -136,11 +128,11 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
|
||||
osname = g_key_file_get_string (keyfile, "os", "Name", error);
|
||||
|
||||
ostree_dir_arg = g_strconcat ("--ostree-dir=",
|
||||
gs_file_get_path_cached (ostree_dir),
|
||||
ostree_dir_arg = g_strconcat ("--sysroot=",
|
||||
gs_file_get_path_cached (admin_opts->sysroot),
|
||||
NULL);
|
||||
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
if (!gs_subprocess_simple_run_sync (NULL,
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", "admin", ostree_dir_arg, "os-init", osname, NULL))
|
||||
@ -157,7 +149,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
osdir = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, NULL);
|
||||
osdir = ot_gfile_get_child_build_path (admin_opts->sysroot, "ostree", "deploy", osname, NULL);
|
||||
dest_osconfig_path = ot_gfile_get_child_strconcat (osdir, osname, ".cfg", NULL);
|
||||
|
||||
if (!g_file_copy (self->osconfig_path, dest_osconfig_path, G_FILE_COPY_OVERWRITE | G_FILE_COPY_TARGET_DEFAULT_PERMS,
|
||||
@ -168,7 +160,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
goto out;
|
||||
|
||||
repoarg = g_strconcat ("--repo=",
|
||||
gs_file_get_path_cached (ostree_dir), "/repo",
|
||||
gs_file_get_path_cached (admin_opts->sysroot), "/ostree/repo",
|
||||
NULL);
|
||||
|
||||
{
|
||||
@ -178,7 +170,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
if (!repourl)
|
||||
goto out;
|
||||
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
if (!gs_subprocess_simple_run_sync (NULL,
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", repoarg, "remote", "add",
|
||||
@ -186,13 +178,13 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
if (!gs_subprocess_simple_run_sync (NULL,
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", "pull", repoarg, osname, NULL))
|
||||
goto out;
|
||||
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
if (!gs_subprocess_simple_run_sync (NULL,
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", "admin", ostree_dir_arg, "deploy", osname,
|
||||
|
@ -38,7 +38,6 @@ ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
GOptionContext *context;
|
||||
gboolean ret = FALSE;
|
||||
const char *osname = NULL;
|
||||
GFile *ostree_dir = admin_opts->ostree_dir;
|
||||
ot_lobj GFile *deploy_dir = NULL;
|
||||
ot_lobj GFile *dir = NULL;
|
||||
__attribute__((unused)) GCancellable *cancellable = NULL;
|
||||
@ -49,7 +48,7 @@ ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
if (!g_option_context_parse (context, &argc, &argv, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_admin_ensure_initialized (ostree_dir, cancellable, error))
|
||||
if (!ot_admin_ensure_initialized (admin_opts->sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (argc < 2)
|
||||
@ -60,7 +59,7 @@ ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
||||
|
||||
osname = argv[1];
|
||||
|
||||
deploy_dir = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, NULL);
|
||||
deploy_dir = ot_gfile_get_child_build_path (admin_opts->sysroot, "ostree", "deploy", osname, NULL);
|
||||
|
||||
/* Ensure core subdirectories of /var exist, since we need them for
|
||||
* dracut generation, and the host will want them too. Note that at
|
||||
|
@ -41,15 +41,12 @@ ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, G
|
||||
{
|
||||
GOptionContext *context;
|
||||
gboolean ret = FALSE;
|
||||
guint i;
|
||||
const char *osname;
|
||||
GFile *ostree_dir = admin_opts->ostree_dir;
|
||||
ot_lobj GFile *repo_path = NULL;
|
||||
ot_lobj GFile *deploy_dir = NULL;
|
||||
ot_lobj GFile *current_deployment = NULL;
|
||||
ot_lobj GFile *previous_deployment = NULL;
|
||||
ot_lobj GFile *active_deployment = NULL;
|
||||
ot_lptrarray GPtrArray *deployments = NULL;
|
||||
gs_free char *active_osname = NULL;
|
||||
__attribute__((unused)) GCancellable *cancellable = NULL;
|
||||
|
||||
@ -68,43 +65,10 @@ ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, G
|
||||
|
||||
osname = argv[1];
|
||||
|
||||
if (!ot_admin_list_deployments (ostree_dir, osname, &deployments,
|
||||
cancellable, error))
|
||||
if (!ot_admin_cleanup (admin_opts->sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_admin_get_current_deployment (ostree_dir, osname, ¤t_deployment,
|
||||
cancellable, error));
|
||||
if (!ot_admin_get_previous_deployment (ostree_dir, osname, &previous_deployment,
|
||||
cancellable, error));
|
||||
if (!ot_admin_get_active_deployment (ostree_dir, &active_osname, &active_deployment,
|
||||
cancellable, error));
|
||||
|
||||
for (i = 0; i < deployments->len; i++)
|
||||
{
|
||||
GFile *deployment = deployments->pdata[i];
|
||||
ot_lobj GFile *deployment_etc = NULL;
|
||||
ot_lobj GFile *parent = NULL;
|
||||
|
||||
if ((current_deployment && g_file_equal (deployment, current_deployment))
|
||||
|| (previous_deployment && g_file_equal (deployment, previous_deployment))
|
||||
|| (active_deployment && g_file_equal (deployment, active_deployment)))
|
||||
continue;
|
||||
|
||||
parent = g_file_get_parent (deployment);
|
||||
deployment_etc = ot_gfile_get_child_strconcat (parent, gs_file_get_basename_cached (deployment),
|
||||
"-etc", NULL);
|
||||
|
||||
g_print ("Deleting deployment %s\n", gs_file_get_path_cached (deployment));
|
||||
if (!gs_shutil_rm_rf (deployment, cancellable, error))
|
||||
goto out;
|
||||
/* Note - not atomic; we may be leaving the -etc directory around
|
||||
* if this fails in the middle =/
|
||||
*/
|
||||
if (!gs_shutil_rm_rf (deployment_etc, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
repo_path = g_file_get_child (ostree_dir, "repo");
|
||||
repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
|
||||
|
||||
if (!opt_no_repo_prune)
|
||||
{
|
||||
@ -112,7 +76,7 @@ ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, G
|
||||
|
||||
repo_arg = g_strconcat ("--repo=", gs_file_get_path_cached (repo_path), NULL);
|
||||
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
if (!gs_subprocess_simple_run_sync (NULL,
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", repo_arg, "prune", "--refs-only",
|
||||
|
@ -1,178 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2012 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Colin Walters <walters@verbum.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ot-admin-builtins.h"
|
||||
#include "ot-admin-functions.h"
|
||||
#include "ostree.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static gboolean opt_no_kernel;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "no-kernel", 0, 0, G_OPTION_ARG_NONE, &opt_no_kernel, "Don't update kernel related config (initramfs, bootloader)", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gboolean
|
||||
ensure_remote_branch (OstreeRepo *repo,
|
||||
const char *remote,
|
||||
const char *branch,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gchar **iter = NULL;
|
||||
gsize len;
|
||||
gs_free char *remote_key = NULL;
|
||||
gs_unref_ptrarray GPtrArray *new_branches = NULL;
|
||||
GKeyFile *config = NULL;
|
||||
gchar **branches = NULL;
|
||||
gboolean have_branch = FALSE;
|
||||
|
||||
config = ostree_repo_copy_config (repo);
|
||||
remote_key = g_strdup_printf ("remote \"%s\"", remote);
|
||||
|
||||
new_branches = g_ptr_array_new ();
|
||||
|
||||
branches = g_key_file_get_string_list (config, remote_key, "branches", &len, error);
|
||||
if (!branches)
|
||||
goto out;
|
||||
|
||||
for (iter = branches; *iter; iter++)
|
||||
{
|
||||
char *item = *iter;
|
||||
if (!have_branch)
|
||||
have_branch = strcmp (item, branch) == 0;
|
||||
g_ptr_array_add (new_branches, item);
|
||||
}
|
||||
|
||||
if (!have_branch)
|
||||
{
|
||||
g_ptr_array_add (new_branches, (char*)branch);
|
||||
g_key_file_set_string_list (config, remote_key, "branches",
|
||||
(const char *const *)new_branches->pdata,
|
||||
new_branches->len);
|
||||
|
||||
if (!ostree_repo_write_config (repo, config, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (config)
|
||||
g_key_file_free (config);
|
||||
if (branches)
|
||||
g_strfreev (branches);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_admin_builtin_pull_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error)
|
||||
{
|
||||
GOptionContext *context;
|
||||
gboolean ret = FALSE;
|
||||
const char *osname;
|
||||
const char *target;
|
||||
GFile *ostree_dir = admin_opts->ostree_dir;
|
||||
ot_lobj OstreeRepo *repo = NULL;
|
||||
ot_lobj GFile *repo_path = NULL;
|
||||
ot_lobj GFile *current_deployment = NULL;
|
||||
ot_lfree char *deploy_name = NULL;
|
||||
ot_lobj GFile *deploy_dir = NULL;
|
||||
ot_lfree char *remote_name = NULL;
|
||||
ot_lptrarray GPtrArray *subproc_args = NULL;
|
||||
__attribute__((unused)) GCancellable *cancellable = NULL;
|
||||
|
||||
context = g_option_context_new ("OSNAME [TREE] - Ensure TREE (default current) is in list of remotes, then download and deploy");
|
||||
|
||||
g_option_context_add_main_entries (context, options, NULL);
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, error))
|
||||
goto out;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
ot_util_usage_error (context, "OSNAME must be specified", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
osname = argv[1];
|
||||
|
||||
repo_path = g_file_get_child (ostree_dir, "repo");
|
||||
|
||||
repo = ostree_repo_new (repo_path);
|
||||
if (!ostree_repo_check (repo, error))
|
||||
goto out;
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
target = argv[2];
|
||||
if (!ensure_remote_branch (repo, osname, target,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
deploy_name = g_strdup (target);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ot_admin_get_current_deployment (ostree_dir, osname, ¤t_deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!current_deployment)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No current deployment");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ot_admin_parse_deploy_name (ostree_dir, osname, current_deployment,
|
||||
&deploy_name, NULL);
|
||||
}
|
||||
|
||||
if (!ot_admin_pull (ostree_dir, osname, cancellable, error))
|
||||
goto out;
|
||||
|
||||
{
|
||||
ot_lfree char *opt_ostree_dir_arg = g_strconcat ("--ostree-dir=",
|
||||
gs_file_get_path_cached (ostree_dir),
|
||||
NULL);
|
||||
ot_lfree char *opt_boot_dir_arg = g_strconcat ("--boot-dir=",
|
||||
gs_file_get_path_cached (admin_opts->boot_dir),
|
||||
NULL);
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", "admin", opt_ostree_dir_arg, opt_boot_dir_arg, "deploy", osname,
|
||||
deploy_name, NULL))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (context)
|
||||
g_option_context_free (context);
|
||||
return ret;
|
||||
}
|
100
src/ostree/ot-admin-builtin-status.c
Normal file
100
src/ostree/ot-admin-builtin-status.c
Normal file
@ -0,0 +1,100 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2012,2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Colin Walters <walters@verbum.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ot-admin-builtins.h"
|
||||
#include "ot-admin-functions.h"
|
||||
#include "ostree.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
gboolean
|
||||
ot_admin_builtin_status (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error)
|
||||
{
|
||||
GOptionContext *context;
|
||||
gboolean ret = FALSE;
|
||||
int bootversion;
|
||||
gs_unref_object OtDeployment *booted_deployment = NULL;
|
||||
gs_unref_ptrarray GPtrArray *deployments = NULL;
|
||||
__attribute__((unused)) GCancellable *cancellable = NULL;
|
||||
guint i;
|
||||
|
||||
context = g_option_context_new ("List deployments");
|
||||
|
||||
g_option_context_add_main_entries (context, options, NULL);
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, error))
|
||||
goto out;
|
||||
|
||||
if (!ot_admin_list_deployments (admin_opts->sysroot, &bootversion, &deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "While listing deployments: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Find the currently booted deployment, if any; we will
|
||||
* ensure it is present in the new deployment list.
|
||||
*/
|
||||
if (!ot_admin_find_booted_deployment (admin_opts->sysroot, deployments,
|
||||
&booted_deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (deployments->len == 0)
|
||||
{
|
||||
g_print ("No deployments.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
int subbootversion;
|
||||
|
||||
if (!ot_admin_read_current_subbootversion (admin_opts->sysroot, bootversion,
|
||||
&subbootversion,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
g_print ("bootversion: %d.%d\n", bootversion, subbootversion);
|
||||
|
||||
for (i = 0; i < deployments->len; i++)
|
||||
{
|
||||
OtDeployment *deployment = deployments->pdata[i];
|
||||
g_print ("%u: %c %s %s.%d\n",
|
||||
i,
|
||||
deployment == booted_deployment ? '*' : ' ',
|
||||
ot_deployment_get_osname (deployment),
|
||||
ot_deployment_get_csum (deployment),
|
||||
ot_deployment_get_deployserial (deployment));
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (context)
|
||||
g_option_context_free (context);
|
||||
return ret;
|
||||
}
|
@ -1,296 +0,0 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2012 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Colin Walters <walters@verbum.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ot-admin-builtins.h"
|
||||
#include "ostree.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
typedef struct {
|
||||
OtAdminBuiltinOpts *admin_opts;
|
||||
GFile *ostree_dir;
|
||||
GFile *boot_ostree_dir;
|
||||
GFile *deploy_path;
|
||||
GFile *kernel_path;
|
||||
char *release;
|
||||
char *osname;
|
||||
} OtAdminUpdateKernel;
|
||||
|
||||
static gboolean opt_no_bootloader;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "no-bootloader", 0, 0, G_OPTION_ARG_NONE, &opt_no_bootloader, "Don't update bootloader", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gboolean
|
||||
get_kernel_from_boot (GFile *path,
|
||||
GFile **out_kernel,
|
||||
GFile **out_initramfs,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_object GFileEnumerator *dir_enum = NULL;
|
||||
gs_unref_object GFileInfo *file_info = NULL;
|
||||
gs_unref_object GFile *ret_kernel = NULL;
|
||||
gs_unref_object GFile *ret_initramfs = NULL;
|
||||
|
||||
dir_enum = g_file_enumerate_children (path, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL, error);
|
||||
if (!dir_enum)
|
||||
goto out;
|
||||
|
||||
while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, error)) != NULL)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
name = g_file_info_get_name (file_info);
|
||||
|
||||
if (ret_kernel == NULL && g_str_has_prefix (name, "vmlinuz-"))
|
||||
ret_kernel = g_file_get_child (path, name);
|
||||
else if (ret_initramfs == NULL && g_str_has_prefix (name, "initramfs-"))
|
||||
ret_initramfs = g_file_get_child (path, name);
|
||||
|
||||
if (ret_kernel && ret_initramfs)
|
||||
break;
|
||||
}
|
||||
|
||||
ot_transfer_out_value (out_kernel, &ret_kernel);
|
||||
ot_transfer_out_value (out_initramfs, &ret_initramfs);
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
grep_literal (GFile *f,
|
||||
const char *string,
|
||||
gboolean *out_matches,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gboolean ret_matches = FALSE;
|
||||
gs_unref_object GInputStream *in = NULL;
|
||||
gs_unref_object GDataInputStream *datain = NULL;
|
||||
ot_lfree char *line = NULL;
|
||||
|
||||
in = (GInputStream*)g_file_read (f, cancellable, error);
|
||||
if (!in)
|
||||
goto out;
|
||||
datain = (GDataInputStream*)g_data_input_stream_new (in);
|
||||
if (!in)
|
||||
goto out;
|
||||
|
||||
while ((line = g_data_input_stream_read_line (datain, NULL, cancellable, error)) != NULL)
|
||||
{
|
||||
if (strstr (line, string))
|
||||
{
|
||||
ret_matches = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (line);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
if (out_matches)
|
||||
*out_matches = ret_matches;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_grub (OtAdminUpdateKernel *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_object GFile *grub_path = g_file_resolve_relative_path (self->admin_opts->boot_dir, "grub/grub.conf");
|
||||
|
||||
if (g_file_query_exists (grub_path, cancellable))
|
||||
{
|
||||
gboolean have_grub_entry;
|
||||
if (!grep_literal (grub_path, "OSTree", &have_grub_entry,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!have_grub_entry)
|
||||
{
|
||||
ot_lfree char *add_kernel_arg = NULL;
|
||||
ot_lfree char *initramfs_arg = NULL;
|
||||
ot_lfree char *initramfs_name = NULL;
|
||||
gs_unref_object GFile *initramfs_path = NULL;
|
||||
|
||||
initramfs_name = g_strconcat ("initramfs-", self->release, ".img", NULL);
|
||||
initramfs_path = g_file_get_child (self->boot_ostree_dir, initramfs_name);
|
||||
|
||||
add_kernel_arg = g_strconcat ("--add-kernel=", gs_file_get_path_cached (self->kernel_path), NULL);
|
||||
initramfs_arg = g_strconcat ("--initrd=", gs_file_get_path_cached (initramfs_path), NULL);
|
||||
|
||||
g_print ("Adding OSTree grub entry...\n");
|
||||
if (!gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_NULL,
|
||||
cancellable, error,
|
||||
"grubby", "--grub", add_kernel_arg, initramfs_arg,
|
||||
"--copy-default", "--title=OSTree", NULL))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
g_print ("Already have OSTree entry in grub config\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_print ("/boot/grub/grub.conf not found, assuming you have GRUB 2\n");
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_admin_builtin_update_kernel (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GOptionContext *context;
|
||||
OtAdminUpdateKernel self_data;
|
||||
OtAdminUpdateKernel *self = &self_data;
|
||||
GFile *ostree_dir = admin_opts->ostree_dir;
|
||||
gs_unref_object GFile *deploy_boot_path = NULL;
|
||||
gs_unref_object GFile *src_kernel_path = NULL;
|
||||
gs_unref_object GFile *src_initramfs_path = NULL;
|
||||
gs_free char *prefix = NULL;
|
||||
gs_free char *initramfs_name = NULL;
|
||||
gs_unref_object GFile *expected_initramfs_path = NULL;
|
||||
const char *release = NULL;
|
||||
const char *kernel_name = NULL;
|
||||
GCancellable *cancellable = NULL;
|
||||
|
||||
memset (self, 0, sizeof (*self));
|
||||
|
||||
self->admin_opts = admin_opts;
|
||||
|
||||
context = g_option_context_new ("OSNAME [DEPLOY_PATH] - Update kernel and regenerate initial ramfs");
|
||||
g_option_context_add_main_entries (context, options, NULL);
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, error))
|
||||
goto out;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
ot_util_usage_error (context, "OSNAME must be specified", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
self->osname = g_strdup (argv[1]);
|
||||
|
||||
if (argc > 2)
|
||||
self->deploy_path = g_file_new_for_path (argv[2]);
|
||||
else
|
||||
{
|
||||
gs_unref_object GFile *osdir = ot_gfile_get_child_build_path (admin_opts->ostree_dir, "deploy", self->osname, NULL);
|
||||
self->deploy_path = g_file_get_child (osdir, "current");
|
||||
}
|
||||
|
||||
self->ostree_dir = g_object_ref (ostree_dir);
|
||||
self->boot_ostree_dir = g_file_get_child (admin_opts->boot_dir, "ostree");
|
||||
|
||||
deploy_boot_path = g_file_get_child (self->deploy_path, "boot");
|
||||
|
||||
if (!get_kernel_from_boot (deploy_boot_path, &src_kernel_path, &src_initramfs_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (src_kernel_path == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No kernel found in %s", gs_file_get_path_cached (deploy_boot_path));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!gs_file_ensure_directory (self->boot_ostree_dir, TRUE, cancellable, error))
|
||||
goto out;
|
||||
|
||||
kernel_name = gs_file_get_basename_cached (src_kernel_path);
|
||||
release = strchr (kernel_name, '-');
|
||||
if (release == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Invalid kernel name %s, no - found", gs_file_get_path_cached (src_kernel_path));
|
||||
goto out;
|
||||
}
|
||||
|
||||
self->release = g_strdup (release + 1);
|
||||
prefix = g_strndup (kernel_name, release - kernel_name);
|
||||
self->kernel_path = ot_gfile_get_child_strconcat (self->boot_ostree_dir, prefix, "-", self->release, NULL);
|
||||
|
||||
if (!g_file_query_exists(self->kernel_path, NULL))
|
||||
{
|
||||
if (!gs_file_linkcopy_sync_data (src_kernel_path, self->kernel_path, G_FILE_COPY_OVERWRITE,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
g_print ("ostadmin: Deployed kernel %s\n", gs_file_get_path_cached (self->kernel_path));
|
||||
}
|
||||
|
||||
initramfs_name = g_strconcat ("initramfs-", self->release, ".img", NULL);
|
||||
expected_initramfs_path = g_file_get_child (self->boot_ostree_dir, initramfs_name);
|
||||
|
||||
if (!g_file_query_exists (expected_initramfs_path, NULL))
|
||||
{
|
||||
if (!gs_file_linkcopy_sync_data (src_initramfs_path, expected_initramfs_path, G_FILE_COPY_OVERWRITE,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* In the fuse case, we need to chown after copying */
|
||||
if (getuid () != 0)
|
||||
{
|
||||
if (!gs_file_chown (expected_initramfs_path, 0, 0, cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Failed to chown initramfs: ");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
g_print ("Deployed initramfs: %s\n", gs_file_get_path_cached (expected_initramfs_path));
|
||||
}
|
||||
|
||||
if (!opt_no_bootloader)
|
||||
{
|
||||
if (!update_grub (self, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_clear_object (&self->ostree_dir);
|
||||
g_clear_object (&self->boot_ostree_dir);
|
||||
g_clear_object (&self->kernel_path);
|
||||
g_free (self->release);
|
||||
if (context)
|
||||
g_option_context_free (context);
|
||||
return ret;
|
||||
}
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "ot-admin-builtins.h"
|
||||
#include "ot-admin-functions.h"
|
||||
#include "ot-admin-deploy.h"
|
||||
#include "ostree.h"
|
||||
#include "otutil.h"
|
||||
|
||||
@ -32,8 +33,10 @@
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static gboolean opt_reboot;
|
||||
static char *opt_osname;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Specify operating system root to use", NULL },
|
||||
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Reboot after a successful upgrade", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
@ -41,79 +44,108 @@ static GOptionEntry options[] = {
|
||||
gboolean
|
||||
ot_admin_builtin_upgrade (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error)
|
||||
{
|
||||
GOptionContext *context;
|
||||
gboolean ret = FALSE;
|
||||
GFile *ostree_dir = admin_opts->ostree_dir;
|
||||
gs_free char *booted_osname = NULL;
|
||||
const char *osname = NULL;
|
||||
gs_unref_object GFile *deployment = NULL;
|
||||
gs_unref_object GFile *repo_path = NULL;
|
||||
gs_unref_object OstreeRepo *repo = NULL;
|
||||
gs_free char *deploy_name = NULL;
|
||||
gs_free char *current_rev = NULL;
|
||||
gs_free char *new_rev = NULL;
|
||||
gs_free char *ostree_dir_arg = NULL;
|
||||
__attribute__((unused)) GCancellable *cancellable = NULL;
|
||||
GOptionContext *context;
|
||||
GFile *sysroot = admin_opts->sysroot;
|
||||
gs_free char *booted_osname = NULL;
|
||||
gs_unref_object OstreeRepo *repo = NULL;
|
||||
gs_unref_object GFile *repo_path = NULL;
|
||||
gs_free char *origin_refspec = NULL;
|
||||
gs_free char *origin_remote = NULL;
|
||||
gs_free char *origin_ref = NULL;
|
||||
gs_free char *new_revision = NULL;
|
||||
gs_unref_object GFile *deployment_path = NULL;
|
||||
gs_unref_object GFile *deployment_origin_path = NULL;
|
||||
gs_unref_object OtDeployment *booted_deployment = NULL;
|
||||
gs_unref_object OtDeployment *merge_deployment = NULL;
|
||||
gs_unref_ptrarray GPtrArray *current_deployments = NULL;
|
||||
gs_unref_ptrarray GPtrArray *new_deployments = NULL;
|
||||
gs_unref_object OtDeployment *new_deployment = NULL;
|
||||
gs_free char *ostree_dir_arg = NULL;
|
||||
int current_bootversion;
|
||||
int new_bootversion;
|
||||
GKeyFile *origin;
|
||||
|
||||
context = g_option_context_new ("[OSNAME] - pull, deploy, and prune");
|
||||
context = g_option_context_new ("Construct new tree from current origin and deploy it, if it changed");
|
||||
g_option_context_add_main_entries (context, options, NULL);
|
||||
|
||||
if (!g_option_context_parse (context, &argc, &argv, error))
|
||||
goto out;
|
||||
|
||||
if (argc > 1)
|
||||
if (!ot_admin_list_deployments (admin_opts->sysroot, ¤t_bootversion,
|
||||
¤t_deployments,
|
||||
cancellable, error))
|
||||
{
|
||||
osname = argv[1];
|
||||
g_prefix_error (error, "While listing deployments: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ot_admin_require_deployment_or_osname (admin_opts->sysroot, current_deployments,
|
||||
opt_osname,
|
||||
&booted_deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (!opt_osname)
|
||||
opt_osname = (char*)ot_deployment_get_osname (booted_deployment);
|
||||
merge_deployment = ot_admin_get_merge_deployment (current_deployments, opt_osname,
|
||||
booted_deployment,
|
||||
NULL);
|
||||
|
||||
deployment_path = ot_admin_get_deployment_directory (admin_opts->sysroot, merge_deployment);
|
||||
deployment_origin_path = ot_admin_get_deployment_origin_path (deployment_path);
|
||||
|
||||
repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
|
||||
repo = ostree_repo_new (repo_path);
|
||||
if (!ostree_repo_check (repo, error))
|
||||
goto out;
|
||||
|
||||
origin = ot_deployment_get_origin (merge_deployment);
|
||||
if (!origin)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No origin known for current deployment");
|
||||
goto out;
|
||||
}
|
||||
origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
|
||||
if (!origin_refspec)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No origin/refspec in current deployment origin; cannot upgrade via ostree");
|
||||
goto out;
|
||||
}
|
||||
if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error))
|
||||
goto out;
|
||||
|
||||
if (origin_remote)
|
||||
{
|
||||
g_print ("Fetching remote %s ref %s\n", origin_remote, origin_ref);
|
||||
if (!ot_admin_pull (admin_opts->sysroot, origin_remote, origin_ref,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ostree_repo_resolve_rev (repo, origin_ref, FALSE, &new_revision,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (strcmp (ot_deployment_get_csum (merge_deployment), new_revision) == 0)
|
||||
{
|
||||
g_print ("Refspec %s is unchanged\n", origin_refspec);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ot_admin_get_booted_os (&booted_osname, NULL,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (booted_osname == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Not in an active OSTree system; must specify OSNAME");
|
||||
goto out;
|
||||
}
|
||||
osname = booted_osname;
|
||||
}
|
||||
|
||||
if (!ot_admin_get_current_deployment (ostree_dir, osname, &deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ot_admin_parse_deploy_name (ostree_dir, osname, deployment, &deploy_name, ¤t_rev);
|
||||
|
||||
ostree_dir_arg = g_strconcat ("--ostree-dir=",
|
||||
gs_file_get_path_cached (ostree_dir),
|
||||
NULL);
|
||||
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", "admin", ostree_dir_arg, "pull-deploy", osname, NULL))
|
||||
goto out;
|
||||
|
||||
if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
|
||||
GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
"ostree", "admin", ostree_dir_arg, "prune", osname, NULL))
|
||||
goto out;
|
||||
|
||||
if (opt_reboot)
|
||||
{
|
||||
repo_path = g_file_get_child (ostree_dir, "repo");
|
||||
|
||||
repo = ostree_repo_new (repo_path);
|
||||
if (!ostree_repo_check (repo, error))
|
||||
gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/");
|
||||
if (!ot_admin_deploy (admin_opts->sysroot,
|
||||
current_bootversion, current_deployments,
|
||||
opt_osname, new_revision, origin,
|
||||
NULL, FALSE,
|
||||
booted_deployment, merge_deployment,
|
||||
&new_deployment, &new_bootversion, &new_deployments,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_repo_resolve_rev (repo, deploy_name, TRUE, &new_rev,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (strcmp (current_rev, new_rev) != 0 && opt_reboot)
|
||||
if (opt_reboot && g_file_equal (sysroot, real_sysroot))
|
||||
{
|
||||
gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
|
||||
cancellable, error,
|
||||
|
@ -28,8 +28,7 @@
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct {
|
||||
GFile *ostree_dir;
|
||||
GFile *boot_dir;
|
||||
GFile *sysroot;
|
||||
} OtAdminBuiltinOpts;
|
||||
|
||||
gboolean ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
@ -37,10 +36,9 @@ gboolean ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *ad
|
||||
gboolean ot_admin_builtin_init_fs (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_pull_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_status (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_diff (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_run_triggers (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_update_kernel (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
gboolean ot_admin_builtin_upgrade (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
1245
src/ostree/ot-admin-deploy.c
Normal file
1245
src/ostree/ot-admin-deploy.c
Normal file
File diff suppressed because it is too large
Load Diff
51
src/ostree/ot-admin-deploy.h
Normal file
51
src/ostree/ot-admin-deploy.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2012 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Author: Colin Walters <walters@verbum.org>
|
||||
*/
|
||||
|
||||
#ifndef __OT_ADMIN_DEPLOY__
|
||||
#define __OT_ADMIN_DEPLOY_
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "ot-deployment.h"
|
||||
#include "ot-bootloader.h"
|
||||
#include "ot-ordered-hash.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean ot_admin_deploy (GFile *sysroot,
|
||||
int current_bootversion,
|
||||
GPtrArray *current_deployments,
|
||||
const char *osname,
|
||||
const char *revision,
|
||||
GKeyFile *origin,
|
||||
char **add_kernel_argv,
|
||||
gboolean retain,
|
||||
OtDeployment *booted_deployment,
|
||||
OtDeployment *merge_deployment,
|
||||
OtDeployment **out_new_deployment,
|
||||
int *out_new_bootversion,
|
||||
GPtrArray **out_new_deployments,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,10 @@
|
||||
#define __OT_ADMIN_FUNCTIONS__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "ostree.h"
|
||||
#include "ot-deployment.h"
|
||||
#include "ot-bootloader.h"
|
||||
#include "ot-ordered-hash.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@ -31,50 +35,84 @@ gboolean ot_admin_ensure_initialized (GFile *ostree_dir,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_get_booted_os (char **out_osname,
|
||||
char **out_tree,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean ot_admin_check_os (GFile *sysroot,
|
||||
const char *osname,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_get_current_deployment (GFile *ostree_dir,
|
||||
const char *osname,
|
||||
GFile **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean ot_admin_get_previous_deployment (GFile *ostree_dir,
|
||||
const char *osname,
|
||||
GFile **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char *ot_admin_split_keyeq (char *str);
|
||||
OtOrderedHash *ot_admin_parse_kernel_args (const char *options);
|
||||
char * ot_admin_kernel_arg_string_serialize (OtOrderedHash *ohash);
|
||||
|
||||
gboolean ot_admin_list_deployments (GFile *ostree_dir,
|
||||
const char *osname,
|
||||
OtBootloader *ot_admin_query_bootloader (GFile *sysroot);
|
||||
|
||||
gboolean ot_admin_read_current_subbootversion (GFile *sysroot,
|
||||
int bootversion,
|
||||
int *out_subbootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_read_boot_loader_configs (GFile *boot_dir,
|
||||
int bootversion,
|
||||
GPtrArray **out_loader_configs,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_list_deployments (GFile *sysroot,
|
||||
int *out_bootversion,
|
||||
GPtrArray **out_deployments,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_get_active_deployment (GFile *ostree_dir,
|
||||
char **out_osname,
|
||||
GFile **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean ot_admin_find_booted_deployment (GFile *sysroot,
|
||||
GPtrArray *deployments,
|
||||
OtDeployment **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_require_booted_deployment (GFile *sysroot,
|
||||
OtDeployment **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_require_deployment_or_osname (GFile *sysroot,
|
||||
GPtrArray *deployment_list,
|
||||
const char *osname,
|
||||
OtDeployment **out_deployment,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
OtDeployment *ot_admin_get_merge_deployment (GPtrArray *deployment_list,
|
||||
const char *osname,
|
||||
OtDeployment *booted_deployment,
|
||||
OtDeployment *new_deployment);
|
||||
|
||||
GFile *ot_admin_get_deployment_origin_path (GFile *deployment_path);
|
||||
|
||||
GFile *ot_admin_get_deployment_directory (GFile *sysroot,
|
||||
OtDeployment *deployment);
|
||||
|
||||
gboolean ot_admin_get_repo (GFile *sysroot,
|
||||
OstreeRepo **out_repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_cleanup (GFile *sysroot,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_admin_get_default_ostree_dir (GFile **out_ostree_dir,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GKeyFile *ot_origin_new_from_refspec (const char *refspec);
|
||||
|
||||
gboolean ot_admin_pull (GFile *ostree_dir,
|
||||
const char *osname,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void
|
||||
ot_admin_parse_deploy_name (GFile *ostree_dir,
|
||||
const char *osname,
|
||||
GFile *deployment,
|
||||
char **out_name,
|
||||
char **out_rev);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
312
src/ostree/ot-bootloader-syslinux.c
Normal file
312
src/ostree/ot-bootloader-syslinux.c
Normal file
@ -0,0 +1,312 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ot-bootloader-syslinux.h"
|
||||
#include "libgsystem.h"
|
||||
#include "otutil.h"
|
||||
#include "ot-admin-functions.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct _OtBootloaderSyslinux
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GFile *sysroot;
|
||||
GFile *config_path;
|
||||
};
|
||||
|
||||
typedef GObjectClass OtBootloaderSyslinuxClass;
|
||||
|
||||
static void ot_bootloader_syslinux_bootloader_iface_init (OtBootloaderInterface *iface);
|
||||
G_DEFINE_TYPE_WITH_CODE (OtBootloaderSyslinux, ot_bootloader_syslinux, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (OT_TYPE_BOOTLOADER, ot_bootloader_syslinux_bootloader_iface_init));
|
||||
|
||||
static gboolean
|
||||
ot_bootloader_syslinux_query (OtBootloader *bootloader)
|
||||
{
|
||||
OtBootloaderSyslinux *self = OT_BOOTLOADER_SYSLINUX (bootloader);
|
||||
|
||||
return g_file_query_file_type (self->config_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
append_config_from_boot_loader_entries (OtBootloaderSyslinux *self,
|
||||
gboolean regenerate_default,
|
||||
int bootversion,
|
||||
GPtrArray *new_lines,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_ptrarray GPtrArray *boot_loader_configs = NULL;
|
||||
guint i;
|
||||
|
||||
if (!ot_admin_read_boot_loader_configs (self->sysroot, bootversion, &boot_loader_configs,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < boot_loader_configs->len; i++)
|
||||
{
|
||||
OtConfigParser *config = boot_loader_configs->pdata[i];
|
||||
const char *val;
|
||||
|
||||
val = ot_config_parser_get (config, "title");
|
||||
if (!val)
|
||||
val = "(Untitled)";
|
||||
|
||||
if (regenerate_default && i == 0)
|
||||
{
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("DEFAULT %s", val));
|
||||
}
|
||||
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("LABEL %s", val));
|
||||
|
||||
val = ot_config_parser_get (config, "linux");
|
||||
if (!val)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No \"linux\" key in bootloader config");
|
||||
goto out;
|
||||
}
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("\tKERNEL %s", val));
|
||||
|
||||
val = ot_config_parser_get (config, "initrd");
|
||||
if (val)
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("\tINITRD %s", val));
|
||||
|
||||
val = ot_config_parser_get (config, "options");
|
||||
if (val)
|
||||
g_ptr_array_add (new_lines, g_strdup_printf ("\tAPPEND %s", val));
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
join_lines (GPtrArray *lines)
|
||||
{
|
||||
GString *buf = g_string_new ("");
|
||||
guint i;
|
||||
gboolean prev_was_empty = FALSE;
|
||||
|
||||
for (i = 0; i < lines->len; i++)
|
||||
{
|
||||
const char *line = lines->pdata[i];
|
||||
/* Special bit to remove extraneous empty lines */
|
||||
if (*line == '\0')
|
||||
{
|
||||
if (prev_was_empty || i == 0)
|
||||
continue;
|
||||
else
|
||||
prev_was_empty = TRUE;
|
||||
}
|
||||
g_string_append (buf, line);
|
||||
g_string_append_c (buf, '\n');
|
||||
}
|
||||
return g_string_free (buf, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ot_bootloader_syslinux_write_config (OtBootloader *bootloader,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
OtBootloaderSyslinux *self = OT_BOOTLOADER_SYSLINUX (bootloader);
|
||||
gs_unref_object GFile *new_config_path = NULL;
|
||||
gs_free char *config_contents = NULL;
|
||||
gs_free char *new_config_contents = NULL;
|
||||
gs_unref_ptrarray GPtrArray *new_lines = NULL;
|
||||
gs_unref_ptrarray GPtrArray *tmp_lines = NULL;
|
||||
gs_free char *kernel_arg = NULL;
|
||||
gboolean saw_default = FALSE;
|
||||
gboolean regenerate_default = FALSE;
|
||||
gboolean parsing_label = FALSE;
|
||||
char **lines = NULL;
|
||||
char **iter;
|
||||
guint i;
|
||||
|
||||
new_config_path = ot_gfile_resolve_path_printf (self->sysroot, "boot/loader.%d/syslinux.cfg",
|
||||
bootversion);
|
||||
|
||||
/* This should follow the symbolic link to the current bootversion. */
|
||||
config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
|
||||
if (!config_contents)
|
||||
goto out;
|
||||
|
||||
lines = g_strsplit (config_contents, "\n", -1);
|
||||
new_lines = g_ptr_array_new_with_free_func (g_free);
|
||||
tmp_lines = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
/* Note special iteration condition here; we want to also loop one
|
||||
* more time at the end where line = NULL to ensure we finish off
|
||||
* processing the last LABEL.
|
||||
*/
|
||||
iter = lines;
|
||||
while (TRUE)
|
||||
{
|
||||
char *line = *iter;
|
||||
gboolean skip = FALSE;
|
||||
|
||||
if (parsing_label &&
|
||||
(line == NULL || !g_str_has_prefix (line, "\t")))
|
||||
{
|
||||
parsing_label = FALSE;
|
||||
if (kernel_arg == NULL)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"No KERNEL argument found after LABEL");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If this is a non-ostree kernel, just emit the lines
|
||||
* we saw.
|
||||
*/
|
||||
if (!g_str_has_prefix (kernel_arg, "/ostree/"))
|
||||
{
|
||||
for (i = 0; i < tmp_lines->len; i++)
|
||||
{
|
||||
g_ptr_array_add (new_lines, tmp_lines->pdata[i]);
|
||||
tmp_lines->pdata[i] = NULL; /* Transfer ownership */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, we drop the config on the floor - it
|
||||
* will be regenerated.
|
||||
*/
|
||||
g_ptr_array_set_size (tmp_lines, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (line == NULL)
|
||||
break;
|
||||
|
||||
if (!parsing_label &&
|
||||
(g_str_has_prefix (line, "LABEL ")))
|
||||
{
|
||||
parsing_label = TRUE;
|
||||
g_ptr_array_set_size (tmp_lines, 0);
|
||||
}
|
||||
else if (parsing_label && g_str_has_prefix (line, "\tKERNEL "))
|
||||
{
|
||||
g_free (kernel_arg);
|
||||
kernel_arg = g_strdup (line + strlen ("\tKERNEL "));
|
||||
}
|
||||
else if (!parsing_label &&
|
||||
(g_str_has_prefix (line, "DEFAULT ")))
|
||||
{
|
||||
saw_default = TRUE;
|
||||
if (g_str_has_prefix (line, "DEFAULT ostree:"))
|
||||
regenerate_default = TRUE;
|
||||
skip = TRUE;
|
||||
}
|
||||
|
||||
if (skip)
|
||||
{
|
||||
g_free (line);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parsing_label)
|
||||
{
|
||||
g_ptr_array_add (tmp_lines, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ptr_array_add (new_lines, line);
|
||||
}
|
||||
}
|
||||
/* Transfer ownership */
|
||||
*iter = NULL;
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (!saw_default)
|
||||
regenerate_default = TRUE;
|
||||
|
||||
if (!append_config_from_boot_loader_entries (self, regenerate_default,
|
||||
bootversion, new_lines,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
new_config_contents = join_lines (new_lines);
|
||||
|
||||
if (strcmp (new_config_contents, config_contents) != 0)
|
||||
{
|
||||
if (!g_file_replace_contents (new_config_path, new_config_contents,
|
||||
strlen (new_config_contents),
|
||||
NULL, FALSE, G_FILE_CREATE_NONE,
|
||||
NULL, cancellable, error))
|
||||
goto out;
|
||||
g_print ("Saved new version of %s\n", gs_file_get_path_cached (self->config_path));
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_free (lines); /* Note we freed elements individually */
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
ot_bootloader_syslinux_finalize (GObject *object)
|
||||
{
|
||||
OtBootloaderSyslinux *self = OT_BOOTLOADER_SYSLINUX (object);
|
||||
|
||||
g_clear_object (&self->sysroot);
|
||||
g_clear_object (&self->config_path);
|
||||
|
||||
G_OBJECT_CLASS (ot_bootloader_syslinux_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
void
|
||||
ot_bootloader_syslinux_init (OtBootloaderSyslinux *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
ot_bootloader_syslinux_bootloader_iface_init (OtBootloaderInterface *iface)
|
||||
{
|
||||
iface->query = ot_bootloader_syslinux_query;
|
||||
iface->write_config = ot_bootloader_syslinux_write_config;
|
||||
}
|
||||
|
||||
void
|
||||
ot_bootloader_syslinux_class_init (OtBootloaderSyslinuxClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = ot_bootloader_syslinux_finalize;
|
||||
}
|
||||
|
||||
OtBootloaderSyslinux *
|
||||
ot_bootloader_syslinux_new (GFile *sysroot)
|
||||
{
|
||||
OtBootloaderSyslinux *self = g_object_new (OT_TYPE_BOOTLOADER_SYSLINUX, NULL);
|
||||
self->sysroot = g_object_ref (sysroot);
|
||||
self->config_path = g_file_resolve_relative_path (self->sysroot, "boot/syslinux/syslinux.cfg");
|
||||
return self;
|
||||
}
|
40
src/ostree/ot-bootloader-syslinux.h
Normal file
40
src/ostree/ot-bootloader-syslinux.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OT_BOOTLOADER_SYSLINUX_H__
|
||||
#define __OT_BOOTLOADER_SYSLINUX_H__
|
||||
|
||||
#include "ot-bootloader.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OT_TYPE_BOOTLOADER_SYSLINUX (ot_bootloader_syslinux_get_type ())
|
||||
#define OT_BOOTLOADER_SYSLINUX(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OT_TYPE_BOOTLOADER_SYSLINUX, OtBootloaderSyslinux))
|
||||
#define OT_IS_BOOTLOADER_SYSLINUX(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OT_TYPE_BOOTLOADER_SYSLINUX))
|
||||
|
||||
typedef struct _OtBootloaderSyslinux OtBootloaderSyslinux;
|
||||
|
||||
GType ot_bootloader_syslinux_get_type (void) G_GNUC_CONST;
|
||||
|
||||
OtBootloaderSyslinux * ot_bootloader_syslinux_new (GFile *sysroot);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __OT_BOOTLOADER_SYSLINUX_H__ */
|
49
src/ostree/ot-bootloader.c
Normal file
49
src/ostree/ot-bootloader.c
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ot-bootloader.h"
|
||||
|
||||
G_DEFINE_INTERFACE (OtBootloader, ot_bootloader, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
ot_bootloader_default_init (OtBootloaderInterface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_bootloader_query (OtBootloader *self)
|
||||
{
|
||||
g_return_val_if_fail (OT_IS_BOOTLOADER (self), FALSE);
|
||||
|
||||
return OT_BOOTLOADER_GET_IFACE (self)->query (self);
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_bootloader_write_config (OtBootloader *self,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (OT_IS_BOOTLOADER (self), FALSE);
|
||||
|
||||
return OT_BOOTLOADER_GET_IFACE (self)->write_config (self, bootversion,
|
||||
cancellable, error);
|
||||
}
|
59
src/ostree/ot-bootloader.h
Normal file
59
src/ostree/ot-bootloader.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OT_BOOTLOADER_H__
|
||||
#define __OT_BOOTLOADER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OT_TYPE_BOOTLOADER (ot_bootloader_get_type ())
|
||||
#define OT_BOOTLOADER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OT_TYPE_BOOTLOADER, OtBootloader))
|
||||
#define OT_IS_BOOTLOADER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OT_TYPE_BOOTLOADER))
|
||||
#define OT_BOOTLOADER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), OT_TYPE_BOOTLOADER, OtBootloaderInterface))
|
||||
|
||||
typedef struct _OtBootloader OtBootloader;
|
||||
typedef struct _OtBootloaderInterface OtBootloaderInterface;
|
||||
|
||||
struct _OtBootloaderInterface
|
||||
{
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/* virtual functions */
|
||||
gboolean (* query) (OtBootloader *self);
|
||||
gboolean (* write_config) (OtBootloader *self,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
GType ot_bootloader_get_type (void) G_GNUC_CONST;
|
||||
|
||||
gboolean ot_bootloader_query (OtBootloader *self);
|
||||
|
||||
gboolean ot_bootloader_write_config (OtBootloader *self,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __OT_BOOTLOADER_H__ */
|
@ -31,12 +31,10 @@
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
static char *opt_ostree_dir = NULL;
|
||||
static char *opt_boot_dir = "/boot";
|
||||
static char *opt_sysroot = "/";
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "ostree-dir", 0, 0, G_OPTION_ARG_STRING, &opt_ostree_dir, "Path to OSTree root directory (default: /ostree)", NULL },
|
||||
{ "boot-dir", 0, 0, G_OPTION_ARG_STRING, &opt_boot_dir, "Path to system boot directory (default: /boot)", NULL },
|
||||
{ "sysroot", 0, 0, G_OPTION_ARG_STRING, &opt_sysroot, "Path to root directory (default: /)", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -51,9 +49,8 @@ static OstreeAdminCommand admin_subcommands[] = {
|
||||
{ "deploy", ot_admin_builtin_deploy },
|
||||
{ "install", ot_admin_builtin_install },
|
||||
{ "upgrade", ot_admin_builtin_upgrade },
|
||||
{ "pull-deploy", ot_admin_builtin_pull_deploy },
|
||||
{ "prune", ot_admin_builtin_prune },
|
||||
{ "update-kernel", ot_admin_builtin_update_kernel },
|
||||
{ "status", ot_admin_builtin_status },
|
||||
{ "config-diff", ot_admin_builtin_diff },
|
||||
{ "run-triggers", ot_admin_builtin_run_triggers },
|
||||
{ NULL, NULL }
|
||||
@ -70,8 +67,6 @@ ostree_builtin_admin (int argc, char **argv, GFile *repo_path, GError **error)
|
||||
int subcmd_argc;
|
||||
OtAdminBuiltinOpts admin_opts;
|
||||
char **subcmd_argv = NULL;
|
||||
ot_lobj GFile *ostree_dir = NULL;
|
||||
ot_lobj GFile *boot_dir = NULL;
|
||||
|
||||
context = g_option_context_new ("[OPTIONS] SUBCOMMAND - Run an administrative subcommand");
|
||||
|
||||
@ -117,21 +112,9 @@ ostree_builtin_admin (int argc, char **argv, GFile *repo_path, GError **error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opt_ostree_dir != NULL)
|
||||
{
|
||||
ostree_dir = g_file_new_for_path (opt_ostree_dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ot_admin_get_default_ostree_dir (&ostree_dir, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
boot_dir = g_file_new_for_path (opt_boot_dir);
|
||||
|
||||
ostree_prep_builtin_argv (subcommand_name, argc-2, argv+2, &subcmd_argc, &subcmd_argv);
|
||||
|
||||
admin_opts.ostree_dir = ostree_dir;
|
||||
admin_opts.boot_dir = boot_dir;
|
||||
admin_opts.sysroot = g_file_new_for_path (opt_sysroot);
|
||||
if (!subcommand->fn (subcmd_argc, subcmd_argv, &admin_opts, error))
|
||||
goto out;
|
||||
|
||||
|
230
src/ostree/ot-config-parser.c
Normal file
230
src/ostree/ot-config-parser.c
Normal file
@ -0,0 +1,230 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ot-config-parser.h"
|
||||
#include "libgsystem.h"
|
||||
|
||||
struct _OtConfigParser
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
gboolean parsed;
|
||||
char *separators;
|
||||
|
||||
GHashTable *options;
|
||||
GPtrArray *lines;
|
||||
};
|
||||
|
||||
typedef GObjectClass OtConfigParserClass;
|
||||
|
||||
G_DEFINE_TYPE (OtConfigParser, ot_config_parser, G_TYPE_OBJECT)
|
||||
|
||||
gboolean
|
||||
ot_config_parser_parse (OtConfigParser *self,
|
||||
GFile *path,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_free char *contents = NULL;
|
||||
char **lines = NULL;
|
||||
char **iter = NULL;
|
||||
|
||||
g_return_val_if_fail (!self->parsed, FALSE);
|
||||
|
||||
contents = gs_file_load_contents_utf8 (path, cancellable, error);
|
||||
if (!contents)
|
||||
goto out;
|
||||
|
||||
lines = g_strsplit (contents, "\n", -1);
|
||||
for (iter = lines; *iter; iter++)
|
||||
{
|
||||
const char *line = *iter;
|
||||
char *keyname = "";
|
||||
|
||||
if (g_ascii_isalpha (*line))
|
||||
{
|
||||
char **items = NULL;
|
||||
items = g_strsplit_set (line, self->separators, 2);
|
||||
if (g_strv_length (items) == 2 && items[0][0] != '\0')
|
||||
{
|
||||
keyname = items[0];
|
||||
g_hash_table_insert (self->options, items[0], items[1]);
|
||||
g_free (items); /* Transfer ownership */
|
||||
}
|
||||
else
|
||||
{
|
||||
g_strfreev (items);
|
||||
}
|
||||
}
|
||||
g_ptr_array_add (self->lines, g_variant_new ("(ss)", keyname, line));
|
||||
}
|
||||
|
||||
self->parsed = TRUE;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_strfreev (lines);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ot_config_parser_set (OtConfigParser *self,
|
||||
const char *key,
|
||||
const char *value)
|
||||
{
|
||||
g_hash_table_replace (self->options, g_strdup (key), g_strdup (value));
|
||||
}
|
||||
|
||||
const char *
|
||||
ot_config_parser_get (OtConfigParser *self,
|
||||
const char *key)
|
||||
{
|
||||
return g_hash_table_lookup (self->options, key);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
write_key (OtConfigParser *self,
|
||||
GDataOutputStream *out,
|
||||
const char *key,
|
||||
const char *value,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
if (!g_data_output_stream_put_string (out, key, cancellable, error))
|
||||
goto out;
|
||||
if (!g_data_output_stream_put_byte (out, self->separators[0], cancellable, error))
|
||||
goto out;
|
||||
if (!g_data_output_stream_put_string (out, value, cancellable, error))
|
||||
goto out;
|
||||
if (!g_data_output_stream_put_byte (out, '\n', cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_config_parser_write (OtConfigParser *self,
|
||||
GFile *output,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GHashTableIter hashiter;
|
||||
gpointer hashkey, hashvalue;
|
||||
gs_unref_object GOutputStream *out = NULL;
|
||||
gs_unref_object GDataOutputStream *dataout = NULL;
|
||||
guint i;
|
||||
gs_unref_hashtable GHashTable *written_overrides = NULL;
|
||||
|
||||
written_overrides = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
out = (GOutputStream*)g_file_replace (output, NULL, FALSE, 0, cancellable, error);
|
||||
if (!out)
|
||||
goto out;
|
||||
|
||||
dataout = g_data_output_stream_new (out);
|
||||
|
||||
for (i = 0; i < self->lines->len; i++)
|
||||
{
|
||||
GVariant *linedata = self->lines->pdata[i];
|
||||
const char *key;
|
||||
const char *value;
|
||||
const char *line;
|
||||
|
||||
g_variant_get (linedata, "(&s&s)", &key, &line);
|
||||
|
||||
value = g_hash_table_lookup (self->options, key);
|
||||
if (value == NULL)
|
||||
{
|
||||
if (!g_data_output_stream_put_string (dataout, line, cancellable, error))
|
||||
goto out;
|
||||
if (!g_data_output_stream_put_byte (dataout, '\n', cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!write_key (self, dataout, key, value, cancellable, error))
|
||||
goto out;
|
||||
g_hash_table_insert (written_overrides, (gpointer)key, (gpointer)key);
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_iter_init (&hashiter, self->options);
|
||||
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
|
||||
{
|
||||
if (g_hash_table_lookup (written_overrides, hashkey))
|
||||
continue;
|
||||
if (!write_key (self, dataout, hashkey, hashvalue, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!g_output_stream_close ((GOutputStream*)dataout, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
ot_config_parser_finalize (GObject *object)
|
||||
{
|
||||
OtConfigParser *self = OT_CONFIG_PARSER (object);
|
||||
|
||||
g_hash_table_unref (self->options);
|
||||
g_ptr_array_unref (self->lines);
|
||||
g_free (self->separators);
|
||||
|
||||
G_OBJECT_CLASS (ot_config_parser_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
ot_config_parser_init (OtConfigParser *self)
|
||||
{
|
||||
self->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||||
self->lines = g_ptr_array_new ();
|
||||
}
|
||||
|
||||
void
|
||||
ot_config_parser_class_init (OtConfigParserClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = ot_config_parser_finalize;
|
||||
}
|
||||
|
||||
OtConfigParser *
|
||||
ot_config_parser_new (const char *separators)
|
||||
{
|
||||
OtConfigParser *self = NULL;
|
||||
|
||||
g_return_val_if_fail (separators != NULL && separators[0], NULL);
|
||||
|
||||
self = g_object_new (OT_TYPE_CONFIG_PARSER, NULL);
|
||||
self->separators = g_strdup (separators);
|
||||
return self;
|
||||
}
|
58
src/ostree/ot-config-parser.h
Normal file
58
src/ostree/ot-config-parser.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OT_CONFIG_PARSER_H__
|
||||
#define __OT_CONFIG_PARSER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OT_TYPE_CONFIG_PARSER (ot_config_parser_get_type ())
|
||||
#define OT_CONFIG_PARSER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OT_TYPE_CONFIG_PARSER, OtConfigParser))
|
||||
#define OT_IS_CONFIG_PARSER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OT_TYPE_CONFIG_PARSER))
|
||||
|
||||
typedef struct _OtConfigParser OtConfigParser;
|
||||
|
||||
GType ot_config_parser_get_type (void) G_GNUC_CONST;
|
||||
|
||||
OtConfigParser * ot_config_parser_new (const char *separator);
|
||||
|
||||
gboolean ot_config_parser_parse (OtConfigParser *self,
|
||||
GFile *path,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean ot_config_parser_write (OtConfigParser *self,
|
||||
GFile *output,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void ot_config_parser_set (OtConfigParser *self,
|
||||
const char *key,
|
||||
const char *value);
|
||||
|
||||
const char *ot_config_parser_get (OtConfigParser *self,
|
||||
const char *key);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __OT_CONFIG_PARSER_H__ */
|
209
src/ostree/ot-deployment.c
Normal file
209
src/ostree/ot-deployment.c
Normal file
@ -0,0 +1,209 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ot-deployment.h"
|
||||
|
||||
struct _OtDeployment
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
int index; /* Global offset */
|
||||
char *osname; /* osname */
|
||||
char *csum; /* OSTree checksum of tree */
|
||||
int deployserial; /* How many times this particular csum appears in deployment list */
|
||||
char *bootcsum; /* Checksum of kernel+initramfs */
|
||||
int bootserial; /* An integer assigned to this tree per its ${bootcsum} */
|
||||
OtConfigParser *bootconfig; /* Bootloader configuration */
|
||||
GKeyFile *origin; /* How to construct an upgraded version of this tree */
|
||||
};
|
||||
|
||||
typedef GObjectClass OtDeploymentClass;
|
||||
|
||||
G_DEFINE_TYPE (OtDeployment, ot_deployment, G_TYPE_OBJECT)
|
||||
|
||||
const char *
|
||||
ot_deployment_get_csum (OtDeployment *self)
|
||||
{
|
||||
return self->csum;
|
||||
}
|
||||
|
||||
const char *
|
||||
ot_deployment_get_bootcsum (OtDeployment *self)
|
||||
{
|
||||
return self->bootcsum;
|
||||
}
|
||||
|
||||
const char *
|
||||
ot_deployment_get_osname (OtDeployment *self)
|
||||
{
|
||||
return self->osname;
|
||||
}
|
||||
|
||||
int
|
||||
ot_deployment_get_deployserial (OtDeployment *self)
|
||||
{
|
||||
return self->deployserial;
|
||||
}
|
||||
|
||||
int
|
||||
ot_deployment_get_bootserial (OtDeployment *self)
|
||||
{
|
||||
return self->bootserial;
|
||||
}
|
||||
|
||||
OtConfigParser *
|
||||
ot_deployment_get_bootconfig (OtDeployment *self)
|
||||
{
|
||||
return self->bootconfig;
|
||||
}
|
||||
|
||||
GKeyFile *
|
||||
ot_deployment_get_origin (OtDeployment *self)
|
||||
{
|
||||
return self->origin;
|
||||
}
|
||||
|
||||
int
|
||||
ot_deployment_get_index (OtDeployment *self)
|
||||
{
|
||||
return self->index;
|
||||
}
|
||||
|
||||
void
|
||||
ot_deployment_set_index (OtDeployment *self, int index)
|
||||
{
|
||||
self->index = index;
|
||||
}
|
||||
|
||||
void
|
||||
ot_deployment_set_bootserial (OtDeployment *self, int index)
|
||||
{
|
||||
self->bootserial = index;
|
||||
}
|
||||
|
||||
void
|
||||
ot_deployment_set_bootconfig (OtDeployment *self, OtConfigParser *bootconfig)
|
||||
{
|
||||
g_clear_object (&self->bootconfig);
|
||||
if (bootconfig)
|
||||
self->bootconfig = g_object_ref (bootconfig);
|
||||
}
|
||||
|
||||
void
|
||||
ot_deployment_set_origin (OtDeployment *self, GKeyFile *origin)
|
||||
{
|
||||
g_clear_pointer (&self->origin, g_key_file_unref);
|
||||
if (origin)
|
||||
self->origin = g_key_file_ref (origin);
|
||||
}
|
||||
|
||||
OtDeployment *
|
||||
ot_deployment_clone (OtDeployment *self)
|
||||
{
|
||||
OtDeployment *ret = ot_deployment_new (self->index, self->osname, self->csum,
|
||||
self->deployserial,
|
||||
self->bootcsum, self->bootserial);
|
||||
ot_deployment_set_bootconfig (ret, self->bootconfig);
|
||||
ot_deployment_set_origin (ret, self->origin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
guint
|
||||
ot_deployment_hash (gconstpointer v)
|
||||
{
|
||||
OtDeployment *d = (OtDeployment*)v;
|
||||
return g_str_hash (ot_deployment_get_osname (d)) +
|
||||
g_str_hash (ot_deployment_get_csum (d)) +
|
||||
ot_deployment_get_deployserial (d);
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_deployment_equal (gconstpointer ap, gconstpointer bp)
|
||||
{
|
||||
OtDeployment *a = (OtDeployment*)ap;
|
||||
OtDeployment *b = (OtDeployment*)bp;
|
||||
|
||||
if (a == NULL && b == NULL)
|
||||
return TRUE;
|
||||
else if (a != NULL && b != NULL)
|
||||
return g_str_equal (ot_deployment_get_osname (a),
|
||||
ot_deployment_get_osname (b)) &&
|
||||
g_str_equal (ot_deployment_get_csum (a),
|
||||
ot_deployment_get_csum (b)) &&
|
||||
ot_deployment_get_deployserial (a) == ot_deployment_get_deployserial (b);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
ot_deployment_finalize (GObject *object)
|
||||
{
|
||||
OtDeployment *self = OT_DEPLOYMENT (object);
|
||||
|
||||
g_free (self->osname);
|
||||
g_free (self->csum);
|
||||
g_free (self->bootcsum);
|
||||
g_clear_object (&self->bootconfig);
|
||||
g_clear_pointer (&self->origin, g_key_file_unref);
|
||||
|
||||
G_OBJECT_CLASS (ot_deployment_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
void
|
||||
ot_deployment_init (OtDeployment *self)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ot_deployment_class_init (OtDeploymentClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = ot_deployment_finalize;
|
||||
}
|
||||
|
||||
OtDeployment *
|
||||
ot_deployment_new (int index,
|
||||
const char *osname,
|
||||
const char *csum,
|
||||
int deployserial,
|
||||
const char *bootcsum,
|
||||
int bootserial)
|
||||
{
|
||||
OtDeployment *self;
|
||||
|
||||
/* index may be -1 */
|
||||
g_return_val_if_fail (osname != NULL, NULL);
|
||||
g_return_val_if_fail (csum != NULL, NULL);
|
||||
g_return_val_if_fail (deployserial >= 0, NULL);
|
||||
/* We can have "disconnected" deployments that don't have a
|
||||
bootcsum/serial */
|
||||
|
||||
self = g_object_new (OT_TYPE_DEPLOYMENT, NULL);
|
||||
self->index = index;
|
||||
self->osname = g_strdup (osname);
|
||||
self->csum = g_strdup (csum);
|
||||
self->deployserial = deployserial;
|
||||
self->bootcsum = g_strdup (bootcsum);
|
||||
self->bootserial = bootserial;
|
||||
return self;
|
||||
}
|
66
src/ostree/ot-deployment.h
Normal file
66
src/ostree/ot-deployment.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OT_DEPLOYMENT_H__
|
||||
#define __OT_DEPLOYMENT_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "ot-config-parser.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OT_TYPE_DEPLOYMENT (ot_deployment_get_type ())
|
||||
#define OT_DEPLOYMENT(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OT_TYPE_DEPLOYMENT, OtDeployment))
|
||||
#define OT_IS_DEPLOYMENT(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OT_TYPE_DEPLOYMENT))
|
||||
|
||||
typedef struct _OtDeployment OtDeployment;
|
||||
|
||||
GType ot_deployment_get_type (void) G_GNUC_CONST;
|
||||
|
||||
guint ot_deployment_hash (gconstpointer v);
|
||||
gboolean ot_deployment_equal (gconstpointer a, gconstpointer b);
|
||||
|
||||
OtDeployment * ot_deployment_new (int index,
|
||||
const char *osname,
|
||||
const char *csum,
|
||||
int deployserial,
|
||||
const char *bootcsum,
|
||||
int bootserial);
|
||||
|
||||
int ot_deployment_get_index (OtDeployment *self);
|
||||
const char *ot_deployment_get_osname (OtDeployment *self);
|
||||
int ot_deployment_get_deployserial (OtDeployment *self);
|
||||
const char *ot_deployment_get_csum (OtDeployment *self);
|
||||
const char *ot_deployment_get_bootcsum (OtDeployment *self);
|
||||
int ot_deployment_get_bootserial (OtDeployment *self);
|
||||
OtConfigParser *ot_deployment_get_bootconfig (OtDeployment *self);
|
||||
GKeyFile *ot_deployment_get_origin (OtDeployment *self);
|
||||
|
||||
void ot_deployment_set_index (OtDeployment *self, int index);
|
||||
void ot_deployment_set_bootserial (OtDeployment *self, int index);
|
||||
void ot_deployment_set_bootconfig (OtDeployment *self, OtConfigParser *bootconfig);
|
||||
void ot_deployment_set_origin (OtDeployment *self, GKeyFile *origin);
|
||||
|
||||
OtDeployment *ot_deployment_clone (OtDeployment *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __OT_DEPLOYMENT_H__ */
|
82
src/ostree/ot-ordered-hash.c
Normal file
82
src/ostree/ot-ordered-hash.c
Normal file
@ -0,0 +1,82 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ot-ordered-hash.h"
|
||||
|
||||
OtOrderedHash *
|
||||
ot_ordered_hash_new (void)
|
||||
{
|
||||
OtOrderedHash *ret;
|
||||
ret = g_new0 (OtOrderedHash, 1);
|
||||
ret->order = g_ptr_array_new_with_free_func (g_free);
|
||||
ret->table = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ot_ordered_hash_free (OtOrderedHash *ohash)
|
||||
{
|
||||
if (!ohash)
|
||||
return;
|
||||
g_ptr_array_unref (ohash->order);
|
||||
g_hash_table_unref (ohash->table);
|
||||
g_free (ohash);
|
||||
}
|
||||
|
||||
void
|
||||
ot_ordered_hash_cleanup (void *loc)
|
||||
{
|
||||
ot_ordered_hash_free (*((OtOrderedHash**)loc));
|
||||
}
|
||||
|
||||
void
|
||||
ot_ordered_hash_replace_key_take (OtOrderedHash *ohash,
|
||||
char *key,
|
||||
const char *value)
|
||||
{
|
||||
gboolean existed;
|
||||
|
||||
existed = g_hash_table_remove (ohash->table, key);
|
||||
if (!existed)
|
||||
g_ptr_array_add (ohash->order, key);
|
||||
g_hash_table_insert (ohash->table, key, (char*)value);
|
||||
}
|
||||
|
||||
void
|
||||
ot_ordered_hash_replace_key (OtOrderedHash *ohash,
|
||||
const char *key,
|
||||
const char *val)
|
||||
{
|
||||
GString *buf;
|
||||
gsize keylen;
|
||||
char *valp;
|
||||
char *valblock;
|
||||
|
||||
buf = g_string_new (key);
|
||||
keylen = buf->len;
|
||||
g_string_append_c (buf, '\0');
|
||||
g_string_append (buf, val);
|
||||
valblock = g_string_free (buf, FALSE);
|
||||
valp = valblock + keylen + 1;
|
||||
|
||||
ot_ordered_hash_replace_key_take (ohash, valblock, valp);
|
||||
}
|
46
src/ostree/ot-ordered-hash.h
Normal file
46
src/ostree/ot-ordered-hash.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright (C) 2013 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* This program 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 licence 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, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __OT_ORDERED_HASH_H__
|
||||
#define __OT_ORDERED_HASH_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct {
|
||||
GPtrArray *order;
|
||||
GHashTable *table;
|
||||
} OtOrderedHash;
|
||||
|
||||
OtOrderedHash *ot_ordered_hash_new (void);
|
||||
void ot_ordered_hash_free (OtOrderedHash *ohash);
|
||||
void ot_ordered_hash_cleanup (void *loc);
|
||||
void ot_ordered_hash_replace_key_take (OtOrderedHash *ohash,
|
||||
char *key,
|
||||
const char *value);
|
||||
void ot_ordered_hash_replace_key (OtOrderedHash *ohash,
|
||||
const char *key,
|
||||
const char *val);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __OT_ORDERED_HASH_H__ */
|
@ -41,18 +41,19 @@
|
||||
|
||||
#include "ostree-mount-util.h"
|
||||
|
||||
static void
|
||||
parse_ostree_cmdline (char **out_osname,
|
||||
char **out_tree)
|
||||
static char *
|
||||
parse_ostree_cmdline (void)
|
||||
{
|
||||
FILE *f = fopen("/proc/cmdline", "r");
|
||||
char *cmdline = NULL;
|
||||
const char *iter;
|
||||
char *ret = NULL;
|
||||
size_t len;
|
||||
|
||||
if (!f)
|
||||
return;
|
||||
return NULL;
|
||||
if (getline (&cmdline, &len, f) < 0)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
if (cmdline[len-1] == '\n')
|
||||
cmdline[len-1] = '\0';
|
||||
@ -66,39 +67,30 @@ parse_ostree_cmdline (char **out_osname,
|
||||
next_nonspc += 1;
|
||||
if (strncmp (iter, "ostree=", strlen ("ostree=")) == 0)
|
||||
{
|
||||
const char *slash = strchr (iter, '/');
|
||||
if (slash)
|
||||
{
|
||||
const char *start = iter + strlen ("ostree=");
|
||||
*out_osname = strndup (start, slash - start);
|
||||
if (next)
|
||||
*out_tree = strndup (slash + 1, next - slash - 1);
|
||||
else
|
||||
*out_tree = strdup (slash + 1);
|
||||
break;
|
||||
}
|
||||
const char *start = iter + strlen ("ostree=");
|
||||
if (next)
|
||||
ret = strndup (start, next - start);
|
||||
else
|
||||
ret = strdup (start);
|
||||
break;
|
||||
}
|
||||
iter = next_nonspc;
|
||||
}
|
||||
|
||||
free (cmdline);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const char *toproot_bind_mounts[] = { "/home", "/root", "/tmp", NULL };
|
||||
const char *ostree_bind_mounts[] = { "/var", NULL };
|
||||
const char *readonly_bind_mounts[] = { "/usr", NULL };
|
||||
const char *root_mountpoint = NULL;
|
||||
char *ostree_osname = NULL;
|
||||
char *ostree_target = NULL;
|
||||
char ostree_target_path[PATH_MAX];
|
||||
char *deploy_path = NULL;
|
||||
char srcpath[PATH_MAX];
|
||||
char destpath[PATH_MAX];
|
||||
struct stat stbuf;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
if (argc < 2)
|
||||
@ -108,15 +100,39 @@ main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
root_mountpoint = argv[1];
|
||||
|
||||
parse_ostree_cmdline (&ostree_osname, &ostree_target);
|
||||
|
||||
if (!ostree_osname)
|
||||
if (strcmp (root_mountpoint, "/sysroot") != 0)
|
||||
{
|
||||
fprintf (stderr, "No OSTree target; expected ostree=OSNAME/TREENAME\n");
|
||||
fprintf (stderr, "ostree-prepare-root: Expected /sysroot\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
ostree_target = parse_ostree_cmdline ();
|
||||
if (!ostree_target)
|
||||
{
|
||||
fprintf (stderr, "No OSTree target; expected ostree=/ostree/boot.N/...\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
snprintf (destpath, sizeof(destpath), "%s/%s", root_mountpoint, ostree_target);
|
||||
fprintf (stderr, "Examining %s\n", destpath);
|
||||
if (lstat (destpath, &stbuf) < 0)
|
||||
{
|
||||
perrorv ("Couldn't find specified OSTree root '%s': ", destpath);
|
||||
exit (1);
|
||||
}
|
||||
if (!S_ISLNK (stbuf.st_mode))
|
||||
{
|
||||
fprintf (stderr, "OSTree target is not a symbolic link: %s\n", destpath);
|
||||
exit (1);
|
||||
}
|
||||
deploy_path = realpath (destpath, NULL);
|
||||
if (deploy_path == NULL)
|
||||
{
|
||||
perrorv ("realpath(%s) failed: ", destpath);
|
||||
exit (1);
|
||||
}
|
||||
fprintf (stderr, "Resolved OSTree target to: %s\n", deploy_path);
|
||||
|
||||
/* Work-around for a kernel bug: for some reason the kernel
|
||||
* refuses switching root if any file systems are mounted
|
||||
* MS_SHARED. Hence remount them MS_PRIVATE here as a
|
||||
@ -129,31 +145,6 @@ main(int argc, char *argv[])
|
||||
exit (1);
|
||||
}
|
||||
|
||||
snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s/%s",
|
||||
root_mountpoint, ostree_osname, ostree_target);
|
||||
fprintf (stderr, "Examining %s\n", destpath);
|
||||
if (lstat (destpath, &stbuf) < 0)
|
||||
{
|
||||
perrorv ("Couldn't find specified OSTree root '%s': ", destpath);
|
||||
exit (1);
|
||||
}
|
||||
if (!S_ISLNK (stbuf.st_mode))
|
||||
{
|
||||
fprintf (stderr, "OSTree target is not a symbolic link: %s\n", destpath);
|
||||
exit (1);
|
||||
}
|
||||
if (readlink (destpath, ostree_target_path, PATH_MAX) < 0)
|
||||
{
|
||||
perrorv ("readlink(%s) failed: ", destpath);
|
||||
exit (1);
|
||||
}
|
||||
len = strlen (ostree_target_path);
|
||||
if (ostree_target_path[len-1] == '/')
|
||||
ostree_target_path[len-1] = '\0';
|
||||
fprintf (stderr, "Resolved OSTree target to: %s\n", ostree_target_path);
|
||||
(void) asprintf (&deploy_path, "%s/ostree/deploy/%s/%s", root_mountpoint,
|
||||
ostree_osname, ostree_target_path);
|
||||
|
||||
/* Make deploy_path a bind mount, so we can move it later */
|
||||
if (mount (deploy_path, deploy_path, NULL, MS_BIND, NULL) < 0)
|
||||
{
|
||||
@ -168,43 +159,14 @@ main(int argc, char *argv[])
|
||||
exit (1);
|
||||
}
|
||||
|
||||
snprintf (srcpath, sizeof(srcpath), "%s-etc", deploy_path);
|
||||
snprintf (destpath, sizeof(destpath), "%s/etc", deploy_path);
|
||||
if (mount (srcpath, destpath, NULL, MS_BIND, NULL) < 0)
|
||||
snprintf (srcpath, sizeof(srcpath), "%s/../../var", deploy_path);
|
||||
snprintf (destpath, sizeof(destpath), "%s/var", deploy_path);
|
||||
if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0)
|
||||
{
|
||||
perrorv ("Failed to bind mount '%s' to '%s'", srcpath, destpath);
|
||||
perrorv ("failed to bind mount %s to %s", srcpath, destpath);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
for (i = 0; toproot_bind_mounts[i] != NULL; i++)
|
||||
{
|
||||
snprintf (srcpath, sizeof(srcpath), "%s%s", root_mountpoint, toproot_bind_mounts[i]);
|
||||
snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, toproot_bind_mounts[i]);
|
||||
/* Only do these bind mounts if the target exists and is a real directory,
|
||||
* not a symbolic link.
|
||||
*/
|
||||
if (lstat (destpath, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
|
||||
{
|
||||
if (mount (srcpath, destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to bind mount (class:toproot) %s to %s", toproot_bind_mounts[i], destpath);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; ostree_bind_mounts[i] != NULL; i++)
|
||||
{
|
||||
snprintf (srcpath, sizeof(srcpath), "%s/ostree/deploy/%s%s", root_mountpoint,
|
||||
ostree_osname, ostree_bind_mounts[i]);
|
||||
snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, ostree_bind_mounts[i]);
|
||||
if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to bind mount (class:bind) %s to %s", srcpath, destpath);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; readonly_bind_mounts[i] != NULL; i++)
|
||||
{
|
||||
snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, readonly_bind_mounts[i]);
|
||||
|
@ -1,337 +0,0 @@
|
||||
/* -*- c-file-style: "gnu" -*-
|
||||
* Switch to new root directory and start init.
|
||||
*
|
||||
* Copyright 2011,2012 Colin Walters <walters@verbum.org>
|
||||
*
|
||||
* Based on code from util-linux/sys-utils/switch_root.c,
|
||||
* Copyright 2002-2009 Red Hat, Inc. All rights reserved.
|
||||
* Authors:
|
||||
* Peter Jones <pjones@redhat.com>
|
||||
* Jeremy Katz <katzj@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "ostree-mount-util.h"
|
||||
|
||||
/* remove all files/directories below dirName -- don't cross mountpoints */
|
||||
static int
|
||||
recursive_remove (int fd)
|
||||
{
|
||||
struct stat rb;
|
||||
DIR *dir;
|
||||
int rc = -1;
|
||||
int dfd;
|
||||
|
||||
if (!(dir = fdopendir (fd)))
|
||||
{
|
||||
perrorv ("failed to open directory");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* fdopendir() precludes us from continuing to use the input fd */
|
||||
dfd = dirfd (dir);
|
||||
|
||||
if (fstat(dfd, &rb))
|
||||
{
|
||||
perrorv("failed to stat directory");
|
||||
goto done;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct dirent *d;
|
||||
|
||||
errno = 0;
|
||||
if (!(d = readdir (dir)))
|
||||
{
|
||||
if (errno)
|
||||
{
|
||||
perrorv ("failed to read directory");
|
||||
goto done;
|
||||
}
|
||||
break; /* end of directory */
|
||||
}
|
||||
|
||||
if (!strcmp (d->d_name, ".") || !strcmp (d->d_name, ".."))
|
||||
continue;
|
||||
|
||||
if (d->d_type == DT_DIR)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (fstatat (dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW))
|
||||
{
|
||||
perrorv ("failed to stat %s", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* remove subdirectories if device is same as dir */
|
||||
if (sb.st_dev == rb.st_dev)
|
||||
{
|
||||
int cfd;
|
||||
|
||||
cfd = openat (dfd, d->d_name, O_RDONLY);
|
||||
if (cfd >= 0)
|
||||
{
|
||||
recursive_remove (cfd);
|
||||
close (cfd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlinkat (dfd, d->d_name,
|
||||
d->d_type == DT_DIR ? AT_REMOVEDIR : 0))
|
||||
perrorv ("failed to unlink %s", d->d_name);
|
||||
}
|
||||
|
||||
rc = 0; /* success */
|
||||
|
||||
done:
|
||||
if (dir)
|
||||
closedir (dir);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const char *initramfs_move_mounts[] = { "/dev", "/proc", "/sys", "/run", NULL };
|
||||
const char *toproot_bind_mounts[] = { "/home", "/root", "/tmp", NULL };
|
||||
const char *ostree_bind_mounts[] = { "/var", NULL };
|
||||
const char *readonly_bind_mounts[] = { "/usr", NULL };
|
||||
const char *root_mountpoint = NULL;
|
||||
const char *ostree_target = NULL;
|
||||
const char *ostree_subinit = NULL;
|
||||
const char *p = NULL;
|
||||
char *ostree_osname = NULL;
|
||||
char ostree_target_path[PATH_MAX];
|
||||
char *deploy_path = NULL;
|
||||
char srcpath[PATH_MAX];
|
||||
char destpath[PATH_MAX];
|
||||
struct stat stbuf;
|
||||
char **init_argv = NULL;
|
||||
size_t len;
|
||||
int initramfs_fd;
|
||||
int i;
|
||||
int before_init_argc = 0;
|
||||
pid_t cleanup_pid;
|
||||
|
||||
if (argc < 4)
|
||||
{
|
||||
fprintf (stderr, "usage: ostree-switch-root NEWROOT TARGET INIT [ARGS...]\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
before_init_argc++;
|
||||
root_mountpoint = argv[1];
|
||||
before_init_argc++;
|
||||
ostree_target = argv[2];
|
||||
before_init_argc++;
|
||||
ostree_subinit = argv[3];
|
||||
before_init_argc++;
|
||||
|
||||
p = strchr (ostree_target, '/');
|
||||
if (p == NULL)
|
||||
{
|
||||
fprintf (stderr, "Malformed OSTree target %s; expected OSNAME/TREENAME\n", ostree_target);
|
||||
exit (1);
|
||||
}
|
||||
ostree_osname = strndup (ostree_target, p - ostree_target);
|
||||
|
||||
snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s",
|
||||
root_mountpoint, ostree_target);
|
||||
if (stat (destpath, &stbuf) < 0)
|
||||
{
|
||||
perrorv ("Invalid ostree root '%s'", destpath);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Work-around for a kernel bug: for some reason the kernel
|
||||
* refuses switching root if any file systems are mounted
|
||||
* MS_SHARED. Hence remount them MS_PRIVATE here as a
|
||||
* work-around.
|
||||
*
|
||||
* https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
|
||||
if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
|
||||
{
|
||||
perrorv ("mount(/, MS_PRIVATE): ");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
initramfs_fd = open ("/", O_RDONLY);
|
||||
|
||||
for (i = 0; initramfs_move_mounts[i] != NULL; i++)
|
||||
{
|
||||
const char *path = initramfs_move_mounts[i];
|
||||
snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s%s", root_mountpoint, ostree_target, path);
|
||||
if (mount (path, destpath, NULL, MS_MOVE, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to move mount of %s to %s", path, destpath);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s", root_mountpoint, ostree_target);
|
||||
fprintf (stderr, "Examining %s\n", destpath);
|
||||
if (lstat (destpath, &stbuf) < 0)
|
||||
{
|
||||
perrorv ("Second stat of ostree root '%s' failed: ", destpath);
|
||||
exit (1);
|
||||
}
|
||||
if (!S_ISLNK (stbuf.st_mode))
|
||||
{
|
||||
fprintf (stderr, "OSTree target is not a symbolic link: %s\n", destpath);
|
||||
exit (1);
|
||||
}
|
||||
if (readlink (destpath, ostree_target_path, PATH_MAX) < 0)
|
||||
{
|
||||
perrorv ("readlink(%s) failed: ", destpath);
|
||||
exit (1);
|
||||
}
|
||||
len = strlen (ostree_target_path);
|
||||
if (ostree_target_path[len-1] == '/')
|
||||
ostree_target_path[len-1] = '\0';
|
||||
fprintf (stderr, "Resolved OSTree target to: %s\n", ostree_target_path);
|
||||
(void) asprintf (&deploy_path, "%s/ostree/deploy/%s/%s", root_mountpoint,
|
||||
ostree_osname, ostree_target_path);
|
||||
|
||||
/* Make deploy_path a bind mount, so we can move it later */
|
||||
if (mount (deploy_path, deploy_path, NULL, MS_BIND, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to initial bind mount %s", deploy_path);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
snprintf (destpath, sizeof(destpath), "%s/sysroot", deploy_path);
|
||||
if (mount (root_mountpoint, destpath, NULL, MS_BIND, NULL) < 0)
|
||||
{
|
||||
perrorv ("Failed to bind mount %s to '%s'", root_mountpoint, destpath);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
snprintf (srcpath, sizeof(srcpath), "%s-etc", deploy_path);
|
||||
snprintf (destpath, sizeof(destpath), "%s/etc", deploy_path);
|
||||
if (mount (srcpath, destpath, NULL, MS_BIND, NULL) < 0)
|
||||
{
|
||||
perrorv ("Failed to bind mount '%s' to '%s'", srcpath, destpath);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
for (i = 0; toproot_bind_mounts[i] != NULL; i++)
|
||||
{
|
||||
snprintf (srcpath, sizeof(srcpath), "%s%s", root_mountpoint, toproot_bind_mounts[i]);
|
||||
snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, toproot_bind_mounts[i]);
|
||||
if (mount (srcpath, destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to bind mount (class:toproot) %s to %s", toproot_bind_mounts[i], destpath);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; ostree_bind_mounts[i] != NULL; i++)
|
||||
{
|
||||
snprintf (srcpath, sizeof(srcpath), "%s/ostree/deploy/%s%s", root_mountpoint,
|
||||
ostree_osname, ostree_bind_mounts[i]);
|
||||
snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, ostree_bind_mounts[i]);
|
||||
if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to bind mount (class:bind) %s to %s", srcpath, destpath);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; readonly_bind_mounts[i] != NULL; i++)
|
||||
{
|
||||
snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, readonly_bind_mounts[i]);
|
||||
if (mount (destpath, destpath, NULL, MS_BIND, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to bind mount (class:readonly) %s", destpath);
|
||||
exit (1);
|
||||
}
|
||||
if (mount (destpath, destpath, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to bind mount (class:readonly) %s", destpath);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
if (chdir (deploy_path) < 0)
|
||||
{
|
||||
perrorv ("failed to chdir to subroot (initial)");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (mount (deploy_path, "/", NULL, MS_MOVE, NULL) < 0)
|
||||
{
|
||||
perrorv ("failed to MS_MOVE %s to /", deploy_path);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (chroot (".") < 0)
|
||||
{
|
||||
perrorv ("failed to change root to '%s'", deploy_path);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (chdir ("/") < 0)
|
||||
{
|
||||
perrorv ("failed to chdir to / (after MS_MOVE of /)");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (initramfs_fd >= 0)
|
||||
{
|
||||
cleanup_pid = fork ();
|
||||
if (cleanup_pid == 0)
|
||||
{
|
||||
recursive_remove (initramfs_fd);
|
||||
exit (0);
|
||||
}
|
||||
close (initramfs_fd);
|
||||
}
|
||||
|
||||
init_argv = malloc (sizeof (char*)*((argc-before_init_argc)+2));
|
||||
init_argv[0] = (char*)ostree_subinit;
|
||||
for (i = 0; i < argc-before_init_argc; i++)
|
||||
init_argv[i+1] = argv[i+before_init_argc];
|
||||
init_argv[i+1] = NULL;
|
||||
|
||||
fprintf (stderr, "ostree-init: Running real init %s (argc=%d)\n", init_argv[0], argc-before_init_argc);
|
||||
fflush (stderr);
|
||||
execv (init_argv[0], init_argv);
|
||||
perrorv ("Failed to exec init '%s'", init_argv[0]);
|
||||
exit (1);
|
||||
}
|
||||
|
@ -34,10 +34,18 @@ assert_streq () {
|
||||
test "$1" = "$2" || (echo 1>&2 "$1 != $2"; exit 1)
|
||||
}
|
||||
|
||||
assert_not_streq () {
|
||||
(! test "$1" = "$2") || (echo 1>&2 "$1 == $2"; exit 1)
|
||||
}
|
||||
|
||||
assert_has_file () {
|
||||
test -f "$1" || (echo 1>&2 "Couldn't find '$1'"; exit 1)
|
||||
}
|
||||
|
||||
assert_has_dir () {
|
||||
test -d "$1" || (echo 1>&2 "Couldn't find '$1'"; exit 1)
|
||||
}
|
||||
|
||||
assert_not_has_file () {
|
||||
if test -f "$1"; then
|
||||
echo 1>&2 "File '$1' exists"; exit 1
|
||||
@ -50,6 +58,12 @@ assert_not_file_has_content () {
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_has_dir () {
|
||||
if test -d "$1"; then
|
||||
echo 1>&2 "Directory '$1' exists"; exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_file_has_content () {
|
||||
if ! grep -q -e "$2" "$1"; then
|
||||
echo 1>&2 "File '$1' doesn't match regexp '$2'"; exit 1
|
||||
@ -136,3 +150,86 @@ setup_fake_remote_repo1() {
|
||||
|
||||
export OSTREE="ostree --repo=repo"
|
||||
}
|
||||
|
||||
setup_os_repository () {
|
||||
mode=$1
|
||||
shift
|
||||
|
||||
oldpwd=`pwd`
|
||||
|
||||
cd ${test_tmpdir}
|
||||
mkdir testos-repo
|
||||
if test -n "$mode"; then
|
||||
ostree --repo=testos-repo init --mode=${mode}
|
||||
else
|
||||
ostree --repo=testos-repo init
|
||||
fi
|
||||
|
||||
cd ${test_tmpdir}
|
||||
mkdir osdata
|
||||
cd osdata
|
||||
mkdir -p boot usr/bin usr/lib/modules/3.6.0 usr/share usr/etc
|
||||
echo "a kernel" > boot/vmlinuz-3.6.0
|
||||
echo "an initramfs" > boot/initramfs-3.6.0
|
||||
echo "a kernel module" > usr/lib/modules/3.6.0/foofs.ko
|
||||
bootcsum=$(cat boot/vmlinuz-3-6.0 boot/initramfs-3.6.0 usr/lib/modules/3.6.0/foofs.ko | sha256sum | cut -f 1 -d ' ')
|
||||
export bootcsum
|
||||
mv boot/vmlinuz-3.6.0 boot/vmlinuz-3.6.0-${bootcsum}
|
||||
mv boot/initramfs-3.6.0 boot/initramfs-3.6.0-${bootcsum}
|
||||
|
||||
echo "an executable" > usr/bin/sh
|
||||
echo "some shared data" > usr/share/langs.txt
|
||||
echo "a library" > usr/lib/libfoo.so.0
|
||||
ln -s usr/bin bin
|
||||
cat > usr/etc/os-release <<EOF
|
||||
NAME=TestOS
|
||||
VERSION=42
|
||||
ID=testos
|
||||
VERSION_ID=42
|
||||
PRETTY_NAME="TestOS 42"
|
||||
EOF
|
||||
echo "a config file" > usr/etc/aconfigfile
|
||||
mkdir -p usr/etc/NetworkManager
|
||||
echo "a default daemon file" > usr/etc/NetworkManager/nm.conf
|
||||
|
||||
ostree --repo=${test_tmpdir}/testos-repo commit -b testos/buildmaster/x86_64-runtime -s "Build"
|
||||
|
||||
echo "a new executable" > usr/bin/sh
|
||||
ostree --repo=${test_tmpdir}/testos-repo commit -b testos/buildmaster/x86_64-runtime -s "Build"
|
||||
|
||||
ostree --repo=${test_tmpdir}/testos-repo fsck -q
|
||||
|
||||
cd ${test_tmpdir}
|
||||
mkdir sysroot
|
||||
ostree admin --sysroot=sysroot init-fs sysroot
|
||||
ostree admin --sysroot=sysroot os-init testos
|
||||
|
||||
# Stub syslinux configuration
|
||||
mkdir -p sysroot/boot/loader.0
|
||||
ln -s loader.0 sysroot/boot/loader
|
||||
touch sysroot/boot/loader/syslinux.cfg
|
||||
# And a compatibility symlink
|
||||
mkdir -p sysroot/boot/syslinux
|
||||
ln -s ../loader/syslinux.cfg sysroot/boot/syslinux/syslinux.cfg
|
||||
}
|
||||
|
||||
os_repository_new_commit ()
|
||||
{
|
||||
cd ${test_tmpdir}/osdata
|
||||
rm boot/*
|
||||
echo "new: a kernel" > boot/vmlinuz-3.6.0
|
||||
echo "new: an initramfs" > boot/initramfs-3.6.0
|
||||
echo "new: a kernel module" > usr/lib/modules/3.6.0/foofs.ko
|
||||
echo "new: another kernel module" > usr/lib/modules/3.6.0/othermod.ko
|
||||
bootcsum=$(cat boot/vmlinuz-3.6.0 boot/initramfs-3.6.0 usr/lib/modules/3.6.0/foofs.ko usr/lib/modules/3.6.0/othermod.ko | sha256sum | cut -f 1 -d ' ')
|
||||
export bootcsum
|
||||
mv boot/vmlinuz-3.6.0 boot/vmlinuz-3.6.0-${bootcsum}
|
||||
mv boot/initramfs-3.6.0 boot/initramfs-3.6.0-${bootcsum}
|
||||
|
||||
echo "a new default config file" > usr/etc/a-new-default-config-file
|
||||
mkdir -p usr/etc/new-default-dir
|
||||
echo "a new default dir and file" > usr/etc/new-default-dir/moo
|
||||
|
||||
ostree --repo=${test_tmpdir}/testos-repo commit -b testos/buildmaster/x86_64-runtime -s "Build"
|
||||
cd ${test_tmpdir}
|
||||
}
|
||||
|
134
tests/t0015-admin-deploy.sh
Executable file
134
tests/t0015-admin-deploy.sh
Executable file
@ -0,0 +1,134 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2011 Colin Walters <walters@verbum.org>
|
||||
#
|
||||
# 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, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
set -e
|
||||
|
||||
. $(dirname $0)/libtest.sh
|
||||
|
||||
echo "1..1"
|
||||
|
||||
setup_os_repository "archive-z2"
|
||||
|
||||
echo "ok setup"
|
||||
|
||||
echo "1..7"
|
||||
|
||||
ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime
|
||||
rev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime)
|
||||
export rev
|
||||
# This initial deployment gets kicked off with some kernel arguments
|
||||
ostree admin --sysroot=sysroot deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime
|
||||
|
||||
echo "ok deploy command"
|
||||
|
||||
assert_not_has_dir sysroot/boot/loader.0
|
||||
assert_has_dir sysroot/boot/loader.1
|
||||
assert_has_dir sysroot/ostree/boot.1.1
|
||||
assert_has_file sysroot/boot/loader/entries/ostree-testos-${rev}-0.conf
|
||||
assert_file_has_content sysroot/boot/loader/entries/ostree-testos-${rev}-0.conf 'options.* root=LABEL=MOO'
|
||||
assert_file_has_content sysroot/boot/loader/entries/ostree-testos-${rev}-0.conf 'options.* quiet'
|
||||
assert_file_has_content sysroot/boot/ostree/testos-${bootcsum}/vmlinuz-3.6.0 'a kernel'
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/etc/os-release 'NAME=TestOS'
|
||||
assert_file_has_content sysroot/ostree/boot.1/testos/${bootcsum}/0/etc/os-release 'NAME=TestOS'
|
||||
|
||||
echo "ok layout"
|
||||
|
||||
ostree admin --sysroot=sysroot deploy --os=testos testos:testos/buildmaster/x86_64-runtime
|
||||
# Need a new bootversion, sine we now have two deployments
|
||||
assert_has_dir sysroot/boot/loader.0
|
||||
assert_not_has_dir sysroot/boot/loader.1
|
||||
assert_has_dir sysroot/ostree/boot.0.1
|
||||
assert_not_has_dir sysroot/ostree/boot.0.0
|
||||
assert_not_has_dir sysroot/ostree/boot.1.0
|
||||
assert_not_has_dir sysroot/ostree/boot.1.1
|
||||
# Ensure we propagated kernel arguments from previous deployment
|
||||
assert_file_has_content sysroot/boot/loader/entries/ostree-testos-${rev}-0.conf 'options.* root=LABEL=MOO'
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.1/etc/os-release 'NAME=TestOS'
|
||||
assert_file_has_content sysroot/ostree/boot.0/testos/${bootcsum}/0/etc/os-release 'NAME=TestOS'
|
||||
|
||||
echo "ok second deploy"
|
||||
|
||||
ostree admin --sysroot=sysroot deploy --os=testos testos:testos/buildmaster/x86_64-runtime
|
||||
# Keep the same bootversion
|
||||
assert_has_dir sysroot/boot/loader.0
|
||||
assert_not_has_dir sysroot/boot/loader.1
|
||||
# But swap subbootversion
|
||||
assert_has_dir sysroot/ostree/boot.0.0
|
||||
assert_not_has_dir sysroot/ostree/boot.0.1
|
||||
|
||||
echo "ok third deploy (swap)"
|
||||
|
||||
ostree admin --sysroot=sysroot deploy --os=otheros testos/buildmaster/x86_64-runtime
|
||||
assert_not_has_dir sysroot/boot/loader.0
|
||||
assert_has_dir sysroot/boot/loader.1
|
||||
assert_has_file sysroot/boot/loader/entries/ostree-testos-${rev}-0.conf
|
||||
assert_has_file sysroot/boot/loader/entries/ostree-otheros-${rev}-0.conf
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.1/etc/os-release 'NAME=TestOS'
|
||||
assert_file_has_content sysroot/ostree/deploy/otheros/deploy/${rev}.0/etc/os-release 'NAME=TestOS'
|
||||
|
||||
echo "ok independent deploy"
|
||||
|
||||
ostree admin --sysroot=sysroot deploy --retain --os=testos testos:testos/buildmaster/x86_64-runtime
|
||||
assert_has_dir sysroot/boot/loader.0
|
||||
assert_not_has_dir sysroot/boot/loader.1
|
||||
assert_has_file sysroot/boot/loader/entries/ostree-testos-${rev}-0.conf
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.2/etc/os-release 'NAME=TestOS'
|
||||
assert_has_file sysroot/boot/loader/entries/ostree-testos-${rev}-2.conf
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/os-release 'NAME=TestOS'
|
||||
|
||||
echo "ok fourth deploy (retain)"
|
||||
|
||||
echo "a new local config file" > sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/a-new-config-file
|
||||
rm sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/aconfigfile
|
||||
ln -s /ENOENT sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/a-new-broken-symlink
|
||||
ostree admin --sysroot=sysroot deploy --retain --os=testos testos:testos/buildmaster/x86_64-runtime
|
||||
linktarget=$(readlink sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/a-new-broken-symlink)
|
||||
test "${linktarget}" = /ENOENT
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/os-release 'NAME=TestOS'
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/os-release 'NAME=TestOS'
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/a-new-config-file 'a new local config file'
|
||||
assert_not_has_file sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/aconfigfile
|
||||
|
||||
echo "ok deploy with modified /etc"
|
||||
|
||||
os_repository_new_commit
|
||||
ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime
|
||||
newrev=$(ostree --repo=sysroot/ostree/repo rev-parse testos:testos/buildmaster/x86_64-runtime)
|
||||
export newrev
|
||||
assert_not_streq ${rev} ${newrev}
|
||||
|
||||
ostree admin --sysroot=sysroot deploy --os=testos testos:testos/buildmaster/x86_64-runtime
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS'
|
||||
# New files in /usr/etc
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/a-new-default-config-file "a new default config file"
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/new-default-dir/moo "a new default dir and file"
|
||||
# And persist /etc changes from before
|
||||
assert_not_has_file sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/aconfigfile
|
||||
|
||||
echo "ok upgrade bare"
|
||||
|
||||
os_repository_new_commit
|
||||
ostree --repo=sysroot/ostree/repo remote add testos file://$(pwd)/testos-repo testos/buildmaster/x86_64-runtime
|
||||
ostree admin --sysroot=sysroot upgrade --os=testos
|
||||
rev=${newrev}
|
||||
newrev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime)
|
||||
assert_not_streq ${rev} ${newrev}
|
||||
assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS'
|
||||
|
||||
echo "ok upgrade"
|
Loading…
Reference in New Issue
Block a user