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.
This commit is contained in:
Matthew Barnes 2015-08-13 13:17:01 -04:00
parent 434a967ab1
commit d69cb7d7c1
9 changed files with 1002 additions and 737 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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
{

View File

@ -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;

View File

@ -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);

View File

@ -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 <libglnx.h>
#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;
}

View File

@ -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);

View File

@ -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",

View File

@ -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);