From 29560adcc79a259a0be3511c056ee7453aa26c04 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 13 Mar 2003 00:56:43 +0000 Subject: 2003-03-12 Havoc Pennington Mega-patch that gets the message bus daemon initially handling out-of-memory. Work still needed. Also lots of random moving stuff to DBusError instead of ResultCode. * dbus/dbus-list.c (_dbus_list_length_is_one): new function * dbus/dbus-connection.c (dbus_connection_send_with_reply_and_block): use DBusError * dbus/dbus-bus.c: adapt to API changes, make it use DBusError not DBusResultCode * dbus/dbus-connection.c (dbus_connection_send): drop the result code here, as the only failure possible is OOM. * bus/connection.c (bus_connection_disconnect): rename bus_connection_disconnected as it's a notification only * bus/driver.c (bus_driver_handle_acquire_service): don't free "name" on get_args failure, should be done by get_args; don't disconnect client for bad args, just return an error. (bus_driver_handle_service_exists): ditto * bus/services.c (bus_services_list): NULL-terminate returned array * bus/driver.c (bus_driver_send_service_lost) (bus_driver_send_service_acquired): send messages from driver to a specific client to the client's unique name, not to the broadcast service. * dbus/dbus-message.c (decode_header_data): reject messages that contain no name field (_dbus_message_get_client_serial): rename to dbus_message_get_serial and make public (_dbus_message_set_serial): rename from set_client_serial (_dbus_message_set_reply_serial): make public (_dbus_message_get_reply_serial): make public * bus/connection.c (bus_connection_foreach): allow stopping iteration by returning FALSE from foreach function. * dbus/dbus-connection.c (dbus_connection_send_preallocated) (dbus_connection_free_preallocated_send) (dbus_connection_preallocate_send): new API for sending a message without possibility of malloc failure. (dbus_connection_send_message): rename to just dbus_connection_send (and same for whole function family) * dbus/dbus-errors.c (dbus_error_free): make this reinit the error * dbus/dbus-sysdeps.c (_dbus_exit): new function * bus/activation.c: handle/return errors * dbus/dbus-errors.h: add more DBUS_ERROR #define * dbus/dbus-sysdeps.c (_dbus_directory_open) (_dbus_file_get_contents) (_dbus_directory_get_next_file): use DBusError instead of DBusResultCode (_dbus_result_from_errno): move to this file --- bus/dispatch.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 228 insertions(+), 53 deletions(-) (limited to 'bus/dispatch.c') diff --git a/bus/dispatch.c b/bus/dispatch.c index 76e10a9f..d9fe81ac 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -30,23 +30,110 @@ static int message_handler_slot; -static void +typedef struct +{ + DBusMessage *message; + BusTransaction *transaction; + DBusError *error; +} SendMessageData; + +static dbus_bool_t send_one_message (DBusConnection *connection, void *data) { - /* Only send messages to registered connections */ - if (bus_connection_get_name (connection) == NULL) - return; + SendMessageData *d = data; - BUS_HANDLE_OOM (dbus_connection_send_message (connection, data, NULL, NULL)); + if (!bus_connection_is_active (connection)) + return TRUE; + + if (!bus_transaction_send_message (d->transaction, + connection, + d->message)) + { + BUS_SET_OOM (d->error); + return FALSE; + } + + return TRUE; } -void -bus_dispatch_broadcast_message (DBusMessage *message) +dbus_bool_t +bus_dispatch_broadcast_message (BusTransaction *transaction, + DBusMessage *message, + DBusError *error) { - _dbus_assert (dbus_message_get_sender (message) != NULL); - - bus_connection_foreach (send_one_message, message); + DBusError tmp_error; + SendMessageData d; + _dbus_assert (dbus_message_get_sender (message) != NULL); + + dbus_error_init (&tmp_error); + d.message = message; + d.transaction = transaction; + d.error = &tmp_error; + bus_connection_foreach (send_one_message, &d); + + if (dbus_error_is_set (&tmp_error)) + { + dbus_move_error (&tmp_error, error); + return FALSE; + } + else + return TRUE; +} + +static dbus_bool_t +send_service_nonexistent_error (BusTransaction *transaction, + DBusConnection *connection, + const char *service_name, + DBusMessage *in_reply_to, + DBusError *error) +{ + DBusMessage *error_reply; + DBusString error_message; + const char *error_str; + + /* Trying to send a message to a non-existant service, + * bounce back an error message. + */ + + if (!_dbus_string_init (&error_message, _DBUS_INT_MAX)) + { + BUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_string_append (&error_message, "Service \"") || + !_dbus_string_append (&error_message, service_name) || + !_dbus_string_append (&error_message, "does not exist")) + { + _dbus_string_free (&error_message); + BUS_SET_OOM (error); + return FALSE; + } + + _dbus_string_get_const_data (&error_message, &error_str); + error_reply = dbus_message_new_error_reply (in_reply_to, + DBUS_ERROR_SERVICE_DOES_NOT_EXIST, + error_str); + + _dbus_string_free (&error_message); + + if (error_reply == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } + + if (!bus_transaction_send_message (transaction, connection, error_reply)) + { + dbus_message_unref (error_reply); + BUS_SET_OOM (error); + return FALSE; + } + + dbus_message_unref (error_reply); + + return TRUE; } static DBusHandlerResult @@ -56,76 +143,164 @@ bus_dispatch_message_handler (DBusMessageHandler *handler, void *user_data) { const char *sender, *service_name, *message_name; + DBusError error; + BusTransaction *transaction; + + transaction = NULL; + dbus_error_init (&error); - /* Assign a sender to the message */ - sender = bus_connection_get_name (connection); - BUS_HANDLE_OOM (dbus_message_set_sender (message, sender)); + /* If we can't even allocate an OOM error, we just go to sleep + * until we can. + */ + while (!bus_connection_preallocate_oom_error (connection)) + bus_wait_for_memory (); + + /* Ref connection in case we disconnect it at some point in here */ + dbus_connection_ref (connection); service_name = dbus_message_get_service (message); message_name = dbus_message_get_name (message); + + _dbus_assert (message_name != NULL); /* DBusMessageLoader is supposed to check this */ + + /* If service_name is NULL, this is a message to the bus daemon, not intended + * to actually go "on the bus"; e.g. a peer-to-peer ping. Handle these + * immediately, especially disconnection messages. + */ + if (service_name == NULL) + { + if (strcmp (message_name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0) + bus_connection_disconnected (connection); + + /* DBusConnection also handles some of these automatically, we leave + * it to do so. + */ + goto out; + } + + _dbus_assert (service_name != NULL); /* this message is intended for bus routing */ - /* TODO: Crashes if service_name == NULL */ + /* Create our transaction */ + transaction = bus_transaction_new (); + if (transaction == NULL) + { + BUS_SET_OOM (&error); + goto out; + } - /* See if the message is to the driver */ - if (message_name && strcmp (message_name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0) + /* Assign a sender to the message */ + if (bus_connection_is_active (connection)) { - bus_connection_disconnect (connection); + sender = bus_connection_get_name (connection); + _dbus_assert (sender != NULL); + + if (!dbus_message_set_sender (message, sender)) + { + BUS_SET_OOM (&error); + goto out; + } } - else if (strcmp (service_name, DBUS_SERVICE_DBUS) == 0) + + if (strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */ { - bus_driver_handle_message (connection, message); + if (!bus_driver_handle_message (connection, transaction, message, &error)) + goto out; } - else if (sender == NULL) + else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */ { _dbus_verbose ("Received message from non-registered client. Disconnecting.\n"); dbus_connection_disconnect (connection); } - else if (strcmp (service_name, DBUS_SERVICE_BROADCAST) == 0) + /* FIXME what if we un-special-case this service and just have a flag + * on services that all service owners will get messages to it, not just + * the primary owner. + */ + else if (strcmp (service_name, DBUS_SERVICE_BROADCAST) == 0) /* spam! */ { - bus_dispatch_broadcast_message (message); + if (!bus_dispatch_broadcast_message (transaction, message, &error)) + goto out; } - else + else /* route to named service */ { DBusString service_string; BusService *service; _dbus_string_init_const (&service_string, service_name); - service = bus_service_lookup (&service_string, FALSE); - - if (!service) - { - DBusMessage *error_reply; - DBusString error_message; - const char *error_str; - - /* Trying to send a message to a non-existant service, - bounce back an error message. */ - - BUS_HANDLE_OOM (_dbus_string_init (&error_message, _DBUS_INT_MAX)); - - BUS_HANDLE_OOM (_dbus_string_append (&error_message, "Service \"")); - BUS_HANDLE_OOM (_dbus_string_append (&error_message, service_name)); - BUS_HANDLE_OOM (_dbus_string_append (&error_message, "does not exist")); + service = bus_service_lookup (&service_string); - _dbus_string_get_const_data (&error_message, &error_str); - BUS_HANDLE_OOM (error_reply = dbus_message_new_error_reply (message, DBUS_ERROR_SERVICE_DOES_NOT_EXIST, - error_str)); - _dbus_string_free (&error_message); + if (service == NULL) + { + if (!send_service_nonexistent_error (transaction, connection, + service_name, + message, &error)) + goto out; + } + else + { + _dbus_assert (bus_service_get_primary_owner (service) != NULL); + + /* Dispatch the message */ + if (!bus_transaction_send_message (transaction, + bus_service_get_primary_owner (service), + message)) + { + BUS_SET_OOM (&error); + goto out; + } + } + } + + out: + if (dbus_error_is_set (&error)) + { + if (!dbus_connection_get_is_connected (connection)) + { + /* If we disconnected it, we won't bother to send it any error + * messages. + */ + } + else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) + { + bus_connection_send_oom_error (connection, message); - /* Dispatch the message */ - BUS_HANDLE_OOM (dbus_connection_send_message (connection, error_reply, NULL, NULL)); - dbus_message_unref (error_reply); - } + /* cancel transaction due to OOM */ + if (transaction != NULL) + { + bus_transaction_cancel_and_free (transaction); + transaction = NULL; + } + } else - { - _dbus_assert (bus_service_get_primary_owner (service) != NULL); + { + /* Try to send the real error, if no mem to do that, send + * the OOM error + */ + _dbus_assert (transaction != NULL); + + if (!bus_transaction_send_error_reply (transaction, connection, + &error, message)) + { + bus_connection_send_oom_error (connection, message); + + /* cancel transaction due to OOM */ + if (transaction != NULL) + { + bus_transaction_cancel_and_free (transaction); + transaction = NULL; + } + } + } - /* Dispatch the message */ - BUS_HANDLE_OOM (dbus_connection_send_message (bus_service_get_primary_owner (service), - message, NULL, NULL)); - } + dbus_error_free (&error); } + if (transaction != NULL) + { + bus_transaction_execute_and_free (transaction); + } + + dbus_connection_unref (connection); + return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } -- cgit