From 2250539aeee0569f8861841d1f5ff16f1539715e Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sat, 5 Apr 2003 19:03:40 +0000 Subject: 2003-04-05 Havoc Pennington * bus/loop.c (bus_loop_iterate): fix the timeout code, using magic from GLib * dbus/dbus-spawn.c (_dbus_babysitter_unref): set sitter_pid to -1 once we've reaped the babysitter (_dbus_babysitter_handle_watch): do as much work as we can, not just one go of it * bus/activation.c: add code using DBusBabysitter so that we handle it when a service fails to start up properly. (bus_activation_service_created): don't remove the activation entries as we go, just let them get removed when we free the pending activation. Unref reply messages after sending them. --- bus/activation.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 253 insertions(+), 12 deletions(-) (limited to 'bus/activation.c') diff --git a/bus/activation.c b/bus/activation.c index 2045baa2..54ddd948 100644 --- a/bus/activation.c +++ b/bus/activation.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -62,8 +63,12 @@ struct BusPendingActivationEntry typedef struct { + BusActivation *activation; char *service_name; DBusList *entries; + DBusBabysitter *babysitter; + DBusTimeout *timeout; + unsigned int timeout_added : 1; } BusPendingActivation; static void @@ -74,21 +79,53 @@ bus_pending_activation_entry_free (BusPendingActivationEntry *entry) if (entry->connection) dbus_connection_unref (entry->connection); - + dbus_free (entry); } static void -bus_pending_activation_free (BusPendingActivation *activation) +handle_timeout_callback (DBusTimeout *timeout, + void *data) +{ + BusPendingActivation *pending_activation = data; + + while (!dbus_timeout_handle (pending_activation->timeout)) + bus_wait_for_memory (); +} + +static void +bus_pending_activation_free (BusPendingActivation *pending_activation) { DBusList *link; - if (!activation) + if (pending_activation == NULL) /* hash table requires this */ return; - dbus_free (activation->service_name); + 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); + pending_activation->timeout_added = FALSE; + } + + if (pending_activation->timeout) + _dbus_timeout_unref (pending_activation->timeout); + + if (pending_activation->babysitter) + { + if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter, + NULL, NULL, NULL, + pending_activation->babysitter, + NULL)) + _dbus_assert_not_reached ("setting watch functions to NULL failed"); + + _dbus_babysitter_unref (pending_activation->babysitter); + } + + dbus_free (pending_activation->service_name); - link = _dbus_list_get_first_link (&activation->entries); + link = _dbus_list_get_first_link (&pending_activation->entries); while (link != NULL) { @@ -96,11 +133,11 @@ bus_pending_activation_free (BusPendingActivation *activation) bus_pending_activation_entry_free (entry); - link = _dbus_list_get_next_link (&activation->entries, link); + link = _dbus_list_get_next_link (&pending_activation->entries, link); } - _dbus_list_clear (&activation->entries); + _dbus_list_clear (&pending_activation->entries); - dbus_free (activation); + dbus_free (pending_activation); } static void @@ -478,11 +515,10 @@ bus_activation_service_created (BusActivation *activation, BUS_SET_OOM (error); goto error; } + + dbus_message_unref (message); } - bus_pending_activation_entry_free (entry); - - _dbus_list_remove_link (&pending_activation->entries, link); link = next; } @@ -495,6 +531,165 @@ bus_activation_service_created (BusActivation *activation, return FALSE; } +/** + * FIXME @todo the error messages here would ideally be preallocated + * so we don't need to allocate memory to send them. + * Using the usual tactic, prealloc an OOM message, then + * if we can't alloc the real error send the OOM error instead. + */ +static dbus_bool_t +try_send_activation_failure (BusPendingActivation *pending_activation, + const DBusError *how) +{ + BusActivation *activation; + DBusMessage *message; + DBusList *link; + BusTransaction *transaction; + + activation = pending_activation->activation; + + transaction = bus_transaction_new (activation->context); + if (transaction == NULL) + return FALSE; + + link = _dbus_list_get_first_link (&pending_activation->entries); + while (link != NULL) + { + BusPendingActivationEntry *entry = link->data; + DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link); + + if (dbus_connection_get_is_connected (entry->connection)) + { + message = dbus_message_new_error_reply (entry->activation_message, + how->name, + how->message); + if (!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; + } + + bus_transaction_execute_and_free (transaction); + + return TRUE; + + error: + if (transaction) + bus_transaction_cancel_and_free (transaction); + return FALSE; +} + +/** + * Free the pending activation and send an error message to all the + * connections that were waiting for it. + */ +static void +pending_activation_failed (BusPendingActivation *pending_activation, + const DBusError *how) +{ + /* FIXME use preallocated OOM messages instead of bus_wait_for_memory() */ + while (!try_send_activation_failure (pending_activation, how)) + bus_wait_for_memory (); + + /* Destroy this pending activation */ + _dbus_hash_table_remove_string (pending_activation->activation->pending_activations, + pending_activation->service_name); +} + +static dbus_bool_t +babysitter_watch_callback (DBusWatch *watch, + unsigned int condition, + void *data) +{ + BusPendingActivation *pending_activation = data; + dbus_bool_t retval; + DBusBabysitter *babysitter; + + babysitter = pending_activation->babysitter; + + _dbus_babysitter_ref (babysitter); + + retval = _dbus_babysitter_handle_watch (babysitter, watch, condition); + + if (_dbus_babysitter_get_child_exited (babysitter)) + { + DBusError error; + + dbus_error_init (&error); + _dbus_babysitter_set_child_exit_error (babysitter, &error); + + /* Destroys the pending activation */ + pending_activation_failed (pending_activation, &error); + + dbus_error_free (&error); + } + + _dbus_babysitter_unref (babysitter); + + return retval; +} + +static dbus_bool_t +add_babysitter_watch (DBusWatch *watch, + void *data) +{ + BusPendingActivation *pending_activation = data; + + return bus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context), + watch, babysitter_watch_callback, pending_activation, + NULL); +} + +static void +remove_babysitter_watch (DBusWatch *watch, + void *data) +{ + BusPendingActivation *pending_activation = data; + + bus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context), + watch, babysitter_watch_callback, pending_activation); +} + +static dbus_bool_t +pending_activation_timed_out (void *data) +{ + BusPendingActivation *pending_activation = data; + DBusError error; + + /* Kill the spawned process, since it sucks + * (not sure this is what we want to do, but + * may as well try it for now) + */ + _dbus_babysitter_kill_child (pending_activation->babysitter); + + dbus_error_init (&error); + + dbus_set_error (&error, DBUS_ERROR_TIMED_OUT, + "Activation of %s timed out", + pending_activation->service_name); + + pending_activation_failed (pending_activation, &error); + + dbus_error_free (&error); + + return TRUE; +} + dbus_bool_t bus_activation_activate_service (BusActivation *activation, DBusConnection *connection, @@ -586,6 +781,9 @@ bus_activation_activate_service (BusActivation *activation, bus_pending_activation_entry_free (pending_activation_entry); return FALSE; } + + pending_activation->activation = activation; + pending_activation->service_name = _dbus_strdup (service_name); if (!pending_activation->service_name) { @@ -595,6 +793,33 @@ bus_activation_activate_service (BusActivation *activation, return FALSE; } + pending_activation->timeout = + _dbus_timeout_new (bus_context_get_activation_timeout (activation->context), + pending_activation_timed_out, + pending_activation, + NULL); + if (!pending_activation->timeout) + { + BUS_SET_OOM (error); + bus_pending_activation_free (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)) + { + BUS_SET_OOM (error); + bus_pending_activation_free (pending_activation); + bus_pending_activation_entry_free (pending_activation_entry); + return FALSE; + } + + pending_activation->timeout_added = TRUE; + if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry)) { BUS_SET_OOM (error); @@ -620,7 +845,7 @@ bus_activation_activate_service (BusActivation *activation, argv[0] = entry->exec; argv[1] = NULL; - if (!_dbus_spawn_async_with_babysitter (NULL, argv, + if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv, child_setup, activation, error)) { @@ -628,6 +853,22 @@ bus_activation_activate_service (BusActivation *activation, pending_activation->service_name); return FALSE; } + + _dbus_assert (pending_activation->babysitter != NULL); + + if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter, + add_babysitter_watch, + remove_babysitter_watch, + NULL, + pending_activation, + NULL)) + { + BUS_SET_OOM (error); + + _dbus_hash_table_remove_string (activation->pending_activations, + pending_activation->service_name); + return FALSE; + } return TRUE; } -- cgit