From d69cb7d7c1d9f2f333cbcae1d5bdb1553e6e35f5 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Thu, 13 Aug 2015 13:17:01 -0400 Subject: [PATCH] daemon: Convert transactions to subclasses Implementing a template pattern for transactions. The TransactionClass is now abstract, and transaction_new() is replaced with various method-specific functions like transaction_new_upgrade(). These custom subclasses live in a new file transaction-types.[ch]. Further, transaction_monitor_new_transaction() is replaced with transaction_monitor_add(). So the handlers for "OS" interface methods need only create an appropriate transaction instance and hand it off to the transaction monitor. --- Makefile-daemon.am | 2 + src/daemon/os.c | 827 ++++++++----------------------- src/daemon/sysroot.c | 4 +- src/daemon/transaction-monitor.c | 33 +- src/daemon/transaction-monitor.h | 12 +- src/daemon/transaction-types.c | 645 ++++++++++++++++++++++++ src/daemon/transaction-types.h | 49 ++ src/daemon/transaction.c | 147 +++--- src/daemon/transaction.h | 20 +- 9 files changed, 1002 insertions(+), 737 deletions(-) create mode 100644 src/daemon/transaction-types.c create mode 100644 src/daemon/transaction-types.h diff --git a/Makefile-daemon.am b/Makefile-daemon.am index db0bcb87..f2e55632 100644 --- a/Makefile-daemon.am +++ b/Makefile-daemon.am @@ -35,6 +35,8 @@ librpmostreed_la_SOURCES = \ src/daemon/transaction.c \ src/daemon/transaction-monitor.h \ src/daemon/transaction-monitor.c \ + src/daemon/transaction-types.h \ + src/daemon/transaction-types.c \ src/daemon/rpmostree-package-variants.h \ src/daemon/rpmostree-package-variants.c \ src/daemon/os.h \ diff --git a/src/daemon/os.c b/src/daemon/os.c index 244e8d11..da7c29a4 100644 --- a/src/daemon/os.c +++ b/src/daemon/os.c @@ -32,9 +32,9 @@ #include "utils.h" #include "transaction.h" #include "transaction-monitor.h" +#include "transaction-types.h" typedef struct _OSStubClass OSStubClass; -typedef struct _TaskData TaskData; struct _OSStub { @@ -48,14 +48,6 @@ struct _OSStubClass RPMOSTreeOSSkeletonClass parent_class; }; -struct _TaskData { - gchar *refspec; - RPMOSTreeTransaction *transaction; - GVariantDict *options; - gchar *success_message; - gulong start_id; -}; - static void osstub_iface_init (RPMOSTreeOSIface *iface); static void osstub_load_internals (OSStub *self, @@ -91,22 +83,6 @@ task_result_invoke (GObject *source_object, g_dbus_method_invocation_return_value (invocation, result); } - -static void -task_data_free (TaskData *data) -{ - if (data->start_id > 0) - g_signal_handler_disconnect (data->transaction, data->start_id); - - g_clear_object (&data->transaction); - - g_free (data->refspec); - g_free (data->success_message); - - g_clear_pointer (&data->options, (GDestroyNotify) g_variant_dict_unref); -} - - static void sysroot_changed (Sysroot *sysroot, OstreeSysroot *ot_sysroot, @@ -171,57 +147,6 @@ osstub_class_init (OSStubClass *klass) /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -change_upgrader_refspec (OstreeSysroot *sysroot, - OstreeSysrootUpgrader *upgrader, - const gchar *refspec, - GCancellable *cancellable, - gchar **out_old_refspec, - gchar **out_new_refspec, - GError **error) -{ - gboolean ret = FALSE; - - g_autofree gchar *old_refspec = NULL; - g_autofree gchar *new_refspec = NULL; - g_autoptr(GKeyFile) new_origin = NULL; - GKeyFile *old_origin = NULL; /* owned by deployment */ - - old_origin = ostree_sysroot_upgrader_get_origin (upgrader); - old_refspec = g_key_file_get_string (old_origin, "origin", - "refspec", NULL); - - if (!refspec_parse_partial (refspec, - old_refspec, - &new_refspec, - error)) - goto out; - - if (strcmp (old_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; - - if (out_new_refspec != NULL) - *out_new_refspec = g_steal_pointer (&new_refspec); - - if (out_old_refspec != NULL) - *out_old_refspec = g_steal_pointer (&old_refspec); - - ret = TRUE; - -out: - return ret; -} - static void set_diff_task_result (GTask *task, GVariant *value, @@ -422,479 +347,8 @@ out: set_diff_task_result (task, value, error); } -static void -osstub_pull_dir_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - RPMOSTreeOS *interface = source_object; - TaskData *data = task_data; - OstreeSysroot *sysroot; - - glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL; - glnx_unref_object OstreeAsyncProgress *progress = NULL; - glnx_unref_object OstreeRepo *repo = NULL; - g_autofree gchar *origin_description = NULL; - - const char *name; - - gboolean changed = FALSE; - GError *local_error = NULL; - - /* libostree iterates and calls quit on main loop - * so we need to run in our own context. */ - GMainContext *m_context = g_main_context_new (); - g_main_context_push_thread_default (m_context); - - name = rpmostree_os_get_name (interface); - sysroot = transaction_get_sysroot (data->transaction); - upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, name, - cancellable, &local_error); - if (upgrader == NULL) - goto out; - - if (data->refspec != NULL) - { - if (!change_upgrader_refspec (sysroot, upgrader, data->refspec, cancellable, - NULL, NULL, &local_error)) - goto out; - } - - origin_description = ostree_sysroot_upgrader_get_origin_description (upgrader); - if (origin_description != NULL) - transaction_emit_message_printf (data->transaction, - "Updating from: %s", - origin_description); - - if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, &local_error)) - goto out; - - progress = ostree_async_progress_new (); - transaction_connect_download_progress (data->transaction, progress); - transaction_connect_signature_progress (data->transaction, repo); - if (!ostree_sysroot_upgrader_pull_one_dir (upgrader, "/usr/share/rpm", - 0, 0, progress, &changed, - cancellable, &local_error)) - goto out; - - rpmostree_transaction_emit_progress_end (data->transaction); - - if (!changed) - data->success_message = g_strdup ("No upgrade available."); - -out: - /* Clean up context */ - g_main_context_pop_thread_default (m_context); - g_main_context_unref (m_context); - - if (local_error != NULL) - g_task_return_error (task, local_error); - else - g_task_return_boolean (task, TRUE); -} - -static void -osstub_upgrade_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - RPMOSTreeOS *interface = source_object; - TaskData *data = task_data; - OstreeSysroot *sysroot; - - glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL; - glnx_unref_object OstreeRepo *repo = NULL; - glnx_unref_object OstreeAsyncProgress *progress = NULL; - - g_autofree gchar *new_refspec = NULL; - g_autofree gchar *old_refspec = NULL; - g_autofree gchar *origin_description = NULL; - - const char *name; - GError *local_error = NULL; - - OstreeSysrootUpgraderPullFlags upgrader_pull_flags = 0; - - gboolean opt_allow_downgrade = FALSE; - gboolean skip_purge = FALSE; - gboolean changed = FALSE; - - /* libostree iterates and calls quit on main loop - * so we need to run in our own context. */ - GMainContext *m_context = g_main_context_new (); - g_main_context_push_thread_default (m_context); - - /* XXX Fail if option type is wrong? */ - g_variant_dict_lookup (data->options, - "allow-downgrade", "b", - &opt_allow_downgrade); - - name = rpmostree_os_get_name (interface); - sysroot = transaction_get_sysroot (data->transaction); - - upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, name, - cancellable, &local_error); - if (upgrader == NULL) - goto out; - - if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, &local_error)) - goto out; - - if (data->refspec != NULL) - { - if (!change_upgrader_refspec (sysroot, upgrader, data->refspec, cancellable, - &old_refspec, &new_refspec, &local_error)) - goto out; - - g_variant_dict_lookup (data->options, - "skip-purge", "b", - &skip_purge); - } - - if (opt_allow_downgrade || new_refspec) - upgrader_pull_flags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER; - - origin_description = ostree_sysroot_upgrader_get_origin_description (upgrader); - if (origin_description != NULL) - transaction_emit_message_printf (data->transaction, - "Updating from: %s", - origin_description); - - progress = ostree_async_progress_new (); - transaction_connect_download_progress (data->transaction, progress); - transaction_connect_signature_progress (data->transaction, repo); - - if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgrader_pull_flags, - progress, &changed, - cancellable, &local_error)) - goto out; - - rpmostree_transaction_emit_progress_end (data->transaction); - - if (changed) - { - if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, &local_error)) - goto out; - - if (!skip_purge && old_refspec != NULL) - { - g_autofree gchar *remote = NULL; - g_autofree gchar *ref = NULL; - - if (!ostree_parse_refspec (old_refspec, &remote, - &ref, &local_error)) - goto out; - - if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, &local_error)) - goto out; - - ostree_repo_transaction_set_ref (repo, remote, ref, NULL); - - transaction_emit_message_printf (data->transaction, - "Deleting ref '%s'", - old_refspec); - - if (!ostree_repo_commit_transaction (repo, NULL, cancellable, &local_error)) - goto out; - } - } - else - { - data->success_message = g_strdup ("No upgrade available."); - } - -out: - /* Clean up context */ - g_main_context_pop_thread_default (m_context); - g_main_context_unref (m_context); - - if (local_error != NULL) - g_task_return_error (task, local_error); - else - g_task_return_boolean (task, TRUE); -} - -static void -osstub_rollback_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - RPMOSTreeOS *interface = source_object; - TaskData *data = task_data; - OstreeSysroot *sysroot; - const char *name; - const char *csum; - g_autoptr(GPtrArray) deployments = NULL; - g_autoptr(GPtrArray) new_deployments = NULL; - GError *error = NULL; - - gint rollback_index; - guint i; - gint deployserial; - - name = rpmostree_os_get_name (interface); - sysroot = transaction_get_sysroot (data->transaction); - - rollback_index = rollback_deployment_index (name, sysroot, &error); - if (rollback_index < 0) - goto out; - - deployments = ostree_sysroot_get_deployments (sysroot); - new_deployments = g_ptr_array_new_with_free_func (g_object_unref); - - /* build out the reordered array */ - g_ptr_array_add (new_deployments, g_object_ref (deployments->pdata[rollback_index])); - for (i = 0; i < deployments->len; i++) - { - if (i == rollback_index) - continue; - - g_ptr_array_add (new_deployments, g_object_ref (deployments->pdata[i])); - } - - csum = ostree_deployment_get_csum (deployments->pdata[rollback_index]); - deployserial = ostree_deployment_get_deployserial (deployments->pdata[rollback_index]); - transaction_emit_message_printf (data->transaction, - "Moving '%s.%d' to be first deployment", - csum, deployserial); - - /* if default changed write it */ - if (deployments->pdata[0] != new_deployments->pdata[0]) - ostree_sysroot_write_deployments (sysroot, - new_deployments, - cancellable, - &error); - -out: - if (error == NULL) - g_task_return_boolean (task, TRUE); - else - g_task_return_error (task, error); -} - -static void -osstub_clear_rollback_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - RPMOSTreeOS *interface = source_object; - TaskData *data = task_data; - OstreeSysroot *sysroot; - const char *name; - - g_autoptr(GPtrArray) deployments = NULL; - g_autoptr(GPtrArray) new_deployments = NULL; - - GError *error = NULL; - - gint rollback_index; - - name = rpmostree_os_get_name (interface); - sysroot = transaction_get_sysroot (data->transaction); - - rollback_index = rollback_deployment_index (name, sysroot, &error); - if (rollback_index < 0) - goto out; - - deployments = ostree_sysroot_get_deployments (sysroot); - - if (deployments->pdata[rollback_index] == ostree_sysroot_get_booted_deployment (sysroot)) - { - g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "Cannot undeploy currently booted deployment %i", - rollback_index); - goto out; - } - - g_ptr_array_remove_index (deployments, rollback_index); - - ostree_sysroot_write_deployments (sysroot, - deployments, - cancellable, - &error); - -out: - if (error == NULL) - g_task_return_boolean (task, TRUE); - else - g_task_return_error (task, error); -} - /* ---------------------------------------------------------------------------------------------------- */ -static void -respond_to_transaction_invocation (GDBusMethodInvocation *invocation, - RPMOSTreeTransaction *transaction, - GError *error) -{ - if (error == NULL) - { - const char *object_path; - - g_return_if_fail (transaction != NULL); - - object_path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON(transaction)); - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(o)", object_path)); - } - else - { - g_dbus_method_invocation_take_error (invocation, error); - } -} - -static void -osstub_transaction_done_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - TaskData *data; - GError *local_error = NULL; - - data = g_task_get_task_data (G_TASK (result)); - - if (g_task_propagate_boolean (G_TASK (result), &local_error)) - { - OstreeSysroot *sysroot; - - sysroot = transaction_get_sysroot (data->transaction); - sysroot_emit_update (sysroot_get (), sysroot); - transaction_done (data->transaction, TRUE, data->success_message); - } - else - { - transaction_done (data->transaction, FALSE, local_error->message); - g_clear_error (&local_error); - } -} - -static void -osstub_start_pull_dir (RPMOSTreeTransaction *transaction, - GTask *task) -{ - TaskData *task_data = g_task_get_task_data (task); - - g_task_run_in_thread (task, osstub_pull_dir_thread); - - /* This handler should only run once. */ - g_signal_handler_disconnect (transaction, task_data->start_id); - task_data->start_id = 0; -} - -static gboolean -osstub_handle_pull_dir (RPMOSTreeOS *interface, - GDBusMethodInvocation *invocation, - gchar *refspec) -{ - OSStub *os = OSSTUB (interface); - g_autoptr(GTask) task = NULL; - glnx_unref_object RPMOSTreeTransaction *transaction = NULL; - glnx_unref_object OstreeSysroot *sysroot = NULL; - glnx_unref_object GCancellable *cancellable = NULL; - TaskData *data; - GError *local_error = NULL; - - cancellable = g_cancellable_new (); - - if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), - cancellable, &sysroot, NULL, &local_error)) - goto out; - - transaction = transaction_monitor_new_transaction (os->transaction_monitor, - invocation, - sysroot, - cancellable, - &local_error); - - if (transaction == NULL) - goto out; - - data = g_slice_new0 (TaskData); - data->refspec = refspec; - data->transaction = g_object_ref (transaction); - data->options = NULL; - - task = g_task_new (interface, cancellable, osstub_transaction_done_cb, NULL); - g_task_set_check_cancellable (task, FALSE); - g_task_set_source_tag (task, osstub_handle_pull_dir); - g_task_set_task_data (task, data, (GDestroyNotify) task_data_free); - - data->start_id = g_signal_connect_data (transaction, "start", - G_CALLBACK (osstub_start_pull_dir), - g_object_ref (task), - (GClosureNotify) g_object_unref, 0); - -out: - respond_to_transaction_invocation (invocation, transaction, local_error); - return TRUE; -} - -static void -osstub_start_deploy (RPMOSTreeTransaction *transaction, - GTask *task) -{ - TaskData *task_data = g_task_get_task_data (task); - - g_task_run_in_thread (task, osstub_upgrade_thread); - - /* This handler should only run once. */ - g_signal_handler_disconnect (transaction, task_data->start_id); - task_data->start_id = 0; -} - -static gboolean -osstub_handle_deploy (RPMOSTreeOS *interface, - GDBusMethodInvocation *invocation, - GVariant *arg_options, - gchar *refspec) -{ - OSStub *os = OSSTUB (interface); - glnx_unref_object GCancellable *cancellable = NULL; - glnx_unref_object OstreeSysroot *sysroot = NULL; - glnx_unref_object RPMOSTreeTransaction *transaction = NULL; - g_autoptr(GTask) task = NULL; - TaskData *data; - GError *local_error = NULL; - - cancellable = g_cancellable_new (); - - if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), - cancellable, &sysroot, NULL, &local_error)) - goto out; - - transaction = transaction_monitor_new_transaction (os->transaction_monitor, - invocation, - sysroot, - cancellable, - &local_error); - - if (transaction == NULL) - goto out; - - data = g_slice_new0 (TaskData); - data->refspec = refspec; - data->transaction = g_object_ref (transaction); - data->options = g_variant_dict_new (arg_options); - - task = g_task_new (interface, cancellable, osstub_transaction_done_cb, NULL); - g_task_set_check_cancellable (task, FALSE); - g_task_set_source_tag (task, osstub_handle_deploy); - g_task_set_task_data (task, data, (GDestroyNotify) task_data_free); - - data->start_id = g_signal_connect_data (transaction, "start", - G_CALLBACK (osstub_start_deploy), - g_object_ref (task), - (GClosureNotify) g_object_unref, 0); - -out: - respond_to_transaction_invocation (invocation, transaction, local_error); - return TRUE; -} - static gboolean handle_get_deployments_rpm_diff (RPMOSTreeOS *interface, GDBusMethodInvocation *invocation, @@ -947,7 +401,42 @@ static gboolean osstub_handle_download_update_rpm_diff (RPMOSTreeOS *interface, GDBusMethodInvocation *invocation) { - return osstub_handle_pull_dir (interface, invocation, NULL); + OSStub *self = OSSTUB (interface); + glnx_unref_object Transaction *transaction = NULL; + glnx_unref_object OstreeSysroot *sysroot = NULL; + glnx_unref_object GCancellable *cancellable = NULL; + const char *object_path; + const char *osname; + GError *local_error = NULL; + + cancellable = g_cancellable_new (); + + if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), + cancellable, &sysroot, NULL, &local_error)) + goto out; + + osname = rpmostree_os_get_name (interface); + + transaction = transaction_new_package_diff (invocation, + sysroot, + osname, + NULL, /* refspec */ + cancellable, + &local_error); + + if (transaction == NULL) + goto out; + + transaction_monitor_add (self->transaction_monitor, transaction); + + object_path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (transaction)); + rpmostree_os_complete_download_update_rpm_diff (interface, invocation, object_path); + +out: + if (local_error != NULL) + g_dbus_method_invocation_take_error (invocation, local_error); + + return TRUE; } static gboolean @@ -955,128 +444,137 @@ osstub_handle_upgrade (RPMOSTreeOS *interface, GDBusMethodInvocation *invocation, GVariant *arg_options) { - return osstub_handle_deploy (interface, invocation, arg_options, NULL); -} + OSStub *self = OSSTUB (interface); + glnx_unref_object Transaction *transaction = NULL; + glnx_unref_object OstreeSysroot *sysroot = NULL; + glnx_unref_object GCancellable *cancellable = NULL; + GVariantDict options_dict; + gboolean opt_allow_downgrade = FALSE; + const char *object_path; + const char *osname; + GError *local_error = NULL; -static void -osstub_start_rollback (RPMOSTreeTransaction *transaction, - GTask *task) -{ - TaskData *task_data = g_task_get_task_data (task); + cancellable = g_cancellable_new (); - g_task_run_in_thread (task, osstub_rollback_thread); + if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), + cancellable, &sysroot, NULL, &local_error)) + goto out; - /* This handler should only run once. */ - g_signal_handler_disconnect (transaction, task_data->start_id); - task_data->start_id = 0; + osname = rpmostree_os_get_name (interface); + + /* XXX Fail if option type is wrong? */ + + g_variant_dict_init (&options_dict, arg_options); + + g_variant_dict_lookup (&options_dict, + "allow-downgrade", "b", + &opt_allow_downgrade); + + g_variant_dict_clear (&options_dict); + + transaction = transaction_new_upgrade (invocation, + sysroot, + osname, + NULL, /* refspec */ + opt_allow_downgrade, + FALSE, /* skip-purge */ + cancellable, + &local_error); + + if (transaction == NULL) + goto out; + + transaction_monitor_add (self->transaction_monitor, transaction); + + object_path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (transaction)); + rpmostree_os_complete_upgrade (interface, invocation, object_path); + +out: + if (local_error != NULL) + g_dbus_method_invocation_take_error (invocation, local_error); + + return TRUE; } static gboolean osstub_handle_rollback (RPMOSTreeOS *interface, GDBusMethodInvocation *invocation) { - OSStub *os = OSSTUB (interface); - glnx_unref_object RPMOSTreeTransaction *transaction = NULL; + OSStub *self = OSSTUB (interface); + glnx_unref_object Transaction *transaction = NULL; glnx_unref_object OstreeSysroot *sysroot = NULL; glnx_unref_object GCancellable *cancellable = NULL; + const char *object_path; + const char *osname; GError *local_error = NULL; - g_autoptr(GTask) task = NULL; - TaskData *data; - cancellable = g_cancellable_new (); if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), cancellable, &sysroot, NULL, &local_error)) goto out; - transaction = transaction_monitor_new_transaction (os->transaction_monitor, - invocation, - sysroot, - cancellable, - &local_error); + osname = rpmostree_os_get_name (interface); + + transaction = transaction_new_rollback (invocation, + sysroot, + osname, + cancellable, + &local_error); if (transaction == NULL) goto out; - data = g_slice_new0 (TaskData); - data->refspec = NULL; - data->transaction = g_object_ref (transaction); - data->options = NULL; + transaction_monitor_add (self->transaction_monitor, transaction); - task = g_task_new (interface, cancellable, osstub_transaction_done_cb, NULL); - g_task_set_check_cancellable (task, FALSE); - g_task_set_source_tag (task, osstub_handle_rollback); - g_task_set_task_data (task, data, (GDestroyNotify) task_data_free); - - data->start_id = g_signal_connect_data (transaction, "start", - G_CALLBACK (osstub_start_rollback), - g_object_ref (task), - (GClosureNotify) g_object_unref, 0); + object_path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (transaction)); + rpmostree_os_complete_rollback (interface, invocation, object_path); out: - respond_to_transaction_invocation (invocation, transaction, local_error); + if (local_error != NULL) + g_dbus_method_invocation_take_error (invocation, local_error); + return TRUE; } -static void -osstub_start_clear_rollback_target (RPMOSTreeTransaction *transaction, - GTask *task) -{ - TaskData *task_data = g_task_get_task_data (task); - - g_task_run_in_thread (task, osstub_clear_rollback_thread); - - /* This handler should only run once. */ - g_signal_handler_disconnect (transaction, task_data->start_id); - task_data->start_id = 0; -} - static gboolean osstub_handle_clear_rollback_target (RPMOSTreeOS *interface, GDBusMethodInvocation *invocation) { - OSStub *os = OSSTUB (interface); - glnx_unref_object RPMOSTreeTransaction *transaction = NULL; + OSStub *self = OSSTUB (interface); + glnx_unref_object Transaction *transaction = NULL; glnx_unref_object OstreeSysroot *sysroot = NULL; glnx_unref_object GCancellable *cancellable = NULL; + const char *object_path; + const char *osname; GError *local_error = NULL; - g_autoptr(GTask) task = NULL; - TaskData *data; - cancellable = g_cancellable_new (); if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), cancellable, &sysroot, NULL, &local_error)) goto out; - transaction = transaction_monitor_new_transaction (os->transaction_monitor, - invocation, - sysroot, - cancellable, - &local_error); + osname = rpmostree_os_get_name (interface); + + transaction = transaction_new_clear_rollback (invocation, + sysroot, + osname, + cancellable, + &local_error); if (transaction == NULL) goto out; - data = g_slice_new0 (TaskData); - data->refspec = NULL; - data->transaction = g_object_ref (transaction); - data->options = NULL; + transaction_monitor_add (self->transaction_monitor, transaction); - task = g_task_new (interface, cancellable, osstub_transaction_done_cb, NULL); - g_task_set_check_cancellable (task, FALSE); - g_task_set_source_tag (task, osstub_handle_clear_rollback_target); - g_task_set_task_data (task, data, (GDestroyNotify) task_data_free); - - data->start_id = g_signal_connect_data (transaction, "start", - G_CALLBACK (osstub_start_clear_rollback_target), - g_object_ref (task), - (GClosureNotify) g_object_unref, 0); + object_path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (transaction)); + rpmostree_os_complete_clear_rollback_target (interface, invocation, object_path); out: - respond_to_transaction_invocation (invocation, transaction, local_error); + if (local_error != NULL) + g_dbus_method_invocation_take_error (invocation, local_error); + return TRUE; } @@ -1088,8 +586,56 @@ osstub_handle_rebase (RPMOSTreeOS *interface, const char * const *arg_packages) { /* TODO: Totally ignoring arg_packages for now */ - return osstub_handle_deploy (interface, invocation, - arg_options, g_strdup (arg_refspec)); + OSStub *self = OSSTUB (interface); + glnx_unref_object Transaction *transaction = NULL; + glnx_unref_object OstreeSysroot *sysroot = NULL; + glnx_unref_object GCancellable *cancellable = NULL; + GVariantDict options_dict; + gboolean opt_skip_purge = FALSE; + const char *object_path; + const char *osname; + GError *local_error = NULL; + + cancellable = g_cancellable_new (); + + if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), + cancellable, &sysroot, NULL, &local_error)) + goto out; + + osname = rpmostree_os_get_name (interface); + + /* XXX Fail if option type is wrong? */ + + g_variant_dict_init (&options_dict, arg_options); + + g_variant_dict_lookup (&options_dict, + "skip-purge", "b", + &opt_skip_purge); + + g_variant_dict_clear (&options_dict); + + transaction = transaction_new_upgrade (invocation, + sysroot, + osname, + arg_refspec, + FALSE, /* allow-downgrade */ + opt_skip_purge, + cancellable, + &local_error); + + if (transaction == NULL) + goto out; + + transaction_monitor_add (self->transaction_monitor, transaction); + + object_path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (transaction)); + rpmostree_os_complete_rebase (interface, invocation, object_path); + +out: + if (local_error != NULL) + g_dbus_method_invocation_take_error (invocation, local_error); + + return TRUE; } static gboolean @@ -1121,7 +667,42 @@ osstub_handle_download_rebase_rpm_diff (RPMOSTreeOS *interface, const char * const *arg_packages) { /* TODO: Totally ignoring arg_packages for now */ - return osstub_handle_pull_dir (interface, invocation, g_strdup (arg_refspec)); + OSStub *self = OSSTUB (interface); + glnx_unref_object Transaction *transaction = NULL; + glnx_unref_object OstreeSysroot *sysroot = NULL; + glnx_unref_object GCancellable *cancellable = NULL; + const char *object_path; + const char *osname; + GError *local_error = NULL; + + cancellable = g_cancellable_new (); + + if (!utils_load_sysroot_and_repo (sysroot_get_sysroot_path (sysroot_get ()), + cancellable, &sysroot, NULL, &local_error)) + goto out; + + osname = rpmostree_os_get_name (interface); + + transaction = transaction_new_package_diff (invocation, + sysroot, + osname, + arg_refspec, + cancellable, + &local_error); + + if (transaction == NULL) + goto out; + + transaction_monitor_add (self->transaction_monitor, transaction); + + object_path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (transaction)); + rpmostree_os_complete_download_rebase_rpm_diff (interface, invocation, object_path); + +out: + if (local_error != NULL) + g_dbus_method_invocation_take_error (invocation, local_error); + + return TRUE; } static void diff --git a/src/daemon/sysroot.c b/src/daemon/sysroot.c index d4f81ce8..190bbc8f 100644 --- a/src/daemon/sysroot.c +++ b/src/daemon/sysroot.c @@ -121,7 +121,7 @@ sysroot_stdout_ready_cb (GPollableInputStream *pollable_stream, StdoutClosure *closure) { glnx_unref_object Sysroot *sysroot = NULL; - glnx_unref_object RPMOSTreeTransaction *transaction = NULL; + glnx_unref_object Transaction *transaction = NULL; GMemoryInputStream *memory_stream; GBufferedInputStream *buffered_stream; char buffer[1024]; @@ -199,7 +199,7 @@ read_another_line: * dump it to the non-redirected standard output stream. */ if (transaction != NULL) { - rpmostree_transaction_emit_message (transaction, line); + rpmostree_transaction_emit_message (RPMOSTREE_TRANSACTION (transaction), line); } else { diff --git a/src/daemon/transaction-monitor.c b/src/daemon/transaction-monitor.c index 8bf379b4..c16d0990 100644 --- a/src/daemon/transaction-monitor.c +++ b/src/daemon/transaction-monitor.c @@ -181,25 +181,14 @@ transaction_monitor_new (void) return g_object_new (TYPE_TRANSACTION_MONITOR, NULL); } -RPMOSTreeTransaction * -transaction_monitor_new_transaction (TransactionMonitor *monitor, - GDBusMethodInvocation *invocation, - OstreeSysroot *sysroot, - GCancellable *cancellable, - GError **error) +void +transaction_monitor_add (TransactionMonitor *monitor, + Transaction *transaction) { - RPMOSTreeTransaction *transaction; - const char *object_path; g_autofree char *child_object_path = NULL; - /* sysroot is optional */ - g_return_val_if_fail (IS_TRANSACTION_MONITOR (monitor), NULL); - g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); - - transaction = transaction_new (invocation, sysroot, cancellable, error); - - if (transaction == NULL) - goto out; + g_return_if_fail (IS_TRANSACTION_MONITOR (monitor)); + g_return_if_fail (IS_TRANSACTION (transaction)); g_signal_connect_object (transaction, "notify::active", G_CALLBACK (transaction_monitor_notify_active_cb), @@ -217,22 +206,18 @@ transaction_monitor_new_transaction (TransactionMonitor *monitor, G_CALLBACK (transaction_monitor_owner_vanished_cb), monitor, 0); - object_path = g_dbus_method_invocation_get_object_path (invocation); - child_object_path = g_build_path ("/", object_path, "Transaction", NULL); + child_object_path = g_build_path ("/", BASE_DBUS_PATH, "Transaction", NULL); daemon_publish (daemon_get (), child_object_path, TRUE, transaction); g_queue_push_head (monitor->transactions, g_object_ref (transaction)); g_object_notify (G_OBJECT (monitor), "active-transaction"); - -out: - return transaction; } -RPMOSTreeTransaction * +Transaction * transaction_monitor_ref_active_transaction (TransactionMonitor *monitor) { - RPMOSTreeTransaction *transaction; + Transaction *transaction; g_return_val_if_fail (IS_TRANSACTION_MONITOR (monitor), NULL); @@ -243,7 +228,7 @@ transaction_monitor_ref_active_transaction (TransactionMonitor *monitor) { /* An "inactive" transaction is waiting to be Finish()'ed by its * client, but it doesn't block other transactions from starting. */ - if (rpmostree_transaction_get_active (transaction)) + if (rpmostree_transaction_get_active (RPMOSTREE_TRANSACTION (transaction))) g_object_ref (transaction); else transaction = NULL; diff --git a/src/daemon/transaction-monitor.h b/src/daemon/transaction-monitor.h index ed7b35ac..ac88af12 100644 --- a/src/daemon/transaction-monitor.h +++ b/src/daemon/transaction-monitor.h @@ -28,13 +28,7 @@ GType transaction_monitor_get_type (void) G_GNUC_CONST; TransactionMonitor * transaction_monitor_new (void); -RPMOSTreeTransaction * - transaction_monitor_new_transaction - (TransactionMonitor *monitor, - GDBusMethodInvocation *invocation, - OstreeSysroot *sysroot, - GCancellable *cancellable, - GError **error); -RPMOSTreeTransaction * - transaction_monitor_ref_active_transaction +void transaction_monitor_add (TransactionMonitor *monitor, + Transaction *transaction); +Transaction * transaction_monitor_ref_active_transaction (TransactionMonitor *monitor); diff --git a/src/daemon/transaction-types.c b/src/daemon/transaction-types.c new file mode 100644 index 00000000..1fe157bc --- /dev/null +++ b/src/daemon/transaction-types.c @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "ostree.h" + +#include + +#include "transaction-types.h" +#include "transaction.h" +#include "deployment-utils.h" +#include "utils.h" + +static gboolean +change_upgrader_refspec (OstreeSysroot *sysroot, + OstreeSysrootUpgrader *upgrader, + const gchar *refspec, + GCancellable *cancellable, + gchar **out_old_refspec, + gchar **out_new_refspec, + GError **error) +{ + gboolean ret = FALSE; + + g_autofree gchar *old_refspec = NULL; + g_autofree gchar *new_refspec = NULL; + g_autoptr(GKeyFile) new_origin = NULL; + GKeyFile *old_origin = NULL; /* owned by deployment */ + + old_origin = ostree_sysroot_upgrader_get_origin (upgrader); + old_refspec = g_key_file_get_string (old_origin, "origin", + "refspec", NULL); + + if (!refspec_parse_partial (refspec, + old_refspec, + &new_refspec, + error)) + goto out; + + if (strcmp (old_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; + + if (out_new_refspec != NULL) + *out_new_refspec = g_steal_pointer (&new_refspec); + + if (out_old_refspec != NULL) + *out_old_refspec = g_steal_pointer (&old_refspec); + + ret = TRUE; + +out: + return ret; +} + +/* ============================= Package Diff ============================= */ + +typedef struct { + Transaction parent; + char *osname; + char *refspec; +} PackageDiffTransaction; + +typedef TransactionClass PackageDiffTransactionClass; + +GType package_diff_transaction_get_type (void); + +G_DEFINE_TYPE (PackageDiffTransaction, + package_diff_transaction, + TYPE_TRANSACTION) + +static void +package_diff_transaction_finalize (GObject *object) +{ + PackageDiffTransaction *self; + + self = (PackageDiffTransaction *) object; + g_free (self->osname); + g_free (self->refspec); + + G_OBJECT_CLASS (package_diff_transaction_parent_class)->finalize (object); +} + +static gboolean +package_diff_transaction_execute (Transaction *transaction, + GCancellable *cancellable, + GError **error) +{ + PackageDiffTransaction *self; + OstreeSysroot *sysroot; + + glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL; + glnx_unref_object OstreeAsyncProgress *progress = NULL; + glnx_unref_object OstreeRepo *repo = NULL; + g_autofree gchar *origin_description = NULL; + + gboolean changed = FALSE; + gboolean ret = FALSE; + + /* libostree iterates and calls quit on main loop + * so we need to run in our own context. */ + GMainContext *m_context = g_main_context_new (); + g_main_context_push_thread_default (m_context); + + self = (PackageDiffTransaction *) transaction; + sysroot = transaction_get_sysroot (transaction); + upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, self->osname, + cancellable, error); + if (upgrader == NULL) + goto out; + + if (self->refspec != NULL) + { + if (!change_upgrader_refspec (sysroot, upgrader, + self->refspec, cancellable, + NULL, NULL, error)) + goto out; + } + + origin_description = ostree_sysroot_upgrader_get_origin_description (upgrader); + if (origin_description != NULL) + transaction_emit_message_printf (transaction, + "Updating from: %s", + origin_description); + + if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) + goto out; + + progress = ostree_async_progress_new (); + transaction_connect_download_progress (transaction, progress); + transaction_connect_signature_progress (transaction, repo); + if (!ostree_sysroot_upgrader_pull_one_dir (upgrader, "/usr/share/rpm", + 0, 0, progress, &changed, + cancellable, error)) + goto out; + + rpmostree_transaction_emit_progress_end (RPMOSTREE_TRANSACTION (transaction)); + + if (!changed) + transaction_emit_message_printf (transaction, "No upgrade available."); + + ret = TRUE; + +out: + /* Clean up context */ + g_main_context_pop_thread_default (m_context); + g_main_context_unref (m_context); + + return ret; +} + +static void +package_diff_transaction_class_init (PackageDiffTransactionClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = package_diff_transaction_finalize; + + class->execute = package_diff_transaction_execute; +} + +static void +package_diff_transaction_init (PackageDiffTransaction *self) +{ +} + +Transaction * +transaction_new_package_diff (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + const char *refspec, + GCancellable *cancellable, + GError **error) +{ + PackageDiffTransaction *self; + + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + g_return_val_if_fail (OSTREE_IS_SYSROOT (sysroot), NULL); + g_return_val_if_fail (osname != NULL, NULL); + + self = g_initable_new (package_diff_transaction_get_type (), + cancellable, error, + "invocation", invocation, + "sysroot", sysroot, + NULL); + + if (self != NULL) + { + self->osname = g_strdup (osname); + self->refspec = g_strdup (refspec); + } + + return (Transaction *) self; +} + +/* =============================== Rollback =============================== */ + +typedef struct { + Transaction parent; + char *osname; +} RollbackTransaction; + +typedef TransactionClass RollbackTransactionClass; + +GType rollback_transaction_get_type (void); + +G_DEFINE_TYPE (RollbackTransaction, + rollback_transaction, + TYPE_TRANSACTION) + +static void +rollback_transaction_finalize (GObject *object) +{ + RollbackTransaction *self; + + self = (RollbackTransaction *) object; + g_free (self->osname); + + G_OBJECT_CLASS (rollback_transaction_parent_class)->finalize (object); +} + +static gboolean +rollback_transaction_execute (Transaction *transaction, + GCancellable *cancellable, + GError **error) +{ + RollbackTransaction *self; + OstreeSysroot *sysroot; + OstreeDeployment *deployment; + g_autoptr(GPtrArray) old_deployments = NULL; + g_autoptr(GPtrArray) new_deployments = NULL; + + gint rollback_index; + guint i; + gboolean ret = FALSE; + + self = (RollbackTransaction *) transaction; + + sysroot = transaction_get_sysroot (transaction); + + rollback_index = rollback_deployment_index (self->osname, sysroot, error); + if (rollback_index < 0) + goto out; + + old_deployments = ostree_sysroot_get_deployments (sysroot); + new_deployments = g_ptr_array_new_with_free_func (g_object_unref); + + /* build out the reordered array */ + + deployment = old_deployments->pdata[rollback_index]; + g_ptr_array_add (new_deployments, g_object_ref (deployment)); + + transaction_emit_message_printf (transaction, + "Moving '%s.%d' to be first deployment", + ostree_deployment_get_csum (deployment), + ostree_deployment_get_deployserial (deployment)); + + for (i = 0; i < old_deployments->len; i++) + { + if (i == rollback_index) + continue; + + deployment = old_deployments->pdata[i]; + g_ptr_array_add (new_deployments, g_object_ref (deployment)); + } + + /* if default changed write it */ + if (old_deployments->pdata[0] != new_deployments->pdata[0]) + ostree_sysroot_write_deployments (sysroot, + new_deployments, + cancellable, + error); + + ret = TRUE; + +out: + return ret; +} + +static void +rollback_transaction_class_init (RollbackTransactionClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = rollback_transaction_finalize; + + class->execute = rollback_transaction_execute; +} + +static void +rollback_transaction_init (RollbackTransaction *self) +{ +} + +Transaction * +transaction_new_rollback (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + GCancellable *cancellable, + GError **error) +{ + RollbackTransaction *self; + + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + g_return_val_if_fail (OSTREE_IS_SYSROOT (sysroot), NULL); + g_return_val_if_fail (osname != NULL, NULL); + + self = g_initable_new (rollback_transaction_get_type (), + cancellable, error, + "invocation", invocation, + "sysroot", sysroot, + NULL); + + if (self != NULL) + { + self->osname = g_strdup (osname); + } + + return (Transaction *) self; +} + +/* ============================ Clear Rollback ============================ */ + +typedef struct { + Transaction parent; + char *osname; +} ClearRollbackTransaction; + +typedef TransactionClass ClearRollbackTransactionClass; + +GType clear_rollback_transaction_get_type (void); + +G_DEFINE_TYPE (ClearRollbackTransaction, + clear_rollback_transaction, + TYPE_TRANSACTION) + +static void +clear_rollback_transaction_finalize (GObject *object) +{ + ClearRollbackTransaction *self; + + self = (ClearRollbackTransaction *) object; + g_free (self->osname); + + G_OBJECT_CLASS (clear_rollback_transaction_parent_class)->finalize (object); +} + +static gboolean +clear_rollback_transaction_execute (Transaction *transaction, + GCancellable *cancellable, + GError **error) +{ + ClearRollbackTransaction *self; + OstreeSysroot *sysroot; + + g_autoptr(GPtrArray) deployments = NULL; + gint rollback_index; + gboolean ret = FALSE; + + self = (ClearRollbackTransaction *) transaction; + + sysroot = transaction_get_sysroot (transaction); + + rollback_index = rollback_deployment_index (self->osname, sysroot, error); + if (rollback_index < 0) + goto out; + + deployments = ostree_sysroot_get_deployments (sysroot); + + if (deployments->pdata[rollback_index] == ostree_sysroot_get_booted_deployment (sysroot)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Cannot undeploy currently booted deployment %i", + rollback_index); + goto out; + } + + g_ptr_array_remove_index (deployments, rollback_index); + + ostree_sysroot_write_deployments (sysroot, + deployments, + cancellable, + error); + + ret = TRUE; + +out: + return ret; +} + +static void +clear_rollback_transaction_class_init (ClearRollbackTransactionClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = clear_rollback_transaction_finalize; + + class->execute = clear_rollback_transaction_execute; +} + +static void +clear_rollback_transaction_init (ClearRollbackTransaction *self) +{ +} + +Transaction * +transaction_new_clear_rollback (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + GCancellable *cancellable, + GError **error) +{ + ClearRollbackTransaction *self; + + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + g_return_val_if_fail (OSTREE_IS_SYSROOT (sysroot), NULL); + g_return_val_if_fail (osname != NULL, NULL); + + self = g_initable_new (clear_rollback_transaction_get_type (), + cancellable, error, + "invocation", invocation, + "sysroot", sysroot, + NULL); + + if (self != NULL) + { + self->osname = g_strdup (osname); + } + + return (Transaction *) self; +} + +/* ================================ Upgrade ================================ */ + +typedef struct { + Transaction parent; + char *osname; + char *refspec; + gboolean allow_downgrade; + gboolean skip_purge; +} UpgradeTransaction; + +typedef TransactionClass UpgradeTransactionClass; + +GType upgrade_transaction_get_type (void); + +G_DEFINE_TYPE (UpgradeTransaction, + upgrade_transaction, + TYPE_TRANSACTION) + +static void +upgrade_transaction_finalize (GObject *object) +{ + UpgradeTransaction *self; + + self = (UpgradeTransaction *) object; + g_free (self->osname); + g_free (self->refspec); + + G_OBJECT_CLASS (upgrade_transaction_parent_class)->finalize (object); +} + +static gboolean +upgrade_transaction_execute (Transaction *transaction, + GCancellable *cancellable, + GError **error) +{ + UpgradeTransaction *self; + OstreeSysroot *sysroot; + + glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL; + glnx_unref_object OstreeRepo *repo = NULL; + glnx_unref_object OstreeAsyncProgress *progress = NULL; + + g_autofree gchar *new_refspec = NULL; + g_autofree gchar *old_refspec = NULL; + g_autofree gchar *origin_description = NULL; + + OstreeSysrootUpgraderPullFlags upgrader_pull_flags = 0; + + gboolean changed = FALSE; + gboolean ret = FALSE; + + /* libostree iterates and calls quit on main loop + * so we need to run in our own context. */ + GMainContext *m_context = g_main_context_new (); + g_main_context_push_thread_default (m_context); + + self = (UpgradeTransaction *) transaction; + + sysroot = transaction_get_sysroot (transaction); + + upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, self->osname, + cancellable, error); + if (upgrader == NULL) + goto out; + + if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) + goto out; + + if (self->refspec != NULL) + { + if (!change_upgrader_refspec (sysroot, upgrader, + self->refspec, cancellable, + &old_refspec, &new_refspec, error)) + goto out; + } + + if (self->allow_downgrade || new_refspec) + upgrader_pull_flags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER; + + origin_description = ostree_sysroot_upgrader_get_origin_description (upgrader); + if (origin_description != NULL) + transaction_emit_message_printf (transaction, + "Updating from: %s", + origin_description); + + progress = ostree_async_progress_new (); + transaction_connect_download_progress (transaction, progress); + transaction_connect_signature_progress (transaction, repo); + + if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgrader_pull_flags, + progress, &changed, + cancellable, error)) + goto out; + + rpmostree_transaction_emit_progress_end (RPMOSTREE_TRANSACTION (transaction)); + + if (changed) + { + if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error)) + goto out; + + if (!self->skip_purge && old_refspec != NULL) + { + g_autofree gchar *remote = NULL; + g_autofree gchar *ref = NULL; + + if (!ostree_parse_refspec (old_refspec, &remote, &ref, error)) + goto out; + + if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) + goto out; + + ostree_repo_transaction_set_ref (repo, remote, ref, NULL); + + transaction_emit_message_printf (transaction, + "Deleting ref '%s'", + old_refspec); + + if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) + goto out; + } + } + else + { + transaction_emit_message_printf (transaction, "No upgrade available."); + } + + ret = TRUE; + +out: + /* Clean up context */ + g_main_context_pop_thread_default (m_context); + g_main_context_unref (m_context); + + return ret; +} + +static void +upgrade_transaction_class_init (UpgradeTransactionClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = upgrade_transaction_finalize; + + class->execute = upgrade_transaction_execute; +} + +static void +upgrade_transaction_init (UpgradeTransaction *self) +{ +} + +Transaction * +transaction_new_upgrade (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + const char *refspec, + gboolean allow_downgrade, + gboolean skip_purge, + GCancellable *cancellable, + GError **error) +{ + UpgradeTransaction *self; + + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + g_return_val_if_fail (OSTREE_IS_SYSROOT (sysroot), NULL); + g_return_val_if_fail (osname != NULL, NULL); + + self = g_initable_new (upgrade_transaction_get_type (), + cancellable, error, + "invocation", invocation, + "sysroot", sysroot, + NULL); + + if (self != NULL) + { + self->osname = g_strdup (osname); + self->refspec = g_strdup (refspec); + self->allow_downgrade = allow_downgrade; + self->skip_purge = skip_purge; + } + + return (Transaction *) self; +} + diff --git a/src/daemon/transaction-types.h b/src/daemon/transaction-types.h new file mode 100644 index 00000000..8223b74b --- /dev/null +++ b/src/daemon/transaction-types.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "types.h" + +Transaction * transaction_new_package_diff (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + const char *refspec, + GCancellable *cancellable, + GError **error); + +Transaction * transaction_new_rollback (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + GCancellable *cancellable, + GError **error); + +Transaction * transaction_new_clear_rollback (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + GCancellable *cancellable, + GError **error); + +Transaction * transaction_new_upgrade (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + const char *refspec, + gboolean allow_downgrade, + gboolean skip_purge, + GCancellable *cancellable, + GError **error); diff --git a/src/daemon/transaction.c b/src/daemon/transaction.c index e9b03fbb..c96bd111 100644 --- a/src/daemon/transaction.c +++ b/src/daemon/transaction.c @@ -23,6 +23,7 @@ #include "transaction.h" #include "errors.h" +#include "sysroot.h" struct _TransactionPrivate { GDBusMethodInvocation *invocation; @@ -45,7 +46,6 @@ enum { }; enum { - START, CANCELLED, FINISHED, OWNER_VANISHED, @@ -61,12 +61,12 @@ static void transaction_dbus_iface_init (RPMOSTreeTransactionIface *iface); * on the 2nd instance and valgrind was going crazy with invalid reads * and writes. So I'm falling back to the allegedly deprecated method * and deferring further investigation. */ -G_DEFINE_TYPE_WITH_CODE (Transaction, transaction, - RPMOSTREE_TYPE_TRANSACTION_SKELETON, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - transaction_initable_iface_init) - G_IMPLEMENT_INTERFACE (RPMOSTREE_TYPE_TRANSACTION, - transaction_dbus_iface_init)) +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (Transaction, transaction, + RPMOSTREE_TYPE_TRANSACTION_SKELETON, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + transaction_initable_iface_init) + G_IMPLEMENT_INTERFACE (RPMOSTREE_TYPE_TRANSACTION, + transaction_dbus_iface_init)) /* XXX This is lame but it's meant to keep it simple to * transition to transaction_get_instance_private(). */ @@ -219,6 +219,58 @@ transaction_gpg_verify_result_cb (OstreeRepo *repo, g_strdup (checksum)); } +static void +transaction_execute_thread (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Transaction *self = TRANSACTION (source_object); + TransactionClass *class = TRANSACTION_GET_CLASS (self); + gboolean success = TRUE; + GError *local_error = NULL; + + if (class->execute != NULL) + success = class->execute (self, cancellable, &local_error); + + if (local_error != NULL) + g_task_return_error (task, local_error); + else + g_task_return_boolean (task, success); +} + +static void +transaction_execute_done_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + Transaction *self = TRANSACTION (source_object); + TransactionPrivate *priv = transaction_get_private (self); + const char *message = NULL; + gboolean success; + GError *local_error = NULL; + + success = g_task_propagate_boolean (G_TASK (result), &local_error); + + /* Sanity check */ + g_warn_if_fail ((success && local_error == NULL) || + (!success && local_error != NULL)); + + if (local_error != NULL) + message = local_error->message; + + if (message == NULL) + message = ""; + + priv->success = success; + priv->message = g_strdup (message); + + if (success && priv->sysroot != NULL) + sysroot_emit_update (sysroot_get (), priv->sysroot); + + rpmostree_transaction_set_active (RPMOSTREE_TRANSACTION (self), FALSE); +} + static void transaction_set_property (GObject *object, guint property_id, @@ -405,10 +457,16 @@ transaction_handle_start (RPMOSTreeTransaction *transaction, } else { + GTask *task; + priv->started = TRUE; - /* FIXME Subclassing would be better than this. */ - g_signal_emit (transaction, signals[START], 0); + task = g_task_new (transaction, + priv->cancellable, + transaction_execute_done_cb, + NULL); + g_task_run_in_thread (task, transaction_execute_thread); + g_object_unref (task); rpmostree_transaction_complete_start (transaction, invocation); } @@ -483,12 +541,6 @@ transaction_class_init (TransactionClass *class) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - signals[START] = g_signal_new ("start", - TYPE_TRANSACTION, - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - signals[CANCELLED] = g_signal_new ("cancelled", TYPE_TRANSACTION, G_SIGNAL_RUN_LAST, @@ -530,81 +582,42 @@ transaction_init (Transaction *self) TransactionPrivate); } -RPMOSTreeTransaction * -transaction_new (GDBusMethodInvocation *invocation, - OstreeSysroot *sysroot, - GCancellable *cancellable, - GError **error) -{ - /* sysroot is optional */ - g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); - - return g_initable_new (TYPE_TRANSACTION, - cancellable, error, - "invocation", invocation, - "sysroot", sysroot, - NULL); -} - OstreeSysroot * -transaction_get_sysroot (RPMOSTreeTransaction *transaction) +transaction_get_sysroot (Transaction *transaction) { - Transaction *self; TransactionPrivate *priv; - g_return_val_if_fail (RPMOSTREE_IS_TRANSACTION (transaction), NULL); + g_return_val_if_fail (IS_TRANSACTION (transaction), NULL); - self = TRANSACTION (transaction); - priv = transaction_get_private (self); + priv = transaction_get_private (transaction); return priv->sysroot; } void -transaction_emit_message_printf (RPMOSTreeTransaction *transaction, +transaction_emit_message_printf (Transaction *transaction, const char *format, ...) { - g_autofree char *message = NULL; + g_autofree char *formatted_message = NULL; va_list args; - g_return_if_fail (RPMOSTREE_IS_TRANSACTION (transaction)); + g_return_if_fail (IS_TRANSACTION (transaction)); g_return_if_fail (format != NULL); va_start (args, format); - message = g_strdup_vprintf (format, args); + formatted_message = g_strdup_vprintf (format, args); va_end (args); - rpmostree_transaction_emit_message (transaction, message); + rpmostree_transaction_emit_message (RPMOSTREE_TRANSACTION (transaction), + formatted_message); } void -transaction_done (RPMOSTreeTransaction *transaction, - gboolean success, - const char *message) -{ - Transaction *self; - TransactionPrivate *priv; - - g_return_if_fail (RPMOSTREE_IS_TRANSACTION (transaction)); - - if (message == NULL) - message = ""; - - self = TRANSACTION (transaction); - priv = transaction_get_private (self); - - priv->success = success; - priv->message = g_strdup (message); - - rpmostree_transaction_set_active (transaction, FALSE); -} - -void -transaction_connect_download_progress (RPMOSTreeTransaction *transaction, +transaction_connect_download_progress (Transaction *transaction, OstreeAsyncProgress *progress) { - g_return_if_fail (RPMOSTREE_IS_TRANSACTION (transaction)); + g_return_if_fail (IS_TRANSACTION (transaction)); g_return_if_fail (OSTREE_IS_ASYNC_PROGRESS (progress)); g_signal_connect_object (progress, @@ -614,10 +627,10 @@ transaction_connect_download_progress (RPMOSTreeTransaction *transaction, } void -transaction_connect_signature_progress (RPMOSTreeTransaction *transaction, +transaction_connect_signature_progress (Transaction *transaction, OstreeRepo *repo) { - g_return_if_fail (RPMOSTREE_IS_TRANSACTION (transaction)); + g_return_if_fail (IS_TRANSACTION (transaction)); g_return_if_fail (OSTREE_REPO (repo)); g_signal_connect_object (repo, "gpg-verify-result", diff --git a/src/daemon/transaction.h b/src/daemon/transaction.h index 8a6d96af..95501908 100644 --- a/src/daemon/transaction.h +++ b/src/daemon/transaction.h @@ -38,24 +38,20 @@ struct _Transaction struct _TransactionClass { RPMOSTreeTransactionSkeletonClass parent_class; + + gboolean (*execute) (Transaction *transaction, + GCancellable *cancellable, + GError **error); }; GType transaction_get_type (void) G_GNUC_CONST; -RPMOSTreeTransaction * - transaction_new (GDBusMethodInvocation *invocation, - OstreeSysroot *sysroot, - GCancellable *cancellable, - GError **error); -void transaction_done (RPMOSTreeTransaction *transaction, - gboolean success, - const char *message); -OstreeSysroot * transaction_get_sysroot (RPMOSTreeTransaction *transaction); -void transaction_emit_message_printf (RPMOSTreeTransaction *transaction, +OstreeSysroot * transaction_get_sysroot (Transaction *transaction); +void transaction_emit_message_printf (Transaction *transaction, const char *format, ...) G_GNUC_PRINTF (2, 3); void transaction_connect_download_progress - (RPMOSTreeTransaction *transaction, + (Transaction *transaction, OstreeAsyncProgress *progress); void transaction_connect_signature_progress - (RPMOSTreeTransaction *transaction, + (Transaction *transaction, OstreeRepo *repo);