From 3832544ac40b5bbfcee3621cc5ab096ac64e578d Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 19 Dec 2012 17:52:46 -0500 Subject: [PATCH] admin: Rework /ostree/deploy to support multiple independent operating systems The real vision of OSTree is to "multiple versions of multiple operating systems". Up until now, it's worked to install gnome-ostree inside a host distribution, but several things don't work quite right if you try to do completely different systems. In the new model, there's the concept of an "osname" which encompasses a few properties: 1) Its own /var 2) A set of trees deployed in /ostree/deploy/OSNAME/ 3) Its own "current" and "previous" links. Now it no longer really makes sense to boot with "ostree=current". Instead, you specify e.g. "ostree=gnome/current". This is an incompatible change to the deployment code - you will need to run init-os gnome and redeploy. All "ostree admin" subcommands now take an OSNAME argument. --- Makefile-ostree.am | 8 +- src/ostree/ostree-curl-fetcher.c | 204 ++++++++++++++++++ src/ostree/ostree-curl-fetcher.h | 59 +++++ src/ostree/ot-admin-builtin-deploy.c | 73 ++++--- src/ostree/ot-admin-builtin-diff.c | 17 +- src/ostree/ot-admin-builtin-install.c | 200 +++++++++++++++++ src/ostree/ot-admin-builtin-os-init.c | 105 +++++++++ src/ostree/ot-admin-builtin-prune.c | 21 +- src/ostree/ot-admin-builtin-pull-deploy.c | 91 ++------ src/ostree/ot-admin-builtin-update-kernel.c | 19 +- ...ltin-init.c => ot-admin-builtin-upgrade.c} | 32 ++- src/ostree/ot-admin-builtins.h | 4 +- src/ostree/ot-admin-functions.c | 53 +---- src/ostree/ot-admin-functions.h | 2 + src/ostree/ot-builtin-admin.c | 4 +- src/switchroot/ostree-switch-root.c | 78 ++++--- 16 files changed, 758 insertions(+), 212 deletions(-) create mode 100644 src/ostree/ostree-curl-fetcher.c create mode 100644 src/ostree/ostree-curl-fetcher.h create mode 100644 src/ostree/ot-admin-builtin-install.c create mode 100644 src/ostree/ot-admin-builtin-os-init.c rename src/ostree/{ot-admin-builtin-init.c => ot-admin-builtin-upgrade.c} (55%) diff --git a/Makefile-ostree.am b/Makefile-ostree.am index d4649c5c..0066765c 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -22,8 +22,10 @@ bin_PROGRAMS += ostree endif ostree_SOURCES = src/ostree/main.c \ - src/ostree/ot-builtins.h \ + src/ostree/ostree-curl-fetcher.h \ + src/ostree/ostree-curl-fetcher.c \ src/ostree/ot-builtin-admin.c \ + src/ostree/ot-builtins.h \ src/ostree/ot-builtin-cat.c \ src/ostree/ot-builtin-config.c \ src/ostree/ot-builtin-checkout.c \ @@ -46,12 +48,14 @@ ostree_SOURCES = src/ostree/main.c \ # Admin subcommand ostree_SOURCES += \ - src/ostree/ot-admin-builtin-init.c \ src/ostree/ot-admin-builtin-init-fs.c \ 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-upgrade.c \ src/ostree/ot-admin-builtin-update-kernel.c \ src/ostree/ot-admin-builtins.h \ src/ostree/ot-admin-functions.h \ diff --git a/src/ostree/ostree-curl-fetcher.c b/src/ostree/ostree-curl-fetcher.c new file mode 100644 index 00000000..fbe96abc --- /dev/null +++ b/src/ostree/ostree-curl-fetcher.c @@ -0,0 +1,204 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2011,2012 Colin Walters + * + * 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 + */ + +#include "config.h" + +#include "ostree-curl-fetcher.h" +#include "ostree.h" + +struct OstreeCurlFetcher +{ + GObject parent_instance; + + GFile *tmpdir; + + GSSubprocess *curl_proc; + GQueue *queue; +}; + +G_DEFINE_TYPE (OstreeCurlFetcher, ostree_curl_fetcher, G_TYPE_OBJECT) + +static void +ostree_curl_fetcher_finalize (GObject *object) +{ + OstreeCurlFetcher *self; + + self = OSTREE_CURL_FETCHER (object); + + g_clear_object (&self->curl_proc); + g_queue_free (self->queue); + + G_OBJECT_CLASS (ostree_curl_fetcher_parent_class)->finalize (object); +} + +static void +ostree_curl_fetcher_class_init (OstreeCurlFetcherClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = ostree_curl_fetcher_finalize; +} + +static void +ostree_curl_fetcher_init (OstreeCurlFetcher *self) +{ + self->queue = g_queue_new (); +} + +OstreeCurlFetcher * +ostree_curl_fetcher_new (GFile *tmpdir) +{ + OstreeCurlFetcher *self = (OstreeCurlFetcher*)g_object_new (OSTREE_TYPE_CURL_FETCHER, NULL); + + self->tmpdir = g_object_ref (tmpdir); + + return self; +} + +typedef struct { + OstreeCurlFetcher *self; + gchar *uri; + GFile *tmpfile; + GCancellable *cancellable; + GSimpleAsyncResult *result; +} OstreeCurlFetcherOp; + +static void +fetcher_op_free (OstreeCurlFetcherOp *op) +{ + g_clear_object (&op->self); + g_free (op->uri); + g_clear_object (&op->tmpfile); + g_clear_object (&op->cancellable); + g_free (op); +} + +static void +maybe_fetch (OstreeCurlFetcher *self); + +static void +on_curl_exited (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GSSubprocess *proc = GS_SUBPROCESS (object); + OstreeCurlFetcherOp *op = user_data; + GError *error = NULL; + int estatus; + + if (!gs_subprocess_wait_finish (proc, result, &estatus, &error)) + goto out; + + if (!g_spawn_check_exit_status (estatus, &error)) + goto out; + + out: + if (error) + g_simple_async_result_take_error (op->result, error); + + g_simple_async_result_complete (op->result); + + g_clear_object (&op->self->curl_proc); + + maybe_fetch (op->self); + + g_object_unref (op->result); +} + +static void +maybe_fetch (OstreeCurlFetcher *self) +{ + OstreeCurlFetcherOp *op; + GError *error = NULL; + gs_unref_object GSSubprocessContext *context = NULL; + + if (self->curl_proc != NULL + || g_queue_is_empty (self->queue)) + return; + + op = g_queue_pop_head (self->queue); + + if (!ostree_create_temp_regular_file (self->tmpdir, NULL, NULL, + &op->tmpfile, NULL, + op->cancellable, &error)) + goto out; + + context = gs_subprocess_context_newv ("curl", op->uri, "-o", + gs_file_get_path_cached (op->tmpfile), + NULL); + g_assert (self->curl_proc == NULL); + self->curl_proc = gs_subprocess_new (context, op->cancellable, &error); + if (!self->curl_proc) + goto out; + + gs_subprocess_wait (self->curl_proc, op->cancellable, + on_curl_exited, op); + + out: + if (error) + { + g_simple_async_result_take_error (op->result, error); + g_simple_async_result_complete (op->result); + } +} + +void +ostree_curl_fetcher_request_uri_async (OstreeCurlFetcher *self, + const char *uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + OstreeCurlFetcherOp *op; + + op = g_new0 (OstreeCurlFetcherOp, 1); + op->self = g_object_ref (self); + op->uri = g_strdup (uri); + op->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + op->result = g_simple_async_result_new ((GObject*) self, callback, user_data, + ostree_curl_fetcher_request_uri_async); + + g_queue_push_tail (self->queue, op); + + g_simple_async_result_set_op_res_gpointer (op->result, op, + (GDestroyNotify) fetcher_op_free); + + maybe_fetch (self); +} + +GFile * +ostree_curl_fetcher_request_uri_finish (OstreeCurlFetcher *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + OstreeCurlFetcherOp *op; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, (GObject*)self, ostree_curl_fetcher_request_uri_async), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + op = g_simple_async_result_get_op_res_gpointer (simple); + + return g_object_ref (op->tmpfile); +} diff --git a/src/ostree/ostree-curl-fetcher.h b/src/ostree/ostree-curl-fetcher.h new file mode 100644 index 00000000..d7276aac --- /dev/null +++ b/src/ostree/ostree-curl-fetcher.h @@ -0,0 +1,59 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 Colin Walters + * + * 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. + */ + +#ifndef _OSTREE_CURL_FETCHER +#define _OSTREE_CURL_FETCHER + +#include + +G_BEGIN_DECLS + +#define OSTREE_TYPE_CURL_FETCHER (ostree_curl_fetcher_get_type ()) +#define OSTREE_CURL_FETCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_CURL_FETCHER, OstreeCurlFetcher)) +#define OSTREE_CURL_FETCHER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_CURL_FETCHER, OstreeCurlFetcherClass)) +#define OSTREE_IS_CURL_FETCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_CURL_FETCHER)) +#define OSTREE_IS_CURL_FETCHER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_CURL_FETCHER)) +#define OSTREE_CURL_FETCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_CURL_FETCHER, OstreeCurlFetcherClass)) + +typedef struct OstreeCurlFetcherClass OstreeCurlFetcherClass; +typedef struct OstreeCurlFetcher OstreeCurlFetcher; + +struct OstreeCurlFetcherClass +{ + GObjectClass parent_class; +}; + +GType ostree_curl_fetcher_get_type (void) G_GNUC_CONST; + +OstreeCurlFetcher *ostree_curl_fetcher_new (GFile *tmpdir); + +void ostree_curl_fetcher_request_uri_async (OstreeCurlFetcher *self, + const char *uri, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +GFile *ostree_curl_fetcher_request_uri_finish (OstreeCurlFetcher *self, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index 7153d20b..d870a990 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -31,6 +31,8 @@ typedef struct { OstreeRepo *repo; GFile *ostree_dir; + char *osname; + GFile *osname_dir; } OtAdminDeploy; static gboolean opt_no_kernel; @@ -63,21 +65,18 @@ update_current (OtAdminDeploy *self, { 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->ostree_dir, "current"); - current_etc_path = g_file_get_child (self->ostree_dir, "current-etc"); - previous_path = g_file_get_child (self->ostree_dir, "previous"); + current_path = g_file_get_child (self->osname_dir, "current"); + previous_path = g_file_get_child (self->osname_dir, "previous"); - relative_current = g_file_get_relative_path (self->ostree_dir, deploy_target); + 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); @@ -92,10 +91,10 @@ update_current (OtAdminDeploy *self, return TRUE; } - tmp_previous_path = g_file_get_child (self->ostree_dir, "tmp-previous"); + 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->ostree_dir, current_deployment); + 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) { @@ -104,7 +103,7 @@ update_current (OtAdminDeploy *self, } } - tmp_current_path = g_file_get_child (self->ostree_dir, "tmp-current"); + 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) @@ -113,20 +112,9 @@ update_current (OtAdminDeploy *self, goto out; } - tmp_current_etc_path = g_file_get_child (self->ostree_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) { @@ -367,7 +355,7 @@ merge_etc_changes (OtAdminDeploy *self, * deploy_tree: * * Look up @revision in the repository, and check it out in - * OSTREE_DIR/deploy/DEPLOY_TARGET. + * OSTREE_DIR/deploy/OS/DEPLOY_TARGET. * * Merge configuration changes from the old deployment, if any. * @@ -383,9 +371,8 @@ deploy_tree (OtAdminDeploy *self, GError **error) { gboolean ret = FALSE; - const char *current_deployment_ref = "deployment/current"; - const char *previous_deployment_ref = "deployment/previous"; - ot_lobj GFile *deploy_dir = NULL; + gs_free char *current_deployment_ref = NULL; + gs_free char *previous_deployment_ref = NULL; ot_lfree char *deploy_target_fullname = NULL; ot_lfree char *deploy_target_fullname_tmp = NULL; ot_lobj GFile *deploy_target_path = NULL; @@ -410,7 +397,16 @@ deploy_tree (OtAdminDeploy *self, if (!revision) revision = deploy_target; - deploy_dir = g_file_get_child (self->ostree_dir, "deploy"); + current_deployment_ref = g_strdup_printf ("deployment/%s/current", self->osname); + previous_deployment_ref = g_strdup_printf ("deployment/%s/previous", self->osname); + + 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, &resolved_commit, error)) goto out; @@ -426,17 +422,17 @@ deploy_tree (OtAdminDeploy *self, goto out; deploy_target_fullname = g_strconcat (deploy_target, "-", resolved_commit, NULL); - deploy_target_path = g_file_resolve_relative_path (deploy_dir, deploy_target_fullname); + 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 (deploy_dir, deploy_target_fullname_tmp); + deploy_target_path_tmp = g_file_resolve_relative_path (self->osname_dir, deploy_target_fullname_tmp); deploy_parent = g_file_get_parent (deploy_target_path); if (!gs_file_ensure_directory (deploy_parent, TRUE, cancellable, error)) goto out; deploy_target_etc_name = g_strconcat (deploy_target, "-", resolved_commit, "-etc", NULL); - deploy_target_etc_path = g_file_resolve_relative_path (deploy_dir, deploy_target_etc_name); + 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)) @@ -470,7 +466,7 @@ deploy_tree (OtAdminDeploy *self, goto out; } - if (!ot_admin_get_current_deployment (self->ostree_dir, &previous_deployment, + if (!ot_admin_get_current_deployment (self->ostree_dir, self->osname, &previous_deployment, cancellable, error)) goto out; if (previous_deployment) @@ -588,6 +584,7 @@ do_update_kernel (OtAdminDeploy *self, ot_ptrarray_add_many (args, "ostree", "admin", "--ostree-dir", gs_file_get_path_cached (self->ostree_dir), "update-kernel", + self->osname, gs_file_get_path_cached (deploy_path), NULL); if (opt_no_kernel) g_ptr_array_add (args, "--modules-only"); @@ -618,22 +615,23 @@ ot_admin_builtin_deploy (int argc, char **argv, GFile *ostree_dir, GError **erro 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; memset (self, 0, sizeof (*self)); - context = g_option_context_new ("NAME [REVISION] - Check out revision NAME (or REVISION as NAME)"); + context = g_option_context_new ("OSNAME TREENAME [REVISION] - In operating system OS, check out revision TREENAME (or REVISION as TREENAME)"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; - if (argc < 2) + if (argc < 3) { - ot_util_usage_error (context, "NAME must be specified", error); + ot_util_usage_error (context, "OSNAME and TREENAME must be specified", error); goto out; } @@ -647,10 +645,13 @@ ot_admin_builtin_deploy (int argc, char **argv, GFile *ostree_dir, GError **erro if (!ostree_repo_check (self->repo, error)) goto out; - deploy_target = argv[1]; - if (argc > 2) - revision = argv[2]; + 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); if (!deploy_tree (self, deploy_target, revision, &deploy_path, cancellable, error)) goto out; @@ -661,7 +662,9 @@ ot_admin_builtin_deploy (int argc, char **argv, GFile *ostree_dir, GError **erro ret = TRUE; out: g_clear_object (&self->repo); + g_free (self->osname); g_clear_object (&self->ostree_dir); + g_clear_object (&self->osname_dir); if (context) g_option_context_free (context); return ret; diff --git a/src/ostree/ot-admin-builtin-diff.c b/src/ostree/ot-admin-builtin-diff.c index 7b9eb498..64c2bcc3 100644 --- a/src/ostree/ot-admin-builtin-diff.c +++ b/src/ostree/ot-admin-builtin-diff.c @@ -37,6 +37,7 @@ ot_admin_builtin_diff (int argc, char **argv, GFile *ostree_dir, GError **error) { GOptionContext *context; gboolean ret = FALSE; + const char *osname; ot_lobj GFile *repo_path = NULL; ot_lobj GFile *deployment = NULL; ot_lobj GFile *deploy_parent = NULL; @@ -47,7 +48,7 @@ ot_admin_builtin_diff (int argc, char **argv, GFile *ostree_dir, GError **error) ot_lobj GFile *new_etc_path = NULL; __attribute__((unused)) GCancellable *cancellable = NULL; - context = g_option_context_new ("[NAME] - Diff configuration for revision NAME"); + context = g_option_context_new ("OSNAME [REVISION] - Diff configuration for OSNAME"); g_option_context_add_main_entries (context, options, NULL); @@ -56,9 +57,17 @@ ot_admin_builtin_diff (int argc, char **argv, GFile *ostree_dir, GError **error) repo_path = g_file_get_child (ostree_dir, "repo"); - if (argc > 1) + if (argc < 2) { - deployment = ot_gfile_get_child_build_path (ostree_dir, "deploy", argv[1], NULL); + ot_util_usage_error (context, "OSNAME must be specified", error); + goto out; + } + + osname = argv[1]; + + if (argc > 2) + { + 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, @@ -68,7 +77,7 @@ ot_admin_builtin_diff (int argc, char **argv, GFile *ostree_dir, GError **error) } else { - if (!ot_admin_get_current_deployment (ostree_dir, &deployment, + if (!ot_admin_get_current_deployment (ostree_dir, osname, &deployment, cancellable, error)) goto out; } diff --git a/src/ostree/ot-admin-builtin-install.c b/src/ostree/ot-admin-builtin-install.c new file mode 100644 index 00000000..7e0cb459 --- /dev/null +++ b/src/ostree/ot-admin-builtin-install.c @@ -0,0 +1,200 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 Colin Walters + * + * 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 + */ + +#include "config.h" + +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ostree-curl-fetcher.h" +#include "otutil.h" + +#include +#include +#include + +typedef struct { + GMainLoop *loop; + GFile *osconfig_path; +} OtAdminBuiltinInstall; + +static GOptionEntry options[] = { + { NULL } +}; + +static void +on_keyfile_retrieved (GObject *obj, + GAsyncResult *result, + gpointer user_data) +{ + OtAdminBuiltinInstall *self = user_data; + GError *error = NULL; + + self->osconfig_path = ostree_curl_fetcher_request_uri_finish ((OstreeCurlFetcher*)obj, result, &error); + if (!self->osconfig_path) + goto out; + + out: + if (error) + { + g_printerr ("%s\n", error->message); + exit (1); + } + g_main_loop_quit (self->loop); +} + +gboolean +ot_admin_builtin_install (int argc, char **argv, GFile *ostree_dir, GError **error) +{ + OtAdminBuiltinInstall self_data; + OtAdminBuiltinInstall *self = &self_data; + GOptionContext *context; + gboolean ret = FALSE; + const char *keyfile_arg = NULL; + const char *treename_arg = NULL; + ot_lobj GFile *deploy_dir = NULL; + ot_lobj GFile *osdir = NULL; + ot_lobj GFile *dest_osconfig_path = NULL; + gs_unref_ptrarray GPtrArray *subproc_args = NULL; + ot_lfree char *osname = NULL; + ot_lfree char *repoarg = NULL; + ot_lfree char *ostree_dir_arg = NULL; + ot_lfree char *tree_to_deploy = NULL; + GKeyFile *keyfile = NULL; + __attribute__((unused)) GCancellable *cancellable = NULL; + + memset (self, 0, sizeof (*self)); + + context = g_option_context_new ("KEYFILE [TREE] - Initialize, download, and deploy operating system"); + 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, "KEYFILE must be specified", error); + goto out; + } + + self->loop = g_main_loop_new (NULL, TRUE); + + keyfile_arg = argv[1]; + if (argc > 2) + treename_arg = argv[2]; + + keyfile = g_key_file_new (); + + if (g_str_has_prefix (keyfile_arg, "http://") || g_str_has_prefix (keyfile_arg, "https://")) + { + ot_lobj GFile *tmp = g_file_new_for_path (g_get_tmp_dir ()); + ot_lobj OstreeCurlFetcher *fetcher = ostree_curl_fetcher_new (tmp); + + g_print ("Fetching %s...\n", keyfile_arg); + ostree_curl_fetcher_request_uri_async (fetcher, keyfile_arg, cancellable, + on_keyfile_retrieved, self); + + g_main_loop_run (self->loop); + } + else + { + self->osconfig_path = g_file_new_for_path (keyfile_arg); + } + + if (!g_key_file_load_from_file (keyfile, gs_file_get_path_cached (self->osconfig_path), 0, + error)) + goto out; + + osname = g_key_file_get_string (keyfile, "os", "Name", error); + + 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_NULL, + cancellable, error, + "ostree", "admin", ostree_dir_arg, "os-init", osname, NULL)) + goto out; + + if (treename_arg) + { + tree_to_deploy = g_strdup (treename_arg); + } + else + { + tree_to_deploy = g_key_file_get_string (keyfile, "os", "TreeDefault", error); + if (!tree_to_deploy) + goto out; + } + + osdir = ot_gfile_get_child_build_path (ostree_dir, "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, + cancellable, NULL, NULL, error)) + goto out; + + if (!gs_file_unlink (self->osconfig_path, cancellable, error)) + goto out; + + repoarg = g_strconcat ("--repo=", + gs_file_get_path_cached (ostree_dir), "/repo", + NULL); + + { + ot_lfree char *repourl = NULL; + + repourl = g_key_file_get_string (keyfile, "os", "Repo", error); + if (!repourl) + goto out; + + if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir), + GS_SUBPROCESS_STREAM_DISPOSITION_NULL, + cancellable, error, + "ostree", repoarg, "remote", "add", + osname, repourl, tree_to_deploy, NULL)) + goto out; + } + + if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir), + GS_SUBPROCESS_STREAM_DISPOSITION_NULL, + cancellable, error, + "ostree", "pull", repoarg, osname, NULL)) + goto out; + + if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir), + GS_SUBPROCESS_STREAM_DISPOSITION_NULL, + cancellable, error, + "ostree", "admin", ostree_dir_arg, "deploy", osname, + tree_to_deploy, NULL)) + goto out; + + ret = TRUE; + out: + if (self->loop) + g_main_loop_unref (self->loop); + g_clear_object (&self->osconfig_path); + g_clear_pointer (&keyfile, (GDestroyNotify) g_key_file_unref); + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostree/ot-admin-builtin-os-init.c b/src/ostree/ot-admin-builtin-os-init.c new file mode 100644 index 00000000..3cd38aa8 --- /dev/null +++ b/src/ostree/ot-admin-builtin-os-init.c @@ -0,0 +1,105 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012 Colin Walters + * + * 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 + */ + +#include "config.h" + +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "otutil.h" + +#include + +static GOptionEntry options[] = { + { NULL } +}; + +gboolean +ot_admin_builtin_os_init (int argc, char **argv, GFile *ostree_dir, GError **error) +{ + GOptionContext *context; + gboolean ret = FALSE; + const char *osname = NULL; + ot_lobj GFile *deploy_dir = NULL; + ot_lobj GFile *dir = NULL; + __attribute__((unused)) GCancellable *cancellable = NULL; + + context = g_option_context_new ("OSNAME - Initialize empty state for given operating system"); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + + if (!ot_admin_ensure_initialized (ostree_dir, cancellable, error)) + goto out; + + if (argc < 2) + { + ot_util_usage_error (context, "OSNAME must be specified", error); + goto out; + } + + osname = argv[1]; + + deploy_dir = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, NULL); + + /* Ensure core subdirectories of /var exist, since we need them for + * dracut generation, and the host will want them too. + */ + g_clear_object (&dir); + dir = ot_gfile_get_child_build_path (deploy_dir, "var", "log", NULL); + if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) + goto out; + + g_clear_object (&dir); + dir = ot_gfile_get_child_build_path (deploy_dir, "var", "tmp", NULL); + if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) + goto out; + if (chmod (gs_file_get_path_cached (dir), 01777) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + + g_clear_object (&dir); + dir = ot_gfile_get_child_build_path (deploy_dir, "var", "lib", NULL); + if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) + goto out; + + g_clear_object (&dir); + dir = ot_gfile_get_child_build_path (deploy_dir, "var", "run", NULL); + if (!g_file_test (gs_file_get_path_cached (dir), G_FILE_TEST_IS_SYMLINK)) + { + if (symlink ("../run", gs_file_get_path_cached (dir)) < 0) + { + ot_util_set_error_from_errno (error, errno); + goto out; + } + } + + g_print ("%s initialized as OSTree root\n", gs_file_get_path_cached (deploy_dir)); + + ret = TRUE; + out: + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostree/ot-admin-builtin-prune.c b/src/ostree/ot-admin-builtin-prune.c index 14ebee6c..d2b2cc52 100644 --- a/src/ostree/ot-admin-builtin-prune.c +++ b/src/ostree/ot-admin-builtin-prune.c @@ -103,29 +103,38 @@ ot_admin_builtin_prune (int argc, char **argv, GFile *ostree_dir, GError **error GOptionContext *context; gboolean ret = FALSE; guint i; + const char *osname; 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_lptrarray GPtrArray *deployments = NULL; __attribute__((unused)) GCancellable *cancellable = NULL; - context = g_option_context_new ("- Delete untagged deployments and repository objects"); + context = g_option_context_new ("OSNAME - Delete untagged deployments and repository objects"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; - if (!ot_admin_ensure_initialized (ostree_dir, cancellable, error)) - goto out; + if (argc < 2) + { + ot_util_usage_error (context, "OSNAME must be specified", error); + goto out; + } + + osname = argv[1]; + + deploy_dir = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, NULL); deployments = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - if (!list_deployments (ostree_dir, deployments, cancellable, error)) + if (!list_deployments (deploy_dir, deployments, cancellable, error)) goto out; - if (!ot_admin_get_current_deployment (ostree_dir, ¤t_deployment, + if (!ot_admin_get_current_deployment (ostree_dir, osname, ¤t_deployment, cancellable, error)); - if (!ot_admin_get_previous_deployment (ostree_dir, &previous_deployment, + if (!ot_admin_get_previous_deployment (ostree_dir, osname, &previous_deployment, cancellable, error)); for (i = 0; i < deployments->len; i++) diff --git a/src/ostree/ot-admin-builtin-pull-deploy.c b/src/ostree/ot-admin-builtin-pull-deploy.c index 4c67f6b0..47c675d1 100644 --- a/src/ostree/ot-admin-builtin-pull-deploy.c +++ b/src/ostree/ot-admin-builtin-pull-deploy.c @@ -36,13 +36,13 @@ static GOptionEntry options[] = { }; static char * -parse_deploy_name_from_path (GFile *ostree_dir, +parse_deploy_name_from_path (GFile *osdir, GFile *path) { - ot_lobj GFile *deploy_dir = g_file_get_child (ostree_dir, "deploy"); - ot_lfree char *relpath = g_file_get_relative_path (deploy_dir, path); + ot_lfree char *relpath = g_file_get_relative_path (osdir, path); const char *last_dash; + g_assert (relpath); last_dash = strrchr (relpath, '-'); if (!last_dash) g_error ("Failed to parse deployment name %s", relpath); @@ -50,80 +50,37 @@ parse_deploy_name_from_path (GFile *ostree_dir, return g_strndup (relpath, last_dash - relpath); } -static char * -remote_name_from_path (GKeyFile *repo_config, - const char *deploy_path) -{ - const char *group_prefix = "remote \""; - char **groups = NULL; - char **group_iter = NULL; - char *ret = NULL; - - groups = g_key_file_get_groups (repo_config, NULL); - for (group_iter = groups; *group_iter; group_iter++) - { - const char *group = *group_iter; - char **configured_branches = NULL; - char **branch_iter = NULL; - gboolean found = FALSE; - - if (!(g_str_has_prefix (group, group_prefix) - && g_str_has_suffix (group, "\""))) - continue; - - configured_branches = g_key_file_get_string_list (repo_config, group, "branches", NULL, NULL); - if (!configured_branches) - continue; - - for (branch_iter = configured_branches; *branch_iter; branch_iter++) - { - const char *branch = *branch_iter; - - if (!strcmp (branch, deploy_path)) - { - found = TRUE; - break; - } - } - - if (found) - break; - } - - if (*group_iter) - { - const char *group = *group_iter; - size_t len; - ret = g_strdup (group + strlen (group_prefix)); - len = strlen (ret); - g_assert (len > 0 && ret[len-1] == '\"'); - ret[len-1] = '\0'; - } - g_strfreev (groups); - return ret; -} - gboolean ot_admin_builtin_pull_deploy (int argc, char **argv, GFile *ostree_dir, GError **error) { GOptionContext *context; gboolean ret = FALSE; + const char *osname; ot_lobj GFile *repo_path = NULL; - ot_lobj OstreeRepo *repo = NULL; ot_lobj GFile *current_deployment = NULL; ot_lfree char *deploy_name = NULL; + ot_lobj GFile *deploy_dir = NULL; + ot_lobj GFile *os_dir = NULL; ot_lfree char *remote_name = NULL; ot_lptrarray GPtrArray *subproc_args = NULL; __attribute__((unused)) GCancellable *cancellable = NULL; - context = g_option_context_new (" - Upgrade and redeploy current tree"); + context = g_option_context_new ("OSNAME - Upgrade and redeploy current tree"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; - if (!ot_admin_get_current_deployment (ostree_dir, ¤t_deployment, + if (argc < 2) + { + ot_util_usage_error (context, "OSNAME must be specified", error); + goto out; + } + + osname = argv[1]; + + if (!ot_admin_get_current_deployment (ostree_dir, osname, ¤t_deployment, cancellable, error)) goto out; @@ -134,15 +91,13 @@ ot_admin_builtin_pull_deploy (int argc, char **argv, GFile *ostree_dir, GError * goto out; } - deploy_name = parse_deploy_name_from_path (ostree_dir, current_deployment); + deploy_dir = g_file_get_child (ostree_dir, "deploy"); + os_dir = g_file_get_child (deploy_dir, osname); + g_print ("%s\n%s\n", gs_file_get_path_cached (os_dir), + gs_file_get_path_cached (current_deployment)); + deploy_name = parse_deploy_name_from_path (os_dir, current_deployment); repo_path = g_file_get_child (ostree_dir, "repo"); - repo = ostree_repo_new (repo_path); - if (!ostree_repo_check (repo, error)) - goto out; - - remote_name = remote_name_from_path (ostree_repo_get_config (repo), - deploy_name); { ot_lfree char *repo_arg = g_strconcat ("--repo=", @@ -152,7 +107,7 @@ ot_admin_builtin_pull_deploy (int argc, char **argv, GFile *ostree_dir, GError * if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir), GS_SUBPROCESS_STREAM_DISPOSITION_NULL, cancellable, error, - "ostree", "pull", repo_arg, remote_name, NULL)) + "ostree", "pull", repo_arg, osname, NULL)) goto out; } @@ -163,7 +118,7 @@ ot_admin_builtin_pull_deploy (int argc, char **argv, GFile *ostree_dir, GError * if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir), GS_SUBPROCESS_STREAM_DISPOSITION_NULL, cancellable, error, - "ostree", "admin", opt_ostree_dir_arg, "deploy", + "ostree", "admin", opt_ostree_dir_arg, "deploy", osname, deploy_name, NULL)) goto out; } diff --git a/src/ostree/ot-admin-builtin-update-kernel.c b/src/ostree/ot-admin-builtin-update-kernel.c index e0fc0746..1fd9ac78 100644 --- a/src/ostree/ot-admin-builtin-update-kernel.c +++ b/src/ostree/ot-admin-builtin-update-kernel.c @@ -33,6 +33,7 @@ typedef struct { const char *deploy_path; GFile *kernel_path; char *release; + char *osname; } OtAdminUpdateKernel; static gboolean opt_modules_only; @@ -200,7 +201,8 @@ update_initramfs (OtAdminUpdateKernel *self, cancellable, error)) goto out; - ostree_vardir = g_file_get_child (self->ostree_dir, "var"); + ostree_vardir = ot_gfile_get_child_build_path (self->ostree_dir, "deploy", + self->osname, "var", NULL); if (opt_host_kernel) ostree_moduledir = g_file_get_child (self->ostree_dir, "modules"); @@ -213,6 +215,7 @@ update_initramfs (OtAdminUpdateKernel *self, if (!g_output_stream_close (tmp_log_out, cancellable, error)) goto out; + mkinitramfs_args = g_ptr_array_new (); /* Note: the hardcoded /tmp path below is not actually a * security flaw, because we've bind-mounted dracut's view * of /tmp to the securely-created tmpdir above. @@ -403,14 +406,22 @@ ot_admin_builtin_update_kernel (int argc, char **argv, GFile *ostree_dir, GError memset (self, 0, sizeof (*self)); - context = g_option_context_new ("[OSTREE_REVISION - Update kernel and regenerate initial ramfs"); + 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 > 1) - self->deploy_path = argv[1]; + 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 = argv[2]; else self->deploy_path = "current"; diff --git a/src/ostree/ot-admin-builtin-init.c b/src/ostree/ot-admin-builtin-upgrade.c similarity index 55% rename from src/ostree/ot-admin-builtin-init.c rename to src/ostree/ot-admin-builtin-upgrade.c index 8774fd25..635ae1f4 100644 --- a/src/ostree/ot-admin-builtin-init.c +++ b/src/ostree/ot-admin-builtin-upgrade.c @@ -26,6 +26,8 @@ #include "ot-admin-functions.h" #include "otutil.h" +#include +#include #include static GOptionEntry options[] = { @@ -33,23 +35,43 @@ static GOptionEntry options[] = { }; gboolean -ot_admin_builtin_init (int argc, char **argv, GFile *ostree_dir, GError **error) +ot_admin_builtin_upgrade (int argc, char **argv, GFile *ostree_dir, GError **error) { GOptionContext *context; gboolean ret = FALSE; - ot_lobj GFile *dir = NULL; + const char *osname = NULL; + gs_free char *ostree_dir_arg = NULL; __attribute__((unused)) GCancellable *cancellable = NULL; - context = g_option_context_new ("- Initialize /ostree directory"); + context = g_option_context_new ("OSNAME - pull, deploy, and prune"); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, error)) goto out; - if (!ot_admin_ensure_initialized (ostree_dir, cancellable, error)) + if (argc < 2) + { + ot_util_usage_error (context, "OSNAME must be specified", error); + goto out; + } + + osname = argv[1]; + + 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_NULL, + cancellable, error, + "ostree", "admin", ostree_dir_arg, "pull-deploy", osname, NULL)) goto out; - g_print ("%s initialized as OSTree root\n", gs_file_get_path_cached (ostree_dir)); + if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir), + GS_SUBPROCESS_STREAM_DISPOSITION_NULL, + cancellable, error, + "ostree", "admin", ostree_dir_arg, "prune", osname, NULL)) + goto out; ret = TRUE; out: diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index 6994c96f..2d535f0f 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -27,13 +27,15 @@ G_BEGIN_DECLS -gboolean ot_admin_builtin_init (int argc, char **argv, GFile *ostree_dir, GError **error); +gboolean ot_admin_builtin_os_init (int argc, char **argv, GFile *ostree_dir, GError **error); +gboolean ot_admin_builtin_install (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_init_fs (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_deploy (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_prune (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_pull_deploy (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_diff (int argc, char **argv, GFile *ostree_dir, GError **error); gboolean ot_admin_builtin_update_kernel (int argc, char **argv, GFile *ostree_dir, GError **error); +gboolean ot_admin_builtin_upgrade (int argc, char **argv, GFile *ostree_dir, GError **error); G_END_DECLS diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c index 0336e80b..7623062a 100644 --- a/src/ostree/ot-admin-functions.c +++ b/src/ostree/ot-admin-functions.c @@ -44,11 +44,6 @@ ot_admin_ensure_initialized (GFile *ostree_dir, if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) goto out; - g_clear_object (&dir); - dir = g_file_get_child (ostree_dir, "modules"); - if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) - goto out; - g_clear_object (&dir); dir = ot_gfile_get_child_build_path (ostree_dir, "repo", "objects", NULL); if (!g_file_query_exists (dir, NULL)) @@ -65,40 +60,6 @@ ot_admin_ensure_initialized (GFile *ostree_dir, } } - /* Ensure core subdirectories of /var exist, since we need them for - * dracut generation, and the host will want them too. - */ - g_clear_object (&dir); - dir = ot_gfile_get_child_build_path (ostree_dir, "var", "log", NULL); - if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) - goto out; - - g_clear_object (&dir); - dir = ot_gfile_get_child_build_path (ostree_dir, "var", "tmp", NULL); - if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) - goto out; - if (chmod (gs_file_get_path_cached (dir), 01777) < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - - g_clear_object (&dir); - dir = ot_gfile_get_child_build_path (ostree_dir, "var", "lib", NULL); - if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) - goto out; - - g_clear_object (&dir); - dir = ot_gfile_get_child_build_path (ostree_dir, "var", "run", NULL); - if (!g_file_test (gs_file_get_path_cached (dir), G_FILE_TEST_IS_SYMLINK)) - { - if (symlink ("../run", gs_file_get_path_cached (dir)) < 0) - { - ot_util_set_error_from_errno (error, errno); - goto out; - } - } - ret = TRUE; out: return ret; @@ -183,13 +144,15 @@ query_symlink_target_allow_noent (GFile *path, */ gboolean ot_admin_get_current_deployment (GFile *ostree_dir, + const char *osname, GFile **out_deployment, GCancellable *cancellable, GError **error) { ot_lobj GFile *current_path = NULL; - current_path = g_file_get_child (ostree_dir, "current"); + current_path = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, + "current", NULL); return query_symlink_target_allow_noent (current_path, out_deployment, cancellable, error); @@ -204,13 +167,15 @@ ot_admin_get_current_deployment (GFile *ostree_dir, */ gboolean ot_admin_get_previous_deployment (GFile *ostree_dir, - GFile **out_deployment, - GCancellable *cancellable, - GError **error) + const char *osname, + GFile **out_deployment, + GCancellable *cancellable, + GError **error) { ot_lobj GFile *previous_path = NULL; - previous_path = g_file_get_child (ostree_dir, "previous"); + previous_path = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, + "previous", NULL); return query_symlink_target_allow_noent (previous_path, out_deployment, cancellable, error); diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h index af114aa6..3c21ad16 100644 --- a/src/ostree/ot-admin-functions.h +++ b/src/ostree/ot-admin-functions.h @@ -32,10 +32,12 @@ gboolean ot_admin_ensure_initialized (GFile *ostree_dir, 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); diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index 2f2340e9..e80ac03a 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -43,9 +43,11 @@ typedef struct { } OstreeAdminCommand; static OstreeAdminCommand admin_subcommands[] = { - { "init", ot_admin_builtin_init }, + { "os-init", ot_admin_builtin_os_init }, { "init-fs", ot_admin_builtin_init_fs }, { "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 }, diff --git a/src/switchroot/ostree-switch-root.c b/src/switchroot/ostree-switch-root.c index 6330223a..86c6855e 100644 --- a/src/switchroot/ostree-switch-root.c +++ b/src/switchroot/ostree-switch-root.c @@ -153,17 +153,20 @@ 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 }; - /* ostree_readonly_bind_mounts /lib/modules -> modules */ const char *readonly_bind_mounts[] = { "/bin", "/lib", "/sbin", "/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; @@ -183,6 +186,14 @@ main(int argc, char *argv[]) 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); + /* For now, we just remount the root filesystem read/write. This is * kind of ugly, but to do this properly we'd basically have to have * to be fully integrated into the init process. @@ -193,7 +204,7 @@ main(int argc, char *argv[]) exit (1); } - snprintf (destpath, sizeof(destpath), "%s/ostree/%s", + snprintf (destpath, sizeof(destpath), "%s/ostree/deploy/%s", root_mountpoint, ostree_target); if (stat (destpath, &stbuf) < 0) { @@ -204,7 +215,7 @@ main(int argc, char *argv[]) for (i = 0; initramfs_move_mounts[i] != NULL; i++) { const char *path = initramfs_move_mounts[i]; - snprintf (destpath, sizeof(destpath), "%s/ostree/%s%s", root_mountpoint, ostree_target, path); + 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); @@ -247,45 +258,38 @@ main(int argc, char *argv[]) * so we no longer refer to root_mountpoint. */ - snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_target); + snprintf (destpath, sizeof(destpath), "/ostree/deploy/%s", 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)) + if (!S_ISLNK (stbuf.st_mode)) { - if (readlink (destpath, ostree_target_path, PATH_MAX) < 0) - { - perrorv ("readlink(%s) failed: ", destpath); - exit (1); - } - fprintf (stderr, "Resolved OSTree target to: %s\n", ostree_target_path); + fprintf (stderr, "OSTree target is not a symbolic link: %s\n", destpath); + exit (1); } - else + if (readlink (destpath, ostree_target_path, PATH_MAX) < 0) { - strncpy (ostree_target_path, ostree_target, PATH_MAX); - fprintf (stderr, "OSTree target is: %s\n", ostree_target_path); + perrorv ("readlink(%s) failed: ", destpath); + exit (1); } - - snprintf (destpath, sizeof(destpath), "/ostree/%s/sysroot", ostree_target_path); + 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); + asprintf (&deploy_path, "/ostree/deploy/%s/%s", ostree_osname, ostree_target_path); + + snprintf (destpath, sizeof(destpath), "%s/sysroot", deploy_path); if (mount ("/", destpath, NULL, MS_BIND, NULL) < 0) { perrorv ("Failed to bind mount / to '%s'", destpath); exit (1); } - snprintf (srcpath, sizeof(srcpath), "/ostree/%s-etc", ostree_target_path); - snprintf (destpath, sizeof(destpath), "/ostree/%s/etc", ostree_target_path); - if (mount (srcpath, destpath, NULL, MS_BIND, NULL) < 0) - { - perrorv ("Failed to bind mount '%s' to '%s'", srcpath, destpath); - exit (1); - } - - snprintf (srcpath, sizeof(srcpath), "%s", "/ostree/var"); - snprintf (destpath, sizeof(destpath), "/ostree/%s/var", ostree_target_path); + 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); @@ -294,7 +298,7 @@ main(int argc, char *argv[]) for (i = 0; toproot_bind_mounts[i] != NULL; i++) { - snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target, toproot_bind_mounts[i]); + snprintf (destpath, sizeof(destpath), "%s%s", deploy_path, toproot_bind_mounts[i]); if (mount (toproot_bind_mounts[i], destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0) { perrorv ("failed to bind mount (class:toproot) %s to %s", toproot_bind_mounts[i], destpath); @@ -304,8 +308,8 @@ main(int argc, char *argv[]) for (i = 0; ostree_bind_mounts[i] != NULL; i++) { - snprintf (srcpath, sizeof(srcpath), "/ostree/%s", ostree_bind_mounts[i]); - snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target_path, ostree_bind_mounts[i]); + snprintf (srcpath, sizeof(srcpath), "/ostree/deploy/%s%s", 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); @@ -315,7 +319,7 @@ main(int argc, char *argv[]) for (i = 0; readonly_bind_mounts[i] != NULL; i++) { - snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target_path, readonly_bind_mounts[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); @@ -328,19 +332,9 @@ main(int argc, char *argv[]) } } - /* This should come after we've bind mounted /lib */ - snprintf (srcpath, sizeof(srcpath), "/ostree/modules"); - snprintf (destpath, sizeof(destpath), "/ostree/%s/lib/modules", ostree_target_path); - if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0) + if (chroot (deploy_path) < 0) { - perrorv ("failed to bind mount %s to %s", srcpath, destpath); - exit (1); - } - - snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_target_path); - if (chroot (destpath) < 0) - { - perrorv ("failed to change root to '%s'", destpath); + perrorv ("failed to change root to '%s'", deploy_path); exit (1); }