[ostree/wip/bootloader] [wip/bootloader] Some stub code for syslinux, and a humongous comment



commit b321368806e1dc5965eb849527324b1346f5febf
Author: Colin Walters <walters verbum org>
Date:   Tue Jun 4 18:05:35 2013 -0400

    [wip/bootloader] Some stub code for syslinux, and a humongous comment

 Makefile-ostree.am                          |    4 +
 src/ostree/ot-admin-builtin-deploy.c        |  332 +++++++++++++++++++++++----
 src/ostree/ot-admin-builtin-update-kernel.c |  203 ++---------------
 src/ostree/ot-bootloader-syslinux.c         |  223 ++++++++++++++++++
 src/ostree/ot-bootloader-syslinux.h         |   40 ++++
 src/ostree/ot-bootloader.c                  |   51 ++++
 src/ostree/ot-bootloader.h                  |   72 ++++++
 7 files changed, 699 insertions(+), 226 deletions(-)
---
diff --git a/Makefile-ostree.am b/Makefile-ostree.am
index 7825db9..61af8b4 100644
--- a/Makefile-ostree.am
+++ b/Makefile-ostree.am
@@ -61,6 +61,10 @@ ostree_SOURCES += \
        src/ostree/ot-admin-builtins.h \
        src/ostree/ot-admin-functions.h \
        src/ostree/ot-admin-functions.c \
+       src/ostree/ot-bootloader.h \
+       src/ostree/ot-bootloader.c \
+       src/ostree/ot-bootloader-syslinux.h \
+       src/ostree/ot-bootloader-syslinux.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\"
diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c
index 4569761..4464b89 100644
--- a/src/ostree/ot-admin-builtin-deploy.c
+++ b/src/ostree/ot-admin-builtin-deploy.c
@@ -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
@@ -28,6 +28,198 @@
 
 #include <glib/gi18n.h>
 
