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



commit 82abaecb99395d9a5ecf1ce21681f437efe3df21
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                          |    7 +-
 src/ostree/DEPLOY2.0                        |  239 +++++++++++++++
 src/ostree/ot-admin-builtin-deploy.c        |  340 ++++++++++------------
 src/ostree/ot-admin-builtin-diff.c          |    7 +-
 src/ostree/ot-admin-builtin-install.c       |   26 +-
 src/ostree/ot-admin-builtin-os-init.c       |    5 +-
 src/ostree/ot-admin-builtin-prune.c         |   13 +-
 src/ostree/ot-admin-builtin-pull-deploy.c   |   20 +-
 src/ostree/ot-admin-builtin-update-kernel.c |  296 -------------------
 src/ostree/ot-admin-builtin-upgrade.c       |   15 +-
 src/ostree/ot-admin-builtins.h              |    4 +-
 src/ostree/ot-admin-functions.c             |  424 +++++++++++++++++++++++----
 src/ostree/ot-admin-functions.h             |    8 +-
 src/ostree/ot-bootloader-syslinux.c         |  225 ++++++++++++++
 src/ostree/ot-bootloader-syslinux.h         |   40 +++
 src/ostree/ot-bootloader.c                  |   53 ++++
 src/ostree/ot-bootloader.h                  |   66 ++++
 src/ostree/ot-builtin-admin.c               |   23 +--
 src/ostree/ot-deployment.c                  |   82 +++++
 src/ostree/ot-deployment.h                  |   47 +++
 20 files changed, 1335 insertions(+), 605 deletions(-)
---
diff --git a/Makefile-ostree.am b/Makefile-ostree.am
index 7825db9..1a241d5 100644
--- a/Makefile-ostree.am
+++ b/Makefile-ostree.am
@@ -57,10 +57,15 @@ ostree_SOURCES += \
        src/ostree/ot-admin-builtin-install.c \
        src/ostree/ot-admin-builtin-run-triggers.c \
        src/ostree/ot-admin-builtin-upgrade.c \
-       src/ostree/ot-admin-builtin-update-kernel.c \
        src/ostree/ot-admin-builtins.h \
        src/ostree/ot-admin-functions.h \
        src/ostree/ot-admin-functions.c \
+       src/ostree/ot-bootloader.h \
+       src/ostree/ot-bootloader.c \
+       src/ostree/ot-bootloader-syslinux.h \
+       src/ostree/ot-bootloader-syslinux.c \
+       src/ostree/ot-deployment.h \
+       src/ostree/ot-deployment.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/DEPLOY2.0 b/src/ostree/DEPLOY2.0
new file mode 100644
index 0000000..ab94fa6
--- /dev/null
+++ b/src/ostree/DEPLOY2.0
@@ -0,0 +1,239 @@
+This should be migrated to https://live.gnome.org/OSTree/DeploymentModel2
+
+/** TODO:
+ *
+ * Drop the bind mount for /etc; instead check out the writable copy
+ * inside the deployment, and keep the lookaside copy inside the tree
+ * as etc.default ?  But that would need its own read-only bind mount,
+ * so maybe /usr/etc.default or even just /usr/etc ?
+ *
+ * For /var, change it to be a systemd generator; then it could
+ * be overriden by an admin.
+ *
+ */
+
+/* ATOMIC DIRECTORY:
+ *
+ * name -> name.l0
+ * name.d:
+ *   foo/
+ *   bar
+ *   baz
+ *   ... and more files
+ * name.l0:
+ *   ../name.d/foo foo
+ *   ../name.d/bar bar
+ *   ../name.d/baz baz
+ *
+ * To update:
+ *  0) Perform cleanup
+ *  1) Add new contents to name.d.  If necessary, rename.
+ *  2) Create new name.l1, with symlinks to contents in name.d
+ *  3) Atomic swap name -> name.l1
+ * Cleanup:
+ *  1) Read value of name, e.g. name.l0
+ *  2) for each (linkname, target) i name.l0 not also in name.l1:
+ *    rm -rf ${linkname} ${target}
+ *  3) rm -rf name.l0
+ */ 
+
+/* 
+ * 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:
+ *
+ * struct deployment {
+ *  int index;  // Global offset
+ *  char *ref;  // originating OSTree refname (e.g. gnome-ostree/buildmaster/x86_64-runtime)
+ *  char *csum;     // OSTree checksum of tree
+ *  int bootserial; // An integer assigned to this tree per its ${bootcsum}
+ *  char *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 these directories:
+ *  /boot/loader/entries/${osname}-${bootcsum}
+ *    Contains kernel+initramfs files and configuration
+ *  /ostree/boot.${bootversion}/${osname}/${bootcsum}/${treebootserial}
+ *    Where ${treebootserial} is a symbolic link 0..N, pointing to 
../../../deploy/${osname}/${treecsum}.${treeserial}
+ *
+ * And these directories:
+ *  /ostree/deploy/${osname}/${treecsum}.${serial}
+ *    Contains "OS/" - this is the chroot target
+ * And the following files:
+ *  /ostree/deploy/${osname}/deploy/${treecsum}.${serial}/usr/share/ostree/ref
+ *    containing ${treeref}
+ *  /ostree/deploy/${osname}/deploy/${treecsum}.${serial}/usr/share/ostree/rev
+ *    containing the full ${treecsum} (it's shortened elsewhere to a minimum unique length, at least 8)
+ *
+ * In order to implement upgrades without touching /boot,
+ * /ostree/boot.${bootversion}/${osname}/${bootcsum} is also a
+ * swapped directory.  So the real target is e.g. ${bootcsum}.[01]
+ *
+ * Booting
+ * -------
+ *
+ * The kernel commandline contains:
+ *   ostree=/ostree/boot.${bootversion}/${osname}/${bootcsum}/${treeserial}
+ * 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 KEEPSET, which is the set of deployments in both OLD and NEW.
+ *    For each deployment (osname, treecsum, bootcsum, serial=0) in KEEPSET, check out
+ *      - Copy symbolic links in /ostree/boot.0/${osname}/${bootcsum} to the
+ *        corresponding .1
+ *      - Clone /boot/loader.0/${osname}-${bootcsum} to /boot/loader.1/${osname}-${bootcsum}
+ * 1) Compute NEWSET, which is the set of deployments that are only in NEW.
+ *    For each deployment (osname, treecsum, bootcsum, serial=0) in NEWSET:
+ *      - Check out /ostree/deploy/${osname}/${treecsum}.${serial}
+ *      - Do the merge into /etc in that directory, and create usr/share/ostree/{ref,rev}.
+ *      - Look in its boot/ directory, and copy the kernel/initramfs to
+ *        /boot/loader.1/entries/${osname}-${bootcsum} (unless the directory already exists).
+ *      -  Allocate an unused treebootserial in /ostree/boot.1/${osname}/${bootcsum}
+ * 2) Generate new bootloader configuration in /boot/loader.1; for example, if we're
+ *    using syslinux, /boot/loader.1/syslinux/syslinux.cfg
+ * 3) fsync() all the above, all files and directories
+ * 4) 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.  Assume here that it's 1.
+ *
+ * 0) rm -rf /boot/loader.0 /ostree/boot.0
+ * 1) Generate set of deployments from symbolic links in /ostree/boot.1; this is CURRENTSET
+ * 2) Generate set of all deployments from /ostree/deploy; this is DISKSET
+ * 3) Delete all deployments in DISKSET - CURRENTSET
+ *    
+ * Reusing existing bootloader configuration
+ * -----------------------------------------
+ *
+ * This only applies when we changing the deployment list only for a
+ * given ${bootcsum}, AND the new and old number of entries is the
+ * same.  However it's worth optimizing for this case because the
+ * default OSTree configuration is to keep only "current" and
+ * "previous", and most updates are not going to change the kernel.
+ *
+ * 0) Read the value of /boot/loader; this is ${bootversion}
+ * 1) Read the value of /ostree/deploy/${osname}/deploy.${bootversion}; this is ${osdeployversion};
+ *    Here we assume ${osdeployversion} is 0, the new version will be 1, and vice versa.
+ * 2) Create the directory /ostree/deploy/${osname}/deploy.${bootversion}.1
+ * 3) Compute KEEPSET, which is the set of deployments in both OLD and NEW.
+ *      Each of these will have a symbolic link number, referenced by the bootloader entry,
+ *      that needs to be preserved.
+ *      for each (treecsum, bootsum, serial, boottag) in KEEPSET:
+ *        ln -s ../deploy.${bootversion}.0/${treecsum} 
/ostree/deploy/${osname}/deploy.${bootversion}.1/${boottag}
+ * 4) Compute NEWSET, which is the set of deployments in only in NEW.
+ *    Assign each entry in NEWSET a boottag that has not yet been used.  The normal case is
+ *    removing previous, so that would be boottag 1.
+ *    for each deployment (treecsum, bootsum, serial) in NEWSET:
+ *      Check out /ostree/deploy/${osname}/deploy.${boottag}.1/${bootcsum}/${treecsum}.0
+ *    then create a corresponding -etc directory, merging from the running tree.
+ *      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.
+ * 
+ */
+
diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c
index 4569761..71001d8 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
@@ -32,127 +32,31 @@ typedef struct {
   OstreeRepo  *repo;
   OtAdminBuiltinOpts *admin_opts;
   GFile *ostree_dir;
-  char  *osname;
-  GFile *osname_dir;
 
-  char        *current_deployment_ref;
-  char        *previous_deployment_ref;
-  char        *resolved_commit;
-  char        *resolved_previous_commit;
-  
-  char        *previous_deployment_revision;
-  GFile       *deploy_target_path;
-  GFile       *previous_deployment;
+  char *initramfs_checksum;
+  GPtrArray *current_list;
+
+  /* legacy */
+  char *resolved_commit;
+  char *resolved_previous_commit;
+  char *osname;
+  char *current_deployment_ref;
+  char *previous_deployment_ref;
+  char *previous_deployment_revision;
+  GFile *deploy_target_path;
+  GFile *osname_dir;
+  GFile *previous_deployment;
 } OtAdminDeploy;
 
