int
rpmostree_builtin_rollback (int             argc,
                            char          **argv,
                            GCancellable   *cancellable,
                            GError        **error)
{
  int exit_status = EXIT_FAILURE;

  GOptionContext *context = g_option_context_new ("- Revert to the previously booted tree");
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  g_autofree char *transaction_address = NULL;

  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT,
                                       cancellable,
                                       &sysroot_proxy,
                                       error))
    goto out;

  if (!rpmostree_load_os_proxy (sysroot_proxy, NULL,
                                cancellable, &os_proxy, error))
    goto out;

  if (!rpmostree_os_call_rollback_sync (os_proxy,
                                        get_args_variant (),
                                        &transaction_address,
                                        cancellable,
                                        error))
    goto out;

  if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                transaction_address,
                                                cancellable,
                                                error))
    goto out;

  if (!opt_reboot)
    {
      const char *sysroot_path;

      sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);

      /* By request, doing this without dbus */
      if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
                                                           cancellable,
                                                           error))
        goto out;

      g_print ("Run \"systemctl reboot\" to start a reboot\n");
    }

  exit_status = EXIT_SUCCESS;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return exit_status;
}
int
rpmostree_builtin_deploy (int            argc,
                          char         **argv,
                          GCancellable  *cancellable,
                          GError       **error)
{
  int exit_status = EXIT_FAILURE;
  g_autoptr(GOptionContext) context = NULL;
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  g_autoptr(GVariant) default_deployment = NULL;
  g_autofree char *transaction_address = NULL;
  const char * const packages[] = { NULL };
  const char *revision;

  context = g_option_context_new ("REVISION - Deploy a specific commit");

  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT,
                                       cancellable,
                                       &sysroot_proxy,
                                       error))
    goto out;

  if (argc < 2)
    {
      rpmostree_usage_error (context, "REVISION must be specified", error);
      goto out;
    }

  revision = argv[1];

  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    goto out;

  if (opt_preview)
    {
      if (!rpmostree_os_call_download_deploy_rpm_diff_sync (os_proxy,
                                                            revision,
                                                            packages,
                                                            &transaction_address,
                                                            cancellable,
                                                            error))
        goto out;
    }
  else
    {
      /* This will set the GVariant if the default deployment changes. */
      g_signal_connect (os_proxy, "notify::default-deployment",
                        G_CALLBACK (default_deployment_changed_cb),
                        &default_deployment);

      if (!rpmostree_os_call_deploy_sync (os_proxy,
                                          revision,
                                          get_args_variant (),
                                          &transaction_address,
                                          cancellable,
                                          error))
        goto out;
    }

  if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                transaction_address,
                                                cancellable,
                                                error))
    goto out;

  if (opt_preview)
    {
      g_autoptr(GVariant) result = NULL;
      g_autoptr(GVariant) details = NULL;

      if (!rpmostree_os_call_get_cached_deploy_rpm_diff_sync (os_proxy,
                                                              revision,
                                                              packages,
                                                              &result,
                                                              &details,
                                                              cancellable,
                                                              error))
        goto out;

      if (g_variant_n_children (result) == 0)
        {
          exit_status = RPM_OSTREE_EXIT_UNCHANGED;
          goto out;
        }

      rpmostree_print_package_diffs (result);
    }
  else if (!opt_reboot)
    {
      const char *sysroot_path;

      if (default_deployment == NULL)
        {
          exit_status = RPM_OSTREE_EXIT_UNCHANGED;
          goto out;
        }

      sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);

      if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
                                                           cancellable,
                                                           error))
        goto out;

      g_print ("Run \"systemctl reboot\" to start a reboot\n");
    }

  exit_status = EXIT_SUCCESS;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return exit_status;
}
gboolean
rpmostree_builtin_upgrade (int             argc,
                           char          **argv,
                           GCancellable   *cancellable,
                           GError        **error)
{
  gboolean ret = FALSE;

  GOptionContext *context = g_option_context_new ("- Perform a system upgrade");
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  g_autoptr(GVariant) default_deployment = NULL;
  g_autofree char *transaction_address = NULL;

  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_NONE,
                                       cancellable,
                                       &sysroot_proxy,
                                       error))
    goto out;

  if (opt_check_diff && opt_reboot)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "cannot specify both --reboot and --check-diff");
      goto out;
    }

  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    goto out;

  if (opt_check_diff)
    {
      if (!rpmostree_os_call_download_update_rpm_diff_sync (os_proxy,
                                                            &transaction_address,
                                                            cancellable,
                                                            error))
        goto out;
    }
  else
    {
      g_signal_connect (os_proxy, "notify::default-deployment",
                        G_CALLBACK (default_changed_callback),
                        &default_deployment);

      if (!rpmostree_os_call_upgrade_sync (os_proxy,
                                           get_args_variant (),
                                           &transaction_address,
                                           cancellable,
                                           error))
        goto out;
    }

  if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                transaction_address,
                                                cancellable,
                                                error))
    goto out;

  if (opt_check_diff)
    {
      /* yes, doing this without using dbus */
      gs_unref_object OstreeSysroot *sysroot = NULL;
      gs_unref_object OstreeRepo *repo = NULL;
      gs_unref_object GFile *rpmdbdir = NULL;
      gs_unref_object GFile *sysroot_file = NULL;
      g_autofree char *origin_description = NULL;
      g_autoptr(GVariant) cached_update = NULL;
      const char *sysroot_path;
      GVariantDict upgrade_dict;

      _cleanup_rpmrev_ struct RpmRevisionData *rpmrev1 = NULL;
      _cleanup_rpmrev_ struct RpmRevisionData *rpmrev2 = NULL;

      gs_free char *ref = NULL; /* location of this rev */
      gs_free char *remote = NULL;

      if (!rpmostree_os_get_has_cached_update_rpm_diff (os_proxy))
        goto out;

      sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);
      sysroot_file = g_file_new_for_path (sysroot_path);
      sysroot = ostree_sysroot_new (sysroot_file);

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

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

      cached_update = rpmostree_os_dup_cached_update(os_proxy);
      g_variant_dict_init (&upgrade_dict, cached_update);
      if (!g_variant_dict_lookup (&upgrade_dict, "origin", "s", &origin_description))
        goto out;

      if (!ostree_parse_refspec (origin_description, &remote, &ref, error))
         goto out;

      if (rpmReadConfigFiles (NULL, NULL))
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "rpm failed to init: %s", rpmlogMessage ());
          goto out;
        }

      if (!(rpmrev1 = rpmrev_new (repo,
                                  ostree_deployment_get_csum (ostree_sysroot_get_booted_deployment (sysroot)),
                                  NULL, cancellable, error)))
        goto out;

      if (!(rpmrev2 = rpmrev_new (repo, ref,
                                  NULL, cancellable, error)))
        goto out;

      rpmhdrs_diff_prnt_diff (rpmhdrs_diff (rpmrev_get_headers (rpmrev1),
                                            rpmrev_get_headers (rpmrev2)));
    }
  else
    {
      /* nothing changed */
      if (default_deployment == NULL)
        {
          goto out;
        }
      if (!opt_reboot)
        {
          const char *sysroot_path;

          sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);

          if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
                                                               cancellable,
                                                               error))
            goto out;

          g_print ("Run \"systemctl reboot\" to start a reboot\n");
        }
    }

  ret = TRUE;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return ret;
}
int
rpmostree_builtin_rebase (int             argc,
                          char          **argv,
                          GCancellable   *cancellable,
                          GError        **error)
{
  int exit_status = EXIT_FAILURE;
  const char *new_provided_refspec;
  const char *revision = NULL;

  /* forced blank for now */
  const char *packages[] = { NULL };

  g_autoptr(GOptionContext) context = g_option_context_new ("REFSPEC [REVISION] - Switch to a different tree");
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  g_autofree char *transaction_address = NULL;

  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_NONE,
                                       cancellable,
                                       &sysroot_proxy,
                                       error))
    goto out;

  if (argc < 2 || argc > 3)
    {
      rpmostree_usage_error (context, "Too few or too many arguments", error);
      goto out;
    }

  new_provided_refspec = argv[1];

  if (argc == 3)
    revision = argv[2];

  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    goto out;

  if (!rpmostree_os_call_rebase_sync (os_proxy,
                                      get_args_variant (revision),
                                      new_provided_refspec,
                                      packages,
                                      &transaction_address,
                                      cancellable,
                                      error))
    goto out;

  if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                transaction_address,
                                                cancellable,
                                                error))
    goto out;

  if (!opt_reboot)
    {
      const char *sysroot_path;

      sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);

      /* By request, doing this without dbus */
      if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
                                                           cancellable,
                                                           error))
        goto out;

      g_print ("Run \"systemctl reboot\" to start a reboot\n");
    }

  exit_status = EXIT_SUCCESS;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return exit_status;
}
static int
pkg_change (RPMOSTreeSysroot *sysroot_proxy,
            const char *const* packages_to_add,
            const char *const* packages_to_remove,
            GCancellable  *cancellable,
            GError       **error)
{
  int exit_status = EXIT_FAILURE;
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  g_autoptr(GVariant) default_deployment = NULL;
  g_autofree char *transaction_address = NULL;
  const char *const strv_empty[] = { NULL };

  if (!packages_to_add)
    packages_to_add = strv_empty;
  if (!packages_to_remove)
    packages_to_remove = strv_empty;

  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    goto out;

  if (!rpmostree_os_call_pkg_change_sync (os_proxy,
                                          get_args_variant (),
                                          packages_to_add,
                                          packages_to_remove,
                                          &transaction_address,
                                          cancellable,
                                          error))
    goto out;

  if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                transaction_address,
                                                cancellable,
                                                error))
    goto out;

  if (opt_dry_run)
    {
      g_print ("Exiting because of '--dry-run' option\n");
    }
  else if (!opt_reboot)
    {
      const char *sysroot_path;


      sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);

      if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
                                                           cancellable,
                                                           error))
        goto out;

      g_print ("Run \"systemctl reboot\" to start a reboot\n");
    }

  exit_status = EXIT_SUCCESS;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return exit_status;
}
int
rpmostree_builtin_initramfs (int             argc,
                             char          **argv,
                             RpmOstreeCommandInvocation *invocation,
                             GCancellable   *cancellable,
                             GError        **error)
{
  g_autoptr(GOptionContext) context = g_option_context_new ("- Enable or disable local initramfs regeneration");

  _cleanup_peer_ GPid peer_pid = 0;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       invocation,
                                       cancellable,
                                       NULL, NULL,
                                       &sysroot_proxy,
                                       &peer_pid,
                                       error))
    return EXIT_FAILURE;

  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    return EXIT_FAILURE;

  if (!(opt_enable || opt_disable))
    {
      GVariantIter iter;
      g_autoptr(GVariant) deployments = rpmostree_sysroot_dup_deployments (sysroot_proxy);

      if (opt_reboot)
        {
          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                               "--reboot must be used with --enable or --disable");
          return EXIT_FAILURE;
        }

      g_variant_iter_init (&iter, deployments);

      while (TRUE)
        {
          gboolean cur_regenerate;
          g_autoptr(GVariant) child = g_variant_iter_next_value (&iter);
          g_autoptr(GVariantDict) dict = NULL;
          g_autofree char **initramfs_args = NULL;
          gboolean is_booted;

          if (child == NULL)
            break;

          dict = g_variant_dict_new (child);

          if (!g_variant_dict_lookup (dict, "booted", "b", &is_booted))
            continue;
          if (!is_booted)
            continue;

          if (!g_variant_dict_lookup (dict, "regenerate-initramfs", "b", &cur_regenerate))
            cur_regenerate = FALSE;
          if (cur_regenerate)
            {
              g_variant_dict_lookup (dict, "initramfs-args", "^a&s", &initramfs_args);
            }

          g_print ("Initramfs regeneration: %s\n", cur_regenerate ? "enabled" : "disabled");
          if (initramfs_args)
            {
              g_print ("Initramfs args: ");
              for (char **iter = initramfs_args; iter && *iter; iter++)
                g_print ("%s ", *iter);
              g_print ("\n");
            }
        }
    }
  else if (opt_enable && opt_disable)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Cannot simultaenously specify --enable and --disable");
      return EXIT_FAILURE;
    }
  else
    {
      char *empty_strv[] = {NULL};
      if (opt_disable && opt_add_arg)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Cannot simultaenously specify --disable and --arg");
          return EXIT_FAILURE;
        }
      if (!opt_add_arg)
        opt_add_arg = empty_strv;

      g_autofree char *transaction_address = NULL;
      if (!rpmostree_os_call_set_initramfs_state_sync (os_proxy,
                                                       opt_enable,
                                                       (const char *const*)opt_add_arg,
                                                       get_args_variant (),
                                                       &transaction_address,
                                                       cancellable,
                                                       error))
        return EXIT_FAILURE;

      if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                    transaction_address,
                                                    cancellable,
                                                    error))
        return EXIT_FAILURE;

      g_print ("Initramfs regeneration is now: %s\n", opt_enable ? "enabled" : "disabled");
    }

  return EXIT_SUCCESS;
}
gboolean
rpmostree_builtin_rebase (int             argc,
                          char          **argv,
                          GCancellable   *cancellable,
                          GError        **error)
{
  gboolean ret = FALSE;
  const char *new_provided_refspec;

  /* forced blank for now */
  const char *packages[] = { NULL };

  GOptionContext *context = g_option_context_new ("REFSPEC - Switch to a different tree");
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  g_autofree char *transaction_address = NULL;

  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_NONE,
                                       cancellable,
                                       &sysroot_proxy,
                                       error))
    goto out;

  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    goto out;

  new_provided_refspec = argv[1];

  if (!rpmostree_os_call_rebase_sync (os_proxy,
                                      get_args_variant (),
                                      new_provided_refspec,
                                      packages,
                                      &transaction_address,
                                      cancellable,
                                      error))
    goto out;

  if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                transaction_address,
                                                cancellable,
                                                error))
    goto out;

  if (!opt_reboot)
    {
      const char *sysroot_path;

      sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);

      /* By request, doing this without dbus */
      if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
                                                           cancellable,
                                                           error))
        goto out;

      g_print ("Run \"systemctl reboot\" to start a reboot\n");
    }

  ret = TRUE;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return ret;
}
int
rpmostree_builtin_upgrade (int             argc,
                           char          **argv,
                           GCancellable   *cancellable,
                           GError        **error)
{
  int exit_status = EXIT_FAILURE;

  GOptionContext *context = g_option_context_new ("- Perform a system upgrade");
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  g_autoptr(GVariant) default_deployment = NULL;
  g_autofree char *transaction_address = NULL;

  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_NONE,
                                       cancellable,
                                       &sysroot_proxy,
                                       error))
    goto out;

  if (opt_reboot && opt_preview)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                   "Cannot specify both --reboot and --preview");
      goto out;
    }

  if (opt_reboot && opt_check)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                   "Cannot specify both --reboot and --check");
      goto out;
    }

  /* If both --check and --preview were passed, --preview overrides. */
  if (opt_preview)
    opt_check = FALSE;

  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    goto out;

  if (opt_preview || opt_check)
    {
      if (!rpmostree_os_call_download_update_rpm_diff_sync (os_proxy,
                                                            &transaction_address,
                                                            cancellable,
                                                            error))
        goto out;
    }
  else
    {
      g_signal_connect (os_proxy, "notify::default-deployment",
                        G_CALLBACK (default_changed_callback),
                        &default_deployment);

      if (!rpmostree_os_call_upgrade_sync (os_proxy,
                                           get_args_variant (),
                                           &transaction_address,
                                           cancellable,
                                           error))
        goto out;
    }

  if (!rpmostree_transaction_get_response_sync (sysroot_proxy,
                                                transaction_address,
                                                cancellable,
                                                error))
    goto out;

  if (opt_preview || opt_check)
    {
      g_autoptr(GVariant) result = NULL;
      g_autoptr(GVariant) details = NULL;

      if (!rpmostree_os_call_get_cached_update_rpm_diff_sync (os_proxy,
                                                              "",
                                                              &result,
                                                              &details,
                                                              cancellable,
                                                              error))
        goto out;

      if (g_variant_n_children (result) == 0)
        {
          exit_status = RPM_OSTREE_EXIT_UNCHANGED;
          goto out;
        }

      if (!opt_check)
        rpmostree_print_package_diffs (result);
    }
  else if (!opt_reboot)
    {
      const char *sysroot_path;

      if (default_deployment == NULL)
        {
          exit_status = RPM_OSTREE_EXIT_UNCHANGED;
          goto out;
        }

      sysroot_path = rpmostree_sysroot_get_path (sysroot_proxy);

      if (!rpmostree_print_treepkg_diff_from_sysroot_path (sysroot_path,
                                                           cancellable,
                                                           error))
        goto out;

      g_print ("Run \"systemctl reboot\" to start a reboot\n");
    }

  exit_status = EXIT_SUCCESS;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return exit_status;
}
int
rpmostree_builtin_status (int             argc,
                          char          **argv,
                          GCancellable   *cancellable,
                          GError        **error)
{
  int exit_status = EXIT_FAILURE;
  GOptionContext *context = g_option_context_new ("- Get the version of the booted system");
  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
  g_autoptr(GVariant) booted_deployment = NULL;
  g_autoptr(GVariant) deployments = NULL;
  g_autoptr(GVariant) booted_signatures = NULL;
  g_autoptr(GPtrArray) deployment_dicts = NULL;
  GVariantIter iter;
  GVariant *child;
  g_autofree gchar *booted_id = NULL;

  const guint CSUM_DISP_LEN = 10; /* number of checksum characters to display */
  guint i, n;
  guint max_timestamp_len = 19; /* length of timestamp "YYYY-MM-DD HH:MM:SS" */
  guint max_id_len = CSUM_DISP_LEN; /* length of checksum ID */
  guint max_osname_len = 0; /* maximum length of osname - determined in code */
  guint max_refspec_len = 0; /* maximum length of refspec - determined in code */
  guint max_version_len = 0; /* maximum length of version - determined in code */
  guint buffer = 5; /* minimum space between end of one entry and new column */


  if (!rpmostree_option_context_parse (context,
                                       option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_NONE,
                                       cancellable,
                                       &sysroot_proxy,
                                       error))
    goto out;

  if (!rpmostree_load_os_proxy (sysroot_proxy, NULL,
                                cancellable, &os_proxy, error))
    goto out;

  booted_deployment = rpmostree_os_dup_booted_deployment (os_proxy);
  if (booted_deployment)
    {
      GVariantDict dict;
      g_variant_dict_init (&dict, booted_deployment);
      g_variant_dict_lookup (&dict, "id", "s", &booted_id);
      booted_signatures = g_variant_dict_lookup_value (&dict, "signatures",
                                                       G_VARIANT_TYPE ("av"));
      g_variant_dict_clear (&dict);
    }

  deployment_dicts = g_ptr_array_new_with_free_func ((GDestroyNotify) g_variant_dict_unref);

  deployments = rpmostree_sysroot_dup_deployments (sysroot_proxy);

  g_variant_iter_init (&iter, deployments);

  while ((child = g_variant_iter_next_value (&iter)) != NULL)
    {
      GVariantDict *dict = g_variant_dict_new (child);

      /* Takes ownership of the dictionary */
      g_ptr_array_add (deployment_dicts, dict);

      /* find lengths for use in column output */
      if (!opt_pretty)
        {
          gchar *origin_refspec = NULL; /* borrowed */
          gchar *os_name = NULL; /* borrowed */
          gchar *version_string = NULL; /* borrowed */

          /* osname should always be present. */
          if (g_variant_dict_lookup (dict, "osname", "&s", &os_name))
            max_osname_len = MAX (max_osname_len, strlen (os_name));
          else
            {
              const char *id = NULL;
              g_variant_dict_lookup (dict, "id", "&s", &id);
              g_critical ("Deployment '%s' missing osname", id != NULL ? id : "?");
            }

          if (g_variant_dict_lookup (dict, "version", "&s", &version_string))
            max_version_len = MAX (max_version_len, strlen (version_string));

          if (g_variant_dict_lookup (dict, "origin", "&s", &origin_refspec))
            max_refspec_len = MAX (max_refspec_len, strlen (origin_refspec));
        }

      g_variant_unref (child);
    }

  if (!opt_pretty)
    {
      /* print column headers */
      g_print ("  %-*s", max_timestamp_len+buffer,"TIMESTAMP (UTC)");
      if (max_version_len)
        g_print ("%-*s", max_version_len+buffer,"VERSION");
      g_print ("%-*s%-*s%-*s\n",
               max_id_len+buffer, "ID",
               max_osname_len+buffer, "OSNAME",
               max_refspec_len+buffer, "REFSPEC");
    }
  /* header for "pretty" row output */
  else
    printchar ("=", 60);

  n = deployment_dicts->len;

  /* print entries for each deployment */
  for (i = 0; i < n; i++)
    {
      GVariantDict *dict;
      g_autoptr(GDateTime) timestamp = NULL;
      g_autofree char *timestamp_string = NULL;
      g_autofree gchar *truncated_csum = NULL;
      g_autoptr(GVariant) signatures = NULL;

      gchar *id = NULL; /* borrowed */
      gchar *origin_refspec = NULL; /* borrowed */
      gchar *os_name = NULL; /* borrowed */
      gchar *version_string = NULL; /* borrowed */
      gchar *checksum = NULL; /* borrowed */

      guint64 t = 0;
      gint serial;
      gboolean is_booted = FALSE;

      dict = g_ptr_array_index (deployment_dicts, i);

      g_variant_dict_lookup (dict, "id", "&s", &id);
      g_variant_dict_lookup (dict, "osname", "&s", &os_name);
      g_variant_dict_lookup (dict, "serial", "i", &serial);
      g_variant_dict_lookup (dict, "checksum", "s", &checksum);
      g_variant_dict_lookup (dict, "version", "s", &version_string);
      g_variant_dict_lookup (dict, "timestamp", "t", &t);
      g_variant_dict_lookup (dict, "origin", "s", &origin_refspec);
      signatures = g_variant_dict_lookup_value (dict, "signatures",
                                                G_VARIANT_TYPE ("av"));

      is_booted = g_strcmp0 (booted_id, id) == 0;

      timestamp = g_date_time_new_from_unix_utc (t);
      if (timestamp != NULL)
        timestamp_string = g_date_time_format (timestamp, "%Y-%m-%d %T");
      else
        timestamp_string = g_strdup_printf ("(invalid)");

      /* truncate checksum */
      truncated_csum = g_strndup (checksum, CSUM_DISP_LEN);

      /* print deployment info column */
      if (!opt_pretty)
        {
          g_print ("%c %-*s",
                   is_booted ? '*' : ' ',
                   max_timestamp_len+buffer, timestamp_string);

          if (max_version_len)
            g_print ("%-*s",
                     max_version_len+buffer, version_string ? version_string : "");
          g_print ("%-*s%-*s%-*s\n",
                   max_id_len+buffer, truncated_csum,
                   max_osname_len+buffer, os_name,
                   max_refspec_len+buffer, origin_refspec);
        }

      /* print "pretty" row info */
      else
        {
          guint tab = 11;
          char *title = NULL;
          if (i==0)
            title = "DEFAULT ON BOOT";
          else if (is_booted || n <= 2)
            title = "NON-DEFAULT ROLLBACK TARGET";
          else
            title = "NON-DEFAULT DEPLOYMENT";
          g_print ("  %c %s\n",
                  is_booted ? '*' : ' ',
                  title);

          printchar ("-", 40);
          if (version_string)
            g_print ("  %-*s%-*s\n", tab, "version", tab, version_string);

          g_print ("  %-*s%-*s\n  %-*s%-*s.%d\n  %-*s%-*s\n  %-*s%-*s\n",
                  tab, "timestamp", tab, timestamp_string,
                  tab, "id", tab, checksum, serial,
                  tab, "osname", tab, os_name,
                  tab, "refspec", tab, origin_refspec);

          if (signatures != NULL)
            rpmostree_print_signatures (signatures, "  GPG: ");

          printchar ("=", 60);
        }
    }

  /* Print any signatures for the booted deployment, but only in NON-pretty
   * mode.  We save this for the end to preserve the tabular formatting for
   * deployments. */
  if (!opt_pretty && booted_signatures != NULL)
    {
      guint n_sigs = g_variant_n_children (booted_signatures);
      if (n_sigs > 0)
        {
          /* XXX If we ever add internationalization, use ngettext() here. */
          g_print ("\nGPG: Found %u signature%s on the booted deployment (*):\n",
                   n_sigs, n_sigs == 1 ? "" : "s");
          rpmostree_print_signatures (booted_signatures, "  ");
        }
    }

  exit_status = EXIT_SUCCESS;

out:
  /* Does nothing if using the message bus. */
  rpmostree_cleanup_peer ();

  return exit_status;
}
static gboolean
pkg_change (RpmOstreeCommandInvocation *invocation,
            RPMOSTreeSysroot *sysroot_proxy,
            const char *const* packages_to_add,
            const char *const* packages_to_remove,
            GCancellable  *cancellable,
            GError       **error)
{
  const char *const strv_empty[] = { NULL };

  if (!packages_to_add)
    packages_to_add = strv_empty;
  if (!packages_to_remove)
    packages_to_remove = strv_empty;

  glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
  if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
                                cancellable, &os_proxy, error))
    return FALSE;

  g_autoptr(GVariant) previous_deployment = rpmostree_os_dup_default_deployment (os_proxy);

  GVariantDict dict;
  g_variant_dict_init (&dict, NULL);
  g_variant_dict_insert (&dict, "reboot", "b", opt_reboot);
  g_variant_dict_insert (&dict, "cache-only", "b", opt_cache_only);
  g_variant_dict_insert (&dict, "download-only", "b", opt_download_only);
  g_variant_dict_insert (&dict, "no-pull-base", "b", TRUE);
  g_variant_dict_insert (&dict, "dry-run", "b", opt_dry_run);
  g_variant_dict_insert (&dict, "allow-inactive", "b", opt_allow_inactive);
  g_variant_dict_insert (&dict, "no-layering", "b", opt_uninstall_all);
  g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_dict_end (&dict));

  gboolean met_local_pkg = FALSE;
  for (const char *const* it = packages_to_add; it && *it; it++)
    met_local_pkg = met_local_pkg || g_str_has_suffix (*it, ".rpm");

  /* Use newer D-Bus API only if we have to. */
  g_autofree char *transaction_address = NULL;
  if (met_local_pkg)
    {
      if (!rpmostree_update_deployment (os_proxy,
                                        NULL, /* refspec */
                                        NULL, /* revision */
                                        packages_to_add,
                                        packages_to_remove,
                                        NULL, /* override replace */
                                        NULL, /* override remove */
                                        NULL, /* override reset */
                                        options,
                                        &transaction_address,
                                        cancellable,
                                        error))
        return FALSE;
    }
  else
    {
      if (!rpmostree_os_call_pkg_change_sync (os_proxy,
                                              options,
                                              packages_to_add,
                                              packages_to_remove,
                                              NULL,
                                              &transaction_address,
                                              NULL,
                                              cancellable,
                                              error))
        return FALSE;
    }

  return rpmostree_transaction_client_run (invocation, sysroot_proxy, os_proxy,
                                           options, FALSE,
                                           transaction_address,
                                           previous_deployment,
                                           cancellable, error);
}