daemon: Make parts of cli use the daemon

This commit is contained in:
petervo 2015-06-12 17:19:16 -07:00 committed by Matthew Barnes
parent 0114507865
commit f525730187
10 changed files with 1084 additions and 747 deletions

View File

@ -31,6 +31,8 @@ rpm_ostree_SOURCES = src/app/main.c \
src/app/rpmostree-db-builtin-diff.c \
src/app/rpmostree-db-builtin-list.c \
src/app/rpmostree-db-builtin-version.c \
src/app/rpmostree-dbus-helpers.c \
src/app/rpmostree-dbus-helpers.h \
src/app/rpmostree-libbuiltin.c \
src/app/rpmostree-libbuiltin.h \
$(NULL)

View File

@ -21,6 +21,7 @@
#include "config.h"
#include <gio/gio.h>
#include <glib-unix.h>
#include <errno.h>
#include <stdlib.h>
@ -124,12 +125,23 @@ rpmostree_print_gpg_verify_result (OstreeGpgVerifyResult *result)
g_string_free (buffer, TRUE);
}
static gboolean
on_sigint (gpointer user_data)
{
GCancellable *cancellable = user_data;
g_debug ("Caught signal. Canceling");
g_cancellable_cancel (cancellable);
return FALSE;
}
int
main (int argc,
char **argv)
{
GError *error = NULL;
GCancellable *cancellable = NULL;
GCancellable *cancellable = g_cancellable_new ();
RpmOstreeCommand *command;
int in, out;
const char *command_name = NULL;
@ -169,6 +181,10 @@ main (int argc,
argc = out;
g_unix_signal_add (SIGINT, on_sigint, cancellable);
g_unix_signal_add (SIGTERM, on_sigint, cancellable);
g_unix_signal_add (SIGHUP, on_sigint, cancellable);
/* Keep the "rpm" command working for backward-compatibility. */
if (g_strcmp0 (command_name, "rpm") == 0)
command_name = "db";
@ -228,6 +244,7 @@ main (int argc,
prefix = "\x1b[31m\x1b[1m"; /* red, bold */
suffix = "\x1b[22m\x1b[0m"; /* bold off, color reset */
}
g_dbus_error_strip_remote_error (error);
g_printerr ("%serror: %s%s\n", prefix, suffix, error->message);
g_error_free (error);
return 1;

View File

@ -26,18 +26,37 @@
#include "rpmostree-builtins.h"
#include "rpmostree-util.h"
#include "rpmostree-libbuiltin.h"
#include "rpmostree-dbus-helpers.h"
#include "libgsystem.h"
#include <libglnx.h>
static char *opt_sysroot = "/";
static char *opt_osname;
static gboolean opt_reboot;
static gboolean opt_skip_purge;
static gboolean opt_force_peer;
static GOptionEntry option_entries[] = {
{ "sysroot", 0, 0, G_OPTION_ARG_STRING, &opt_sysroot, "Use system root SYSROOT (default: /)", "SYSROOT" },
{ "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Operate on provided OSNAME", "OSNAME" },
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after rebase is finished", NULL },
{ "skip-purge", 0, 0, G_OPTION_ARG_NONE, &opt_skip_purge, "Keep previous refspec after rebase", NULL },
{ "peer", 0, 0, G_OPTION_ARG_NONE, &opt_force_peer, "Force a peer to peer connection instead of using the system message bus", NULL },
{ NULL }
};
static GVariant *
get_args_variant (void)
{
GVariantDict dict;
g_variant_dict_init (&dict, NULL);
g_variant_dict_insert (&dict, "skip-purge", "b", opt_skip_purge);
return g_variant_dict_end (&dict);
}
gboolean
rpmostree_builtin_rebase (int argc,
char **argv,
@ -45,129 +64,73 @@ rpmostree_builtin_rebase (int argc,
GError **error)
{
gboolean ret = FALSE;
GOptionContext *context = g_option_context_new ("REFSPEC - Switch to a different tree");
gboolean is_peer = FALSE;
const char *new_provided_refspec;
gs_unref_object OstreeSysroot *sysroot = NULL;
gs_unref_object OstreeRepo *repo = NULL;
gs_free char *origin_refspec = NULL;
gs_free char *origin_remote = NULL;
gs_free char *origin_ref = NULL;
gs_free char *new_remote = NULL;
gs_free char *new_ref = NULL;
gs_free char *new_refspec = NULL;
gs_unref_object GFile *sysroot_path = NULL;
gs_unref_object OstreeSysrootUpgrader *upgrader = NULL;
gs_unref_object OstreeAsyncProgress *progress = NULL;
gboolean changed;
GSConsole *console = NULL;
gs_unref_keyfile GKeyFile *old_origin = NULL;
gs_unref_keyfile GKeyFile *new_origin = NULL;
/* forced blank for now */
const char *packages[] = { NULL };
GOptionContext *context = g_option_context_new ("REFSPEC - Switch to a different tree");
glnx_unref_object GDBusConnection *connection = NULL;
glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
g_autofree char *transaction_object_path = NULL;
if (!rpmostree_option_context_parse (context, option_entries, &argc, &argv, error))
goto out;
if (argc < 2)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"REFSPEC must be specified");
if (!rpmostree_load_connection_and_sysroot (opt_sysroot,
opt_force_peer,
cancellable,
&connection,
&sysroot_proxy,
&is_peer,
error))
goto out;
if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname, is_peer,
cancellable, &os_proxy, error))
goto out;
}
new_provided_refspec = argv[1];
sysroot_path = g_file_new_for_path (opt_sysroot);
sysroot = ostree_sysroot_new (sysroot_path);
if (!ostree_sysroot_load (sysroot, cancellable, error))
if (!rpmostree_os_call_rebase_sync (os_proxy,
get_args_variant (),
new_provided_refspec,
packages,
&transaction_object_path,
cancellable,
error))
goto out;
upgrader = ostree_sysroot_upgrader_new_for_os_with_flags (sysroot, opt_osname,
OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED,
cancellable, error);
if (!upgrader)
if (!rpmostree_transaction_get_response_sync (connection,
transaction_object_path,
cancellable,
error))
goto out;
old_origin = ostree_sysroot_upgrader_get_origin (upgrader);
origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL);
if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error))
goto out;
/* Allow just switching remotes */
if (g_str_has_suffix (new_provided_refspec, ":"))
if (!opt_reboot)
{
new_remote = g_strdup (new_provided_refspec);
new_remote[strlen(new_remote)-1] = '\0';
new_ref = g_strdup (origin_ref);
// by request, doing this without dbus
if (!rpmostree_print_treepkg_diff_from_sysroot_path (opt_sysroot,
cancellable,
error))
goto out;
g_print ("Run \"systemctl reboot\" to start a reboot\n");
}
else
{
if (!ostree_parse_refspec (new_provided_refspec, &new_remote, &new_ref, error))
goto out;
gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
cancellable, error,
"systemctl", "reboot", NULL);
}
if (!new_remote)
new_refspec = g_strconcat (origin_remote, ":", new_ref, NULL);
else
new_refspec = g_strconcat (new_remote, ":", new_ref, NULL);
if (strcmp (origin_refspec, new_refspec) == 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Old and new refs are equal: %s", new_refspec);
goto out;
}
new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec);
if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error))
goto out;
console = gs_console_get ();
if (console)
{
gs_console_begin_status_line (console, "", NULL, NULL);
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
}
/* Always allow older...there's not going to be a chronological
* relationship necessarily.
*/
if (!ostree_sysroot_upgrader_pull (upgrader, 0,
OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER,
progress, &changed,
cancellable, error))
goto out;
if (console)
{
if (!gs_console_end_status_line (console, cancellable, error))
{
console = NULL;
goto out;
}
console = NULL;
}
if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
goto out;
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
goto out;
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
goto out;
g_print ("Deleting ref '%s:%s'\n", origin_remote, origin_ref);
ostree_repo_transaction_set_ref (repo, origin_remote, origin_ref, NULL);
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
goto out;
if (!rpmostree_print_treepkg_diff (sysroot, cancellable, error))
goto out;
ret = TRUE;
out:
if (console)
(void) gs_console_end_status_line (console, NULL, NULL);
out:
if (is_peer)
rpmostree_cleanup_peer ();
return ret;
}