-static gboolean opt_no_kernel;
+static gboolean opt_no_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:
- *
- * Atomically swap the /ostree/current symbolic link to point to a new
- * path.  If successful, the old current will be saved as
- * /ostree/previous, and /ostree/current-etc will be a link to the
- * current /etc subdirectory.
- *
- * Unless the new-current equals current, in which case, do nothing.
- */
-static gboolean
-update_current (OtAdminDeploy      *self,
-                GFile              *current_deployment,
-                GFile              *deploy_target,
-                GCancellable       *cancellable,
-                GError            **error)
-{
-  gboolean ret = FALSE;
-  ot_lobj GFile *current_path = NULL;
-  ot_lobj GFile *current_etc_path = NULL;
-  ot_lobj GFile *previous_path = NULL;
-  ot_lobj GFile *tmp_current_path = NULL;
-  ot_lobj GFile *tmp_current_etc_path = NULL;
-  ot_lobj GFile *tmp_previous_path = NULL;
-  ot_lobj GFileInfo *previous_info = NULL;
-  ot_lfree char *relative_current = NULL;
-  ot_lfree char *relative_current_etc = NULL;
-  ot_lfree char *relative_previous = NULL;
-
-  current_path = g_file_get_child (self->osname_dir, "current");
-  current_etc_path = g_file_get_child (self->osname_dir, "current-etc");
-  previous_path = g_file_get_child (self->osname_dir, "previous");
-
-  relative_current = g_file_get_relative_path (self->osname_dir, deploy_target);
-  g_assert (relative_current);
-  relative_current_etc = g_strconcat (relative_current, "-etc", NULL);
-
-  if (current_deployment)
-    {
-      ot_lfree char *relative_previous = NULL;
-
-      if (g_file_equal (current_deployment, deploy_target))
-        {
-          g_print ("ostadmin: %s already points to %s\n", gs_file_get_path_cached (current_path),
-                   relative_current);
-          return TRUE;
-        }
-
-      tmp_previous_path = g_file_get_child (self->osname_dir, "tmp-previous");
-      (void) gs_file_unlink (tmp_previous_path, NULL, NULL);
-
-      relative_previous = g_file_get_relative_path (self->osname_dir, current_deployment);
-      g_assert (relative_previous);
-      if (symlink (relative_previous, gs_file_get_path_cached (tmp_previous_path)) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-    }
-
-  tmp_current_path = g_file_get_child (self->osname_dir, "tmp-current");
-  (void) gs_file_unlink (tmp_current_path, NULL, NULL);
-
-  if (symlink (relative_current, gs_file_get_path_cached (tmp_current_path)) < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  tmp_current_etc_path = g_file_get_child (self->osname_dir, "tmp-current-etc");
-  (void) gs_file_unlink (tmp_current_etc_path, NULL, NULL);
-  if (symlink (relative_current_etc, gs_file_get_path_cached (tmp_current_etc_path)) < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  if (!gs_file_rename (tmp_current_path, current_path,
-                       cancellable, error))
-    goto out;
-  if (!gs_file_rename (tmp_current_etc_path, current_etc_path,
-                       cancellable, error))
-    goto out;
-
-  if (tmp_previous_path)
-    {
-      if (!gs_file_rename (tmp_previous_path, previous_path,
-                           cancellable, error))
-        goto out;
-    }
-
-  g_print ("ostadmin: %s set to %s\n", gs_file_get_path_cached (current_path),
-           relative_current);
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
 typedef struct {
   GError **error;
   gboolean caught_error;
@@ -221,8 +125,7 @@ ensure_unlinked (GFile         *path,
  * file there.
  */
 static gboolean
-copy_one_config_file (OtAdminDeploy      *self,
-                      GFile              *orig_etc,
+copy_one_config_file (GFile              *orig_etc,
                       GFile              *modified_etc,
                       GFile              *new_etc,
                       GFile              *src,
@@ -262,7 +165,7 @@ copy_one_config_file (OtAdminDeploy      *self,
         {
           ot_lobj GFile *child = g_file_get_child (src, g_file_info_get_name (child_info));
 
-          if (!copy_one_config_file (self, orig_etc, modified_etc, new_etc, child,
+          if (!copy_one_config_file (orig_etc, modified_etc, new_etc, child,
                                      cancellable, error))
             goto out;
         }
@@ -303,8 +206,7 @@ copy_one_config_file (OtAdminDeploy      *self,
  * changed in @new_etc, the modified version always wins.
  */
 static gboolean
-merge_etc_changes (OtAdminDeploy  *self,
-                   GFile          *orig_etc,
+merge_etc_changes (GFile          *orig_etc,
                    GFile          *modified_etc,
                    GFile          *new_etc,
                    GCancellable   *cancellable,
@@ -355,7 +257,7 @@ merge_etc_changes (OtAdminDeploy  *self,
     {
       OstreeDiffItem *diff = modified->pdata[i];
 
-      if (!copy_one_config_file (self, orig_etc, modified_etc, new_etc, diff->target,
+      if (!copy_one_config_file (orig_etc, modified_etc, new_etc, diff->target,
                                  cancellable, error))
         goto out;
     }
@@ -363,7 +265,7 @@ merge_etc_changes (OtAdminDeploy  *self,
     {
       GFile *file = added->pdata[i];
 
-      if (!copy_one_config_file (self, orig_etc, modified_etc, new_etc, file,
+      if (!copy_one_config_file (orig_etc, modified_etc, new_etc, file,
                                  cancellable, error))
         goto out;
     }
@@ -374,22 +276,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;
@@ -412,14 +311,6 @@ deploy_tree (OtAdminDeploy     *self,
   if (!revision)
     revision = deploy_target;
 
-  if (!g_file_query_exists (self->osname_dir, cancellable))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "No OS \"%s\" found in \"%s\"", self->osname,
-                   gs_file_get_path_cached (self->osname_dir));
-      goto out;
-    }
-
   if (!ostree_repo_resolve_rev (self->repo, revision, FALSE, &self->resolved_commit, error))
     goto out;
   if (!ostree_repo_resolve_rev (self->repo, revision, TRUE, &self->resolved_previous_commit, error))
@@ -480,7 +371,7 @@ deploy_tree (OtAdminDeploy     *self,
       goto out;
     }
 
-  if (!ot_admin_get_current_deployment (self->ostree_dir, self->osname, &self->previous_deployment,
+  if (!ot_admin_get_current_deployment (self->admin_opts->sysroot, self->osname, &self->previous_deployment,
                                         cancellable, error))
     goto out;
   if (self->previous_deployment)
@@ -549,7 +440,7 @@ deploy_tree (OtAdminDeploy     *self,
 
       if (previous_deployment_etc)
         {
-          if (!merge_etc_changes (self, previous_deployment_etc_default,
+          if (!merge_etc_changes (previous_deployment_etc_default,
                                   previous_deployment_etc, deploy_target_etc_path, 
                                   cancellable, error))
             goto out;
@@ -567,39 +458,100 @@ 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 *boot_dir = NULL;
+  gs_unref_object GFile *src_kernel_path = NULL;
+  gs_unref_object GFile *src_initramfs_path = NULL;
+  gs_free char *initramfs_checksum = NULL;
+
+  boot_dir = g_file_get_child (self->admin_opts->sysroot, "boot");
+
+  if (!get_kernel_from_boot (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;
 }
@@ -627,15 +579,23 @@ complete_deployment (OtAdminDeploy     *self,
         goto out;
     }
 
-  if (!update_current (self, self->previous_deployment, self->deploy_target_path,
-                       cancellable, error))
-    goto out;
-
   ret = TRUE;
  out:
   return ret;
 }
 
+static void
+compute_new_deployment_list (int           current_bootversion,
+                             GPtrArray    *current_deployments,
+                             const char   *osname,
+                             const char   *revision,
+                             GPtrArray   **out_new_deployments,
+                             int          *out_new_bootversion)
+{
+  *out_new_deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
+  *out_new_bootversion = current_bootversion;
+}
+
 gboolean
 ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error)
 {
@@ -646,13 +606,18 @@ ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
   ot_lobj GFile *repo_path = NULL;
   ot_lobj GFile *deploy_path = NULL;
   const char *osname = NULL;
-  const char *deploy_target = NULL;
+  const char *ref = NULL;
   const char *revision = NULL;
+  gs_free char*resolved_revision = NULL;
   __attribute__((unused)) GCancellable *cancellable = NULL;
+  gs_unref_ptrarray GPtrArray *current_deployments = NULL;
+  gs_unref_ptrarray GPtrArray *new_deployments = NULL;
+  int current_bootversion;
+  int new_bootversion;
 
   memset (self, 0, sizeof (*self));
 
-  context = g_option_context_new ("OSNAME TREENAME [REVISION] - In operating system OS, check out revision 
TREENAME (or REVISION as TREENAME)");
+  context = g_option_context_new ("OSNAME REF [REVISION] - In operating system OS, check out revision REF 
(or REVISION as REF)");
 
   g_option_context_add_main_entries (context, options, NULL);
 
@@ -666,34 +631,47 @@ ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
     }
 
   self->admin_opts = admin_opts;
-  self->ostree_dir = g_object_ref (admin_opts->ostree_dir);
 
-  if (!ot_admin_ensure_initialized (self->ostree_dir, cancellable, error))
+  if (!ot_admin_ensure_initialized (admin_opts->sysroot, cancellable, error))
     goto out;
 
-  repo_path = g_file_get_child (self->ostree_dir, "repo");
+  repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
   self->repo = ostree_repo_new (repo_path);
   if (!ostree_repo_check (self->repo, error))
     goto out;
 
   osname = argv[1];
-  deploy_target = argv[2];
+  ref = argv[2];
   if (argc > 3)
     revision = argv[3];
 
+  if (revision == NULL)
+    {
+      if (!ostree_repo_resolve_rev (self->repo, ref, FALSE, &resolved_revision,
+                                    error))
+        goto out;
+      revision = resolved_revision;
+    }
+
   self->osname = g_strdup (osname);
-  self->osname_dir = ot_gfile_get_child_build_path (self->ostree_dir, "deploy", osname, NULL);
-  self->current_deployment_ref = g_strdup_printf ("deployment/%s/current", self->osname);
-  self->previous_deployment_ref = g_strdup_printf ("deployment/%s/previous", self->osname);
+  self->osname_dir = ot_gfile_get_child_build_path (admin_opts->sysroot, "ostree", "deploy", osname, NULL);
 
-  if (!deploy_tree (self, deploy_target, revision, cancellable, error))
+  if (!ot_admin_list_deployments (admin_opts->sysroot, &current_deployments,
+                                  cancellable, error))
     goto out;
 
-  if (!opt_no_kernel)
-    {
-      if (!do_update_kernel (self, cancellable, error))
-        goto out;
-    }
+  if (!ot_admin_read_current_bootversion (admin_opts->sysroot, &current_bootversion,
+                                          cancellable, error))
+    goto out;
+
+  compute_new_deployment_list (current_bootversion,
+                               current_deployments,
+                               osname, revision,
+                               &new_deployments,
+                               &new_bootversion);
+
+  if (!checkout_tree (self, ref, revision, cancellable, error))
+    goto out;
 
   if (!complete_deployment (self, cancellable, error))
     goto out;
diff --git a/src/ostree/ot-admin-builtin-diff.c b/src/ostree/ot-admin-builtin-diff.c
index 7792db3..af3ee3f 100644
--- a/src/ostree/ot-admin-builtin-diff.c
+++ b/src/ostree/ot-admin-builtin-diff.c
@@ -38,7 +38,6 @@ ot_admin_builtin_diff (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GE
   GOptionContext *context;
   gboolean ret = FALSE;
   const char *osname;
-  GFile *ostree_dir = admin_opts->ostree_dir;
   ot_lobj GFile *repo_path = NULL;
   ot_lobj GFile *deployment = NULL;
   ot_lobj GFile *deploy_parent = NULL;
@@ -56,7 +55,7 @@ ot_admin_builtin_diff (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GE
   if (!g_option_context_parse (context, &argc, &argv, error))
     goto out;
   
-  repo_path = g_file_get_child (ostree_dir, "repo");
+  repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
 
   if (argc < 2)
     {
@@ -68,7 +67,7 @@ ot_admin_builtin_diff (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GE
 
   if (argc > 2)
     {
-      deployment = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, argv[2], NULL);
+      deployment = ot_gfile_get_child_build_path (admin_opts->sysroot, "ostree", "deploy", osname, argv[2], 
NULL);
       if (!g_file_query_exists (deployment, NULL))
         {
           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -78,7 +77,7 @@ ot_admin_builtin_diff (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GE
     }
   else
     {
-      if (!ot_admin_get_current_deployment (ostree_dir, osname, &deployment,
+      if (!ot_admin_get_current_deployment (admin_opts->sysroot, osname, &deployment,
                                             cancellable, error))
         goto out;
     }
diff --git a/src/ostree/ot-admin-builtin-install.c b/src/ostree/ot-admin-builtin-install.c
index 5f5fb3d..675f52c 100644
--- a/src/ostree/ot-admin-builtin-install.c
+++ b/src/ostree/ot-admin-builtin-install.c
@@ -70,7 +70,6 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
   gboolean ret = FALSE;
   const char *keyfile_arg = NULL;
   const char *treename_arg = NULL;
-  GFile *ostree_dir = admin_opts->ostree_dir;
   ot_lobj GFile *deploy_dir = NULL;
   ot_lobj GFile *osdir = NULL;
   ot_lobj GFile *dest_osconfig_path = NULL;
@@ -96,14 +95,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
       goto out;
     }
 
-  if (admin_opts->ostree_dir == NULL)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "No existing /ostree found; use --ostree-dir");
-      goto out;
-    }
-
-  if (!ot_admin_ensure_initialized (admin_opts->ostree_dir, cancellable, error))
+  if (!ot_admin_ensure_initialized (admin_opts->sysroot, cancellable, error))
     goto out;
 
   self->loop = g_main_loop_new (NULL, TRUE);
@@ -136,11 +128,11 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
 
   osname = g_key_file_get_string (keyfile, "os", "Name", error);
 
-  ostree_dir_arg = g_strconcat ("--ostree-dir=",
-                                gs_file_get_path_cached (ostree_dir),
+  ostree_dir_arg = g_strconcat ("--sysroot=",
+                                gs_file_get_path_cached (admin_opts->sysroot),
                                 NULL);
 
-  if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+  if (!gs_subprocess_simple_run_sync (NULL,
                                       GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                       cancellable, error,
                                       "ostree", "admin", ostree_dir_arg, "os-init", osname, NULL))
@@ -157,7 +149,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
         goto out;
     }
 
-  osdir = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, NULL);
+  osdir = ot_gfile_get_child_build_path (admin_opts->sysroot, "ostree", "deploy", osname, NULL);
   dest_osconfig_path = ot_gfile_get_child_strconcat (osdir, osname, ".cfg", NULL);
 
   if (!g_file_copy (self->osconfig_path, dest_osconfig_path, G_FILE_COPY_OVERWRITE | 
G_FILE_COPY_TARGET_DEFAULT_PERMS,
@@ -168,7 +160,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
     goto out;
 
   repoarg = g_strconcat ("--repo=",
-                         gs_file_get_path_cached (ostree_dir), "/repo",
+                         gs_file_get_path_cached (admin_opts->sysroot), "/ostree/repo",
                          NULL);
 
   {
@@ -178,7 +170,7 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
     if (!repourl)
       goto out;
     
-    if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+    if (!gs_subprocess_simple_run_sync (NULL,
                                         GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                         cancellable, error,
                                         "ostree", repoarg, "remote", "add",
@@ -186,13 +178,13 @@ ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
       goto out;
   }
 
-  if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+  if (!gs_subprocess_simple_run_sync (NULL,
                                       GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                       cancellable, error,
                                         "ostree", "pull", repoarg, osname, NULL))
     goto out;
 
-  if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+  if (!gs_subprocess_simple_run_sync (NULL,
                                       GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                       cancellable, error,
                                       "ostree", "admin", ostree_dir_arg, "deploy", osname,
diff --git a/src/ostree/ot-admin-builtin-os-init.c b/src/ostree/ot-admin-builtin-os-init.c
index 0536167..8d725fd 100644
--- a/src/ostree/ot-admin-builtin-os-init.c
+++ b/src/ostree/ot-admin-builtin-os-init.c
@@ -38,7 +38,6 @@ ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
   GOptionContext *context;
   gboolean ret = FALSE;
   const char *osname = NULL;
-  GFile *ostree_dir = admin_opts->ostree_dir;
   ot_lobj GFile *deploy_dir = NULL;
   ot_lobj GFile *dir = NULL;
   __attribute__((unused)) GCancellable *cancellable = NULL;
@@ -49,7 +48,7 @@ ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
   if (!g_option_context_parse (context, &argc, &argv, error))
     goto out;
 
-  if (!ot_admin_ensure_initialized (ostree_dir, cancellable, error))
+  if (!ot_admin_ensure_initialized (admin_opts->sysroot, cancellable, error))
     goto out;
 
   if (argc < 2)
@@ -60,7 +59,7 @@ ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
 
   osname = argv[1];
 
-  deploy_dir = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, NULL);
+  deploy_dir = ot_gfile_get_child_build_path (admin_opts->sysroot, "ostree", "deploy", osname, NULL);
 
   /* Ensure core subdirectories of /var exist, since we need them for
    * dracut generation, and the host will want them too.  Note that at
diff --git a/src/ostree/ot-admin-builtin-prune.c b/src/ostree/ot-admin-builtin-prune.c
index 4d60099..03270e0 100644
--- a/src/ostree/ot-admin-builtin-prune.c
+++ b/src/ostree/ot-admin-builtin-prune.c
@@ -43,7 +43,6 @@ ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, G
   gboolean ret = FALSE;
   guint i;
   const char *osname;
-  GFile *ostree_dir = admin_opts->ostree_dir;
   ot_lobj GFile *repo_path = NULL;
   ot_lobj GFile *deploy_dir = NULL;
   ot_lobj GFile *current_deployment = NULL;
@@ -68,15 +67,15 @@ ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, G
 
   osname = argv[1];
 
-  if (!ot_admin_list_deployments (ostree_dir, osname, &deployments,
+  if (!ot_admin_list_deployments (admin_opts->sysroot, &deployments,
                                   cancellable, error))
     goto out;
 
-  if (!ot_admin_get_current_deployment (ostree_dir, osname, &current_deployment,
+  if (!ot_admin_get_current_deployment (admin_opts->sysroot, osname, &current_deployment,
                                         cancellable, error));
-  if (!ot_admin_get_previous_deployment (ostree_dir, osname, &previous_deployment,
+  if (!ot_admin_get_previous_deployment (admin_opts->sysroot, osname, &previous_deployment,
                                          cancellable, error));
-  if (!ot_admin_get_active_deployment (ostree_dir, &active_osname, &active_deployment,
+  if (!ot_admin_get_active_deployment (admin_opts->sysroot, &active_osname, &active_deployment,
                                        cancellable, error));
 
   for (i = 0; i < deployments->len; i++)
@@ -104,7 +103,7 @@ ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, G
         goto out;
     }
   
-  repo_path = g_file_get_child (ostree_dir, "repo");
+  repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
 
   if (!opt_no_repo_prune)
     {
@@ -112,7 +111,7 @@ ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, G
 
       repo_arg = g_strconcat ("--repo=", gs_file_get_path_cached (repo_path), NULL);
       
-      if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+      if (!gs_subprocess_simple_run_sync (NULL,
                                           GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                           cancellable, error,
                                           "ostree", repo_arg, "prune", "--refs-only",
diff --git a/src/ostree/ot-admin-builtin-pull-deploy.c b/src/ostree/ot-admin-builtin-pull-deploy.c
index cfdd9e6..b8be36f 100644
--- a/src/ostree/ot-admin-builtin-pull-deploy.c
+++ b/src/ostree/ot-admin-builtin-pull-deploy.c
@@ -95,7 +95,6 @@ ot_admin_builtin_pull_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_o
   gboolean ret = FALSE;
   const char *osname;
   const char *target;
-  GFile *ostree_dir = admin_opts->ostree_dir;
   ot_lobj OstreeRepo *repo = NULL;
   ot_lobj GFile *repo_path = NULL;
   ot_lobj GFile *current_deployment = NULL;
@@ -120,7 +119,7 @@ ot_admin_builtin_pull_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_o
 
   osname = argv[1];
 
-  repo_path = g_file_get_child (ostree_dir, "repo");
+  repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
 
   repo = ostree_repo_new (repo_path);
   if (!ostree_repo_check (repo, error))
@@ -137,7 +136,7 @@ ot_admin_builtin_pull_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_o
     }
   else
     {
-      if (!ot_admin_get_current_deployment (ostree_dir, osname, &current_deployment,
+      if (!ot_admin_get_current_deployment (admin_opts->sysroot, osname, &current_deployment,
                                             cancellable, error))
         goto out;
       
@@ -148,24 +147,21 @@ ot_admin_builtin_pull_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_o
           goto out;
         }
       
-      ot_admin_parse_deploy_name (ostree_dir, osname, current_deployment,
+      ot_admin_parse_deploy_name (admin_opts->sysroot, osname, current_deployment,
                                   &deploy_name, NULL);
     }
 
-  if (!ot_admin_pull (ostree_dir, osname, cancellable, error))
+  if (!ot_admin_pull (admin_opts->sysroot, osname, cancellable, error))
     goto out;
 
   {
-    ot_lfree char *opt_ostree_dir_arg = g_strconcat ("--ostree-dir=",
-                                                     gs_file_get_path_cached (ostree_dir),
+    ot_lfree char *opt_ostree_dir_arg = g_strconcat ("--sysroot=",
+                                                     gs_file_get_path_cached (admin_opts->sysroot),
                                                      NULL);
-    ot_lfree char *opt_boot_dir_arg = g_strconcat ("--boot-dir=",
-                                                     gs_file_get_path_cached (admin_opts->boot_dir),
-                                                     NULL);
-    if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+    if (!gs_subprocess_simple_run_sync (NULL,
                                         GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                         cancellable, error,
-                                        "ostree", "admin", opt_ostree_dir_arg, opt_boot_dir_arg, "deploy", 
osname,
+                                        "ostree", "admin", opt_ostree_dir_arg, "deploy", osname,
                                         deploy_name, NULL))
       goto out;
   }
diff --git a/src/ostree/ot-admin-builtin-upgrade.c b/src/ostree/ot-admin-builtin-upgrade.c
index f13ce21..c0d35e8 100644
--- a/src/ostree/ot-admin-builtin-upgrade.c
+++ b/src/ostree/ot-admin-builtin-upgrade.c
@@ -43,7 +43,6 @@ ot_admin_builtin_upgrade (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
 {
   GOptionContext *context;
   gboolean ret = FALSE;
-  GFile *ostree_dir = admin_opts->ostree_dir;
   gs_free char *booted_osname = NULL;
   const char *osname = NULL;
   gs_unref_object GFile *deployment = NULL;
@@ -79,23 +78,23 @@ ot_admin_builtin_upgrade (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
       osname = booted_osname;
     }
   
-  if (!ot_admin_get_current_deployment (ostree_dir, osname, &deployment,
+  if (!ot_admin_get_current_deployment (admin_opts->sysroot, osname, &deployment,
                                         cancellable, error))
     goto out;
 
-  ot_admin_parse_deploy_name (ostree_dir, osname, deployment, &deploy_name, &current_rev);
+  ot_admin_parse_deploy_name (admin_opts->sysroot, osname, deployment, &deploy_name, &current_rev);
 
-  ostree_dir_arg = g_strconcat ("--ostree-dir=",
-                                gs_file_get_path_cached (ostree_dir),
+  ostree_dir_arg = g_strconcat ("--sysroot=",
+                                gs_file_get_path_cached (admin_opts->sysroot),
                                 NULL);
   
-  if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+  if (!gs_subprocess_simple_run_sync (NULL,
                                       GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                       cancellable, error,
                                       "ostree", "admin", ostree_dir_arg, "pull-deploy", osname, NULL))
     goto out;
 
-  if (!gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+  if (!gs_subprocess_simple_run_sync (NULL,
                                       GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                       cancellable, error,
                                       "ostree", "admin", ostree_dir_arg, "prune", osname, NULL))
@@ -103,7 +102,7 @@ ot_admin_builtin_upgrade (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
 
   if (opt_reboot)
     {
-      repo_path = g_file_get_child (ostree_dir, "repo");
+      repo_path = g_file_resolve_relative_path (admin_opts->sysroot, "ostree/repo");
 
       repo = ostree_repo_new (repo_path);
       if (!ostree_repo_check (repo, error))
diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h
index 5fbdacd..5305170 100644
--- a/src/ostree/ot-admin-builtins.h
+++ b/src/ostree/ot-admin-builtins.h
@@ -28,8 +28,7 @@
 G_BEGIN_DECLS
 
 typedef struct {
-  GFile *ostree_dir;
-  GFile *boot_dir;
+  GFile *sysroot;
 } OtAdminBuiltinOpts;
 
 gboolean ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
@@ -40,7 +39,6 @@ gboolean ot_admin_builtin_prune (int argc, char **argv, OtAdminBuiltinOpts *admi
 gboolean ot_admin_builtin_pull_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError 
**error);
 gboolean ot_admin_builtin_diff (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
 gboolean ot_admin_builtin_run_triggers (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError 
**error);
-gboolean ot_admin_builtin_update_kernel (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError 
**error);
 gboolean ot_admin_builtin_upgrade (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error);
 
 G_END_DECLS
diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c
index 28eb077..3339d38 100644
--- a/src/ostree/ot-admin-functions.c
+++ b/src/ostree/ot-admin-functions.c
@@ -23,16 +23,21 @@
 #include "config.h"
 
 #include "ot-admin-functions.h"
+#include "ot-deployment.h"
 #include "otutil.h"
 #include "ostree-core.h"
+#include "libgsystem.h"
 
 gboolean
-ot_admin_ensure_initialized (GFile         *ostree_dir,
+ot_admin_ensure_initialized (GFile         *sysroot,
                              GCancellable  *cancellable,
                              GError       **error)
 {
   gboolean ret = FALSE;
-  ot_lobj GFile *dir = NULL;
+  gs_unref_object GFile *dir = NULL;
+  gs_unref_object GFile *ostree_dir = NULL;
+
+  ostree_dir = g_file_get_child (sysroot, "ostree");
 
   g_clear_object (&dir);
   dir = g_file_get_child (ostree_dir, "repo");
@@ -143,7 +148,7 @@ query_symlink_target_allow_noent (GFile         *path,
  * %NULL if none.
  */
 gboolean
-ot_admin_get_current_deployment (GFile           *ostree_dir,
+ot_admin_get_current_deployment (GFile           *sysroot,
                                  const char      *osname,
                                  GFile          **out_deployment,
                                  GCancellable    *cancellable,
@@ -151,7 +156,7 @@ ot_admin_get_current_deployment (GFile           *ostree_dir,
 {
   ot_lobj GFile *current_path = NULL;
 
-  current_path = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname,
+  current_path = ot_gfile_get_child_build_path (sysroot, "ostree", "deploy", osname,
                                                 "current", NULL);
 
   return query_symlink_target_allow_noent (current_path, out_deployment,
@@ -226,62 +231,361 @@ ot_admin_list_osnames (GFile               *ostree_dir,
 }
 */
 
+gboolean
+ot_admin_read_current_bootversion (GFile         *sysroot,
+                                   int           *out_bootversion,
+                                   GCancellable  *cancellable,
+                                   GError       **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *boot_loader_path = g_file_resolve_relative_path (sysroot, "boot/loader");
+  gs_unref_object GFileInfo *info = NULL;
+  const char *target;
+  int ret_bootversion;
+
+  info = g_file_query_info (boot_loader_path, OSTREE_GIO_FAST_QUERYINFO,
+                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+  if (!info)
+    goto out;
+
+  if (g_file_info_get_file_type (info) != G_FILE_TYPE_SYMBOLIC_LINK)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Not a symbolic link: %s", gs_file_get_path_cached (boot_loader_path));
+      goto out;
+    }
+
+  target = g_file_info_get_symlink_target (info);
+  if (g_strcmp0 (target, "0") == 0)
+    ret_bootversion = 0;
+  else if (g_strcmp0 (target, "1") == 0)
+    ret_bootversion = 1;
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid target '%s' in %s", target, gs_file_get_path_cached (boot_loader_path));
+      goto out;
+    }
+
+  ret = TRUE;
+  *out_bootversion = ret_bootversion;
+ out:
+  return ret;
+}
+
 static gboolean
-list_deployments_internal (GFile               *from_dir,
-                           GPtrArray           *inout_deployments,
-                           GCancellable        *cancellable,
-                           GError             **error)
+read_one_boot_loader_entry_lines (GFile           *boot_dir,
+                                  GFile           *config,
+                                  GVariant       **out_entry,
+                                  GCancellable    *cancellable,
+                                  GError         **error)
 {
   gboolean ret = FALSE;
-  GError *temp_error = NULL;
-  ot_lobj GFileEnumerator *dir_enum = NULL;
-  ot_lobj GFileInfo *file_info = NULL;
+  gs_unref_variant GVariant*ret_entry = NULL;
+  gboolean builder_initialized = FALSE;
+  GVariantBuilder builder;
+  gs_free char *contents = NULL;
+  gs_free char *relpath = NULL;
+  char **lines = NULL;
+  char **iter = NULL;
+
+  contents = gs_file_load_contents_utf8 (config, cancellable, error);
+  if (!contents)
+    goto out;
+
+  builder_initialized = TRUE;
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sss)"));
+
+  lines = g_strsplit (contents, "\n", -1);
+  for (iter = lines; *iter; iter++)
+    {
+      const char *line = *iter;
+      char **items = NULL;
+      gboolean iskey = FALSE;
+      
+      if (g_ascii_isalpha (*line))
+        {
+          items = g_strsplit_set (line, " \t", 2);
+          if (g_strv_length (items) == 2)
+            {
+              g_variant_builder_add (&builder, "(sss)", items[0], items[1], line);
+              iskey = TRUE;
+            }
+        }
+      if (!iskey)
+        g_variant_builder_add (&builder, "(sss)", "", "",
+                               line);
+      g_strfreev (items);
+    }
 
-  dir_enum = g_file_enumerate_children (from_dir, OSTREE_GIO_FAST_QUERYINFO,
+  relpath = g_file_get_relative_path (boot_dir, config);
+  *out_entry = g_variant_new ("(sa(sss))", relpath, 
+                              g_variant_builder_end (&builder));
+
+  ret = TRUE;
+  builder_initialized = FALSE;
+ out:
+  if (builder_initialized)
+    g_variant_builder_clear (&builder);
+  g_strfreev (lines);
+  return ret;
+}
+
+static gboolean
+read_boot_loader_entry_lines (GFile         *boot_dir,
+                               GPtrArray    **out_loader_entries,
+                               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 *loader_entries_dir = NULL;
+  gs_unref_ptrarray GPtrArray *ret_loader_entries = NULL;
+
+  loader_entries_dir = g_file_resolve_relative_path (boot_dir, "loader/entries");
+  ret_loader_entries = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
+
+  dir_enum = g_file_enumerate_children (loader_entries_dir, OSTREE_GIO_FAST_QUERYINFO,
+                                        0, NULL, error);
+  if (!dir_enum)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, error)) != NULL)
+    {
+      const char *name = g_file_info_get_name (file_info);
+      gs_unref_object GFile *child = g_file_get_child (loader_entries_dir, name);
+
+      if (g_str_has_prefix (name, "ostree-") &&
+          g_str_has_suffix (name, ".conf") &&
+          g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
+        {
+          GVariant *entry;
+          if (!read_one_boot_loader_entry_lines (boot_dir, child, &entry,
+                                                 cancellable, error))
+            goto out;
+          g_ptr_array_add (ret_loader_entries, entry);  /* Transfer ownership */
+        }
+      g_clear_object (&file_info);
+    }
+
+  ot_transfer_out_value (out_loader_entries, &ret_loader_entries);
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static void
+match_info_cleanup (void *loc)
+{
+  GMatchInfo **match = (GMatchInfo**)loc;
+  if (*match) g_match_info_unref (*match);
+}
+
+static gboolean
+list_deployments_for_os (GFile               *osdir,
+                         GPtrArray           *inout_deployments,
+                         GCancellable        *cancellable,
+                         GError             **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFileEnumerator *dir_enum = NULL;
+
+  static gsize regex_initialized;
+  static GRegex *tree_regex;
+
+  if (g_once_init_enter (&regex_initialized))
+    {
+      tree_regex = g_regex_new ("^([0-9a-f]+)\\.([0-9]+)$", 0, 0, NULL);
+      g_assert (tree_regex);
+      g_once_init_leave (&regex_initialized, 1);
+    }
+
+  dir_enum = g_file_enumerate_children (osdir, 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)
+  while (TRUE)
     {
       const char *name;
-      ot_lobj GFile *child = NULL;
-      ot_lobj GFile *possible_etc = NULL;
-      ot_lobj GFile *possible_usr = NULL;
+      GFileInfo *file_info = NULL;
+      GFile *child = NULL;
+      gs_unref_object OtDeployment *deployment = NULL;
+      __attribute__((cleanup(match_info_cleanup))) GMatchInfo *match = NULL;
+
+      if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child,
+                                       cancellable, error))
+        goto out;
+      if (file_info == NULL)
+        break;
 
       name = g_file_info_get_name (file_info);
 
-      if (g_str_has_suffix (name, "-etc"))
-        goto next;
       if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
-        goto next;
+        continue;
 
-      child = g_file_get_child (from_dir, name);
+      if (!g_regex_match (tree_regex, name, 0, &match))
+        continue;
 
-      possible_etc = ot_gfile_get_child_strconcat (from_dir, name, "-etc", NULL);
-      /* Bit of a hack... */
-      possible_usr = g_file_get_child (child, "usr");
+      deployment = NULL;
+      g_ptr_array_add (inout_deployments, child);
+    }
 
-      if (g_file_query_exists (possible_etc, cancellable))
-        g_ptr_array_add (inout_deployments, g_file_get_child (from_dir, name));
-      else if (g_file_query_exists (possible_usr, cancellable))
-        goto next;
-      else
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+get_deployment_directories (GFile               *sysroot,
+                            GPtrArray          **out_deployments,
+                            GCancellable        *cancellable,
+                            GError             **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFileEnumerator *dir_enum = NULL;
+  gs_unref_object GFile *deploydir = NULL;
+  gs_unref_object GFile *osdir = NULL;
+  gs_unref_ptrarray GPtrArray *ret_deployments = NULL;
+
+  deploydir = ot_gfile_get_child_build_path (sysroot, "ostree", "deploy", NULL);
+
+  dir_enum = g_file_enumerate_children (deploydir, OSTREE_GIO_FAST_QUERYINFO,
+                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                        NULL, error);
+  if (!dir_enum)
+    goto out;
+
+  ret_deployments = g_ptr_array_new_with_free_func (g_object_unref);
+
+  while (TRUE)
+    {
+      GFileInfo *file_info = NULL;
+      GFile *child = NULL;
+
+      if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child,
+                                       NULL, error))
+        goto out;
+      if (file_info == NULL)
+        break;
+
+      if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
+        continue;
+      
+      if (!list_deployments_for_os (child, ret_deployments, cancellable, error))
+        goto out;
+    }
+  
+  ret = TRUE;
+  ot_transfer_out_value (out_deployments, &ret_deployments);
+ out:
+  return ret;
+}
+
+static char *
+get_ostree_kernel_arg_from_entry (GVariant  *boot_loader_entry)
+{
+  GVariantIter iter;
+  const char *key;
+  const char *value;
+  const char *line;
+  char *ret = NULL;
+  gs_unref_variant GVariant *lines = g_variant_get_child_value (boot_loader_entry, 1);
+
+  g_variant_iter_init (&iter, lines);
+  while (g_variant_iter_loop (&iter, "(&s&s&s)", &key, &value, &line))
+    {
+      char **opts, **iter;
+      if (g_strcmp0 (key, "options") != 0)
+        continue;
+     
+      opts = g_strsplit (key, " ", -1);
+      for (iter = opts; *iter; iter++)
         {
-          if (!list_deployments_internal (child, inout_deployments,
-                                          cancellable, error))
-            goto out;
+          if (g_str_has_prefix (*iter, "ostree="))
+            {
+              ret = g_strdup (*iter + strlen ("ostree="));
+              break;
+            }
         }
+      if (ret)
+        break;
+    }
 
-    next:
-      g_clear_object (&file_info);
+  return ret;
+}
+
+static gboolean
+parse_ostree_arg (const char    *ostree_arg,
+                  int           *out_entry_bootversion,
+                  char         **out_osname,
+                  char         **out_bootcsum,
+                  int           *out_treebootserial,
+                  GError       **error)
+{
+  gboolean ret = FALSE;
+  __attribute__((cleanup(match_info_cleanup))) GMatchInfo *match = NULL;
+  gs_free char *bootversion_str = NULL;
+  gs_free char *treebootserial_str = NULL;
+
+  static gsize regex_initialized;
+  static GRegex *regex;
+
+  if (g_once_init_enter (&regex_initialized))
+    {
+      regex = g_regex_new ("^/ostree/boot.([01])/([^/]+)/([^/]+)/([0-9]+)$", 0, 0, NULL);
+      g_assert (regex);
+      g_once_init_leave (&regex_initialized, 1);
     }
-  if (temp_error != NULL)
+
+  if (!g_regex_match (regex, ostree_arg, 0, &match))
     {
-      g_propagate_error (error, temp_error);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid ostree= argument '%s', expected 
ostree=/ostree/boot.BOOTVERSION/OSNAME/BOOTCSUM/TREESERIAL", ostree_arg);
+      goto out;
+    }
+    
+  bootversion_str = g_match_info_fetch (match, 1);
+  *out_entry_bootversion = (int)g_ascii_strtoll (bootversion_str, NULL, 10);
+  *out_osname = g_match_info_fetch (match, 2);
+  *out_bootcsum = g_match_info_fetch (match, 3);
+  treebootserial_str = g_match_info_fetch (match, 4);
+  *out_treebootserial = (int)g_ascii_strtoll (treebootserial_str, NULL, 10);
+  
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+
+static gboolean
+list_deployments_process_one_boot_entry (GFile               *sysroot,
+                                         GVariant            *entry,
+                                         GPtrArray           *inout_deployments,
+                                         GCancellable        *cancellable,
+                                         GError             **error)
+{
+  gboolean ret = FALSE;
+  gs_free char *ostree_arg = NULL;
+  int entry_boot_version;
+  gs_free char *osname = NULL;
+  gs_free char *bootcsum = NULL;
+  int treebootserial;
+
+  ostree_arg = get_ostree_kernel_arg_from_entry (entry);
+  if (ostree_arg == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "No ostree= kernel argument found");
       goto out;
     }
+      
+  if (!parse_ostree_arg (ostree_arg, &entry_boot_version,
+                         &osname, &bootcsum, &treebootserial,
+                         error))
+    goto out;
 
   ret = TRUE;
  out:
@@ -289,24 +593,41 @@ list_deployments_internal (GFile               *from_dir,
 }
 
 gboolean
-ot_admin_list_deployments (GFile               *ostree_dir,
-                           const char          *osname,
+ot_admin_list_deployments (GFile               *sysroot,
                            GPtrArray          **out_deployments,
                            GCancellable        *cancellable,
                            GError             **error)
 {
   gboolean ret = FALSE;
-  ot_lobj GFileEnumerator *dir_enum = NULL;
-  ot_lobj GFileInfo *file_info = NULL;
-  ot_lobj GFile *osdir = NULL;
-  ot_lptrarray GPtrArray *ret_deployments = NULL;
+  gs_unref_object GFile *boot_dir = g_file_get_child (sysroot, "boot");
+  gs_unref_ptrarray GPtrArray *boot_loader_entries = NULL;
+  gs_unref_ptrarray GPtrArray *ret_deployments = NULL;
+  guint i;
+  int bootversion;
 
-  osdir = ot_gfile_get_child_build_path (ostree_dir, "deploy", osname, NULL);
-  ret_deployments = g_ptr_array_new_with_free_func (g_object_unref);
+  if (!ot_admin_read_current_bootversion (sysroot, &bootversion, cancellable, error))
+    goto out;
 
-  if (!list_deployments_internal (osdir, ret_deployments, cancellable, error))
+  if (!read_boot_loader_entry_lines (boot_dir, &boot_loader_entries,
+                                     cancellable, error))
     goto out;
-  
+
+  ret_deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
+
+  for (i = 0; i < boot_loader_entries->len; i++)
+    {
+      const char *path;
+      GVariant *entry = boot_loader_entries->pdata[i];
+      g_variant_get_child (entry, 0, "&s", &path);
+
+      if (!list_deployments_process_one_boot_entry (sysroot, entry, ret_deployments,
+                                                    cancellable, error))
+        {
+          g_prefix_error (error, "Parsing bootloader entry '%s': ", path);
+          goto out;
+        }
+    }
+
   ret = TRUE;
   ot_transfer_out_value (out_deployments, &ret_deployments);
  out:
@@ -362,7 +683,7 @@ ot_admin_get_booted_os (char        **out_osname,
 }
 
 gboolean
-ot_admin_get_active_deployment (GFile           *ostree_dir,
+ot_admin_get_active_deployment (GFile           *sysroot,
                                 char           **out_osname,
                                 GFile          **out_deployment,
                                 GCancellable    *cancellable,
@@ -395,7 +716,7 @@ ot_admin_get_active_deployment (GFile           *ostree_dir,
       root_dev = g_file_info_get_attribute_uint32 (rootfs_info, "unix::device");
       root_inode = g_file_info_get_attribute_uint64 (rootfs_info, "unix::inode");
 
-      if (!ot_admin_list_deployments (ostree_dir, ret_osname, &deployments,
+      if (!ot_admin_list_deployments (sysroot, &deployments,
                                       cancellable, error))
         goto out;
       
@@ -469,29 +790,30 @@ ot_admin_get_default_ostree_dir (GFile        **out_ostree_dir,
 }
 
 gboolean
-ot_admin_pull (GFile         *ostree_dir,
+ot_admin_pull (GFile         *sysroot,
                const char    *osname,
                GCancellable  *cancellable,
                GError       **error)
 {
-  gs_unref_object GFile *repo_path = g_file_get_child (ostree_dir, "repo");
+  gs_unref_object GFile *repo_path = g_file_resolve_relative_path (sysroot, "ostree/repo");
   gs_free char *repo_arg = g_strconcat ("--repo=",
                                         gs_file_get_path_cached (repo_path),
                                         NULL);
 
-  return gs_subprocess_simple_run_sync (gs_file_get_path_cached (ostree_dir),
+  return gs_subprocess_simple_run_sync (NULL,
                                         GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
                                         cancellable, error,
                                         "ostree", repo_arg, "pull", osname, NULL);
 }
 
 void
-ot_admin_parse_deploy_name (GFile       *ostree_dir,
+ot_admin_parse_deploy_name (GFile       *sysroot,
                             const char  *osname,
                             GFile       *deployment,
                             char       **out_name,
                             char       **out_rev)
 {
+  gs_unref_object GFile *ostree_dir = g_file_get_child (sysroot, "ostree");
   gs_unref_object GFile *deploy_dir = g_file_get_child (ostree_dir, "deploy");
   gs_unref_object GFile *os_dir = g_file_get_child (deploy_dir, osname);
   gs_free char *relpath = g_file_get_relative_path (os_dir, deployment);
diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h
index a03ef59..140f850 100644
--- a/src/ostree/ot-admin-functions.h
+++ b/src/ostree/ot-admin-functions.h
@@ -31,6 +31,11 @@ gboolean ot_admin_ensure_initialized (GFile         *ostree_dir,
                                      GCancellable  *cancellable,
                                      GError       **error);
 
+gboolean ot_admin_read_current_bootversion (GFile         *sysroot,
+                                            int           *out_bootversion,
+                                            GCancellable  *cancellable,
+                                            GError       **error);
+
 gboolean ot_admin_get_booted_os (char        **out_osname,
                                  char        **out_tree,
                                  GCancellable *cancellable,
@@ -47,8 +52,7 @@ gboolean ot_admin_get_previous_deployment (GFile           *ostree_dir,
                                            GCancellable    *cancellable,
                                            GError         **error);
 
-gboolean ot_admin_list_deployments (GFile               *ostree_dir,
-                                    const char          *osname,
+gboolean ot_admin_list_deployments (GFile               *sysroot,
                                     GPtrArray          **out_deployments,
                                     GCancellable        *cancellable,
                                     GError             **error);
diff --git a/src/ostree/ot-bootloader-syslinux.c b/src/ostree/ot-bootloader-syslinux.c
new file mode 100644
index 0000000..7687568
--- /dev/null
+++ b/src/ostree/ot-bootloader-syslinux.c
@@ -0,0 +1,225 @@
+/* -*- 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);
+}
+
+#if 0
+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;
+}
+#endif
+
+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..661df1b
--- /dev/null
+++ b/src/ostree/ot-bootloader.c
@@ -0,0 +1,53 @@
+/* -*- 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..e4e3877
--- /dev/null
+++ b/src/ostree/ot-bootloader.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OT_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);
+};
+
+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__ */
diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c
index c8f60fb..fdcb570 100644
--- a/src/ostree/ot-builtin-admin.c
+++ b/src/ostree/ot-builtin-admin.c
@@ -31,12 +31,10 @@
 
 #include <glib/gi18n.h>
 
-static char *opt_ostree_dir = NULL;
-static char *opt_boot_dir = "/boot";
+static char *opt_sysroot = "/";
 
 static GOptionEntry options[] = {
-  { "ostree-dir", 0, 0, G_OPTION_ARG_STRING, &opt_ostree_dir, "Path to OSTree root directory (default: 
/ostree)", NULL },
-  { "boot-dir", 0, 0, G_OPTION_ARG_STRING, &opt_boot_dir, "Path to system boot directory (default: /boot)", 
NULL },
+  { "sysroot", 0, 0, G_OPTION_ARG_STRING, &opt_sysroot, "Path to root directory (default: /)", NULL },
   { NULL }
 };
 
@@ -53,7 +51,6 @@ static OstreeAdminCommand admin_subcommands[] = {
   { "upgrade", ot_admin_builtin_upgrade },
   { "pull-deploy", ot_admin_builtin_pull_deploy },
   { "prune", ot_admin_builtin_prune },
-  { "update-kernel", ot_admin_builtin_update_kernel },
   { "config-diff", ot_admin_builtin_diff },
   { "run-triggers", ot_admin_builtin_run_triggers },
   { NULL, NULL }
@@ -70,8 +67,6 @@ ostree_builtin_admin (int argc, char **argv, GFile *repo_path, GError **error)
   int subcmd_argc;
   OtAdminBuiltinOpts admin_opts;
   char **subcmd_argv = NULL;
-  ot_lobj GFile *ostree_dir = NULL;
-  ot_lobj GFile *boot_dir = NULL;
 
   context = g_option_context_new ("[OPTIONS] SUBCOMMAND - Run an administrative subcommand");
 
@@ -117,21 +112,9 @@ ostree_builtin_admin (int argc, char **argv, GFile *repo_path, GError **error)
       goto out;
     }
 
-  if (opt_ostree_dir != NULL)
-    {
-      ostree_dir = g_file_new_for_path (opt_ostree_dir);
-    }
-  else
-    {
-      if (!ot_admin_get_default_ostree_dir (&ostree_dir, cancellable, error))
-        goto out;
-    }
-  boot_dir = g_file_new_for_path (opt_boot_dir);
-
   ostree_prep_builtin_argv (subcommand_name, argc-2, argv+2, &subcmd_argc, &subcmd_argv);
 
-  admin_opts.ostree_dir = ostree_dir;
-  admin_opts.boot_dir = boot_dir;
+  admin_opts.sysroot = g_file_new_for_path (opt_sysroot);
   if (!subcommand->fn (subcmd_argc, subcmd_argv, &admin_opts, error))
     goto out;
  
diff --git a/src/ostree/ot-deployment.c b/src/ostree/ot-deployment.c
new file mode 100644
index 0000000..e016f20
--- /dev/null
+++ b/src/ostree/ot-deployment.c
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "ot-deployment.h"
+
+struct _OtDeployment
+{
+  GObject       parent_instance;
+
+  int index;  /* Global offset */
+  char *ref;  /* originating OSTree refname (e.g. gnome-ostree/buildmaster/x86_64-runtime) */
+  char *csum;  /* OSTree checksum of tree */
+  int deployserial;  /* How many times this particular csum appears in deployment list */
+  char *bootcsum;  /* Checksum of kernel+initramfs */
+  int bootserial; /* An integer assigned to this tree per its ${bootcsum} */
+};
+
+typedef GObjectClass OtDeploymentClass;
+
+G_DEFINE_TYPE (OtDeployment, ot_deployment, G_TYPE_OBJECT)
+
+static void
+ot_deployment_finalize (GObject *object)
+{
+  OtDeployment *self = OT_DEPLOYMENT (object);
+
+  g_free (self->ref);
+  g_free (self->csum);
+  g_free (self->bootcsum);
+
+  G_OBJECT_CLASS (ot_deployment_parent_class)->finalize (object);
+}
+
+void
+ot_deployment_init (OtDeployment *self)
+{
+}
+
+void
+ot_deployment_class_init (OtDeploymentClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = ot_deployment_finalize;
+}
+
+OtDeployment *
+ot_deployment_new (int    index,
+                   char  *ref,
+                   char  *csum,
+                   int    deployserial,
+                   char  *bootcsum,
+                   int    bootserial)
+{
+  OtDeployment *self = g_object_new (OT_TYPE_DEPLOYMENT, NULL);
+  self->index = index;
+  self->ref = g_strdup (ref);
+  self->csum = g_strdup (csum);
+  self->deployserial = deployserial;
+  self->bootcsum = g_strdup (bootcsum);
+  self->bootserial = bootserial;
+  return self;
+}
diff --git a/src/ostree/ot-deployment.h b/src/ostree/ot-deployment.h
new file mode 100644
index 0000000..d37be96
--- /dev/null
+++ b/src/ostree/ot-deployment.h
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __OT_DEPLOYMENT_H__
+#define __OT_DEPLOYMENT_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define OT_TYPE_DEPLOYMENT (ot_deployment_get_type ())
+#define OT_DEPLOYMENT(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OT_TYPE_DEPLOYMENT, OtDeployment))
+#define G_IS_DEPLOYMENT(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OT_TYPE_DEPLOYMENT))
+
+typedef struct _OtDeployment OtDeployment;
+
+GType ot_deployment_get_type (void) G_GNUC_CONST;
+
+OtDeployment * ot_deployment_new (int    index,
+                                  char  *ref,
+                                  char  *csum,
+                                  int    deployserial,
+                                  char  *bootcsum,
+                                  int    bootserial);
+
+int ot_deployment_get_index (OtDeployment *self);
+
+G_END_DECLS
+
+#endif /* __OT_DEPLOYMENT_H__ */



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