From f525730187d82bba31e603a056d6215da37fc731 Mon Sep 17 00:00:00 2001 From: petervo Date: Fri, 12 Jun 2015 17:19:16 -0700 Subject: [PATCH] daemon: Make parts of cli use the daemon --- Makefile-rpm-ostree.am | 2 + src/app/main.c | 21 +- src/app/rpmostree-builtin-rebase.c | 173 +++----- src/app/rpmostree-builtin-rollback.c | 131 +++--- src/app/rpmostree-builtin-status.c | 329 +++++--------- src/app/rpmostree-builtin-upgrade.c | 469 ++++++-------------- src/app/rpmostree-dbus-helpers.c | 612 +++++++++++++++++++++++++++ src/app/rpmostree-dbus-helpers.h | 60 +++ src/app/rpmostree-libbuiltin.c | 29 +- src/app/rpmostree-libbuiltin.h | 5 + 10 files changed, 1084 insertions(+), 747 deletions(-) create mode 100644 src/app/rpmostree-dbus-helpers.c create mode 100644 src/app/rpmostree-dbus-helpers.h diff --git a/Makefile-rpm-ostree.am b/Makefile-rpm-ostree.am index 824e908a..694b962e 100644 --- a/Makefile-rpm-ostree.am +++ b/Makefile-rpm-ostree.am @@ -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) diff --git a/src/app/main.c b/src/app/main.c index e38b1a43..6f4629f5 100644 --- a/src/app/main.c +++ b/src/app/main.c @@ -21,6 +21,7 @@ #include "config.h" #include +#include #include #include @@ -124,17 +125,28 @@ 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; gs_free char *prgname = NULL; - + /* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */ g_setenv ("GIO_USE_VFS", "local", TRUE); g_set_prgname (argv[0]); @@ -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; diff --git a/src/app/rpmostree-builtin-rebase.c b/src/app/rpmostree-builtin-rebase.c index ab46cc02..62e1d134 100644 --- a/src/app/rpmostree-builtin-rebase.c +++ b/src/app/rpmostree-builtin-rebase.c @@ -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 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"); - goto out; - } + 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); - } - else - { - if (!ostree_parse_refspec (new_provided_refspec, &new_remote, &new_ref, error)) + // 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"); } - - 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; + gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT, + cancellable, error, + "systemctl", "reboot", NULL); } - 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; } diff --git a/src/app/rpmostree-builtin-rollback.c b/src/app/rpmostree-builtin-rollback.c index f729382e..9347e426 100644 --- a/src/app/rpmostree-builtin-rollback.c +++ b/src/app/rpmostree-builtin-rollback.c @@ -25,15 +25,19 @@ #include "rpmostree-builtins.h" #include "rpmostree-libbuiltin.h" +#include "rpmostree-dbus-helpers.h" #include "libgsystem.h" +#include 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)) + if (!rpmostree_load_connection_and_sysroot (opt_sysroot, + opt_force_peer, + cancellable, + &connection, + &sysroot_proxy, + &is_peer, + 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, - error)) + if (!rpmostree_load_os_proxy (sysroot_proxy, NULL, is_peer, + cancellable, &os_proxy, 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_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) { - if (!rpmostree_print_treepkg_diff (sysroot, cancellable, error)) + // by request, doing this without dbus + if (!rpmostree_print_treepkg_diff_from_sysroot_path (opt_sysroot, + cancellable, + error)) goto out; - g_print ("Successfully reset deployment order; run \"systemctl reboot\" to start a reboot\n"); + 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); } - if (opt_reboot) ret = TRUE; - out: + +out: + if (is_peer) + rpmostree_cleanup_peer (); + return ret; } diff --git a/src/app/rpmostree-builtin-status.c b/src/app/rpmostree-builtin-status.c index 2d17db40..5c4faebe 100644 --- a/src/app/rpmostree-builtin-status.c +++ b/src/app/rpmostree-builtin-status.c @@ -24,19 +24,22 @@ #include #include "rpmostree-builtins.h" +#include "rpmostree-dbus-helpers.h" -#include "libgsystem.h" +#include 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 } }; -static void +static void printchar (char *s, int n) { int i; @@ -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++) + g_variant_get_child (booted_deployment, ID, "&s", &booted_id); + booted_signatures = g_variant_get_child_value (booted_deployment, SIGNATURES); + } + + 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) { - 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; + g_autoptr (GVariant) v = NULL; + gchar *origin_refspec = NULL; // borrowed + gchar *os_name = NULL; // borrowed + gchar *version_string = NULL; // borrowed - max_osname_len = MAX (max_osname_len, strlen (ostree_deployment_get_osname (deployment))); + 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); - 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 (""); - } + max_osname_len = MAX (max_osname_len, strlen (os_name)); max_refspec_len = MAX (max_refspec_len, strlen (origin_refspec)); - - version_string = version_of_commit (repo, csum); - if (version_string) - max_version_len = MAX (max_version_len, strlen (version_string)); + 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; ilen; 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 (""); - } /* 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: - return ret; + +out: + if (is_peer) + rpmostree_cleanup_peer (); + + return ret; } diff --git a/src/app/rpmostree-builtin-upgrade.c b/src/app/rpmostree-builtin-upgrade.c index ce93a732..f5909e44 100644 --- a/src/app/rpmostree-builtin-upgrade.c +++ b/src/app/rpmostree-builtin-upgrade.c @@ -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 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; } diff --git a/src/app/rpmostree-dbus-helpers.c b/src/app/rpmostree-dbus-helpers.c new file mode 100644 index 00000000..25ecb773 --- /dev/null +++ b/src/app/rpmostree-dbus-helpers.c @@ -0,0 +1,612 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014 Colin Walters + * + * 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 +#include "glib-unix.h" +#include + +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); +} diff --git a/src/app/rpmostree-dbus-helpers.h b/src/app/rpmostree-dbus-helpers.h new file mode 100644 index 00000000..95cc1f2b --- /dev/null +++ b/src/app/rpmostree-dbus-helpers.h @@ -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 +#include + +#include "rpm-ostreed-generated.h" + +#include +#include +#include + +#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); diff --git a/src/app/rpmostree-libbuiltin.c b/src/app/rpmostree-libbuiltin.c index 49e5641a..14a7c5ae 100644 --- a/src/app/rpmostree-libbuiltin.c +++ b/src/app/rpmostree-libbuiltin.c @@ -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, @@ -34,11 +55,11 @@ rpmostree_print_treepkg_diff (OstreeSysroot *sysroot, gboolean ret = FALSE; OstreeDeployment *booted_deployment; OstreeDeployment *new_deployment; - gs_unref_ptrarray GPtrArray *deployments = + gs_unref_ptrarray GPtrArray *deployments = ostree_sysroot_get_deployments (sysroot); booted_deployment = ostree_sysroot_get_booted_deployment (sysroot); - + g_assert (deployments->len > 1); new_deployment = deployments->pdata[0]; @@ -52,10 +73,10 @@ rpmostree_print_treepkg_diff (OstreeSysroot *sysroot, g_autoptr(GPtrArray) modified_old = NULL; g_autoptr(GPtrArray) modified_new = NULL; guint i; - + if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) goto out; - + if (!rpm_ostree_db_diff (repo, from_rev, to_rev, &removed, &added, &modified_old, &modified_new, cancellable, error)) diff --git a/src/app/rpmostree-libbuiltin.h b/src/app/rpmostree-libbuiltin.h index d63e7b12..94075110 100644 --- a/src/app/rpmostree-libbuiltin.h +++ b/src/app/rpmostree-libbuiltin.h @@ -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