gboolean
ot_admin_builtin_diff (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error)
{
  GOptionContext *context;
  gboolean ret = FALSE;
  gs_unref_object OstreeDeployment *deployment = NULL;
  gs_unref_object GFile *deployment_dir = NULL;
  gs_unref_ptrarray GPtrArray *modified = NULL;
  gs_unref_ptrarray GPtrArray *removed = NULL;
  gs_unref_ptrarray GPtrArray *added = NULL;
  gs_unref_object GFile *orig_etc_path = NULL;
  gs_unref_object GFile *new_etc_path = NULL;

  context = g_option_context_new ("Diff current /etc configuration versus default");

  g_option_context_add_main_entries (context, options, NULL);

  if (!g_option_context_parse (context, &argc, &argv, error))
    goto out;
  
  if (!ostree_sysroot_load (sysroot, cancellable, error))
    goto out;

  if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname,
                                                     cancellable, error))
    goto out;
  if (opt_osname != NULL)
    {
      deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname);
      if (deployment == NULL)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
                       "No deployment for OS '%s'", opt_osname);
          goto out;
        }
    }
  else
    deployment = g_object_ref (ostree_sysroot_get_booted_deployment (sysroot));

  deployment_dir = ostree_sysroot_get_deployment_directory (sysroot, deployment);

  orig_etc_path = g_file_resolve_relative_path (deployment_dir, "usr/etc");
  new_etc_path = g_file_resolve_relative_path (deployment_dir, "etc");
  
  modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
  removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
  added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
  if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_IGNORE_XATTRS,
                         orig_etc_path, new_etc_path, modified, removed, added,
                         cancellable, error))
    goto out;

  ostree_diff_print (orig_etc_path, new_etc_path, modified, removed, added);

  ret = TRUE;
 out:
  if (context)
    g_option_context_free (context);
  return ret;
}
gint
rpmostreed_rollback_deployment_index (const gchar *name,
                                      OstreeSysroot *ot_sysroot,
                                      GError **error)
{
  g_autoptr(GPtrArray) deployments = NULL;
  glnx_unref_object OstreeDeployment *merge_deployment = NULL;

  gint index_to_prepend = -1;
  gint merge_index = -1;
  gint previous_index = -1;
  guint i;

  merge_deployment = ostree_sysroot_get_merge_deployment (ot_sysroot, name);
  if (merge_deployment == NULL)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "No deployments found for os %s", name);
      goto out;
    }

  deployments = ostree_sysroot_get_deployments (ot_sysroot);
  if (deployments->len < 2)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Found %u deployments, at least 2 required for rollback",
                   deployments->len);
      goto out;
    }

  g_assert (merge_deployment != NULL);
  for (i = 0; i < deployments->len; i++)
    {
      if (deployments->pdata[i] == merge_deployment)
        merge_index = i;

      if (g_strcmp0 (ostree_deployment_get_osname (deployments->pdata[i]), name) == 0 &&
              deployments->pdata[i] != merge_deployment &&
              previous_index < 0)
        {
            previous_index = i;
        }
    }

  g_assert (merge_index < deployments->len);
  g_assert (deployments->pdata[merge_index] == merge_deployment);

  /* If merge deployment is not booted assume we are using it. */
  if (merge_index == 0 && previous_index > 0)
      index_to_prepend = previous_index;
  else
      index_to_prepend = merge_index;