+/* 
+ * Terms
+ * -----
+ * We have an ordered list of deployments:
+ * "new": The new tree
+ * "current/deploy0": The currently deployed tree, set as the bootloader default
+ * "previous/deploy1": (optional) The previous tree relative to "current"
+ * "deploy2"...:  Any number of previous deployments
+ *
+ * Also in the mix is the special "running" deployment, the one we're
+ * currently running.  We want to support generating an arbitrary set
+ * of new deployments, atomically.  But the normal case is to make
+ * "new" be "current", shift everything down by one, and "previous"
+ * would drop off.
+ *
+ * Because we want to support deploying the same tree twice,
+ * e.g. going from foo -> bar -> foo, the deployments have a serial
+ * number.  A deployment list looks like:
+ *
+ * deploy0:   da39a538021.1  (tag:current, ref:x86_64-runtime, running)
+ * deploy1:   1ca9d348d67.0  (tag:previous, ref:x86_64-devel)
+ * deploy2:   da39a538021.0  (ref:x86_64-runtime)
+ * deploy3:   587e2144109.0  (ref:x86_64-runtime)
+ *
+ * The very common case of course is for the deployment serial to be
+ * 0, so we should omit it from the list.
+ *
+ * Also, exactly one deployment can be "running".  Let's say I just ran:
+ * $ ostree admin pull-deploy gnome-ostree/buildmaster/x86_64-runtime
+ * but I haven't rebooted yet.  Then:
+ *
+ * deploy0:   da39a538021.1  (tag:current, ref:x86_64-runtime)
+ * deploy1:   1ca9d348d67.0  (tag:previous, ref:x86_64-devel, running)
+ * deploy2:   da39a538021.0  (ref:x86_64-runtime)
+ * deploy3:   587e2144109.0  (ref:x86_64-runtime)
+ *
+ * In this scenario, we expect doing:
+ * $ ostree admin upgrade -r
+ * would delete the deploy0, and configuration changes are merged based on deploy1.
+ *
+ * It's also possible for deploy2/3 to be running, if the user
+ * explicitly rebooted back into them.  We should have a command:
+ * $ ostree admin reset-current
+ * to make deploy2 current, and delete the old current/previous.
+ *
+ * Each deployment has a few variables:
+ *
+ * treecsum: OSTree checksum of tree
+ * serial: How many times ${treecsum} appears in the list, starting from 0
+ * bootcsum: Checksum of kernel+initramfs
+ *
+ * Global variables:
+ *
+ * bootversion: Either 0 or 1, as expressed by /boot/loader.${bootversion}.
+ *
+ * Constraints
+ * -----------
+ *
+ * We want fully atomic upgrades.  Some constraints though:
+ *
+ *   1) We don't want to update /boot unless the kernel actually
+ *   changed; it might be on flash storage for example, and constant
+ *   wear on that would be bad.
+ *
+ *   2) /boot may be on a separate partition, so we can't just swap
+ *      one symbolic link and have it take effect for everything.
+ *
+ * Implementation
+ * --------------
+ *
+ * We have two deployment lists: OLD and NEW.  We need to preserve any
+ * entries from OLD in NEW.  Let's take the case above:  
+ * 
+ * deploy0:   da39a538021.1  (tag:current, ref:x86_64-runtime, bootcsum:c90d227a60, running)
+ * deploy1:   1ca9d348d67.0  (tag:previous, ref:x86_64-devel, bootcsum:648a9123b4)
+ * deploy2:   da39a538021.0  (ref:x86_64-runtime, bootcsum:c90d227a60)
+ * deploy3:   587e2144109.0  (ref:x86_64-runtime, bootcsum:c90d227a60)
+ *
+ * And we want to generate the new deployment list:
+ *
+ * deploy0:   afd96ee01b1.0  (tag:current, ref:x86_64-runtime, bootcsum:c90d227a60)
+ * deploy1:   da39a538021.1  (tag:current, ref:x86_64-runtime, bootcsum:c90d227a60, running)
+ * deploy2:   1ca9d348d67.0  (tag:previous, ref:x86_64-devel, bootcsum:648a9123b4)
+ * deploy3:   da39a538021.0  (ref:x86_64-runtime, bootcsum:c90d227a60)
+ * deploy4:   587e2144109.0  (ref:x86_64-runtime, bootcsum:c90d227a60)
+ *
+ * Filesystem layout
+ * -----------------
+ *
+ * If we need to change kernel configuration, we do so atomically via
+ * the "swapped directory pattern".  In this case, that's
+ * /boot/loader.{0,1}, and /boot/loader is a symbolic link to one or
+ * the other.
+ *
+ * A tree deployment is represented by three directories:
+ *  /boot/loader/entries/${osname}-${bootcsum}
+ *  /ostree/deploy/${osname}/deploy.${bootversion}/${bootcsum}/${treecsum}.${serial}
+ *  /ostree/deploy/${osname}/deploy.${bootversion}/${bootcsum}/${treecsum}.${serial}-etc
+ *
+ * In order to implement upgrades without touching /boot,
+ * /ostree/deploy/${osname}/deploy.${bootversion} is also a swapped directory.  So
+ * the real target is e.g. /ostree/deploy/${osname}/deploy.${bootversion}.[01]
+ *
+ * Furthermore, inside the "bootcsum directory", there are
+ * numbered symbolic links from 0..N for each deployment specific to
+ * this ${bootcsum}.  This allows us to generate bootloader entries
+ * independent of ${treecsum}, which allows us to do non-kernel
+ * upgrades without touching /boot.
+ * 
+ * Booting
+ * -------
+ *
+ * The kernel commandline contains:
+ *   ostree=gnome-ostree/deploy.${bootversion}/${bootcsum}/0
+ * Inside the initramfs (dracut), the ostree-prepare-root tool parses this, and
+ * sets up /sysroot.
+ *
+ * Redeploying
+ * -----------
+ *
+ * We have two deployment lists, call them CURRENT and NEW.  First,
+ * determine if we need to generate a new bootloader configuration;
+ * this will be true if NEW adds or removes "bootcsum" entries.
+ *
+ * New bootloader configuration
+ * ----------------------------
+ *
+ * Let's assume the current bootversion is 0; the new bootversion will
+ * therefore be 1.
+ *
+ * 0) Compute NEWSET, which is the set of deployments that are only in NEW.
+ * 1) Make directory /boot/loader.1 and /ostree/deploy/${osname}/deploy.1.0,
+ *    and ln -s /ostree/deploy/${osname}/deploy.1 /ostree/deploy/${osname}/deploy.1.0
+ * 2) For each deployment (treecsum, bootcsum, serial=0) in NEWSET, check out
+ *    /ostree/deploy/${osname}/deploy.1/${bootcsum}/${treecsum}.0
+ *    then create a corresponding -etc directory, merging from the running tree.
+ *    Look in its boot/ directory, and copy the kernel/initramfs to
+ *    /boot/loader/entries/${osname}-${bootcsum} (unless the directory already exists).
+ * 3) Compute KEEPSET, which is the set of deployments in both OLD and NEW.
+ *    For each deployment (treecsum, bootsum, serial):
+ *      cp -al /ostree/deploy/${osname}/deploy.0/${bootcsum}/${treecsum}.${serial} to corresponding .1
+ *      cp -al /ostree/deploy/${osname}/deploy.0/${bootcsum}/${treecsum}.${serial}-etc to corresponding .1
+ *      if !exists(/boot/loader.1/${osname}-${bootcsum}) then
+ *        cp -al /boot/loader.0/${osname}-${bootcsum} to corresponding .1
+ *    Note we hardlink even the writable copy of -etc; only one of these can be active
+ *    at a time, so it's fine if mutating one affects another.  We also don't make
+ *    any guarantees about whether modifications to /etc during or after a deployment
+ *    will be visible on reboot.
+ * 4) for each ${bootcsum} in NEW:
+ *      let i = 0
+ *      for each ${treecsum} with a matching ${bootcsum}, sorted by deployment order:
+ *        ln -s /ostree/deploy/${osname}/deploy.1/${bootcsum}/${treecsum}.0 ${i}
+ *        i += 1
+ * 5) Generate new bootloader configuration in /boot/loader.1; for example, if we're
+ *    using syslinux, /boot/loader.1/syslinux/syslinux.cfg
+ * 6) fsync() all the above, all files and directories
+ * 7) Atomically swap /boot/loader to point to /boot/loader.1
+ *
+ * Cleanup
+ * -------
+ *
+ * Read value of /boot/loader.  If it's 1, then the cleanup value is
+ * 0, and vice versa.
+ *
+ * 0) For each deployed ${osname}, rm -rf /ostree/deploy/${osname}/deploy.0.[01]
+ * 1) rm -rf /boot/loader.0
+ *    
+ * Reusing existing bootloader configuration
+ * -----------------------------------------
+ *
+ * Let's simplify and assume we're simply changing the deployment list
+ * to add a new entry, and drop off the last entry, and that both the
+ * newly added entry and the one to be dropped off have the same
+ * ${bootcsum}.
+ *
+ * 
+ * 0) Read the value of /boot/loader; this is ${bootversion}
+ * 1) Read the value of /ostree/deploy/${osname}/deploy.${bootversion}; this is ${osdeployversion};
+ *    If ${osdeployversion} is 0, the new version will be 1, and vice versa.
+ * 2) Create the directory /ostree/deploy/${osname}/deploy.0
+ *    For each deployment (treecsum, bootsum, serial):
+ *      cp -al /ostree/deploy/${osname}/deploy.0/${bootcsum}/${treecsum}.${serial} to corresponding .1
+ *      cp -al /ostree/deploy/${osname}/deploy.0/${bootcsum}/${treecsum}.${serial}-etc to corresponding .1
+ *      if !exists(/boot/loader.1/${osname}-${bootcsum}) then
+ *        cp -al /boot/loader.0/${osname}-${bootcsum} to corresponding .1
+ *    Note we hardlink even the writable copy of -etc; only one of these can be active
+ *    at a time, so it's fine if mutating one affects another.  We also don't make
+ *    any guarantees about whether modifications to /etc during or after a deployment
+ *    will be visible on reboot.
+ * 
+ */
+
 typedef struct {
   OstreeRepo  *repo;
   OtAdminBuiltinOpts *admin_opts;
@@ -35,6 +227,8 @@ typedef struct {
   char  *osname;
   GFile *osname_dir;
 
+  char        *initramfs_checksum;
+
   char        *current_deployment_ref;
   char        *previous_deployment_ref;
   char        *resolved_commit;
@@ -45,17 +239,17 @@ typedef struct {
   GFile       *previous_deployment;
 } OtAdminDeploy;
 
-static gboolean opt_no_kernel;
+static gboolean opt_bootloader;
 static gboolean opt_force;
 
 static GOptionEntry options[] = {
-  { "no-kernel", 0, 0, G_OPTION_ARG_NONE, &opt_no_kernel, "Don't update kernel related config (initramfs, 
bootloader)", NULL },
+  { "no-bootloader", 0, 0, G_OPTION_ARG_NONE, &opt_no_bootloader, "Don't update bootloader", NULL },
   { "force", 0, 0, G_OPTION_ARG_NONE, &opt_force, "Overwrite any existing deployment", NULL },
   { NULL }
 };
 
 /**
- * update_current:
+ * swap_state:
  *
  * Atomically swap the /ostree/current symbolic link to point to a new
  * path.  If successful, the old current will be saved as
@@ -374,22 +568,19 @@ merge_etc_changes (OtAdminDeploy  *self,
 }
 
 /**
- * deploy_tree:
+ * checkout_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)
+checkout_tree (OtAdminDeploy     *self,
+               const char        *deploy_target,
+               const char        *revision,
+               GCancellable      *cancellable,
+               GError           **error)
 {
   gboolean ret = FALSE;
   ot_lfree char *deploy_target_fullname = NULL;
@@ -567,39 +758,97 @@ deploy_tree (OtAdminDeploy     *self,
   return ret;
 }
 
+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
+checksum_from_kernel_src (GFile        *src,
+                          char        **out_checksum,
+                          GError     **error)
+{
+  const char *last_dash = strrchr (gs_file_get_path_cached (src), '-');
+  if (!last_dash)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Malformed initramfs name '%s', missing '-'", gs_file_get_basename_cached (src));
+      return FALSE;
+    }
+  *out_checksum = g_strdup (last_dash + 1);
+  return TRUE;
+}
+
 /**
- * do_update_kernel:
+ * deploy_kernel:
  *
- * Ensure we have a GRUB entry, initramfs set up, etc.
+ * Install the kernel in /boot/loader per the systemd spec:
+ * http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/
  */
 static gboolean
-do_update_kernel (OtAdminDeploy     *self,
-                  GCancellable      *cancellable,
-                  GError           **error)
+deploy_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))
+  gs_unref_object GFile *src_kernel_path = NULL;
+  gs_unref_object GFile *src_initramfs_path = NULL;
+  gs_free char *initramfs_checksum = NULL;
+
+  if (!get_kernel_from_boot (self->admin_opts->boot_dir, &src_kernel_path, &src_initramfs_path,
+                             cancellable, error))
     goto out;