View File

@ -25,15 +25,19 @@
#include "rpmostree-builtins.h"
#include "rpmostree-libbuiltin.h"
#include "rpmostree-dbus-helpers.h"
#include "libgsystem.h"
#include <libglnx.h>
static char *opt_sysroot = "/";
static gboolean opt_reboot;
static gboolean opt_force_peer;
static GOptionEntry option_entries[] = {
{ "sysroot", 0, 0, G_OPTION_ARG_STRING, &opt_sysroot, "Use system root SYSROOT (default: /)", "SYSROOT" },
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after rollback is prepared", NULL },
{ "peer", 0, 0, G_OPTION_ARG_NONE, &opt_force_peer, "Force a peer to peer connection instead of using the system message bus", NULL },
{ NULL }
};
@ -44,101 +48,64 @@ rpmostree_builtin_rollback (int argc,
GError **error)
{
gboolean ret = FALSE;
gboolean is_peer = FALSE;
GOptionContext *context = g_option_context_new ("- Revert to the previously booted tree");
gs_unref_object GFile *sysroot_path = NULL;
gs_unref_object OstreeSysroot *sysroot = NULL;
gs_free char *origin_description = NULL;
gs_unref_ptrarray GPtrArray *deployments = NULL;
gs_unref_ptrarray GPtrArray *new_deployments =
g_ptr_array_new_with_free_func (g_object_unref);
OstreeDeployment *booted_deployment = NULL;
guint i;
guint booted_index;
guint index_to_prepend;
glnx_unref_object GDBusConnection *connection = NULL;
glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
g_autofree char *transaction_object_path = NULL;
if (!rpmostree_option_context_parse (context, option_entries, &argc, &argv, error))
goto out;
sysroot_path = g_file_new_for_path (opt_sysroot);
sysroot = ostree_sysroot_new (sysroot_path);
if (!ostree_sysroot_load (sysroot, cancellable, error))
goto out;
booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
if (booted_deployment == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Not currently booted into an OSTree system");
goto out;
}
deployments = ostree_sysroot_get_deployments (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 (booted_deployment != NULL);
for (booted_index = 0; booted_index < deployments->len; booted_index++)
{
if (deployments->pdata[booted_index] == booted_deployment)
break;
}
g_assert (booted_index < deployments->len);
g_assert (deployments->pdata[booted_index] == booted_deployment);
if (booted_index != 0)
{
/* There is an earlier deployment, let's assume we want to just
* insert the current one in front.
*/
/*
What this does is, if we're NOT in the default boot index, it plans to prepend
our current index (1, since we can't have more than two trees) so that it becomes index 0
(default) and the current default becomes index 1
*/
index_to_prepend = booted_index;
}
else
{
/* We're booted into the first, let's roll back to the previous */
index_to_prepend = 1;
}
g_ptr_array_add (new_deployments, g_object_ref (deployments->pdata[index_to_prepend]));
for (i = 0; i < deployments->len; i++)
{
if (i == index_to_prepend)
continue;
g_ptr_array_add (new_deployments, g_object_ref (deployments->pdata[i]));
}
g_print ("Moving '%s.%d' to be first deployment\n",
ostree_deployment_get_csum (deployments->pdata[index_to_prepend]),
ostree_deployment_get_deployserial (deployments->pdata[index_to_prepend]));
if (!ostree_sysroot_write_deployments (sysroot, new_deployments, cancellable,
if (!rpmostree_load_connection_and_sysroot (opt_sysroot,
opt_force_peer,
cancellable,
&connection,
&sysroot_proxy,
&is_peer,
error))
goto out;
if (opt_reboot)
if (!rpmostree_load_os_proxy (sysroot_proxy, NULL, is_peer,
cancellable, &os_proxy, error))
goto out;
if (!rpmostree_os_call_rollback_sync (os_proxy,
&transaction_object_path,
cancellable,
error))
goto out;
if (!rpmostree_transaction_get_response_sync (connection,
transaction_object_path,
cancellable,
error))
goto out;
if (!opt_reboot)
{
// by request, doing this without dbus
if (!rpmostree_print_treepkg_diff_from_sysroot_path (opt_sysroot,
cancellable,
error))
goto out;
g_print ("Run \"systemctl reboot\" to start a reboot\n");
}
else
{
gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
cancellable, error,
"systemctl", "reboot", NULL);
else
{
if (!rpmostree_print_treepkg_diff (sysroot, cancellable, error))
goto out;
g_print ("Successfully reset deployment order; run \"systemctl reboot\" to start a reboot\n");
}
if (opt_reboot)
ret = TRUE;
out:
out:
if (is_peer)
rpmostree_cleanup_peer ();
return ret;
}

View File

@ -24,15 +24,18 @@
#include <glib-unix.h>
#include "rpmostree-builtins.h"
#include "rpmostree-dbus-helpers.h"
#include "libgsystem.h"
#include <libglnx.h>
static char *opt_sysroot = "/";
static gboolean opt_pretty;
static gboolean opt_force_peer;
static GOptionEntry option_entries[] = {
{ "sysroot", 0, 0, G_OPTION_ARG_STRING, &opt_sysroot, "Use system root SYSROOT (default: /)", "SYSROOT" },
{ "pretty", 'p', 0, G_OPTION_ARG_NONE, &opt_pretty, "Display status in formatted rows", NULL },
{ "peer", 0, 0, G_OPTION_ARG_NONE, &opt_force_peer, "Force a peer to peer connection instead of using the system message bus", NULL },
{ NULL }
};
@ -45,66 +48,16 @@ printchar (char *s, int n)
g_print ("\n");
}
/* FIXME: This is a copy of ot_admin_checksum_version */
static char *
checksum_version (GVariant *checksum)
{
gs_unref_variant GVariant *metadata = NULL;
const char *ret = NULL;
metadata = g_variant_get_child_value (checksum, 0);
if (!g_variant_lookup (metadata, "version", "&s", &ret))
return NULL;
return g_strdup (ret);
}
static char *
version_of_commit (OstreeRepo *repo, const char *checksum)
{
gs_unref_variant GVariant *variant = NULL;
/* Shouldn't fail, but if it does, we ignore it */
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum,
&variant, NULL))
goto out;
return checksum_version (variant);
out:
return NULL;
}
static gboolean
deployment_get_gpg_verify (OstreeDeployment *deployment,
OstreeRepo *repo)
{
/* XXX Something like this could be added to the OstreeDeployment
* API in libostree if the OstreeRepo parameter is acceptable. */
GKeyFile *origin;
gs_free char *refspec = NULL;
gs_free char *remote = NULL;
gboolean gpg_verify = FALSE;
origin = ostree_deployment_get_origin (deployment);
if (origin == NULL)
goto out;
refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
if (refspec == NULL)
goto out;
if (!ostree_parse_refspec (refspec, &remote, NULL, NULL))
goto out;
(void) ostree_repo_remote_get_gpg_verify (repo, remote, &gpg_verify, NULL);
out:
return gpg_verify;
}
enum {
ID,
OSNAME,
SERIAL,
CHECKSUM,
VERSION,
TIMESTAMP,
ORIGIN,
SIGNATURES
};
gboolean
rpmostree_builtin_status (int argc,
@ -113,65 +66,78 @@ rpmostree_builtin_status (int argc,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFile *sysroot_path = NULL;
gs_unref_object OstreeSysroot *sysroot = NULL;
gs_unref_object OstreeRepo *repo = NULL;
gs_unref_ptrarray GPtrArray *deployments = NULL; // list of all depoyments
OstreeDeployment *booted_deployment = NULL; // current booted deployment
gboolean is_peer = FALSE;
GOptionContext *context = g_option_context_new ("- Get the version of the booted system");
glnx_unref_object GDBusConnection *connection = NULL;
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;
gchar *booted_id = NULL; // borrowed
const guint CSUM_DISP_LEN = 10; // number of checksum characters to display
guint i, j;
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 conde
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
gs_free char *booted_csum = NULL;
if (!rpmostree_option_context_parse (context, option_entries, &argc, &argv, error))
goto out;
sysroot_path = g_file_new_for_path (opt_sysroot);
sysroot = ostree_sysroot_new (sysroot_path);
if (!ostree_sysroot_load (sysroot, cancellable, error))
if (!rpmostree_load_connection_and_sysroot (opt_sysroot,
opt_force_peer,
cancellable,
&connection,
&sysroot_proxy,
&is_peer,
error))
goto out;
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
if (!rpmostree_load_os_proxy (sysroot_proxy, NULL, is_peer,
cancellable, &os_proxy, error))
goto out;
booted_deployment = ostree_sysroot_get_booted_deployment (sysroot);
deployments = ostree_sysroot_get_deployments (sysroot);
/* find lengths for use in column output */
if(!opt_pretty)
booted_deployment = rpmostree_os_dup_booted_deployment (os_proxy);
if (booted_deployment)
{
/* find max lengths of osname and refspec */
for (j = 0; j < deployments->len; j++)
{
const char *csum = ostree_deployment_get_csum (deployments->pdata[j]);
GKeyFile *origin;
gs_free char *origin_refspec = NULL;
OstreeDeployment *deployment = deployments->pdata[j];
gs_free char *version_string = NULL;
max_osname_len = MAX (max_osname_len, strlen (ostree_deployment_get_osname (deployment)));
origin = ostree_deployment_get_origin (deployment);
if (!origin)
origin_refspec = "none";
else
{
origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
if (!origin_refspec)
origin_refspec = g_strdup ("<unknown origin type>");
g_variant_get_child (booted_deployment, ID, "&s", &booted_id);
booted_signatures = g_variant_get_child_value (booted_deployment, SIGNATURES);
}
max_refspec_len = MAX (max_refspec_len, strlen (origin_refspec));
version_string = version_of_commit (repo, csum);
if (version_string)
deployments = rpmostree_sysroot_dup_deployments (sysroot_proxy);
if (deployments)
n = g_variant_n_children (deployments);
else
n = 0;
for (i = 0; i < n; i++)
{
/* find lengths for use in column output */
if (!opt_pretty)
{
g_autoptr (GVariant) v = NULL;
gchar *origin_refspec = NULL; // borrowed
gchar *os_name = NULL; // borrowed
gchar *version_string = NULL; // borrowed
v = g_variant_get_child_value (deployments, i);
g_variant_get_child (v, OSNAME, "&s", &os_name);
g_variant_get_child (v, VERSION, "&s", &version_string);
g_variant_get_child (v, ORIGIN, "&s", &origin_refspec);
max_osname_len = MAX (max_osname_len, strlen (os_name));
max_refspec_len = MAX (max_refspec_len, strlen (origin_refspec));
max_version_len = MAX (max_version_len, strlen (version_string));
}
}
if (!opt_pretty)
{
/* print column headers */
g_print (" %-*s", max_timestamp_len+buffer,"TIMESTAMP (UTC)");
if (max_version_len)
@ -186,59 +152,45 @@ rpmostree_builtin_status (int argc,
printchar ("=", 60);
/* print entries for each deployment */
for (i=0; i<deployments->len; i++)
for (i = 0; i < n; i++)
{
gs_unref_variant GVariant *commit = NULL;
const char *csum = ostree_deployment_get_csum (deployments->pdata[i]);
OstreeDeployment *deployment = deployments->pdata[i];
GKeyFile *origin;
gs_free char *origin_refspec = NULL;
GDateTime *timestamp = NULL;
gs_free char *timestamp_string = NULL;
char *truncated_csum = NULL;
gs_free char *version_string = NULL;
g_autofree char *timestamp_string = NULL;
g_autofree gchar *truncated_csum = NULL;
g_autoptr (GVariant) v = NULL;
g_autoptr (GVariant) signatures = NULL;
/* get commit for timestamp */
if (!ostree_repo_load_variant (repo,
OSTREE_OBJECT_TYPE_COMMIT,
csum,
&commit,
error))
goto out;
gchar *id = NULL; // borrowed
gchar *origin_refspec = NULL; // borrowed
gchar *os_name = NULL; // borrowed
gchar *version_string = NULL; // borrowed
gchar *checksum = NULL; // borrowed
/* format timestamp*/
timestamp = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (commit));
gint64 t;
gint serial;
gboolean is_booted = FALSE;
v = g_variant_get_child_value (deployments, i);
g_variant_get (v, "(&s&si&s&st&sav)",
&id, &os_name, &serial,
&checksum, &version_string,
&t, &origin_refspec, NULL);
signatures = g_variant_get_child_value (v, SIGNATURES);
is_booted = g_strcmp0 (booted_id, id) == 0;
timestamp = g_date_time_new_from_unix_utc (t);
g_assert (timestamp);
timestamp_string = g_date_time_format (timestamp, "%Y-%m-%d %T");
g_date_time_unref (timestamp);
version_string = checksum_version (commit);
/* get origin refspec */
origin = ostree_deployment_get_origin (deployment);
if (!origin)
origin_refspec = g_strdup ("none");
else
{
origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
if (!origin_refspec)
origin_refspec = g_strdup ("<unknown origin type>");
}
/* truncate checksum */
truncated_csum = g_strndup (csum, CSUM_DISP_LEN);
truncated_csum = g_strndup (checksum, CSUM_DISP_LEN);
/* print deployment info column */
if (!opt_pretty)
{
if (deployment == booted_deployment)
{
/* Stash this for printing signatures later. */
if (deployment_get_gpg_verify (deployment, repo))
booted_csum = g_strdup (csum);
}
g_print ("%c %-*s",
deployment == booted_deployment ? '*' : ' ',
is_booted ? '*' : ' ',
max_timestamp_len+buffer, timestamp_string);
if (max_version_len)
@ -246,73 +198,39 @@ rpmostree_builtin_status (int argc,
max_version_len+buffer, version_string ? version_string : "");
g_print ("%-*s%-*s%-*s\n",
max_id_len+buffer, truncated_csum,
max_osname_len+buffer, ostree_deployment_get_osname (deployment),
max_osname_len+buffer, os_name,
max_refspec_len+buffer, origin_refspec);
}
/* print "pretty" row info */
else
{
gs_unref_object OstreeGpgVerifyResult *result = NULL;
guint n_sigs;
guint tab = 11;
char *title = NULL;
GError *local_error = NULL;
if (i==0)
title = "DEFAULT ON BOOT";
else if (deployment == booted_deployment ||
deployments->len <= 2)
else if (is_booted || n <= 2)
title = "NON-DEFAULT ROLLBACK TARGET";
else
title = "NON-DEFAULT DEPLOYMENT";
g_print (" %c %s\n",
deployment == booted_deployment ? '*' : ' ',
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, csum, ostree_deployment_get_deployserial (deployment),
tab, "osname", tab, ostree_deployment_get_osname (deployment),
tab, "id", tab, checksum, serial,
tab, "osname", tab, os_name,
tab, "refspec", tab, origin_refspec);
if (deployment_get_gpg_verify (deployment, repo))
{
result = ostree_repo_verify_commit_ext (repo, csum, NULL, NULL,
cancellable, &local_error);
/* NOT_FOUND just means the commit is not signed. */
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&local_error);
}
else if (local_error != NULL)
{
g_propagate_error (error, local_error);
goto out;
}
else
{
GString *sigs_buffer;
guint n_sigs, ii;
n_sigs = ostree_gpg_verify_result_count_all (result);
sigs_buffer = g_string_sized_new (256);
for (ii = 0; ii < n_sigs; ii++)
{
g_string_append_c (sigs_buffer, '\n');
ostree_gpg_verify_result_describe (result, ii, sigs_buffer, " GPG: ",
OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT);
}
g_print ("%s", sigs_buffer->str);
g_string_free (sigs_buffer, TRUE);
}
}
n_sigs = g_variant_n_children (signatures);
if (n_sigs > 0)
rpmostree_print_signatures (signatures, " GPG: ");
printchar ("=", 60);
}
@ -321,50 +239,23 @@ rpmostree_builtin_status (int argc,
/* 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 (booted_csum != NULL)
if (!opt_pretty && booted_signatures != NULL)
{
gs_unref_object OstreeGpgVerifyResult *result = NULL;
GError *local_error = NULL;
result = ostree_repo_verify_commit_ext (repo, booted_csum, NULL, NULL,
cancellable, &local_error);
/* NOT_FOUND just means the commit is not signed. */
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
guint n_sigs = g_variant_n_children (booted_signatures);
if (n_sigs > 0)
{
g_clear_error (&local_error);
}
else if (local_error != NULL)
{
g_propagate_error (error, local_error);
goto out;
}
else
{
GString *sigs_buffer;
guint n_sigs, ii;
n_sigs = ostree_gpg_verify_result_count_all (result);
/* 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");
sigs_buffer = g_string_sized_new (256);
for (ii = 0; ii < n_sigs; ii++)
{
g_string_append_c (sigs_buffer, '\n');
ostree_gpg_verify_result_describe (result, ii, sigs_buffer, " ",
OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT);
}
g_print ("%s", sigs_buffer->str);
g_string_free (sigs_buffer, TRUE);
rpmostree_print_signatures (booted_signatures, " ");
}
}
ret = TRUE;
out:
out:
if (is_peer)
rpmostree_cleanup_peer ();
return ret;
}

View File

@ -27,15 +27,17 @@
#include "rpmostree-builtins.h"
#include "rpmostree-libbuiltin.h"
#include "rpmostree-rpm-util.h"
#include "rpm-ostreed-generated.h"
#include "rpmostree-dbus-helpers.h"
#include "libgsystem.h"
#include <libglnx.h>
static char *opt_sysroot = "/";
static char *opt_osname;
static gboolean opt_reboot;
static gboolean opt_allow_downgrade;
static gboolean opt_check_diff;
static gboolean opt_force_peer;
static GOptionEntry option_entries[] = {
{ "sysroot", 0, 0, G_OPTION_ARG_STRING, &opt_sysroot, "Use system root SYSROOT (default: /)", "SYSROOT" },
@ -43,322 +45,28 @@ static GOptionEntry option_entries[] = {
{ "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after an upgrade is prepared", NULL },
{ "allow-downgrade", 0, 0, G_OPTION_ARG_NONE, &opt_allow_downgrade, "Permit deployment of chronologically older trees", NULL },
{ "check-diff", 0, 0, G_OPTION_ARG_NONE, &opt_check_diff, "Check for upgrades and print package diff only", NULL },
{ "peer", 0, 0, G_OPTION_ARG_NONE, &opt_force_peer, "Force a peer to peer connection instead of using the system message bus", NULL },
{ NULL }
};
#define DBUS_NAME "org.projectatomic.rpmostree1"
#define BASE_DBUS_PATH "/org/projectatomic/rpmostree1"
static GVariant *
get_args_variant (void)
{
GVariantDict dict;
g_variant_dict_init (&dict, NULL);
g_variant_dict_insert (&dict, "allow-downgrade", "b", opt_allow_downgrade);
return g_variant_dict_end (&dict);
}
static void
gpg_verify_result_cb (OstreeRepo *repo,
const char *checksum,
OstreeGpgVerifyResult *result,
GSConsole *console)
default_changed_callback (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
/* Temporarily place the GSConsole stream (which is just stdout)
* back in normal mode before printing GPG verification results. */
gs_console_end_status_line (console, NULL, NULL);
g_print ("\n");
rpmostree_print_gpg_verify_result (result);
}
static gboolean
rpmostree_builtin_upgrade_check_diff (GFile *sysroot_path,
GCancellable *cancellable,
GError **error)
{
glnx_unref_object OstreeSysroot *sysroot = NULL;
glnx_unref_object OstreeRepo *repo = NULL;
glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL;
glnx_unref_object OstreeAsyncProgress *progress = NULL;
g_autofree char *origin_description = NULL;
GSConsole *console = NULL;
gulong signal_handler_id = 0;
gboolean changed = FALSE;
gboolean ret = FALSE;
sysroot = ostree_sysroot_new (sysroot_path);
if (!ostree_sysroot_load (sysroot, cancellable, error))
goto out;
upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname,
cancellable, error);
if (!upgrader)
goto out;
origin_description = ostree_sysroot_upgrader_get_origin_description (upgrader);
if (origin_description != NULL)
g_print ("Updating from: %s\n", origin_description);
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
goto out;
console = gs_console_get ();
if (console != NULL)
{
gs_console_begin_status_line (console, "", NULL, NULL);
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
G_CALLBACK (gpg_verify_result_cb),
console);
}
if (!ostree_sysroot_upgrader_pull_one_dir (upgrader, "/usr/share/rpm",
0, 0, progress, &changed,
cancellable, error))
goto out;
if (console != NULL)
{
if (!gs_console_end_status_line (console, cancellable, error))
{
console = NULL;
goto out;
}
console = NULL;
}
if (!changed)
{
g_print ("No upgrade available.\n");
}
else
{
glnx_unref_object GFile *rpmdbdir = NULL;
_cleanup_rpmrev_ struct RpmRevisionData *rpmrev1 = NULL;
_cleanup_rpmrev_ struct RpmRevisionData *rpmrev2 = NULL;
gs_free char *tmpd = g_mkdtemp (g_strdup ("/tmp/rpm-ostree.XXXXXX"));
gs_free char *ref = NULL; // location of this rev
gs_free char *remote = NULL;
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;
}
rpmdbdir = g_file_new_for_path (tmpd);
if (!(rpmrev1 = rpmrev_new (repo, rpmdbdir,
ostree_deployment_get_csum (ostree_sysroot_get_booted_deployment (sysroot)),
NULL, cancellable, error)))
goto out;
if (!(rpmrev2 = rpmrev_new (repo, rpmdbdir, ref,
NULL, cancellable, error)))
goto out;
rpmhdrs_diff_prnt_diff (rpmrev1->root, rpmrev2->root,
rpmhdrs_diff (rpmrev1->rpmdb, rpmrev2->rpmdb));
}
ret = TRUE;
out:
if (console != NULL)
(void) gs_console_end_status_line (console, NULL, NULL);
if (signal_handler_id > 0)
g_signal_handler_disconnect (repo, signal_handler_id);
return ret;
}
static gboolean
rpmostree_builtin_upgrade_for_sysroot (GFile *sysroot_path,
GCancellable *cancellable,
GError **error)
{
glnx_unref_object OstreeSysroot *sysroot = NULL;
glnx_unref_object OstreeRepo *repo = NULL;
glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL;
glnx_unref_object OstreeAsyncProgress *progress = NULL;
g_autofree char *origin_description = NULL;
GSConsole *console = NULL;
OstreeSysrootUpgraderPullFlags upgrader_pull_flags = 0;
gulong signal_handler_id = 0;
gboolean changed = FALSE;
gboolean ret = FALSE;
sysroot = ostree_sysroot_new (sysroot_path);
if (!ostree_sysroot_load (sysroot, cancellable, error))
goto out;
upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname,
cancellable, error);
if (!upgrader)
goto out;
origin_description = ostree_sysroot_upgrader_get_origin_description (upgrader);
if (origin_description != NULL)
g_print ("Updating from: %s\n", origin_description);
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
goto out;
console = gs_console_get ();
if (console != NULL)
{
gs_console_begin_status_line (console, "", NULL, NULL);
progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
G_CALLBACK (gpg_verify_result_cb),
console);
}
if (opt_allow_downgrade)
upgrader_pull_flags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER;
if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgrader_pull_flags,
progress, &changed,
cancellable, error))
goto out;
if (console != NULL)
{
if (!gs_console_end_status_line (console, cancellable, error))
{
console = NULL;
goto out;
}
console = NULL;
}
if (!changed)
{
g_print ("No upgrade available.\n");
}
else
{
if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
goto out;
if (opt_reboot)
gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
cancellable, error,
"systemctl", "reboot", NULL);
else
{
if (!rpmostree_print_treepkg_diff (sysroot, cancellable, error))
goto out;
g_print ("Upgrade prepared for next boot; run \"systemctl reboot\" to start a reboot\n");
}
}
ret = TRUE;
out:
if (console != NULL)
(void) gs_console_end_status_line (console, NULL, NULL);
if (signal_handler_id > 0)
g_signal_handler_disconnect (repo, signal_handler_id);
return ret;
}
static gboolean
rpmostree_builtin_upgrade_system (GCancellable *cancellable,
GError **error)
{
glnx_unref_object GDBusConnection *connection = NULL;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
glnx_unref_object RPMOSTreeTransaction *transaction_proxy = NULL;
g_auto(GVariantDict) options;
g_autofree char *os_object_path = NULL;
g_autofree char *transaction_object_path = NULL;
gboolean ret = FALSE;
g_variant_dict_init (&options, NULL);
g_variant_dict_insert (&options,
"allow-downgrade",
"b", opt_allow_downgrade);
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
if (connection == NULL)
goto out;
sysroot_proxy = rpmostree_sysroot_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
DBUS_NAME,
BASE_DBUS_PATH,
cancellable,
error);
if (sysroot_proxy == NULL)
goto out;
if (opt_osname == NULL)
{
os_object_path = rpmostree_sysroot_dup_booted (sysroot_proxy);
}
else
{
if (!rpmostree_sysroot_call_get_os_sync (sysroot_proxy,
opt_osname,
&os_object_path,
cancellable,
error))
goto out;
}
os_proxy = rpmostree_os_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
DBUS_NAME,
os_object_path,
cancellable,
error);
if (os_proxy != NULL)
goto out;
if (!rpmostree_os_call_upgrade_sync (os_proxy,
g_variant_dict_end (&options),
&transaction_object_path,
cancellable,
error))
goto out;
/* XXX I worry this part might be racy. If the transaction completes
* before we connect to the interface, we may miss it entirely and
* not know whether the transaction succeeded and end up reporting
* a bogus D-Bus error.
*
* One pattern I've used in the past is to add a Start() method to
* the transaction interface to call once the client is set up, but
* that complicates the server-side considerably: have to abort the
* transaction if the client dies or a timer expires before Start()
* is called.
*
* Reference code for this pattern:
* https://git.gnome.org/browse/evolution-data-server/tree/libebackend/e-authentication-mediator.c?h=evolution-data-server-3-12
*/
transaction_proxy = rpmostree_transaction_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
DBUS_NAME,
transaction_object_path,
cancellable,
error);
if (transaction_proxy != NULL)
goto out;
/* FIXME Monitor the transaction, report progress, etc. */
ret = TRUE;
out:
return ret;
GVariant **value = user_data;
g_object_get (object, pspec->name, value, NULL);
}
gboolean
@ -368,47 +76,138 @@ rpmostree_builtin_upgrade (int argc,
GError **error)
{
gboolean ret = FALSE;
gboolean is_peer = FALSE;
GOptionContext *context = g_option_context_new ("- Perform a system upgrade");
g_autoptr(GFile) sysroot_path = NULL;
glnx_unref_object GDBusConnection *connection = NULL;
glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
g_autofree char *transaction_object_path = NULL;
g_autoptr (GVariant) default_deployment = NULL;
if (!rpmostree_option_context_parse (context, option_entries, &argc, &argv, error))
goto out;
/* XXX Like g_file_new_for_commandline_arg() but no URIs. */
if (g_path_is_absolute (opt_sysroot))
{
sysroot_path = g_file_new_for_path (opt_sysroot);
}
else
{
g_autofree char *current_dir = NULL;
g_autofree char *filename = NULL;
if (!rpmostree_load_connection_and_sysroot (opt_sysroot,
opt_force_peer,
cancellable,
&connection,
&sysroot_proxy,
&is_peer,
error))
goto out;
current_dir = g_get_current_dir ();
filename = g_build_filename (current_dir, opt_sysroot, NULL);
sysroot_path = g_file_new_for_path (filename);
}
if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname, is_peer,
cancellable, &os_proxy, error))
goto out;
if (opt_check_diff)
{
if (!rpmostree_builtin_upgrade_check_diff (sysroot_path,
cancellable, error))
goto out;
}
else if (g_file_has_parent (sysroot_path, NULL))
{
if (!rpmostree_builtin_upgrade_for_sysroot (sysroot_path,
cancellable, error))
if (!rpmostree_os_call_download_update_rpm_diff_sync (os_proxy,
&transaction_object_path,
cancellable,
error))
goto out;
}
else
{
if (!rpmostree_builtin_upgrade_system (cancellable, error))
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_object_path,
cancellable,
error))
goto out;
}
if (!rpmostree_transaction_get_response_sync (connection,
transaction_object_path,
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_path = NULL;
g_autofree char *origin_description = NULL;
_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 = g_file_new_for_path (opt_sysroot);
sysroot = ostree_sysroot_new (sysroot_path);
if (!ostree_sysroot_load (sysroot, cancellable, error))
goto out;
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
goto out;
origin_description = rpmostree_os_dup_upgrade_origin (os_proxy);
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)
{
gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT,
cancellable, error,
"systemctl", "reboot", NULL);
}
else
{
if (!rpmostree_print_treepkg_diff_from_sysroot_path (opt_sysroot,
cancellable,
error))
goto out;
g_print ("Run \"systemctl reboot\" to start a reboot\n");
}
}
ret = TRUE;
out:
out:
if (is_peer)
rpmostree_cleanup_peer ();
return ret;
}

