DBusHashTable *pending_activations;
char *server_address;
BusContext *context;
+ int n_pending_activations; /**< This is in fact the number of BusPendingActivationEntry,
+ * i.e. number of pending activation requests, not pending
+ * activations per se
+ */
};
typedef struct
typedef struct
{
+ int refcount;
BusActivation *activation;
char *service_name;
DBusList *entries;
+ int n_entries;
DBusBabysitter *babysitter;
DBusTimeout *timeout;
unsigned int timeout_added : 1;
BusPendingActivation *pending_activation = data;
while (!dbus_timeout_handle (pending_activation->timeout))
- bus_wait_for_memory ();
+ _dbus_wait_for_memory ();
+}
+
+static void
+bus_pending_activation_ref (BusPendingActivation *pending_activation)
+{
+ _dbus_assert (pending_activation->refcount > 0);
+ pending_activation->refcount += 1;
}
static void
-bus_pending_activation_free (BusPendingActivation *pending_activation)
+bus_pending_activation_unref (BusPendingActivation *pending_activation)
{
DBusList *link;
if (pending_activation == NULL) /* hash table requires this */
return;
+ _dbus_assert (pending_activation->refcount > 0);
+ pending_activation->refcount -= 1;
+
+ if (pending_activation->refcount > 0)
+ return;
+
if (pending_activation->timeout_added)
{
- bus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
- pending_activation->timeout,
- handle_timeout_callback, pending_activation);
+ _dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
+ pending_activation->timeout,
+ handle_timeout_callback, pending_activation);
pending_activation->timeout_added = FALSE;
}
link = _dbus_list_get_next_link (&pending_activation->entries, link);
}
_dbus_list_clear (&pending_activation->entries);
+
+ pending_activation->activation->n_pending_activations -=
+ pending_activation->n_entries;
+
+ _dbus_assert (pending_activation->activation->n_pending_activations >= 0);
dbus_free (pending_activation);
}
activation->refcount = 1;
activation->context = context;
+ activation->n_pending_activations = 0;
if (!_dbus_string_copy_data (address, &activation->server_address))
{
}
activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
- (DBusFreeFunction)bus_pending_activation_free);
+ (DBusFreeFunction)bus_pending_activation_unref);
if (activation->pending_activations == NULL)
{
}
}
+typedef struct
+{
+ BusPendingActivation *pending_activation;
+ DBusPreallocatedHash *hash_entry;
+} RestorePendingData;
+
+static void
+restore_pending (void *data)
+{
+ RestorePendingData *d = data;
+
+ _dbus_assert (d->pending_activation != NULL);
+ _dbus_assert (d->hash_entry != NULL);
+
+ _dbus_verbose ("Restoring pending activation for service %s, has timeout = %d\n",
+ d->pending_activation->service_name,
+ d->pending_activation->timeout_added);
+
+ _dbus_hash_table_insert_string_preallocated (d->pending_activation->activation->pending_activations,
+ d->hash_entry,
+ d->pending_activation->service_name, d->pending_activation);
+
+ bus_pending_activation_ref (d->pending_activation);
+
+ d->hash_entry = NULL;
+}
+
+static void
+free_pending_restore_data (void *data)
+{
+ RestorePendingData *d = data;
+
+ if (d->hash_entry)
+ _dbus_hash_table_free_preallocated_entry (d->pending_activation->activation->pending_activations,
+ d->hash_entry);
+
+ bus_pending_activation_unref (d->pending_activation);
+
+ dbus_free (d);
+}
+
+static dbus_bool_t
+add_restore_pending_to_transaction (BusTransaction *transaction,
+ BusPendingActivation *pending_activation)
+{
+ RestorePendingData *d;
+
+ d = dbus_new (RestorePendingData, 1);
+ if (d == NULL)
+ return FALSE;
+
+ d->pending_activation = pending_activation;
+ d->hash_entry = _dbus_hash_table_preallocate_entry (d->pending_activation->activation->pending_activations);
+
+ bus_pending_activation_ref (d->pending_activation);
+
+ if (d->hash_entry == NULL ||
+ !bus_transaction_add_cancel_hook (transaction, restore_pending, d,
+ free_pending_restore_data))
+ {
+ free_pending_restore_data (d);
+ return FALSE;
+ }
+
+ _dbus_verbose ("Saved pending activation to be restored if the transaction fails\n");
+
+ return TRUE;
+}
+
dbus_bool_t
bus_activation_service_created (BusActivation *activation,
const char *service_name,
goto error;
}
- if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
- !dbus_message_append_args (message,
+ if (!dbus_message_append_args (message,
DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
0))
{
goto error;
}
- if (!bus_transaction_send_message (transaction, entry->connection, message))
+ if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
{
dbus_message_unref (message);
BUS_SET_OOM (error);
goto error;
}
-
+
dbus_message_unref (message);
}
link = next;
}
+
+ if (!add_restore_pending_to_transaction (transaction, pending_activation))
+ {
+ _dbus_verbose ("Could not add cancel hook to transaction to revert removing pending activation\n");
+ BUS_SET_OOM (error);
+ goto error;
+ }
_dbus_hash_table_remove_string (activation->pending_activations, service_name);
return TRUE;
error:
- _dbus_hash_table_remove_string (activation->pending_activations, service_name);
return FALSE;
}
const DBusError *how)
{
BusActivation *activation;
- DBusMessage *message;
DBusList *link;
BusTransaction *transaction;
if (dbus_connection_get_is_connected (entry->connection))
{
- message = dbus_message_new_error_reply (entry->activation_message,
- how->name,
- how->message);
- if (!message)
+ if (!bus_transaction_send_error_reply (transaction,
+ entry->connection,
+ how,
+ entry->activation_message))
goto error;
-
- if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
- {
- dbus_message_unref (message);
- goto error;
- }
-
- if (!bus_transaction_send_message (transaction, entry->connection, message))
- {
- dbus_message_unref (message);
- goto error;
- }
-
- dbus_message_unref (message);
}
-
+
link = next;
}
{
/* FIXME use preallocated OOM messages instead of bus_wait_for_memory() */
while (!try_send_activation_failure (pending_activation, how))
- bus_wait_for_memory ();
+ _dbus_wait_for_memory ();
/* Destroy this pending activation */
_dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
_dbus_babysitter_ref (babysitter);
- retval = _dbus_babysitter_handle_watch (babysitter, watch, condition);
-
+ retval = dbus_watch_handle (watch, condition);
+
+ /* FIXME this is broken in the same way that
+ * connection watches used to be; there should be
+ * a separate callback for status change, instead
+ * of doing "if we handled a watch status might
+ * have changed"
+ *
+ * Fixing this lets us move dbus_watch_handle
+ * calls into dbus-mainloop.c
+ */
+
if (_dbus_babysitter_get_child_exited (babysitter))
{
DBusError error;
{
BusPendingActivation *pending_activation = data;
- return bus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
- watch, babysitter_watch_callback, pending_activation,
- NULL);
+ return _dbus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
+ watch, babysitter_watch_callback, pending_activation,
+ NULL);
}
static void
{
BusPendingActivation *pending_activation = data;
- bus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
- watch, babysitter_watch_callback, pending_activation);
+ _dbus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
+ watch, babysitter_watch_callback, pending_activation);
}
static dbus_bool_t
return TRUE;
}
+static void
+cancel_pending (void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ _dbus_verbose ("Canceling pending activation of %s\n",
+ pending_activation->service_name);
+
+ if (pending_activation->babysitter)
+ _dbus_babysitter_kill_child (pending_activation->babysitter);
+
+ _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
+ pending_activation->service_name);
+}
+
+static void
+free_pending_cancel_data (void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ bus_pending_activation_unref (pending_activation);
+}
+
+static dbus_bool_t
+add_cancel_pending_to_transaction (BusTransaction *transaction,
+ BusPendingActivation *pending_activation)
+{
+ if (!bus_transaction_add_cancel_hook (transaction, cancel_pending,
+ pending_activation,
+ free_pending_cancel_data))
+ return FALSE;
+
+ bus_pending_activation_ref (pending_activation);
+
+ _dbus_verbose ("Saved pending activation to be canceled if the transaction fails\n");
+
+ return TRUE;
+}
+
dbus_bool_t
bus_activation_activate_service (BusActivation *activation,
DBusConnection *connection,
dbus_bool_t retval;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (activation->n_pending_activations >=
+ bus_context_get_max_pending_activations (activation->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The maximum number of pending activations has been reached, activation of %s failed",
+ service_name);
+ return FALSE;
+ }
entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
_dbus_string_init_const (&service_str, service_name);
if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
{
+ _dbus_verbose ("Service \"%s\" is already active\n", service_name);
+
message = dbus_message_new_reply (activation_message);
if (!message)
{
+ _dbus_verbose ("No memory to create reply to activate message\n");
BUS_SET_OOM (error);
return FALSE;
}
- if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
- !dbus_message_append_args (message,
+ if (!dbus_message_append_args (message,
DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE,
0))
{
+ _dbus_verbose ("No memory to set args of reply to activate message\n");
BUS_SET_OOM (error);
dbus_message_unref (message);
return FALSE;
}
- retval = bus_transaction_send_message (transaction, connection, message);
+ retval = bus_transaction_send_from_driver (transaction, connection, message);
dbus_message_unref (message);
if (!retval)
- BUS_SET_OOM (error);
+ {
+ _dbus_verbose ("Failed to send reply\n");
+ BUS_SET_OOM (error);
+ }
return retval;
}
pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
if (!pending_activation_entry)
{
+ _dbus_verbose ("Failed to create pending activation entry\n");
BUS_SET_OOM (error);
return FALSE;
}
{
if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
{
+ _dbus_verbose ("Failed to append a new entry to pending activation\n");
+
BUS_SET_OOM (error);
bus_pending_activation_entry_free (pending_activation_entry);
-
return FALSE;
}
+
+ pending_activation->n_entries += 1;
+ pending_activation->activation->n_pending_activations += 1;
}
else
{
pending_activation = dbus_new0 (BusPendingActivation, 1);
if (!pending_activation)
{
+ _dbus_verbose ("Failed to create pending activation\n");
+
BUS_SET_OOM (error);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
pending_activation->activation = activation;
+ pending_activation->refcount = 1;
pending_activation->service_name = _dbus_strdup (service_name);
if (!pending_activation->service_name)
{
+ _dbus_verbose ("Failed to copy service name for pending activation\n");
+
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
NULL);
if (!pending_activation->timeout)
{
+ _dbus_verbose ("Failed to create timeout for pending activation\n");
+
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
- if (!bus_loop_add_timeout (bus_context_get_loop (activation->context),
- pending_activation->timeout,
- handle_timeout_callback,
- pending_activation,
- NULL))
+ if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
+ pending_activation->timeout,
+ handle_timeout_callback,
+ pending_activation,
+ NULL))
{
+ _dbus_verbose ("Failed to add timeout for pending activation\n");
+
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
{
+ _dbus_verbose ("Failed to add entry to just-created pending activation\n");
+
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
bus_pending_activation_entry_free (pending_activation_entry);
return FALSE;
}
+
+ pending_activation->n_entries += 1;
+ pending_activation->activation->n_pending_activations += 1;
if (!_dbus_hash_table_insert_string (activation->pending_activations,
- pending_activation->service_name, pending_activation))
+ pending_activation->service_name,
+ pending_activation))
{
+ _dbus_verbose ("Failed to put pending activation in hash table\n");
+
BUS_SET_OOM (error);
- bus_pending_activation_free (pending_activation);
+ bus_pending_activation_unref (pending_activation);
return FALSE;
}
}
+
+ if (!add_cancel_pending_to_transaction (transaction, pending_activation))
+ {
+ _dbus_verbose ("Failed to add pending activation cancel hook to transaction\n");
+ BUS_SET_OOM (error);
+ _dbus_hash_table_remove_string (activation->pending_activations,
+ pending_activation->service_name);
+ return FALSE;
+ }
/* FIXME we need to support a full command line, not just a single
* argv[0]
child_setup, activation,
error))
{
- _dbus_hash_table_remove_string (activation->pending_activations,
- pending_activation->service_name);
+ _dbus_verbose ("Failed to spawn child\n");
+ _DBUS_ASSERT_ERROR_IS_SET (error);
return FALSE;
}
NULL))
{
BUS_SET_OOM (error);
-
- _dbus_hash_table_remove_string (activation->pending_activations,
- pending_activation->service_name);
+ _dbus_verbose ("Failed to set babysitter watch functions\n");
return FALSE;
}