+  
+  if (src_initramfs_path != NULL)
+    {
+      if (!checksum_from_kernel_src (src_initramfs_path, &initramfs_checksum, error))
+        goto out;
+    }
+  else
+    {
+      if (!checksum_from_kernel_src (src_kernel_path, &initramfs_checksum, error))
+        goto out;
+    }
 
   ret = TRUE;
+  self->initramfs_checksum = initramfs_checksum;
+  initramfs_checksum = NULL;
  out:
   return ret;
 }
@@ -686,14 +935,11 @@ ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
   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))
+  if (!checkout_tree (self, deploy_target, revision, cancellable, error))
     goto out;
 
-  if (!opt_no_kernel)
-    {
-      if (!do_update_kernel (self, cancellable, error))
-        goto out;
-    }
+  if (!do_update_kernel (self, cancellable, error))
+    goto out;
 
   if (!complete_deployment (self, cancellable, error))
     goto out;
diff --git a/src/ostree/ot-admin-builtin-update-kernel.c b/src/ostree/ot-admin-builtin-update-kernel.c
index 1437ba3..e073954 100644
--- a/src/ostree/ot-admin-builtin-update-kernel.c
+++ b/src/ostree/ot-admin-builtin-update-kernel.c
@@ -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,147 +24,37 @@
 
 #include "ot-admin-builtins.h"
 #include "ostree.h"