out:
  return index_to_prepend;
}
gboolean
ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error)
{
  gboolean ret = FALSE;
  const char *refspec;
  GOptionContext *context;
  GKeyFile *origin = NULL;
  gs_unref_object OstreeRepo *repo = NULL;
  gs_unref_ptrarray GPtrArray *new_deployments = NULL;
  gs_unref_object OstreeDeployment *new_deployment = NULL;
  gs_unref_object OstreeDeployment *merge_deployment = NULL;
  gs_free char *revision = NULL;
  __attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL;

  context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment");

  g_option_context_add_main_entries (context, options, NULL);

  if (!g_option_context_parse (context, &argc, &argv, error))
    goto out;

  if (argc < 2)
    {
      ot_util_usage_error (context, "REF/REV must be specified", error);
      goto out;
    }

  refspec = argv[1];

  if (!ostree_sysroot_load (sysroot, cancellable, error))
    goto out;

  if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
    goto out;

  /* Find the currently booted deployment, if any; we will ensure it
   * is present in the new deployment list.
   */
  if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname,
                                                     cancellable, error))
    {
      g_prefix_error (error, "Looking for booted deployment: ");
      goto out;
    }

  if (opt_origin_path)
    {
      origin = g_key_file_new ();
      
      if (!g_key_file_load_from_file (origin, opt_origin_path, 0, error))
        goto out;
    }
  else
    {
      origin = ostree_sysroot_origin_new_from_refspec (sysroot, refspec);
    }

  if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error))
    goto out;

  merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname);

  /* Here we perform cleanup of any leftover data from previous
   * partial failures.  This avoids having to call gs_shutil_rm_rf()
   * at random points throughout the process.
   *
   * TODO: Add /ostree/transaction file, and only do this cleanup if
   * we find it.
   */
  if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
    {
      g_prefix_error (error, "Performing initial cleanup: ");
      goto out;
    }

  kargs = _ostree_kernel_args_new ();

  /* If they want the current kernel's args, they very likely don't
   * want the ones from the merge.
   */
  if (opt_kernel_proc_cmdline)
    {
      gs_unref_object GFile *proc_cmdline_path = g_file_new_for_path ("/proc/cmdline");
      gs_free char *proc_cmdline = NULL;
      gsize proc_cmdline_len = 0;
      gs_strfreev char **proc_cmdline_args = NULL;

      if (!g_file_load_contents (proc_cmdline_path, cancellable,
                                 &proc_cmdline, &proc_cmdline_len,
                                 NULL, error))
        goto out;

      g_strchomp (proc_cmdline);

      proc_cmdline_args = g_strsplit (proc_cmdline, " ", -1);
      _ostree_kernel_args_replace_argv (kargs, proc_cmdline_args);
    }
  else if (merge_deployment)
    {
      OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment);
      gs_strfreev char **previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1);

      _ostree_kernel_args_replace_argv (kargs, previous_args);
    }

  if (opt_kernel_argv)
    {
      _ostree_kernel_args_replace_argv (kargs, opt_kernel_argv);
    }

  if (opt_kernel_argv_append)
    {
      _ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append);
    }

  {
    gs_strfreev char **kargs_strv = _ostree_kernel_args_to_strv (kargs);

    if (!ostree_sysroot_deploy_tree (sysroot,
                                     opt_osname, revision, origin,
                                     merge_deployment, kargs_strv,
                                     &new_deployment,
                                     cancellable, error))
      goto out;
  }

  if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname,
                                               new_deployment, merge_deployment,
                                               opt_retain ? OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN : 0,
                                               cancellable, error))
    goto out;

  ret = TRUE;
 out:
  if (origin)
    g_key_file_unref (origin);
  if (context)
    g_option_context_free (context);
  return ret;
}
static gboolean
package_diff_transaction_execute (RpmostreedTransaction *transaction,
                                  GCancellable *cancellable,
                                  GError **error)
{
  PackageDiffTransaction *self;
  OstreeSysroot *sysroot;

  glnx_unref_object RpmOstreeSysrootUpgrader *upgrader = NULL;
  glnx_unref_object OstreeAsyncProgress *progress = NULL;
  glnx_unref_object OstreeRepo *repo = NULL;
  glnx_unref_object OstreeDeployment *merge_deployment = NULL;
  g_autofree gchar *origin_description = NULL;

  RpmOstreeSysrootUpgraderFlags upgrader_flags = 0;
  gboolean upgrading = FALSE;
  gboolean changed = FALSE;
  gboolean ret = FALSE;

  self = (PackageDiffTransaction *) transaction;

  if (self->revision != NULL || self->refspec == NULL)
    upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER;

  sysroot = rpmostreed_transaction_get_sysroot (transaction);
  upgrader = rpmostree_sysroot_upgrader_new (sysroot,
					     self->osname,
					     upgrader_flags,
					     cancellable,
					     error);
  if (upgrader == NULL)
    goto out;

  if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
    goto out;

  merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, self->osname);

  /* Determine if we're upgrading before we set the refspec. */
  upgrading = (self->refspec == NULL && self->revision == NULL);

  if (self->refspec != NULL)
    {
      if (!change_upgrader_refspec (sysroot, upgrader,
				    self->refspec, cancellable,
				    NULL, NULL, error))
	goto out;
    }
  else
    {
      self->refspec = g_strdup (rpmostree_sysroot_upgrader_get_refspec (upgrader));
      if (!self->refspec)
	{
	  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
			       "Booted deployment has no origin");
	  goto out;
	}
    }

  progress = ostree_async_progress_new ();
  rpmostreed_transaction_connect_download_progress (transaction, progress);
  rpmostreed_transaction_connect_signature_progress (transaction, repo);

  if (self->revision != NULL)
    {
      g_autofree char *checksum = NULL;
      g_autofree char *version = NULL;

      if (!rpmostreed_parse_revision (self->revision,
                                      &checksum,
                                      &version,
                                      error))
        goto out;

      if (version != NULL)
        {
          rpmostreed_transaction_emit_message_printf (transaction,
                                                      "Resolving version '%s'",
                                                      version);

          if (!rpmostreed_repo_lookup_version (repo,
                                               self->refspec,
                                               version,
                                               progress,
                                               cancellable,
                                               &checksum,
                                               error))
            goto out;
        }

      rpmostree_sysroot_upgrader_set_origin_override (upgrader, checksum);
    }
  else if (upgrading)
    {
      rpmostree_sysroot_upgrader_set_origin_override (upgrader, NULL);
    }

  origin_description = rpmostree_sysroot_upgrader_get_origin_description (upgrader);
  if (origin_description != NULL)
    rpmostreed_transaction_emit_message_printf (transaction,
                                                "Updating from: %s",
                                                origin_description);

  if (!rpmostree_sysroot_upgrader_pull (upgrader,
                                        "/usr/share/rpm",
                                        0,
                                        progress,
                                        &changed,
                                        cancellable,
                                        error))
    goto out;

  rpmostree_transaction_emit_progress_end (RPMOSTREE_TRANSACTION (transaction));

  if (!changed)
    {
      if (upgrading)
        rpmostreed_transaction_emit_message_printf (transaction,
                                                    "No upgrade available.");
      else
        rpmostreed_transaction_emit_message_printf (transaction,
                                                    "No change.");
    }

  ret = TRUE;
out:
  return ret;
}