View File

@ -0,0 +1,612 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2014 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 "rpmostree-dbus-helpers.h"
#include "libgsystem.h"
#include "libglnx.h"
#include <sys/socket.h>
#include "glib-unix.h"
#include <signal.h>
static GPid peer_pid = 0;
void
rpmostree_cleanup_peer ()
{
if (peer_pid > 0)
kill (peer_pid, SIGTERM);
}
static gboolean
get_connection_for_path (gchar *sysroot,
gboolean force_peer,
GCancellable *cancellable,
GDBusConnection **out_connection,
gboolean *out_is_peer,
GError **error)
{
glnx_unref_object GDBusConnection *connection = NULL;
glnx_unref_object GDBusObjectManager *om = NULL;
glnx_unref_object GSocketConnection *stream = NULL;
glnx_unref_object GSocket *socket = NULL;
gchar buffer[16];
int pair[2];
gboolean ret = FALSE;
gboolean is_peer = FALSE;
const gchar *args[] = {
"rpm-ostreed",
"--sysroot", sysroot,
"--dbus-peer", buffer,
NULL
};
if (!sysroot)
sysroot = "/";
if (g_strcmp0 ("/", sysroot) == 0 && force_peer == FALSE)
{
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
goto out;
}
g_print ("Running in single user mode. Be sure no other users are modifying the system\n");
is_peer = TRUE;
if (socketpair (AF_UNIX, SOCK_STREAM, 0, pair) < 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Couldn't create socket pair: %s",
g_strerror (errno));
goto out;
}
g_snprintf (buffer, sizeof (buffer), "%d", pair[1]);
socket = g_socket_new_from_fd (pair[0], error);
if (socket == NULL)
{
close (pair[0]);
close (pair[1]);
goto out;
}
if (!g_spawn_async (NULL, (gchar **)args, NULL,
G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, &peer_pid, error))
{
close (pair[1]);
goto out;
}
stream = g_socket_connection_factory_create_connection (socket);
connection = g_dbus_connection_new_sync (G_IO_STREAM (stream), NULL,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, cancellable, error);
out:
if (connection)
{
ret = TRUE;
*out_connection = g_steal_pointer (&connection);
*out_is_peer = is_peer;
}
return ret;
}
/**
* rpmostree_load_connection_and_sysroot
* @sysroot: sysroot path
* @force_peer: Force a peer connection
* @cancellable: A GCancellable
* @out_connection: (out) Return location for connection.
* @out_sysroot: (out) Return location for sysroot
* @out_is_peer: (out) indicates if connection is connected to a peer.
* @error: A pointer to a GError pointer.
*
* Returns: True on success
**/
gboolean
rpmostree_load_connection_and_sysroot (gchar *sysroot,
gboolean force_peer,
GCancellable *cancellable,
GDBusConnection **out_connection,
RPMOSTreeSysroot **out_sysroot_proxy,
gboolean *out_is_peer,
GError **error)
{
gboolean ret = FALSE;
gboolean is_peer = FALSE;
glnx_unref_object GDBusConnection *connection = NULL;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
if (!get_connection_for_path (sysroot,
force_peer,
cancellable,
&connection,
&is_peer,
error))
goto out;
sysroot_proxy = rpmostree_sysroot_proxy_new_sync (connection,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
is_peer ? NULL : BUS_NAME,
"/org/projectatomic/rpmostree1/Sysroot",
NULL,
error);
if (sysroot_proxy == NULL)
goto out;
*out_connection = g_steal_pointer (&connection);
*out_is_peer = is_peer;
*out_sysroot_proxy = g_steal_pointer (&sysroot_proxy);
ret = TRUE;
out:
return ret;
}
gboolean
rpmostree_load_os_proxy (RPMOSTreeSysroot *sysroot_proxy,
gchar *opt_osname,
gboolean is_peer,
GCancellable *cancellable,
RPMOSTreeOS **out_os_proxy,
GError **error)
{
gboolean ret = FALSE;
g_autofree char *os_object_path = NULL;
glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
GDBusConnection *connection = NULL; // owned by sysroot_proxy
if (opt_osname == NULL)
{
os_object_path = rpmostree_sysroot_dup_booted (sysroot_proxy);
}
if (os_object_path == NULL)
{
/* Usually if opt_osname is null and the property isn't
populated that means the daemon isn't listen on the bus
make the call anyways to get the standard error.
*/
if (!opt_osname)
opt_osname = "";
if (!rpmostree_sysroot_call_get_os_sync (sysroot_proxy,
opt_osname,
&os_object_path,
cancellable,
error))
goto out;
}
connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (sysroot_proxy));
os_proxy = rpmostree_os_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
is_peer ? NULL : BUS_NAME,
os_object_path,
cancellable,
error);
if (os_proxy == NULL)
goto out;
*out_os_proxy = g_steal_pointer (&os_proxy);
ret = TRUE;
out:
return ret;
}
/**
* transaction_console_get_progress_line
*
* Similar to ostree_repo_pull_default_console_progress_changed
*
* Displays outstanding fetch progress in bytes/sec,
* or else outstanding content or metadata writes to the repository in
* number of objects.
**/
static gchar *
transaction_get_progress_line (guint64 start_time,
guint64 elapsed_secs,
guint outstanding_fetches,
guint outstanding_writes,
guint n_scanned_metadata,
guint metadata_fetched,
guint outstanding_metadata_fetches,
guint total_delta_parts,
guint fetched_delta_parts,
guint total_delta_superblocks,
guint64 total_delta_part_size,
guint fetched,
guint requested,
guint64 bytes_transferred,
guint64 bytes_sec)
{
GString *buf;
buf = g_string_new ("");
if (outstanding_fetches)
{
g_autofree gchar *formatted_bytes_transferred = g_format_size_full (bytes_transferred, 0);
g_autofree gchar *formatted_bytes_sec = NULL;
if (!bytes_sec)
formatted_bytes_sec = g_strdup ("-");
else
formatted_bytes_sec = g_format_size (bytes_sec);
if (total_delta_parts > 0)
{
g_autofree gchar *formatted_total = g_format_size (total_delta_part_size);
g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/s %s/%s",
fetched_delta_parts, total_delta_parts,
formatted_bytes_sec, formatted_bytes_transferred,
formatted_total);
}
else if (outstanding_metadata_fetches)
{
g_string_append_printf (buf, "Receiving metadata objects: %u/(estimating) %s/s %s",
metadata_fetched, formatted_bytes_sec, formatted_bytes_transferred);
}
else
{
g_string_append_printf (buf, "Receiving objects: %u%% (%u/%u) %s/s %s",
(guint)((((double)fetched) / requested) * 100),
fetched, requested, formatted_bytes_sec, formatted_bytes_transferred);
}
}
else if (outstanding_writes)
{
g_string_append_printf (buf, "Writing objects: %u", outstanding_writes);
}
else
{
g_string_append_printf (buf, "Scanning metadata: %u", n_scanned_metadata);
}
return g_string_free (buf, FALSE);
}
typedef struct
{
GSConsole *console;
GError *error;
GMainLoop *loop;
gboolean complete;
} TransactionProgress;
static TransactionProgress *
transaction_progress_new (void)
{
TransactionProgress *self = NULL;
self = g_slice_new (TransactionProgress);
self->error = NULL;
self->console = NULL;
self->loop = g_main_loop_new (NULL, FALSE);
self->complete = FALSE;
return self;
}
static void
transaction_progress_free (TransactionProgress *self)
{
g_main_loop_unref (self->loop);
g_slice_free (TransactionProgress, self);
}
static gboolean
end_status_line (TransactionProgress *self)
{
gboolean ret = TRUE;
if (self->console != NULL)
ret = gs_console_end_status_line (self->console, NULL, NULL);
return ret;
}
static gboolean
add_status_line (TransactionProgress *self,
const char *line)
{
gboolean ret = TRUE;
if (self->console == NULL)
self->console = gs_console_get ();
ret = gs_console_begin_status_line (self->console, line, NULL, NULL);
return ret;
}
static void
transaction_progress_end (TransactionProgress *self)
{
end_status_line (self);
self->console = NULL;
g_main_loop_quit (self->loop);
}
static void
on_transaction_progress (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
TransactionProgress *tp = user_data;
if (g_strcmp0(signal_name, "SignatureProgress") == 0)
{
g_autoptr (GVariant) sig = NULL;
sig = g_variant_get_child_value (parameters, 0);
rpmostree_print_signatures (g_variant_ref (sig), " ");
add_status_line (tp, "\n");
}
if (g_strcmp0(signal_name, "Message") == 0)
{
g_autofree gchar *message = NULL;
g_variant_get_child (parameters, 0, "s", &message);
add_status_line (tp, message);
}
else if (g_strcmp0(signal_name, "DownloadProgress") == 0)
{
g_autofree gchar *line = NULL;
guint64 start_time;
guint64 elapsed_secs;
guint outstanding_fetches;
guint outstanding_writes;
guint n_scanned_metadata;
guint metadata_fetched;
guint outstanding_metadata_fetches;
guint total_delta_parts;
guint fetched_delta_parts;
guint total_delta_superblocks;
guint64 total_delta_part_size;
guint fetched;
guint requested;
guint64 bytes_transferred;
guint64 bytes_sec;
g_variant_get (parameters, "((tt)(uu)(uuu)(uuut)(uu)(tt))",
&start_time, &elapsed_secs,
&outstanding_fetches, &outstanding_writes,
&n_scanned_metadata, &metadata_fetched,
&outstanding_metadata_fetches,
&total_delta_parts, &fetched_delta_parts,
&total_delta_superblocks, &total_delta_part_size,
&fetched, &requested, &bytes_transferred, &bytes_sec);
line = transaction_get_progress_line (start_time, elapsed_secs,
outstanding_fetches,
outstanding_writes,
n_scanned_metadata,
metadata_fetched,
outstanding_metadata_fetches,
total_delta_parts,
fetched_delta_parts,
total_delta_superblocks,
total_delta_part_size,
fetched,
requested,
bytes_transferred,
bytes_sec);
add_status_line (tp, line);
}
}
static void
on_owner_changed (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
/* Owner shouldn't change durning a transaction
* that messes with notifications, abort, abort.
*/
TransactionProgress *tp = user_data;
tp->error = g_dbus_error_new_for_dbus_error ("org.projectatomic.rpmostreed.Error.Failed",
"Bus owner changed, aborting.");
transaction_progress_end (tp);
}
static void
transaction_finished (RPMOSTreeTransaction *transaction,
TransactionProgress *tp)
{
g_autofree gchar *message = NULL;
gboolean success = FALSE;
gboolean complete = FALSE;
/* if we are already finished don't process */
if (tp->complete)
return;
tp->complete = TRUE;
if (!tp->error)
{
complete = rpmostree_transaction_get_complete (transaction);
success = rpmostree_transaction_get_success (transaction);
message = rpmostree_transaction_dup_result_message (transaction);
g_return_if_fail (complete == TRUE);
if (success)
{
add_status_line (tp, message);
}
else
{
tp->error = g_dbus_error_new_for_dbus_error ("org.projectatomic.rpmostreed.Error.Failed",
message);
}
}
transaction_progress_end (tp);
}
static void
on_transaction_finished (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
TransactionProgress *tp = user_data;
transaction_finished (RPMOSTREE_TRANSACTION (object), tp);
}
static void
cancelled_handler (GCancellable *cancellable,
gpointer user_data)
{
RPMOSTreeTransaction *transaction = user_data;
rpmostree_transaction_call_cancel_sync (transaction, NULL, NULL);
}
gboolean
rpmostree_transaction_get_response_sync (GDBusConnection *connection,
const gchar *object_path,
GCancellable *cancellable,
GError **error)
{
glnx_unref_object GDBusObjectManager *object_manager = NULL;
glnx_unref_object RPMOSTreeTransaction *transaction = NULL;
TransactionProgress *tp = transaction_progress_new ();
gint cancel_handler;
gboolean is_peer = TRUE;
gulong property_handler = 0;
gulong signal_handler = 0;
gboolean success = FALSE;
// If we are on the message bus, setup object manager connection
// to notify if the owner changes.
if (g_dbus_connection_get_unique_name (connection) != NULL)
{
is_peer = FALSE;
object_manager = rpmostree_object_manager_client_new_sync (connection,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
BUS_NAME,
"/org/projectatomic/rpmostree1",
cancellable,
error);
if (object_manager == NULL)
goto out;
g_signal_connect (object_manager,
"notify::name-owner",
G_CALLBACK (on_owner_changed),
tp);
}
transaction = rpmostree_transaction_proxy_new_sync (connection,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
is_peer ? NULL : BUS_NAME,
object_path,
cancellable,
error);
if (transaction == NULL)
goto out;
// setup cancel handler
cancel_handler = g_cancellable_connect (cancellable,
G_CALLBACK (cancelled_handler),
transaction, NULL);
signal_handler = g_signal_connect (transaction, "g-signal",
G_CALLBACK (on_transaction_progress),
tp);
// Setup finished signal handlers
property_handler = g_signal_connect (transaction, "notify::complete",
G_CALLBACK (on_transaction_finished),
tp);
if (!rpmostree_transaction_get_complete (transaction))
g_main_loop_run (tp->loop);
else
transaction_finished (transaction, tp);
g_cancellable_disconnect (cancellable, cancel_handler);
if (!g_cancellable_set_error_if_cancelled (cancellable, error))
{
if (tp->error)
{
g_propagate_error (error, tp->error);
}
else
{
success = TRUE;
}
}
out:
if (property_handler)
g_signal_handler_disconnect (transaction, property_handler);
if (signal_handler)
g_signal_handler_disconnect (transaction, signal_handler);
transaction_progress_free (tp);
return success;
}
void
rpmostree_print_signatures (GVariant *variant,
const gchar *sep)
{
GString *sigs_buffer;
guint i;
guint n_sigs = g_variant_n_children (variant);
sigs_buffer = g_string_sized_new (256);
for (i = 0; i < n_sigs; i++)
{
g_autoptr (GVariant) v = NULL;
g_string_append_c (sigs_buffer, '\n');
g_variant_get_child (variant, i, "v", &v);
ostree_gpg_verify_result_describe_variant (v, sigs_buffer, sep,
OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT);
}
g_print ("%s", sigs_buffer->str);
g_string_free (sigs_buffer, TRUE);
}