+#include "ot-bootloader-syslinux.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)
+update_bootloader (OtAdminUpdateKernel  *self,
+                   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;
+  gs_unref_object OtBootloader *loader = NULL;
 
-  while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, error)) != NULL)
+  if (!loader)
     {
-      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;
+      loader = OT_BOOTLOADER (ot_bootloader_syslinux_new (self->admin_opts->boot_dir));
+      if (!ot_bootloader_query (loader))
+        g_clear_object (&loader);
     }
 
-  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 (loader == 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");
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "No known bootloader found, can't update kernel");
+      goto out;
     }
   
   ret = TRUE;
@@ -177,8 +67,6 @@ ot_admin_builtin_update_kernel (int argc, char **argv, OtAdminBuiltinOpts *admin
 {
   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;
@@ -194,30 +82,20 @@ ot_admin_builtin_update_kernel (int argc, char **argv, OtAdminBuiltinOpts *admin
 
   self->admin_opts = admin_opts;
 
-  context = g_option_context_new ("OSNAME [DEPLOY_PATH] - 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 < 2)
+  if (argc < 3)
     {
-      ot_util_usage_error (context, "OSNAME must be specified", error);
+      ot_util_usage_error (context, "OSNAME and DEPLOY_PATH 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");
+  self->deploy_path = g_file_new_for_path (argv[2]);
 
   deploy_boot_path = g_file_get_child (self->deploy_path, "boot"); 
 
@@ -235,52 +113,11 @@ ot_admin_builtin_update_kernel (int argc, char **argv, OtAdminBuiltinOpts *admin
   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));
-    }
+  if (!update_boot_loader
 
-  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))
+      if (!update_bootloader (self, cancellable, error))
         goto out;
     }
 
diff --git a/src/ostree/ot-bootloader-syslinux.c b/src/ostree/ot-bootloader-syslinux.c
new file mode 100644
index 0000000..2902cc1
--- /dev/null
+++ b/src/ostree/ot-bootloader-syslinux.c
@@ -0,0 +1,223 @@
+/* -*- 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"
+
+struct _OtBootloaderSyslinux
+{
+  GObject       parent_instance;
+
+  GFile        *boot_dir;
+  GFile        *config_path;
+};
+
+typedef GObjectClass OtBootloaderSyslinuxClass;
+
+static void ot_bootloader_syslinux_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_iface_init))
+
+static gboolean
+ot_bootloader_syslinux_query (OtBootloader *iface)
+{
+  OtBootloaderSyslinux *self = OT_BOOTLOADER_SYSLINUX (iface);
+
+  return g_file_query_exists (self->config_path, NULL);
+}
+
+static gboolean
+is_legacy_gnome_ostree_label (const char *osname,
+                              const char *line)
+{
+  return strcmp (osname, "gnome-ostree") == 0
+    && strcmp (line, "OSTree") == 0;
+}
+
+static GFile *
+get_target_boot_dir (OtBootloader  *self,
+                     const char    *osname,
+                     const char    *revision)
+{
+  /* TODO - adjust for systemd bootloader spec */
+  gs_free char *shortrev = g_strndup (revision, 16);
+  gs_free char *name = g_strconcat ("ostree/", osname, "/", shortrev, NULL);
+  return g_file_resolve_relative_path (self->boot_dir, name);
+}
+
+static gboolean
+ot_bootloader_syslinux_update_entry (OtBootloader  *self,
+                                     const char    *osname,
+                                     const char    *os_label,
+                                     const char    *revision,
+                                     GFile         *kernel_path,
+                                     const char    *kernel_checksum,
+                                     GFile         *initramfs_path,
+                                     const char    *initramfs_checksum,
+                                     GCancellable  *cancellable,
+                                     GError       **error)
+{
+  gboolean ret = FALSE;
+  gs_free char *config_contents = NULL;
+  gs_free char *new_config_contents = NULL;
+  gs_free char *label_prefix = NULL;
+  gs_unref_ptrarray GPtrArray *new_lines = NULL;
+  gs_unref_object GFile *target_boot_dir = NULL;
+  gs_unref_object GFile *target_kernel_path = NULL;
+  gs_unref_object GFile *target_initramfs_path = NULL;
+  gs_free char *kernel_relpath = NULL;
+  gs_free char *initramfs_relpath = NULL;
+  gboolean parsing_label = FALSE;
+  char **lines = NULL;
+  char **iter;
+  char **append_opts = NULL;
+
+  config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
+  if (!config_contents)
+    goto out;
+
+  target_boot_dir = get_target_boot_dir (self, osname, revision);
+  if (!gs_file_ensure_directory (target_boot_dir, cancellable, error))
+    goto out;
+
+  target_kernel_path = g_file_get_child (target_boot_dir, gs_file_get_basename_cached (kernel_path));
+  target_initramfs_path = g_file_get_child (target_boot_dir, gs_file_get_basename_cached (initramfs_path));
+
+  if (!gs_file_linkcopy_sync_data (kernel_path, target_kernel_path, G_FILE_COPY_OVERWRITE,
+                                   cancellable, error))
+    goto out;
+  if (!gs_file_linkcopy_sync_data (initramfs_path, target_initramfs_path, G_FILE_COPY_OVERWRITE,
+                                   cancellable, error))
+    goto out;
+
+  kernel_relpath = g_file_get_relative_path (self->boot_dir, target_kernel_path);
+  initramfs_relpath = g_file_get_relative_path (self->boot_dir, target_initramfs_path);
+
+  label_prefix = g_strconcat ("LABEL [ostree:", osname, "] ", NULL);
+
+  lines = g_strsplit (config_contents, "\n", -1);
+
+  new_lines = g_ptr_array_new_with_free_func (g_free);
+  
+  for (iter = lines; *iter; iter++)
+    {
+      char *line = *iter;
+      gboolean skip = parsing_label;
+
+      if (!parsing_label &&
+          (g_str_has_prefix (line, label_prefix) ||
+           is_legacy_gnome_ostree (osname, line)))
+        {
+          skip = parsing_label = TRUE;
+          g_ptr_array_add (new_lines, g_strconcat (label_prefix, os_label, NULL));
+          g_ptr_array_add (new_lines, g_strconcat ("\tLINUX ", kernel_relpath, NULL));
+          g_ptr_array_add (new_lines, g_strconcat ("\tINITRD ", initramfs_relpath, NULL));
+        }
+      else if (parsing_label && g_str_has_prefix (line, "\tAPPEND "))
+        {
+          g_strfreev (append_opts);
+          append_opts = g_strsplit (line + strlen ("\tAPPEND "), " ");
+        }
+      else if (parsing_label && !g_str_has_prefix (line, "\t"))
+        {
+          char **append_iter;
+          char *append_joined;
+          for (append_iter = append_opts; *append_iter; append_iter++)
+            {
+              char *append_opt = *append_iter;
+              if (g_str_has_prefix (append_opt, "ostree="))
+                {
+                  g_free (append_opt);
+                  *append_opt = g_strconcat ("ostree=", osname, "/current");
+                }
+            }
+          append_joined = g_strjoinv (append_opts, " ");
+          g_ptr_array_add (new_lines, g_strconcat ("\tAPPEND ", append_joined, NULL));
+          skip = parsing_label = FALSE;
+        }
+      
+      if (!skip)
+        {
+          /* Transfer ownership */
+          g_ptr_array_add (new_lines, line);
+          *iter = NULL;
+        }
+      else
+        {
+          g_free (*iter);
+        }
+    }
+
+  g_ptr_array_add (new_lines, NULL);
+  new_config_contents = g_strjoinv ("\n", (gchar**)new_lines->pdata);
+
+  if (strcmp (new_config_contents, config_contents) != 0)
+    {
+      if (!g_file_replace_contents (self->config_path, strlen (new_config_contents), 
+                                    NULL, FALSE, G_FILE_CREATE_NONE,
+                                    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->boot_dir);
+
+  G_OBJECT_CLASS (ot_bootloader_syslinux_parent_class)->finalize (object);
+}
+
+void
+ot_bootloader_syslinux_init (OtBootloaderSyslinux *self)
+{
+}
+
+void
+ot_bootloader_syslinux_iface_init (OtBootloaderInterface *iface)
+{
+  iface->query = ot_bootloader_syslinux_query;
+}
+
+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 *boot_dir)
+{
+  OtBootloaderSyslinux *self = g_object_new (OT_TYPE_BOOTLOADER_SYSLINUX, NULL);
+  self->boot_dir = g_object_ref (boot_dir);
+  self->config_path = g_file_resolve_relative_path (self->boot_dir, "syslinux/syslinux.cfg");
+  return self;
+}
diff --git a/src/ostree/ot-bootloader-syslinux.h b/src/ostree/ot-bootloader-syslinux.h
new file mode 100644
index 0000000..3cd1a0a
--- /dev/null
+++ b/src/ostree/ot-bootloader-syslinux.h
@@ -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 G_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 *boot_dir);
+
+G_END_DECLS
+
+#endif /* __OT_BOOTLOADER_SYSLINUX_H__ */
diff --git a/src/ostree/ot-bootloader.c b/src/ostree/ot-bootloader.c
new file mode 100644
index 0000000..aac3d64
--- /dev/null
+++ b/src/ostree/ot-bootloader.c
@@ -0,0 +1,51 @@
+/* -*- 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_update_entry (OtBootloader  *self,
+                            const char    *osname,
+                            GFile         *kernel_path,
+                            GFile         *initramfs_path,
+                            GCancellable  *cancellable,
+                            GError       **error)
+{
+  g_return_val_if_fail (OT_IS_BOOTLOADER (self), FALSE);
+
+  return OT_BOOTLOADER_GET_IFACE (self)->update_entry (self, osname, kernel_path, initramfs_path,
+                                                       cancellable, error);
+}
diff --git a/src/ostree/ot-bootloader.h b/src/ostree/ot-bootloader.h
new file mode 100644
index 0000000..c760fff
--- /dev/null
+++ b/src/ostree/ot-bootloader.h
@@ -0,0 +1,72 @@
+/* -*- 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             (* update_entry)            (OtBootloader  *self,
+                                                   const char    *osname,
+                                                   const char    *os_label,
+                                                   const char    *revision,
+                                                   GFile         *kernel_path,
+                                                   GFile         *initramfs_path,
+                                                   GCancellable  *cancellable,
+                                                   GError       **error);
+};
+
+GType ot_bootloader_get_type (void) G_GNUC_CONST;
+
+gboolean ot_bootloader_query (OtBootloader *self);
+
+gboolean ot_bootloader_update_entry (OtBootloader  *self,
+                                    const char    *osname,
+                                     const char    *os_label,
+                                     const char    *revision,
+                                    GFile         *kernel_path,
+                                    GFile         *initramfs_path,
+                                    GCancellable  *cancellable,
+                                    GError       **error);
+
+gboolean ot_bootloader_prune (OtBootloader  *self,
+                             const char    *osname,
+                             GCancellable  *cancellable,
+                             GError       **error);
+
+G_END_DECLS
+
+#endif /* __OT_BOOTLOADER_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]