daemon: Time out client txn connections after 30s

This removes the "transaction monitor".  Originally the idea
here was things would queue, but we never really made that
work. Since transactions started grabbing the
libostree sysroot lock in particular.

Rip out the transaction monitor code with the queue and have
a singleton txn ref in the sysroot object.  This should ensure
that if a txn is active, one always gets an error message about
which one.

Next, add a 30s timeout for connections to the transaction DBus
sockets after they're complete.

This should address https://github.com/projectatomic/rpm-ostree/issues/1692
which is a case where gnome-software was leaking the txn DBus
connection and keeping it alive.

Closes: #1755
Approved by: cgwalters
This commit is contained in:
Colin Walters 2019-02-09 21:40:19 +00:00 committed by Atomic Bot
parent e58ee20996
commit aea374710e
12 changed files with 243 additions and 457 deletions

View File

@ -40,8 +40,6 @@ librpmostreed_la_SOURCES = \
src/daemon/rpmostreed-deployment-utils.c \
src/daemon/rpmostreed-transaction.h \
src/daemon/rpmostreed-transaction.c \
src/daemon/rpmostreed-transaction-monitor.h \
src/daemon/rpmostreed-transaction-monitor.c \
src/daemon/rpmostreed-transaction-types.h \
src/daemon/rpmostreed-transaction-types.c \
src/daemon/rpmostreed-transaction-livefs.c \

View File

