summaryrefslogtreecommitdiffstats
path: root/bus/dispatch.c
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-03-13 00:56:43 +0000
committerHavoc Pennington <hp@redhat.com>2003-03-13 00:56:43 +0000
commit29560adcc79a259a0be3511c056ee7453aa26c04 (patch)
tree57e72dd26b5876da48379e5ff910c63e66cb7001 /bus/dispatch.c
parent799a3ff443f5357ae7857ebe989a7f92f7bd84df (diff)
2003-03-12 Havoc Pennington <hp@redhat.com>
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
Diffstat (limited to 'bus/dispatch.c')
-rw-r--r--bus/dispatch.c281
1 files changed, 228 insertions, 53 deletions
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;
}