View File

@ -0,0 +1,60 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* 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.
*/
#pragma once
#include <glib-unix.h>
#include <gio/gio.h>
#include "rpm-ostreed-generated.h"
#include <stdint.h>
#include <string.h>
#include <ostree.h>
#define BUS_NAME "org.projectatomic.rpmostree1"
void
rpmostree_cleanup_peer (void);
gboolean
rpmostree_load_connection_and_sysroot (gchar *sysroot,
gboolean force_peer,
GCancellable *cancellable,
GDBusConnection **out_connection,
RPMOSTreeSysroot **out_manager,
gboolean *out_is_peer,
GError **error);
gboolean
rpmostree_load_os_proxy (RPMOSTreeSysroot *sysroot_proxy,
gchar *opt_osname,
gboolean is_peer,
GCancellable *cancellable,
RPMOSTreeOS **out_os_proxy,
GError **error);
gboolean
rpmostree_transaction_get_response_sync (GDBusConnection *out_connection,
const gchar *object_path,
GCancellable *cancellable,
GError **error);
void
rpmostree_print_signatures (GVariant *variant,
const gchar *sep);

View File

@ -26,6 +26,27 @@
#include "rpmostree.h"
#include "rpmostree-cleanup.h"
gboolean
rpmostree_print_treepkg_diff_from_sysroot_path (const gchar *sysroot_path,
GCancellable *cancellable,
GError **error)
{
gs_unref_object OstreeSysroot *sysroot = NULL;
gs_unref_object GFile *sysroot_file = NULL;
gboolean ret = FALSE;
sysroot_file = g_file_new_for_path (sysroot_path);
sysroot = ostree_sysroot_new (sysroot_file);
if (!ostree_sysroot_load (sysroot, cancellable, error))
goto out;
ret = rpmostree_print_treepkg_diff (sysroot, cancellable, error);
out:
return ret;
}
gboolean
rpmostree_print_treepkg_diff (OstreeSysroot *sysroot,
GCancellable *cancellable,

View File

@ -28,4 +28,9 @@ gboolean
rpmostree_print_treepkg_diff (OstreeSysroot *sysroot,
GCancellable *cancellable,
GError **error);
gboolean
rpmostree_print_treepkg_diff_from_sysroot_path (const gchar *sysroot_path,
GCancellable *cancellable,
GError **error);
G_END_DECLS