Exemple #1
0
static void
test_g_variant_dict (void)
{
  g_autoptr(GVariant) data = g_variant_new_from_data (G_VARIANT_TYPE ("a{sv}"), "", 0, FALSE, NULL, NULL);
  g_auto(GVariantDict) stackval;
  g_autoptr(GVariantDict) val = g_variant_dict_new (data);

  g_variant_dict_init (&stackval, data);
  g_assert (val != NULL);
}
Exemple #2
0
enum connection_type connection_type_from_properties(GVariant *properties)
{
	enum connection_type type = CONNECTION_TYPE_UNKNOWN;
	GVariantDict *dict = g_variant_dict_new(properties);
	GVariant *type_v = g_variant_dict_lookup_value(dict, "Type", NULL);
	if(type_v) {
		const gchar *type_s;

		type_s = g_variant_get_string(type_v, NULL);
		type = connection_type_from_string(type_s);
		g_variant_unref(type_v);
	}
	g_variant_dict_unref(dict);

	return type;
}
Exemple #3
0
static void
parse_configuration (Setting *setting)
{
  GVariantDict *dict = NULL;
  GVariant *value = NULL;
  LoomSetting *loom_setting = LOOM_SETTING (setting);

  dict = g_variant_dict_new (setting->configuration);
  value = g_variant_dict_lookup_value (dict, "address", G_VARIANT_TYPE_STRING);
  loom_setting_set_address (loom_setting, g_variant_get_string (value, NULL));

  if (g_variant_dict_contains (dict, "router"))
    {
      value = g_variant_dict_lookup_value (dict, "router",
                                           G_VARIANT_TYPE_STRING);
      loom_setting_set_router (loom_setting, g_variant_get_string (value, NULL));
    }

  if (g_variant_dict_contains (dict, "nameservers"))
    {
      value = g_variant_dict_lookup_value (dict, "nameservers",
                                           G_VARIANT_TYPE_STRING_ARRAY);
      gchar **strv = g_variant_dup_strv (value, NULL);
      loom_setting_set_name_servers (loom_setting, (const gchar **)strv);
      g_strfreev (strv);
    }

  if (g_variant_dict_contains (dict, "domain"))
    {
      value = g_variant_dict_lookup_value (dict, "domains",
                                           G_VARIANT_TYPE_STRING);
      loom_setting_set_domain (loom_setting, g_variant_get_string (value, NULL));
    }

  if (g_variant_dict_contains (dict, "searches"))
    {
      value = g_variant_dict_lookup_value (dict, "searches",
                                           G_VARIANT_TYPE_STRING_ARRAY);
      gchar **strv = g_variant_dup_strv (value, NULL);
      loom_setting_set_searches (loom_setting, (const gchar **)strv);
      g_strfreev (strv);
    }

  g_variant_dict_unref (dict);
}
Exemple #4
0
static gboolean
validate_configuration (GVariant *configuration,
                        GError **error)
{
  GVariantDict *dict = NULL;
  GVariant *value = NULL;

  dict = g_variant_dict_new (configuration);

  if (g_variant_dict_contains (dict, "address"))
    {
      value = g_variant_dict_lookup_value (dict, "address",
                                           G_VARIANT_TYPE_STRING);
      if (value == NULL)
        {
          g_variant_dict_unref (dict);
          *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                _("'address' entry must be a string"));
          return FALSE;
        }
      if (!validate_address ("address", g_variant_get_string (value, NULL),
                             TRUE, error))
        {
          g_variant_dict_unref (dict);
          return FALSE;
        }
    }
  else
    {
      g_variant_dict_unref (dict);
      *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                            _("'address' entry is required"));
      return FALSE;
    }

  if (g_variant_dict_contains (dict, "router"))
    {
      value = g_variant_dict_lookup_value (dict, "router",
                                           G_VARIANT_TYPE_STRING);
      if (value == NULL)
        {
          g_variant_dict_unref (dict);
          *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                _("'router' entry must be a string"));
          return FALSE;
        }
      if (!validate_address ("router", g_variant_get_string (value, NULL),
                             FALSE, error))
        {
          g_variant_dict_unref (dict);
          return FALSE;
        }
    }

  if (g_variant_dict_contains (dict, "nameservers"))
    {
      value = g_variant_dict_lookup_value (dict, "nameservers",
                                           G_VARIANT_TYPE_STRING_ARRAY);
      if (value == NULL)
        {
          g_variant_dict_unref (dict);
          *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                            _("'nameservers' entry must be a string array"));
          return FALSE;
        }
      gsize len;
      gs_strfreev gchar **nameservers = g_variant_dup_strv (value, &len);
      for (gsize i = 0; i < len; i++)
        {
          if (!validate_address ("nameservers", nameservers[i],
                                 FALSE, error))
            {
              g_variant_dict_unref (dict);
              return FALSE;
            }
        }
    }

  if (g_variant_dict_contains (dict, "domain"))
    {
      value = g_variant_dict_lookup_value (dict, "domain",
                                           G_VARIANT_TYPE_STRING);
      if (value == NULL)
        {
          g_variant_dict_unref (dict);
          *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                _("'domain' entry must be a string"));
          return FALSE;
        }
      if (!validate_domainname ("domain", g_variant_get_string (value, NULL),
                                error))
        {
          g_variant_dict_unref (dict);
          return FALSE;
        }
    }

  if (g_variant_dict_contains (dict, "searches"))
    {
      value = g_variant_dict_lookup_value (dict, "searches",
                                           G_VARIANT_TYPE_STRING_ARRAY);
      if (value == NULL)
        {
          g_variant_dict_unref (dict);
          *error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                               _("'searches' entry must be a string array"));

          return FALSE;
        }
      gsize len;
      gs_strfreev gchar **searches = g_variant_dup_strv (value, &len);
      for (gsize i = 0; i < len; i++)
        {
          if (!validate_domainname ("searches", searches[i], error))
            {
              g_variant_dict_unref (dict);
              return FALSE;
            }
        }
    }

  g_variant_dict_unref (dict);

  return TRUE;
}
OstreeDeltaEndianness
_ostree_delta_get_endianness (GVariant *superblock,
                              gboolean *out_was_heuristic)
{
  guint8 endianness_char;
  g_autoptr(GVariant) delta_meta = NULL;
  g_autoptr(GVariantDict) delta_metadict = NULL;
  guint64 total_size = 0;
  guint64 total_usize = 0;
  guint total_objects = 0;

  delta_meta = g_variant_get_child_value (superblock, 0);
  delta_metadict = g_variant_dict_new (delta_meta);

  if (out_was_heuristic)
    *out_was_heuristic = FALSE;

  if (g_variant_dict_lookup (delta_metadict, "ostree.endianness", "y", &endianness_char))
    {
      switch (endianness_char)
        {
        case 'l':
          return OSTREE_DELTA_ENDIAN_LITTLE;
        case 'B':
          return OSTREE_DELTA_ENDIAN_BIG;
        default:
          return OSTREE_DELTA_ENDIAN_INVALID;
        }
    }

  if (out_was_heuristic)
    *out_was_heuristic = TRUE;

  { g_autoptr(GVariant) meta_entries = NULL;
    guint n_parts;
    guint i;
    gboolean is_byteswapped = FALSE;

    g_variant_get_child (superblock, 6, "@a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT, &meta_entries);
    n_parts = g_variant_n_children (meta_entries);

    for (i = 0; i < n_parts; i++)
      {
        g_autoptr(GVariant) objects = NULL;
        guint64 size, usize;
        guint n_objects;

        g_variant_get_child (meta_entries, i, "(u@aytt@ay)", NULL, NULL, &size, &usize, &objects);
        n_objects = (guint)(g_variant_get_size (objects) / OSTREE_STATIC_DELTA_OBJTYPE_CSUM_LEN);

        total_objects += n_objects;
        total_size += size;
        total_usize += usize;

        if (size > usize)
          {
            double ratio = ((double)size)/((double)usize);

            /* This should really never happen where compressing things makes it more than 50% bigger.
             */ 
            if (ratio > 1.2)
              {
                is_byteswapped = TRUE;
                break;
              }
          }
      }

    if (!is_byteswapped)
      {
        /* If the average object size is greater than 4GiB, let's assume
         * we're dealing with opposite endianness.  I'm fairly confident
         * no one is going to be shipping peta- or exa- byte size ostree
         * deltas, period.  Past the gigabyte scale you really want
         * bittorrent or something.
         */
        if ((total_size / total_objects) > G_MAXUINT32)
          {
            is_byteswapped = TRUE;
          }
      }

    if (is_byteswapped)
      {
        switch (G_BYTE_ORDER)
          {
          case G_BIG_ENDIAN:
            return OSTREE_DELTA_ENDIAN_LITTLE;
          case G_LITTLE_ENDIAN:
            return OSTREE_DELTA_ENDIAN_BIG;
          default:
            g_assert_not_reached ();
          }
      }

    return OSTREE_DELTA_ENDIAN_INVALID;
  }
}
gboolean
rpmostree_container_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  int exit_status = EXIT_FAILURE;
  GOptionContext *context = g_option_context_new ("NAME");
  g_auto(ROContainerContext) rocctx_data = RO_CONTAINER_CONTEXT_INIT;
  ROContainerContext *rocctx = &rocctx_data;
  g_autoptr(RpmOstreeInstall) install = NULL;
  const char *name;
  g_autofree char *commit_checksum = NULL;
  g_autofree char *new_commit_checksum = NULL;
  g_autoptr(GVariant) commit = NULL;
  g_autoptr(GVariant) metadata = NULL;
  g_autoptr(GVariant) input_packages_v = NULL;
  g_autoptr(RpmOstreeTreespec) treespec = NULL;
  guint current_version;
  guint new_version;
  g_autofree char *previous_state_sha512 = NULL;
  const char *target_current_root;
  const char *target_new_root;
  
  if (!rpmostree_option_context_parse (context,
                                       assemble_option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
                                       cancellable,
                                       NULL,
                                       error))
    goto out;

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

  name = argv[1];

  if (!roc_context_init (rocctx, error))
    goto out;

  target_current_root = glnx_readlinkat_malloc (rocctx->roots_dfd, name, cancellable, error);
  if (!target_current_root)
    {
      g_prefix_error (error, "Reading app link %s: ", name);
      goto out;
    }

  if (!parse_app_version (target_current_root, &current_version, error))
    goto out;

  { g_autoptr(GVariantDict) metadata_dict = NULL;
    g_autoptr(GVariant) spec_v = NULL;
    g_autoptr(GVariant) previous_sha512_v = NULL;

    if (!ostree_repo_resolve_rev (rocctx->repo, name, FALSE, &commit_checksum, error))
      goto out;

    if (!ostree_repo_load_variant (rocctx->repo, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum,
                                   &commit, error))
      goto out;

    metadata = g_variant_get_child_value (commit, 0);
    metadata_dict = g_variant_dict_new (metadata);

    spec_v = _rpmostree_vardict_lookup_value_required (metadata_dict, "rpmostree.spec",
                                                                 (GVariantType*)"a{sv}", error);
    if (!spec_v)
      goto out;

    treespec = rpmostree_treespec_new (spec_v);

    previous_sha512_v = _rpmostree_vardict_lookup_value_required (metadata_dict,
                                                                  "rpmostree.state-sha512",
                                                                  (GVariantType*)"s", error);
    if (!previous_sha512_v)
      goto out;

    previous_state_sha512 = g_variant_dup_string (previous_sha512_v, NULL);
  }

  new_version = current_version == 0 ? 1 : 0;
  if (new_version == 0)
    target_new_root = glnx_strjoina (name, ".0");
  else
    target_new_root = glnx_strjoina (name, ".1");

  if (!roc_context_prepare_for_root (rocctx, name, treespec, cancellable, error))
    goto out;

  /* --- Downloading metadata --- */
  if (!rpmostree_context_download_metadata (rocctx->ctx, cancellable, error))
    goto out;

  /* --- Resolving dependencies --- */
  if (!rpmostree_context_prepare_install (rocctx->ctx, &install,
                                          cancellable, error))
    goto out;

  { g_autofree char *new_state_sha512 = rpmostree_context_get_state_sha512 (rocctx->ctx);

    if (strcmp (new_state_sha512, previous_state_sha512) == 0)
      {
        g_print ("No changes in inputs to %s (%s)\n", name, commit_checksum);
        exit_status = EXIT_SUCCESS;
        goto out;
      }
  }

  /* --- Download and import as necessary --- */
  if (!rpmostree_context_download_import (rocctx->ctx, install,
                                          cancellable, error))
    goto out;

  { glnx_fd_close int tmpdir_dfd = -1;

    if (!glnx_opendirat (rocctx->userroot_dfd, "tmp", TRUE, &tmpdir_dfd, error))
      goto out;
    
    if (!rpmostree_context_assemble_commit (rocctx->ctx, tmpdir_dfd,
                                            name,
                                            install,
                                            &new_commit_checksum,
                                            cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...\n", name, new_commit_checksum);

  { OstreeRepoCheckoutOptions opts = { OSTREE_REPO_CHECKOUT_MODE_USER,
                                       OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES, };

    /* For now... to be crash safe we'd need to duplicate some of the
     * boot-uuid/fsync gating at a higher level.
     */
    opts.disable_fsync = TRUE;

    if (!ostree_repo_checkout_tree_at (rocctx->repo, &opts, rocctx->roots_dfd, target_new_root,
                                       new_commit_checksum, cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...done\n", name, new_commit_checksum);

  if (!symlink_at_replace (target_new_root, rocctx->roots_dfd, name,
                           cancellable, error))
    goto out;

  g_print ("Creating current symlink...done\n");

  exit_status = EXIT_SUCCESS;
 out:
  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;
}
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;
}