@ -31,7 +31,6 @@
#include "rpmostreed-utils.h"
#include "rpmostree-util.h"
#include "rpmostreed-transaction.h"
#include "rpmostreed-transaction-monitor.h"
#include "rpmostreed-transaction-types.h"
typedef struct _RpmostreedOSExperimentalClass RpmostreedOSExperimentalClass;
@ -39,7 +38,6 @@ typedef struct _RpmostreedOSExperimentalClass RpmostreedOSExperimentalClass;
struct _RpmostreedOSExperimental
{
RPMOSTreeOSSkeleton parent_instance;
RpmostreedTransactionMonitor *transaction_monitor;
};
struct _RpmostreedOSExperimentalClass
@ -57,23 +55,6 @@ G_DEFINE_TYPE_WITH_CODE (RpmostreedOSExperimental,
rpmostreed_osexperimental_iface_init)
);
static RpmostreedTransaction *
merge_compatible_txn (RpmostreedOSExperimental *self,
GDBusMethodInvocation *invocation)
{
glnx_unref_object RpmostreedTransaction *transaction = NULL;
/* If a compatible transaction is in progress, share its bus address. */
transaction = rpmostreed_transaction_monitor_ref_active_transaction (self->transaction_monitor);
if (transaction != NULL)
{
if (rpmostreed_transaction_is_compatible (transaction, invocation))
return g_steal_pointer (&transaction);
}
return NULL;
}
/* ---------------------------------------------------------------------------------------------------- */
static void
@ -89,8 +70,6 @@ os_dispose (GObject *object)
object_path, object);
}
g_clear_object (&self->transaction_monitor);
G_OBJECT_CLASS (rpmostreed_osexperimental_parent_class)->dispose (object);
}
@ -159,13 +138,15 @@ osexperimental_handle_live_fs (RPMOSTreeOSExperimental *interface,
GDBusMethodInvocation *invocation,
GVariant *arg_options)
{
RpmostreedOSExperimental *self = RPMOSTREED_OSEXPERIMENTAL (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -184,7 +165,7 @@ osexperimental_handle_live_fs (RPMOSTreeOSExperimental *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -213,23 +194,18 @@ rpmostreed_osexperimental_iface_init (RPMOSTreeOSExperimentalIface *iface)
RPMOSTreeOSExperimental *
rpmostreed_osexperimental_new (OstreeSysroot *sysroot,
OstreeRepo *repo,
const char *name,
RpmostreedTransactionMonitor *monitor)
const char *name)
{
RpmostreedOSExperimental *obj = NULL;
g_autofree char *path = NULL;
g_return_val_if_fail (OSTREE_IS_SYSROOT (sysroot), NULL);
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (RPMOSTREED_IS_TRANSACTION_MONITOR (monitor), NULL);
path = rpmostreed_generate_object_path (BASE_DBUS_PATH, name, NULL);
obj = g_object_new (RPMOSTREED_TYPE_OSEXPERIMENTAL, NULL);
/* FIXME Make this a construct-only property? */
obj->transaction_monitor = g_object_ref (monitor);
rpmostreed_daemon_publish (rpmostreed_daemon_get (), path, FALSE, obj);
return RPMOSTREE_OSEXPERIMENTAL (obj);

View File

@ -27,5 +27,4 @@
GType rpmostreed_osexperimental_get_type (void) G_GNUC_CONST;
RPMOSTreeOSExperimental * rpmostreed_osexperimental_new (OstreeSysroot *sysroot,
OstreeRepo *repo,
const char *name,
RpmostreedTransactionMonitor *monitor);
const char *name);

View File

@ -34,7 +34,6 @@
#include "rpmostreed-utils.h"
#include "rpmostree-util.h"
#include "rpmostreed-transaction.h"
#include "rpmostreed-transaction-monitor.h"
#include "rpmostreed-transaction-types.h"
typedef struct _RpmostreedOSClass RpmostreedOSClass;
@ -42,7 +41,6 @@ typedef struct _RpmostreedOSClass RpmostreedOSClass;
struct _RpmostreedOS
{
RPMOSTreeOSSkeleton parent_instance;
RpmostreedTransactionMonitor *transaction_monitor;
gboolean on_session_bus;
guint signal_id;
};
@ -260,8 +258,6 @@ os_dispose (GObject *object)
object_path, object);
}
g_clear_object (&self->transaction_monitor);
if (self->signal_id > 0)
g_signal_handler_disconnect (rpmostreed_sysroot_get (), self->signal_id);
@ -448,23 +444,6 @@ out:
return TRUE;
}
static RpmostreedTransaction *
merge_compatible_txn (RpmostreedOS *self,
GDBusMethodInvocation *invocation)
{
glnx_unref_object RpmostreedTransaction *transaction = NULL;
/* If a compatible transaction is in progress, share its bus address. */
transaction = rpmostreed_transaction_monitor_ref_active_transaction (self->transaction_monitor);
if (transaction != NULL)
{
if (rpmostreed_transaction_is_compatible (transaction, invocation))
return g_steal_pointer (&transaction);
}
return NULL;
}
static gboolean
refresh_cached_update (RpmostreedOS*, GError **error);
@ -484,13 +463,16 @@ os_handle_download_update_rpm_diff (RPMOSTreeOS *interface,
GDBusMethodInvocation *invocation)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
const char *osname;
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -514,13 +496,13 @@ os_handle_download_update_rpm_diff (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_sysroot_set_txn (rsysroot, transaction);
/* Make sure we refresh CachedUpdate after the transaction. This normally happens
* automatically if new data was downloaded (through the repo mtime bump --> UPDATED
* signal), but we also want to do this even if the data was already present. */
g_signal_connect (transaction, "closed", G_CALLBACK (on_auto_update_done), self);
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
out:
if (local_error != NULL)
{
@ -576,9 +558,11 @@ os_merge_or_start_deployment_txn (RPMOSTreeOS *interface,
g_autoptr(GError) local_error = NULL;
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto err;
glnx_unref_object RpmostreedTransaction *transaction =
merge_compatible_txn (self, invocation);
if (!transaction)
{
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
@ -594,7 +578,7 @@ os_merge_or_start_deployment_txn (RPMOSTreeOS *interface,
if (!transaction)
goto err;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
/* For the AutomaticUpdateTrigger "check" case, we want to make sure we refresh
* the CachedUpdate property; "stage" will do this through sysroot_changed */
@ -815,8 +799,6 @@ os_handle_rollback (RPMOSTreeOS *interface,
GDBusMethodInvocation *invocation,
GVariant *arg_options)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
const char *osname;
@ -824,7 +806,11 @@ os_handle_rollback (RPMOSTreeOS *interface,
GVariantDict options_dict;
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -855,7 +841,7 @@ os_handle_rollback (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -877,8 +863,6 @@ os_handle_refresh_md (RPMOSTreeOS *interface,
GDBusMethodInvocation *invocation,
GVariant *arg_options)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
const char *osname;
@ -886,7 +870,11 @@ os_handle_refresh_md (RPMOSTreeOS *interface,
RpmOstreeTransactionRefreshMdFlags flags = 0;
g_auto(GVariantDict) dict;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -912,7 +900,7 @@ os_handle_refresh_md (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -934,15 +922,17 @@ os_handle_clear_rollback_target (RPMOSTreeOS *interface,
GDBusMethodInvocation *invocation,
GVariant *arg_options)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
RpmOstreeTransactionCleanupFlags flags = 0;
const char *osname;
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -963,7 +953,7 @@ os_handle_clear_rollback_target (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -986,8 +976,6 @@ os_handle_set_initramfs_state (RPMOSTreeOS *interface,
const char *const*args,
GVariant *arg_options)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
g_autoptr(GVariantDict) dict = NULL;
@ -995,7 +983,11 @@ os_handle_set_initramfs_state (RPMOSTreeOS *interface,
gboolean reboot = FALSE;
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -1022,7 +1014,7 @@ os_handle_set_initramfs_state (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -1060,13 +1052,15 @@ os_handle_kernel_args (RPMOSTreeOS *interface,
const char * const *kernel_args_deleted,
GVariant *arg_options)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -1091,7 +1085,7 @@ os_handle_kernel_args (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -1195,15 +1189,17 @@ os_handle_cleanup (RPMOSTreeOS *interface,
GDBusMethodInvocation *invocation,
const char *const*args)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
RpmOstreeTransactionCleanupFlags flags = 0;
const char *osname;
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -1245,7 +1241,7 @@ os_handle_cleanup (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -1338,14 +1334,16 @@ os_handle_download_rebase_rpm_diff (RPMOSTreeOS *interface,
const char * const *arg_packages)
{
/* TODO: Totally ignoring arg_packages for now */
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
const char *osname;
GError *local_error = NULL;
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -1369,7 +1367,7 @@ os_handle_download_rebase_rpm_diff (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -1475,8 +1473,6 @@ os_handle_download_deploy_rpm_diff (RPMOSTreeOS *interface,
const char *arg_revision,
const char * const *arg_packages)
{
RpmostreedOS *self = RPMOSTREED_OS (interface);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
glnx_unref_object OstreeSysroot *ot_sysroot = NULL;
g_autoptr(GCancellable) cancellable = g_cancellable_new ();
const char *osname;
@ -1484,7 +1480,11 @@ os_handle_download_deploy_rpm_diff (RPMOSTreeOS *interface,
/* XXX Ignoring arg_packages for now. */
transaction = merge_compatible_txn (self, invocation);
/* try to merge with an existing transaction, otherwise start a new one */
glnx_unref_object RpmostreedTransaction *transaction = NULL;
RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get ();
if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error))
goto out;
if (transaction)
goto out;
@ -1507,7 +1507,7 @@ os_handle_download_deploy_rpm_diff (RPMOSTreeOS *interface,
if (transaction == NULL)
goto out;
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
rpmostreed_sysroot_set_txn (rsysroot, transaction);
out:
if (local_error != NULL)
@ -1693,23 +1693,18 @@ rpmostreed_os_iface_init (RPMOSTreeOSIface *iface)
RPMOSTreeOS *
rpmostreed_os_new (OstreeSysroot *sysroot,
OstreeRepo *repo,
const char *name,
RpmostreedTransactionMonitor *monitor)
const char *name)
{
RpmostreedOS *obj = NULL;
g_autofree char *path = NULL;
g_return_val_if_fail (OSTREE_IS_SYSROOT (sysroot), NULL);
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (RPMOSTREED_IS_TRANSACTION_MONITOR (monitor), NULL);
path = rpmostreed_generate_object_path (BASE_DBUS_PATH, name, NULL);
obj = g_object_new (RPMOSTREED_TYPE_OS, "name", name, NULL);
/* FIXME Make this a construct-only property? */
obj->transaction_monitor = g_object_ref (monitor);
/* FIXME - use GInitable */
{ g_autoptr(GError) local_error = NULL;
if (!rpmostreed_os_load_internals (obj, &local_error))

View File

@ -30,5 +30,4 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(RpmOstreeUpdateDeploymentModifiers, g_variant_unre
GType rpmostreed_os_get_type (void) G_GNUC_CONST;
RPMOSTreeOS * rpmostreed_os_new (OstreeSysroot *sysroot,
OstreeRepo *repo,
const char *name,
RpmostreedTransactionMonitor *monitor);
const char *name);

View File

@ -28,7 +28,6 @@
#include "rpmostreed-deployment-utils.h"
#include "rpmostreed-errors.h"
#include "rpmostreed-transaction.h"
#include "rpmostreed-transaction-monitor.h"
#include "rpmostree-output.h"
@ -39,6 +38,9 @@
#include <systemd/sd-login.h>
#include <systemd/sd-journal.h>
/* Avoid clients leaking their bus connections keeping the transaction open */
#define FORCE_CLOSE_TXN_TIMEOUT_SECS 30
static gboolean
sysroot_reload_ostree_configs_and_deployments (RpmostreedSysroot *self,
gboolean *out_changed,
@ -66,7 +68,8 @@ struct _RpmostreedSysroot {
OstreeSysroot *ot_sysroot;
OstreeRepo *repo;
struct stat repo_last_stat;
RpmostreedTransactionMonitor *transaction_monitor;
RpmostreedTransaction *transaction;
guint close_transaction_timeout_id;
PolkitAuthority *authority;
gboolean on_session_bus;
@ -103,7 +106,6 @@ static void
sysroot_output_cb (RpmOstreeOutputType type, void *data, void *opaque)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (opaque);
glnx_unref_object RpmostreedTransaction *transaction = NULL;
gboolean output_to_self = FALSE;
// The API previously passed these each time, but now we retain them as
@ -112,22 +114,20 @@ sysroot_output_cb (RpmOstreeOutputType type, void *data, void *opaque)
static bool progress_state_percent;
static guint progress_state_n_items;
transaction =
rpmostreed_transaction_monitor_ref_active_transaction (self->transaction_monitor);
if (transaction)
g_object_get (transaction, "output-to-self", &output_to_self, NULL);
if (self->transaction)
g_object_get (self->transaction, "output-to-self", &output_to_self, NULL);
if (!transaction || output_to_self)
if (!self->transaction || output_to_self)
{
rpmostree_output_default_handler (type, data, opaque);
return;
}
RPMOSTreeTransaction *transaction = RPMOSTREE_TRANSACTION (self->transaction);
switch (type)
{
case RPMOSTREE_OUTPUT_MESSAGE:
rpmostree_transaction_emit_message (RPMOSTREE_TRANSACTION (transaction),
((RpmOstreeOutputMessage*)data)->text);
rpmostree_transaction_emit_message (transaction, ((RpmOstreeOutputMessage*)data)->text);
break;
case RPMOSTREE_OUTPUT_PROGRESS_BEGIN:
{
@ -138,8 +138,7 @@ sysroot_output_cb (RpmOstreeOutputType type, void *data, void *opaque)
if (begin->percent)
{
progress_str = g_strdup (begin->prefix);
rpmostree_transaction_emit_percent_progress (RPMOSTREE_TRANSACTION (transaction),
progress_str, 0);
rpmostree_transaction_emit_percent_progress (transaction, progress_str, 0);
progress_state_percent = true;
}
else if (begin->n > 0)
@ -147,13 +146,11 @@ sysroot_output_cb (RpmOstreeOutputType type, void *data, void *opaque)
progress_str = g_strdup (begin->prefix);
progress_state_n_items = begin->n;
/* For backcompat, this is a percentage. See below */
rpmostree_transaction_emit_percent_progress (RPMOSTREE_TRANSACTION (transaction),
progress_str, 0);
rpmostree_transaction_emit_percent_progress (transaction, progress_str, 0);
}
else
{
rpmostree_transaction_emit_task_begin (RPMOSTREE_TRANSACTION (transaction),
begin->prefix);
rpmostree_transaction_emit_task_begin (transaction, begin->prefix);
}
}
break;
@ -169,11 +166,11 @@ sysroot_output_cb (RpmOstreeOutputType type, void *data, void *opaque)
int percentage = (update->c == progress_state_n_items) ? 100 :
(((double)(update->c)) / (progress_state_n_items) * 100);
g_autofree char *newtext = g_strdup_printf ("%s (%u/%u)", progress_str, update->c, progress_state_n_items);
rpmostree_transaction_emit_percent_progress (RPMOSTREE_TRANSACTION (transaction), newtext, percentage);
rpmostree_transaction_emit_percent_progress (transaction, newtext, percentage);
}
else
{
rpmostree_transaction_emit_percent_progress (RPMOSTREE_TRANSACTION (transaction), progress_str, update->c);
rpmostree_transaction_emit_percent_progress (transaction, progress_str, update->c);
}
}
break;
@ -186,11 +183,11 @@ sysroot_output_cb (RpmOstreeOutputType type, void *data, void *opaque)
{
if (progress_state_percent || progress_state_n_items > 0)
{
rpmostree_transaction_emit_progress_end (RPMOSTREE_TRANSACTION (transaction));
rpmostree_transaction_emit_progress_end (transaction);
}
else
{
rpmostree_transaction_emit_task_end (RPMOSTREE_TRANSACTION (transaction), "done");
rpmostree_transaction_emit_task_end (transaction, "done");
}
}
break;
@ -270,51 +267,6 @@ handle_get_os (RPMOSTreeSysroot *object,
return TRUE;
}
static gboolean
sysroot_transform_transaction_to_attrs (GBinding *binding,
const GValue *src_value,
GValue *dst_value,
gpointer user_data)
{
RpmostreedTransaction *transaction;
GVariant *variant;
const char *method_name = "";
const char *path = "";
const char *sender_name = "";
transaction = g_value_get_object (src_value);
if (transaction != NULL)
{
GDBusMethodInvocation *invocation;
invocation = rpmostreed_transaction_get_invocation (transaction);
method_name = g_dbus_method_invocation_get_method_name (invocation);
path = g_dbus_method_invocation_get_object_path (invocation);
sender_name = g_dbus_method_invocation_get_sender (invocation);
}
variant = g_variant_new ("(sss)", method_name, sender_name, path);
g_value_set_variant (dst_value, variant);
return TRUE;
}
static gboolean
sysroot_transform_transaction_to_address (GBinding *binding,
const GValue *src_value,
GValue *dst_value,
gpointer user_data)
{
RpmostreedTransaction *transaction = g_value_get_object (src_value);
const char *address = "";
if (transaction != NULL)
address = rpmostreed_transaction_get_client_address (transaction);
g_value_set_string (dst_value, address);
return TRUE;
}
static gboolean
sysroot_populate_deployments_unlocked (RpmostreedSysroot *self,
gboolean *out_changed,
@ -386,13 +338,11 @@ sysroot_populate_deployments_unlocked (RpmostreedSysroot *self,
if (!g_hash_table_contains (self->os_interfaces, deployment_os))
{
RPMOSTreeOS *obj = rpmostreed_os_new (self->ot_sysroot, self->repo,
deployment_os,
self->transaction_monitor);
deployment_os);
g_hash_table_insert (self->os_interfaces, g_strdup (deployment_os), obj);
RPMOSTreeOSExperimental *eobj = rpmostreed_osexperimental_new (self->ot_sysroot, self->repo,
deployment_os,
self->transaction_monitor);
deployment_os);
g_hash_table_insert (self->osexperimental_interfaces, g_strdup (deployment_os), eobj);
}
@ -569,7 +519,7 @@ sysroot_dispose (GObject *object)
g_hash_table_remove_all (self->os_interfaces);
g_hash_table_remove_all (self->osexperimental_interfaces);
g_clear_object (&self->transaction_monitor);
g_clear_object (&self->transaction);
g_clear_object (&self->authority);
G_OBJECT_CLASS (rpmostreed_sysroot_parent_class)->dispose (object);
@ -604,8 +554,6 @@ rpmostreed_sysroot_init (RpmostreedSysroot *self)
self->monitor = NULL;
self->transaction_monitor = rpmostreed_transaction_monitor_new ();
if (g_getenv ("RPMOSTREE_USE_SESSION_BUS") != NULL)
self->on_session_bus = TRUE;
@ -623,35 +571,6 @@ rpmostreed_sysroot_init (RpmostreedSysroot *self)
rpmostree_output_set_callback (sysroot_output_cb, self);
}
static void
sysroot_constructed (GObject *object)
{
RpmostreedSysroot *self = RPMOSTREED_SYSROOT (object);
g_object_bind_property_full (self->transaction_monitor,
"active-transaction",
self,
"active-transaction",
G_BINDING_DEFAULT |
G_BINDING_SYNC_CREATE,
sysroot_transform_transaction_to_attrs,
NULL,
NULL,
NULL);
g_object_bind_property_full (self->transaction_monitor,
"active-transaction",
self,
"active-transaction-path",
G_BINDING_DEFAULT |
G_BINDING_SYNC_CREATE,
sysroot_transform_transaction_to_address,
NULL,
NULL,
NULL);
G_OBJECT_CLASS (rpmostreed_sysroot_parent_class)->constructed (object);
}
static gboolean
sysroot_authorize_method (GDBusInterfaceSkeleton *interface,
GDBusMethodInvocation *invocation)
@ -751,7 +670,6 @@ rpmostreed_sysroot_class_init (RpmostreedSysrootClass *klass)
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = sysroot_dispose;
gobject_class->finalize = sysroot_finalize;
gobject_class->constructed = sysroot_constructed;
signals[UPDATED] = g_signal_new ("updated",
RPMOSTREED_TYPE_SYSROOT,
@ -903,6 +821,108 @@ rpmostreed_sysroot_load_state (RpmostreedSysroot *self,
return TRUE;
}
gboolean
rpmostreed_sysroot_prep_for_txn (RpmostreedSysroot *self,
GDBusMethodInvocation *invocation,
RpmostreedTransaction **out_compat_txn,
GError **error)
{
if (self->transaction)
{
if (rpmostreed_transaction_is_compatible (self->transaction, invocation))
{
*out_compat_txn = g_object_ref (self->transaction);
return TRUE;
}
const char *title = rpmostree_transaction_get_title ((RPMOSTreeTransaction*)(self->transaction));
return glnx_throw (error, "Transaction in progress: %s", title);
}
*out_compat_txn = NULL;
return TRUE;
}
gboolean
rpmostreed_sysroot_has_txn (RpmostreedSysroot *self)
{
return self->transaction != NULL;
}
static gboolean
on_force_close (gpointer data)
{
RpmostreedSysroot *self = data;
if (self->transaction)
{
rpmostreed_transaction_force_close (self->transaction);
rpmostreed_sysroot_set_txn (self, NULL);
}
return FALSE;
}
static void
on_txn_executed_changed (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
RpmostreedSysroot *self = user_data;
gboolean executed;
g_object_get (object, "executed", &executed, NULL);
if (executed && self->close_transaction_timeout_id == 0)
{
self->close_transaction_timeout_id =
g_timeout_add_seconds (FORCE_CLOSE_TXN_TIMEOUT_SECS, on_force_close, self);
}
}
void
rpmostreed_sysroot_set_txn (RpmostreedSysroot *self,
RpmostreedTransaction *txn)
{
/* If the transaction is changing, clear the timer */
if (self->close_transaction_timeout_id > 0)
{
g_source_remove (self->close_transaction_timeout_id);
self->close_transaction_timeout_id = 0;
}
if (txn != NULL)
{
g_assert (self->transaction == NULL);
self->transaction = g_object_ref (txn);
g_signal_connect (self->transaction, "notify::executed",
G_CALLBACK (on_txn_executed_changed), self);
GDBusMethodInvocation *invocation = rpmostreed_transaction_get_invocation (self->transaction);
g_autoptr(GVariant) v = g_variant_ref_sink (g_variant_new ("(sss)",
g_dbus_method_invocation_get_method_name (invocation),
g_dbus_method_invocation_get_object_path (invocation),
g_dbus_method_invocation_get_sender (invocation)));
rpmostree_sysroot_set_active_transaction ((RPMOSTreeSysroot*)self, v);
rpmostree_sysroot_set_active_transaction_path ((RPMOSTreeSysroot*)self,
rpmostreed_transaction_get_client_address (self->transaction));
}
else
{
g_assert (self->transaction);
g_clear_object (&self->transaction);
g_autoptr(GVariant) v = g_variant_ref_sink (g_variant_new ("(sss)", "", "", ""));
rpmostree_sysroot_set_active_transaction ((RPMOSTreeSysroot *)self, v);
rpmostree_sysroot_set_active_transaction_path ((RPMOSTreeSysroot *)self, "");
}
}
void
rpmostreed_sysroot_finish_txn (RpmostreedSysroot *self,
RpmostreedTransaction *txn)
{
g_assert (self->transaction == txn);
rpmostreed_sysroot_set_txn (self, NULL);
}
OstreeSysroot *
rpmostreed_sysroot_get_root (RpmostreedSysroot *self)
{

View File

@ -49,4 +49,17 @@ gboolean rpmostreed_sysroot_load_state (RpmostreedSysroot *self
OstreeRepo **out_repo,
GError **error);
gboolean rpmostreed_sysroot_prep_for_txn (RpmostreedSysroot *self,
GDBusMethodInvocation *invocation,
RpmostreedTransaction **out_compat_txn,
GError **error);
gboolean rpmostreed_sysroot_has_txn (RpmostreedSysroot *self);
void rpmostreed_sysroot_finish_txn (RpmostreedSysroot *self,
RpmostreedTransaction *txn);
void rpmostreed_sysroot_set_txn (RpmostreedSysroot *self,
RpmostreedTransaction *txn);
void rpmostreed_sysroot_emit_update (RpmostreedSysroot *self);

View File

@ -1,204 +0,0 @@
/*
* 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 "rpmostreed-transaction-monitor.h"
#include <libglnx.h>
#include "rpmostreed-transaction.h"
typedef struct _RpmostreedTransactionMonitorClass RpmostreedTransactionMonitorClass;
struct _RpmostreedTransactionMonitor {
GObjectClass parent;
/* The head of the queue is the active transaction. */
GQueue *transactions;
};
struct _RpmostreedTransactionMonitorClass {
GObjectClass parent_class;
};
enum {
PROP_0,
PROP_ACTIVE_TRANSACTION
};
G_DEFINE_TYPE (RpmostreedTransactionMonitor,
rpmostreed_transaction_monitor,
G_TYPE_OBJECT)
static void
transaction_monitor_remove_transaction (RpmostreedTransactionMonitor *monitor,
RPMOSTreeTransaction *transaction)
{
GList *link;
link = g_queue_find (monitor->transactions, transaction);
if (link != NULL)
{
GList *head;
gboolean need_notify;
/* The head of the queue is the active transaction. */
head = g_queue_peek_head_link (monitor->transactions);
need_notify = (link == head);
g_object_unref (link->data);
g_queue_delete_link (monitor->transactions, link);
if (need_notify)
{
/* Issue a notification so property bindings get updated. */
g_object_notify (G_OBJECT (monitor), "active-transaction");
}
}
}
static void
transaction_monitor_notify_active_cb (RPMOSTreeTransaction *transaction,
GParamSpec *pspec,
RpmostreedTransactionMonitor *monitor)
{
GList *head, *link;
/* The head of the queue is the active transaction. */
head = g_queue_peek_head_link (monitor->transactions);
link = g_queue_find (monitor->transactions, transaction);
if (link == head)
{
/* Issue a notification so property bindings get updated. */
g_object_notify (G_OBJECT (monitor), "active-transaction");
}
}
static void
transaction_monitor_closed_cb (RPMOSTreeTransaction *transaction,
RpmostreedTransactionMonitor *monitor)
{
transaction_monitor_remove_transaction (monitor, transaction);
}
static void
transaction_monitor_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
RpmostreedTransactionMonitor *monitor = RPMOSTREED_TRANSACTION_MONITOR (object);
gpointer v_object;
switch (property_id)
{
case PROP_ACTIVE_TRANSACTION:
v_object = rpmostreed_transaction_monitor_ref_active_transaction (monitor);
g_value_take_object (value, v_object);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
transaction_monitor_dispose (GObject *object)
{
RpmostreedTransactionMonitor *monitor = RPMOSTREED_TRANSACTION_MONITOR (object);
g_queue_free_full (monitor->transactions, (GDestroyNotify) g_object_unref);
G_OBJECT_CLASS (rpmostreed_transaction_monitor_parent_class)->dispose (object);
}
static void
rpmostreed_transaction_monitor_class_init (RpmostreedTransactionMonitorClass *class)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (class);
object_class->get_property = transaction_monitor_get_property;
object_class->dispose = transaction_monitor_dispose;
g_object_class_install_property (object_class,
PROP_ACTIVE_TRANSACTION,
g_param_spec_object ("active-transaction",
NULL,
NULL,
RPMOSTREED_TYPE_TRANSACTION,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
}
static void
rpmostreed_transaction_monitor_init (RpmostreedTransactionMonitor *monitor)
{
monitor->transactions = g_queue_new ();
}
RpmostreedTransactionMonitor *
rpmostreed_transaction_monitor_new (void)
{
return g_object_new (RPMOSTREED_TYPE_TRANSACTION_MONITOR, NULL);
}
void
rpmostreed_transaction_monitor_add (RpmostreedTransactionMonitor *monitor,
RpmostreedTransaction *transaction)
{
g_return_if_fail (RPMOSTREED_IS_TRANSACTION_MONITOR (monitor));
g_return_if_fail (RPMOSTREED_IS_TRANSACTION (transaction));
g_signal_connect_object (transaction, "notify::active",
G_CALLBACK (transaction_monitor_notify_active_cb),
monitor, 0);
g_signal_connect_object (transaction, "closed",
G_CALLBACK (transaction_monitor_closed_cb),
monitor, 0);
g_queue_push_head (monitor->transactions, g_object_ref (transaction));
g_object_notify (G_OBJECT (monitor), "active-transaction");
}
RpmostreedTransaction *
rpmostreed_transaction_monitor_ref_active_transaction (RpmostreedTransactionMonitor *monitor)
{
RpmostreedTransaction *transaction;
g_return_val_if_fail (RPMOSTREED_IS_TRANSACTION_MONITOR (monitor), NULL);
/* The head of the queue is the active transaction. */
transaction = g_queue_peek_head (monitor->transactions);
if (transaction != NULL)
{
/* An "inactive" transaction has completed its task
* and does not block other transactions from starting. */
if (rpmostreed_transaction_get_active (transaction))
g_object_ref (transaction);
else
transaction = NULL;
}
return transaction;
}

View File

@ -1,35 +0,0 @@
/*
* 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 "ostree.h"
#include "rpmostreed-types.h"
#define RPMOSTREED_TYPE_TRANSACTION_MONITOR (rpmostreed_transaction_monitor_get_type ())
#define RPMOSTREED_TRANSACTION_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RPMOSTREED_TYPE_TRANSACTION_MONITOR, RpmostreedTransactionMonitor))
#define RPMOSTREED_IS_TRANSACTION_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), RPMOSTREED_TYPE_TRANSACTION_MONITOR))
GType rpmostreed_transaction_monitor_get_type (void) G_GNUC_CONST;
RpmostreedTransactionMonitor *
rpmostreed_transaction_monitor_new (void);
void rpmostreed_transaction_monitor_add (RpmostreedTransactionMonitor *monitor,
RpmostreedTransaction *transaction);
RpmostreedTransaction *
rpmostreed_transaction_monitor_ref_active_transaction
(RpmostreedTransactionMonitor *monitor);

View File

@ -28,6 +28,7 @@
struct _RpmostreedTransactionPrivate {
GDBusMethodInvocation *invocation;
gboolean executed; /* TRUE if the transaction has completed (successfully or not) */
GCancellable *cancellable;
/* For the duration of the transaction, we hold a ref to a new
@ -50,7 +51,7 @@ struct _RpmostreedTransactionPrivate {
enum {
PROP_0,
PROP_ACTIVE,
PROP_EXECUTED,
PROP_INVOCATION,
PROP_SYSROOT_PATH,
PROP_REDIRECT_OUTPUT
@ -98,6 +99,8 @@ transaction_maybe_emit_closed (RpmostreedTransaction *self)
return;
g_signal_emit (self, signals[CLOSED], 0);
rpmostreed_sysroot_finish_txn (rpmostreed_sysroot_get (), self);
}
static void
@ -365,7 +368,8 @@ transaction_execute_done_cb (GObject *source_object,
priv->finished_params = g_variant_new ("(bs)", success, error_message);
g_variant_ref_sink (priv->finished_params);
g_object_notify (G_OBJECT (self), "active");
priv->executed = TRUE;
g_object_notify (G_OBJECT (self), "executed");
transaction_maybe_emit_closed (self);
}
@ -416,6 +420,9 @@ transaction_get_property (GObject *object,
case PROP_REDIRECT_OUTPUT:
g_value_set_boolean (value, priv->redirect_output);
break;
case PROP_EXECUTED:
g_value_set_boolean (value, priv->executed);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
@ -428,9 +435,6 @@ transaction_dispose (GObject *object)
RpmostreedTransaction *self = RPMOSTREED_TRANSACTION (object);
RpmostreedTransactionPrivate *priv = rpmostreed_transaction_get_private (self);
if (priv->sysroot != NULL)
ostree_sysroot_unlock (priv->sysroot);
g_hash_table_remove_all (priv->peer_connections);
g_clear_object (&priv->invocation);
@ -452,6 +456,9 @@ transaction_finalize (GObject *object)
g_debug ("%s (%p): Finalized", G_OBJECT_TYPE_NAME (self), self);
if (priv->sysroot != NULL)
ostree_sysroot_unlock (priv->sysroot);
if (priv->watch_id > 0)
g_bus_unwatch_name (priv->watch_id);
@ -490,6 +497,25 @@ transaction_constructed (GObject *object)
}
}
static gboolean
foreach_close_peer (gpointer key,
gpointer value,
gpointer user_data)
{
GDBusConnection *conn = key;
g_dbus_connection_close_sync (conn, NULL, NULL);
return TRUE;
}
void
rpmostreed_transaction_force_close (RpmostreedTransaction *transaction)
{
RpmostreedTransactionPrivate *priv = rpmostreed_transaction_get_private (transaction);
g_assert (priv->executed);
g_dbus_server_stop (priv->server);
g_hash_table_foreach_remove (priv->peer_connections, foreach_close_peer, NULL);
}
static void
on_sysroot_journal_msg (OstreeSysroot *sysroot,
const char *msg,
@ -663,11 +689,11 @@ rpmostreed_transaction_class_init (RpmostreedTransactionClass *class)
object_class->constructed = transaction_constructed;
g_object_class_install_property (object_class,
PROP_ACTIVE,
g_param_spec_boolean ("active",
"Active",
"Whether the transaction is active (unfinished)",
TRUE,
PROP_EXECUTED,
g_param_spec_boolean ("executed",
"Executed",
"Whether the transaction has finished",
FALSE,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
@ -724,6 +750,7 @@ rpmostreed_transaction_dbus_iface_init (RPMOSTreeTransactionIface *iface)
static void
rpmostreed_transaction_init (RpmostreedTransaction *self)
{
g_assert (!rpmostreed_sysroot_has_txn (rpmostreed_sysroot_get ()));
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
RPMOSTREED_TYPE_TRANSACTION,
RpmostreedTransactionPrivate);

View File

@ -56,3 +56,4 @@ void rpmostreed_transaction_connect_download_progress
void rpmostreed_transaction_connect_signature_progress
(RpmostreedTransaction *transaction,
OstreeRepo *repo);
void rpmostreed_transaction_force_close (RpmostreedTransaction *transaction);

View File

@ -39,6 +39,3 @@ typedef struct _RpmostreedOSExperimental RpmostreedOSExperimental;
struct _RpmostreedTransaction;
typedef struct _RpmostreedTransaction RpmostreedTransaction;
struct _RpmostreedTransactionMonitor;
typedef struct _RpmostreedTransactionMonitor RpmostreedTransactionMonitor;