#include "signals.h"
#include "expirelist.h"
#include "selinux.h"
+#include "apparmor.h"
+#include "check.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-timeout.h>
#include <dbus/dbus-connection-internal.h>
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-message-internal.h>
+#ifdef DBUS_ENABLE_CYNARA
+#include <stdlib.h>
+#include <cynara-session.h>
+#include <stdio.h>
+#endif
/* Trim executed commands to this length; we want to keep logs readable */
#define MAX_LOG_COMMAND_LEN 50
int stamp; /**< Incrementing number */
BusExpireList *pending_replies; /**< List of pending replies */
+ /** List of all monitoring connections, a subset of completed.
+ * Each member is a #DBusConnection. */
+ DBusList *monitors;
+ BusMatchmaker *monitor_matchmaker;
+
#ifdef DBUS_ENABLE_STATS
int total_match_rules;
int peak_match_rules;
DBusMessage *oom_message;
DBusPreallocatedSend *oom_preallocated;
BusClientPolicy *policy;
+ DBusList *deferred_messages; /**< Queue of messages deferred due to pending policy check */
char *cached_loginfo_string;
BusSELinuxID *selinux_id;
+ BusAppArmorConfinement *apparmor_confinement;
long connection_tv_sec; /**< Time when we connected (seconds component) */
long connection_tv_usec; /**< Time when we connected (microsec component) */
#ifdef DBUS_ENABLE_STATS
int peak_match_rules;
int peak_bus_names;
+ int peak_pending_replies;
#endif
int n_pending_unix_fds;
DBusTimeout *pending_unix_fds_timeout;
+
+ /** non-NULL if and only if this is a monitor */
+ DBusList *link_in_monitors;
+#ifdef DBUS_ENABLE_CYNARA
+ char *cynara_session_id;
+#endif
} BusConnectionData;
static dbus_bool_t bus_pending_reply_expired (BusExpireList *list,
#define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
-static DBusLoop*
-connection_get_loop (DBusConnection *connection)
+DBusLoop*
+bus_connection_get_loop (DBusConnection *connection)
{
BusConnectionData *d;
d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert(d != NULL);
return bus_context_get_loop (d->connections->context);
}
bus_transaction_execute_and_free (transaction);
}
+ bus_connection_clear_deferred_messages(connection);
+
bus_dispatch_remove_connection (connection);
/* no more watching */
bus_connection_remove_transactions (connection);
+ if (d->link_in_monitors != NULL)
+ {
+ BusMatchmaker *mm = d->connections->monitor_matchmaker;
+
+ if (mm != NULL)
+ bus_matchmaker_disconnected (mm, connection);
+
+ _dbus_list_remove_link (&d->connections->monitors, d->link_in_monitors);
+ d->link_in_monitors = NULL;
+ }
+
if (d->link_in_connection_list != NULL)
{
if (d->name != NULL)
{
DBusConnection *connection = data;
- return _dbus_loop_add_watch (connection_get_loop (connection), watch);
+ return _dbus_loop_add_watch (bus_connection_get_loop (connection), watch);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_watch (connection_get_loop (connection), watch);
+ _dbus_loop_remove_watch (bus_connection_get_loop (connection), watch);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_toggle_watch (connection_get_loop (connection), watch);
+ _dbus_loop_toggle_watch (bus_connection_get_loop (connection), watch);
}
static dbus_bool_t
{
DBusConnection *connection = data;
- return _dbus_loop_add_timeout (connection_get_loop (connection), timeout);
+ return _dbus_loop_add_timeout (bus_connection_get_loop (connection), timeout);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_timeout (connection_get_loop (connection), timeout);
+ _dbus_loop_remove_timeout (bus_connection_get_loop (connection), timeout);
}
static void
if (d->policy)
bus_client_policy_unref (d->policy);
- if (d->selinux_id)
- bus_selinux_id_unref (d->selinux_id);
+ if (d->apparmor_confinement)
+ bus_apparmor_confinement_unref (d->apparmor_confinement);
dbus_free (d->cached_loginfo_string);
dbus_free (d->name);
+#ifdef DBUS_ENABLE_CYNARA
+ free (d->cynara_session_id);
+#endif
+
dbus_free (d);
}
if (connections->expire_timeout == NULL)
goto failed_3;
- _dbus_timeout_set_enabled (connections->expire_timeout, FALSE);
+ _dbus_timeout_disable (connections->expire_timeout);
connections->pending_replies = bus_expire_list_new (bus_context_get_loop (context),
bus_context_get_reply_timeout (context),
}
_dbus_assert (connections->n_incomplete == 0);
-
+
+ /* drop all monitors */
+ _dbus_list_clear (&connections->monitors);
+
/* drop all real connections */
while (connections->completed != NULL)
{
_dbus_timeout_unref (connections->expire_timeout);
_dbus_hash_table_unref (connections->completed_by_user);
-
+
+ if (connections->monitor_matchmaker != NULL)
+ bus_matchmaker_unref (connections->monitor_matchmaker);
+
dbus_free (connections);
dbus_connection_free_data_slot (&connection_data_slot);
}
}
+static dbus_bool_t
+is_context_type_session (BusConnectionData *d)
+{
+ const char *context_type = bus_context_get_type (d->connections->context);
+ return context_type && !strcmp (context_type, "session");
+}
+
/* Used for logging */
static dbus_bool_t
cache_peer_loginfo_string (BusConnectionData *d,
DBusString loginfo_buf;
unsigned long uid;
unsigned long pid;
- char *windows_sid;
+ char *windows_sid = NULL, *security_label = NULL;
dbus_bool_t prev_added;
if (!_dbus_string_init (&loginfo_buf))
if (!_dbus_string_append_printf (&loginfo_buf, "pid=%ld comm=\"", pid))
goto oom;
/* Ignore errors here; we may not have permissions to read the
- * proc file. */
- _dbus_command_for_pid (pid, &loginfo_buf, MAX_LOG_COMMAND_LEN, NULL);
+ * proc file.
+ * Don't even try it for the session daemon, to avoid cluttering logs with security error logs for
+ * accessing the proc file.
+ */
+ if (!is_context_type_session(d))
+ {
+ _dbus_command_for_pid (pid, &loginfo_buf, MAX_LOG_COMMAND_LEN, NULL);
+ }
+ else
+ {
+ if (!_dbus_string_append (&loginfo_buf, "<not-read>")) /* for session daemon just say that we didn't try */
+ goto oom;
+ }
if (!_dbus_string_append_byte (&loginfo_buf, '"'))
goto oom;
+ else
+ prev_added = TRUE;
}
if (dbus_connection_get_windows_user (connection, &windows_sid))
{
dbus_bool_t did_append;
+
+ if (prev_added)
+ {
+ if (!_dbus_string_append_byte (&loginfo_buf, ' '))
+ goto oom;
+ }
+
did_append = _dbus_string_append_printf (&loginfo_buf,
- "sid=\"%s\" ", windows_sid);
+ "sid=\"%s\"", windows_sid);
dbus_free (windows_sid);
+ windows_sid = NULL;
if (!did_append)
goto oom;
+ else
+ prev_added = TRUE;
+ }
+
+ if (_dbus_connection_get_linux_security_label (connection, &security_label))
+ {
+ dbus_bool_t did_append;
+
+ if (prev_added)
+ {
+ if (!_dbus_string_append_byte (&loginfo_buf, ' '))
+ goto oom;
+ }
+
+ did_append = _dbus_string_append_printf (&loginfo_buf,
+ "label=\"%s\"", security_label);
+ dbus_free (security_label);
+ security_label = NULL;
+ if (!did_append)
+ goto oom;
+ else
+ prev_added = TRUE;
}
if (!_dbus_string_steal_data (&loginfo_buf, &(d->cached_loginfo_string)))
return TRUE;
oom:
_dbus_string_free (&loginfo_buf);
+ if (security_label != NULL)
+ dbus_free (security_label);
+ if (windows_sid != NULL)
+ dbus_free (windows_sid);
+
return FALSE;
}
check_pending_fds_cb (DBusConnection *connection)
{
BusConnectionData *d = BUS_CONNECTION_DATA (connection);
- int n_pending_unix_fds_old = d->n_pending_unix_fds;
+ int n_pending_unix_fds_old;
int n_pending_unix_fds_new;
+ _dbus_assert(d != NULL);
+
+ n_pending_unix_fds_old = d->n_pending_unix_fds;
n_pending_unix_fds_new = _dbus_connection_get_pending_fds_count (connection);
_dbus_verbose ("Pending fds count changed on connection %p: %d -> %d\n",
if (n_pending_unix_fds_old == 0 && n_pending_unix_fds_new > 0)
{
- _dbus_timeout_set_interval (d->pending_unix_fds_timeout,
+ _dbus_timeout_restart (d->pending_unix_fds_timeout,
bus_context_get_pending_fd_timeout (d->connections->context));
- _dbus_timeout_set_enabled (d->pending_unix_fds_timeout, TRUE);
}
if (n_pending_unix_fds_old > 0 && n_pending_unix_fds_new == 0)
{
- _dbus_timeout_set_enabled (d->pending_unix_fds_timeout, FALSE);
+ _dbus_timeout_disable (d->pending_unix_fds_timeout);
}
pending_unix_fds_timeout_cb (void *data)
{
DBusConnection *connection = data;
+ BusConnectionData *d = BUS_CONNECTION_DATA (connection);
+ int limit;
+
+ _dbus_assert (d != NULL);
+ limit = bus_context_get_pending_fd_timeout (d->connections->context);
+ bus_context_log (d->connections->context, DBUS_SYSTEM_LOG_WARNING,
+ "Connection \"%s\" (%s) has had Unix fds pending for too long, "
+ "closing it (pending_fd_timeout=%d ms)",
+ d->name != NULL ? d->name : "(null)",
+ bus_connection_get_loginfo (connection),
+ limit);
+
dbus_connection_close (connection);
return TRUE;
}
DBusConnection *connection)
{
- BusConnectionData *d;
- dbus_bool_t retval;
+ BusConnectionData *d = NULL;
DBusError error;
-
d = dbus_new0 (BusConnectionData, 1);
if (d == NULL)
- return FALSE;
+ goto oom;
d->connections = connections;
d->connection = connection;
connection_data_slot,
d, free_connection_data))
{
+ /* We have to free d explicitly, because this is the only code
+ * path where it's non-NULL but dbus_connection_set_data() hasn't
+ * taken responsibility for freeing it. */
dbus_free (d);
- return FALSE;
+ d = NULL;
+ goto oom;
}
dbus_connection_set_route_peer_messages (connection, TRUE);
-
- retval = FALSE;
dbus_error_init (&error);
d->selinux_id = bus_selinux_init_connection_id (connection,
&error);
if (dbus_error_is_set (&error))
{
- /* This is a bit bogus because we pretend all errors
- * are OOM; this is done because we know that in bus.c
- * an OOM error disconnects the connection, which is
- * the same thing we want on any other error.
- */
+ bus_context_log (connections->context, DBUS_SYSTEM_LOG_WARNING,
+ "Unable to set up new connection: %s", error.message);
+ dbus_error_free (&error);
+ goto error;
+ }
+
+ d->apparmor_confinement = bus_apparmor_init_connection_confinement (connection,
+ &error);
+ if (dbus_error_is_set (&error))
+ {
+ bus_context_log (connections->context, DBUS_SYSTEM_LOG_WARNING,
+ "Unable to set up new connection: %s", error.message);
dbus_error_free (&error);
- goto out;
+ goto error;
}
if (!dbus_connection_set_watch_functions (connection,
toggle_connection_watch,
connection,
NULL))
- goto out;
+ goto oom;
if (!dbus_connection_set_timeout_functions (connection,
add_connection_timeout,
remove_connection_timeout,
NULL,
connection, NULL))
- goto out;
+ goto oom;
/* For now we don't need to set a Windows user function because
* there are no policies in the config file controlling what
d->link_in_connection_list = _dbus_list_alloc_link (connection);
if (d->link_in_connection_list == NULL)
- goto out;
+ goto oom;
/* Setup the connection with the dispatcher */
if (!bus_dispatch_add_connection (connection))
- goto out;
+ goto oom;
if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE)
{
if (!_dbus_loop_queue_dispatch (bus_context_get_loop (connections->context), connection))
{
bus_dispatch_remove_connection (connection);
- goto out;
+ goto oom;
}
}
pending_unix_fds_timeout_cb,
connection, NULL);
if (d->pending_unix_fds_timeout == NULL)
- goto out;
+ goto oom;
- _dbus_timeout_set_enabled (d->pending_unix_fds_timeout, FALSE);
+ _dbus_timeout_disable (d->pending_unix_fds_timeout);
if (!_dbus_loop_add_timeout (bus_context_get_loop (connections->context),
d->pending_unix_fds_timeout))
- goto out;
+ goto oom;
_dbus_connection_set_pending_fds_function (connection,
(DBusPendingFdsChangeFunction) check_pending_fds_cb,
* stop accept()ing any more, to avert a DoS. See fd.o #80919 */
bus_context_check_all_watches (d->connections->context);
- retval = TRUE;
+ return TRUE;
- out:
- if (!retval)
+oom:
+ bus_context_log (connections->context, DBUS_SYSTEM_LOG_WARNING,
+ "No memory to set up new connection");
+ /* fall through */
+error:
+ if (d != NULL)
{
- if (d->selinux_id)
- bus_selinux_id_unref (d->selinux_id);
d->selinux_id = NULL;
+
+ if (d->apparmor_confinement)
+ bus_apparmor_confinement_unref (d->apparmor_confinement);
+ d->apparmor_confinement = NULL;
if (!dbus_connection_set_watch_functions (connection,
NULL, NULL, NULL,
/* "d" has now been freed */
}
- return retval;
+ return FALSE;
}
void
BusConnectionData *d;
d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert(d != NULL);
if (!bus_connection_is_active (connection))
return "inactive";
return d->policy;
}
+#ifdef DBUS_ENABLE_CYNARA
+const char *bus_connection_get_cynara_session_id (DBusConnection *connection)
+{
+ BusConnectionData *d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ if (d->cynara_session_id == NULL)
+ {
+ unsigned long pid;
+ if (dbus_connection_get_unix_process_id(connection, &pid))
+ d->cynara_session_id = cynara_session_from_pid(pid);
+
+ /* If client exits as soon as async call, cynara_session_from_pid() returns null.
+ cynara_session_from_pid checks /proc/pid to verify process */
+ if (d->cynara_session_id == NULL)
+ asprintf (&d->cynara_session_id, "/proc/%ld", pid);
+ }
+ return d->cynara_session_id;
+}
+#endif
+
static dbus_bool_t
foreach_active (BusConnections *connections,
BusConnectionForeachFunction function,
return d->selinux_id;
}
+BusAppArmorConfinement*
+bus_connection_dup_apparmor_confinement (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ bus_apparmor_confinement_ref (d->apparmor_confinement);
+ return d->apparmor_confinement;
+}
+
/**
* Checks whether the connection is registered with the message bus.
*
BusConnectionData *d;
d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert(d != NULL);
- return d != NULL && d->name != NULL;
+ return d->name != NULL;
}
dbus_bool_t
_dbus_assert (d != NULL);
_dbus_assert (d->oom_message != NULL);
+ bus_context_log (d->connections->context, DBUS_SYSTEM_LOG_WARNING,
+ "dbus-daemon transaction failed (OOM), sending error to "
+ "sender %s", bus_connection_get_loginfo (connection));
+
/* should always succeed since we set it to a placeholder earlier */
if (!dbus_message_set_reply_serial (d->oom_message,
dbus_message_get_serial (in_reply_to)))
return d->n_match_rules;
}
+dbus_bool_t
+bus_connection_is_service_owner_by_prefix (DBusConnection *connection,
+ const char *name_prefix)
+{
+ BusConnectionData *d;
+ DBusList *link;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ link = _dbus_list_get_first_link (&d->services_owned);
+ while (link != NULL)
+ {
+ BusService *service = link->data;
+ DBusString str;
+
+ _dbus_string_init_const (&str, bus_service_get_name (service));
+
+ if (_dbus_string_starts_with_words_c_str (&str, name_prefix, '.'))
+ return TRUE;
+
+ link = _dbus_list_get_next_link (&d->services_owned, link);
+ }
+
+ return FALSE;
+}
+
+const DBusList *
+bus_connection_get_owned_services_list (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return d->services_owned;
+}
+
void
bus_connection_add_owned_service_link (DBusConnection *connection,
DBusList *link)
dbus_bool_t
bus_connections_check_limits (BusConnections *connections,
DBusConnection *requesting_completion,
+ const char **limit_name_out,
+ int *limit_out,
DBusError *error)
{
unsigned long uid;
+ int limit;
+
+ limit = bus_context_get_max_completed_connections (connections->context);
- if (connections->n_completed >=
- bus_context_get_max_completed_connections (connections->context))
+ if (connections->n_completed >= limit)
{
+ if (limit_name_out != NULL)
+ *limit_name_out = "max_completed_connections";
+
+ if (limit_out != NULL)
+ *limit_out = limit;
+
dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
"The maximum number of active connections has been reached");
return FALSE;
if (dbus_connection_get_unix_user (requesting_completion, &uid))
{
- if (get_connections_for_uid (connections, uid) >=
- bus_context_get_max_connections_per_user (connections->context))
+ limit = bus_context_get_max_connections_per_user (connections->context);
+
+ if (get_connections_for_uid (connections, uid) >= limit)
{
+ if (limit_name_out != NULL)
+ *limit_name_out = "max_connections_per_user";
+
+ if (limit_out != NULL)
+ *limit_out = limit;
+
dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
"The maximum number of active connections for UID %lu has been reached",
uid);
DBusList *link;
CancelPendingReplyData *cprd;
int count;
+ int limit;
_dbus_assert (will_get_reply != NULL);
_dbus_assert (will_send_reply != NULL);
if (pending->will_get_reply == will_get_reply)
++count;
}
-
- if (count >=
- bus_context_get_max_replies_per_connection (connections->context))
+
+ limit = bus_context_get_max_replies_per_connection (connections->context);
+
+ if (count >= limit)
{
+ bus_context_log (connections->context, DBUS_SYSTEM_LOG_WARNING,
+ "The maximum number of pending replies for "
+ "\"%s\" (%s) has been reached "
+ "(max_replies_per_connection=%d)",
+ bus_connection_get_name (will_get_reply),
+ bus_connection_get_loginfo (will_get_reply),
+ limit);
+
dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
"The maximum number of pending replies per connection has been reached");
return FALSE;
}
+#ifdef DBUS_ENABLE_STATS
+ {
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (will_get_reply);
+ _dbus_assert (d != NULL);
+
+ update_peak(&d->peak_pending_replies, count);
+ }
+#endif
+
pending = dbus_new0 (BusPendingReply, 1);
if (pending == NULL)
{
return transaction->context;
}
+/**
+ * Reserve enough memory to capture the given message if the
+ * transaction goes through.
+ */
+dbus_bool_t
+bus_transaction_capture (BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusMessage *message)
+{
+ BusConnections *connections;
+ BusMatchmaker *mm;
+ DBusList *link;
+ DBusList *recipients = NULL;
+ dbus_bool_t ret = FALSE;
+
+ connections = bus_context_get_connections (transaction->context);
+
+ /* shortcut: don't compose the message unless someone wants it */
+ if (connections->monitors == NULL)
+ return TRUE;
+
+ mm = connections->monitor_matchmaker;
+ /* This is non-null if there has ever been a monitor - we don't GC it.
+ * There's little point, since there is up to 1 per process. */
+ _dbus_assert (mm != NULL);
+
+ if (!bus_matchmaker_get_recipients (mm, connections, sender,
+ addressed_recipient, message, &recipients))
+ goto out;
+
+ for (link = _dbus_list_get_first_link (&recipients);
+ link != NULL;
+ link = _dbus_list_get_next_link (&recipients, link))
+ {
+ DBusConnection *recipient = link->data;
+
+ if (!bus_transaction_send (transaction, recipient, message, FALSE))
+ goto out;
+ }
+
+ ret = TRUE;
+
+out:
+ _dbus_list_clear (&recipients);
+ return ret;
+}
+
+dbus_bool_t
+bus_transaction_capture_error_reply (BusTransaction *transaction,
+ DBusConnection *addressed_recipient,
+ const DBusError *error,
+ DBusMessage *in_reply_to)
+{
+ BusConnections *connections;
+ DBusMessage *reply;
+ dbus_bool_t ret = FALSE;
+
+ _dbus_assert (error != NULL);
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ connections = bus_context_get_connections (transaction->context);
+
+ /* shortcut: don't compose the message unless someone wants it */
+ if (connections->monitors == NULL)
+ return TRUE;
+
+ reply = dbus_message_new_error (in_reply_to,
+ error->name,
+ error->message);
+
+ if (reply == NULL)
+ return FALSE;
+
+ if (!dbus_message_set_sender (reply, DBUS_SERVICE_DBUS))
+ goto out;
+
+ ret = bus_transaction_capture (transaction, NULL, addressed_recipient, reply);
+
+out:
+ dbus_message_unref (reply);
+ return ret;
+}
+
dbus_bool_t
bus_transaction_send_from_driver (BusTransaction *transaction,
DBusConnection *connection,
DBusMessage *message)
{
+ DBusError error = DBUS_ERROR_INIT;
+ BusDeferredMessage *deferred_message = NULL;
+
/* We have to set the sender to the driver, and have
* to check security policy since it was not done in
* dispatch.c
/* bus driver never wants a reply */
dbus_message_set_no_reply (message, TRUE);
-
- /* If security policy doesn't allow the message, we silently
- * eat it; the driver doesn't care about getting a reply.
+
+ /* Capture it for monitors, even if the real recipient's receive policy
+ * does not allow it to receive this message from us (which would be odd).
*/
- if (!bus_context_check_security_policy (bus_transaction_get_context (transaction),
- transaction,
- NULL, connection, connection, message, NULL))
- return TRUE;
+ if (!bus_transaction_capture (transaction, NULL, connection, message))
+ return FALSE;
- return bus_transaction_send (transaction, connection, message);
+ /* If security policy doesn't allow the message, we would silently
+ * eat it; the driver doesn't care about getting a reply. However,
+ * if we're actively capturing messages, it's nice to log that we
+ * tried to send it and did not allow ourselves to do so.
+ */
+ switch (bus_context_check_security_policy (bus_transaction_get_context (transaction),
+ transaction,
+ NULL, connection, connection,
+ message, NULL, &error,
+ &deferred_message))
+ {
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
+ if (!bus_transaction_capture_error_reply (transaction, connection,
+ &error, message))
+ {
+ bus_context_log (transaction->context, DBUS_SYSTEM_LOG_WARNING,
+ "message from dbus-daemon rejected but not enough "
+ "memory to capture it");
+ }
+
+ /* This is not fatal to the transaction so silently eat the disallowed
+ * message (see reasoning above) */
+ dbus_error_free (&error);
+ return TRUE;
+ break;
+ case BUS_RESULT_LATER:
+ if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, FALSE, FALSE))
+ return FALSE;
+ return TRUE; /* pretend to have sent it */
+ }
+
+ return bus_transaction_send (transaction, connection, message, FALSE);
}
dbus_bool_t
bus_transaction_send (BusTransaction *transaction,
DBusConnection *connection,
- DBusMessage *message)
+ DBusMessage *message,
+ dbus_bool_t deferred_dispatch)
{
MessageToSend *to_send;
BusConnectionData *d;
d = BUS_CONNECTION_DATA (connection);
_dbus_assert (d != NULL);
-
+
+ if (!deferred_dispatch && d->deferred_messages != NULL)
+ {
+ BusDeferredMessage *deferred_message;
+ dbus_bool_t success;
+ /* sender and addressed recipient are not required at this point as we only need to send message
+ * to a single recipient without performing policy check. */
+ deferred_message = bus_deferred_message_new (message,
+ NULL,
+ NULL,
+ connection,
+ BUS_RESULT_TRUE);
+ if (deferred_message == NULL)
+ return FALSE;
+
+ success = bus_deferred_message_queue_at_recipient(deferred_message, transaction,
+ FALSE, FALSE);
+ bus_deferred_message_unref(deferred_message);
+
+ return success;
+ }
+
to_send = dbus_new (MessageToSend, 1);
if (to_send == NULL)
{
return TRUE;
}
+void
+bus_connection_dispatch_deferred (DBusConnection *connection)
+{
+ BusDeferredMessage *message;
+
+ _dbus_return_if_fail (connection != NULL);
+
+ while ((message = bus_connection_pop_deferred_message(connection)) != NULL)
+ {
+ bus_deferred_message_dispatch(message);
+ bus_deferred_message_unref(message);
+ }
+}
+
+dbus_bool_t
+bus_connection_has_deferred_messages (DBusConnection *connection)
+{
+ BusConnectionData *d = BUS_CONNECTION_DATA(connection);
+
+ _dbus_assert (d != NULL);
+
+ return d->deferred_messages != NULL ? TRUE : FALSE;
+}
+
+dbus_bool_t
+bus_connection_queue_deferred_message (DBusConnection *connection,
+ BusDeferredMessage *message,
+ dbus_bool_t prepend)
+{
+ BusConnectionData *d = BUS_CONNECTION_DATA(connection);
+ dbus_bool_t success;
+
+ _dbus_assert (d != NULL);
+
+ if (prepend)
+ success = _dbus_list_prepend(&d->deferred_messages, message);
+ else
+ success = _dbus_list_append(&d->deferred_messages, message);
+
+ if (success)
+ {
+ bus_deferred_message_ref(message);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BusDeferredMessage *
+bus_connection_pop_deferred_message (DBusConnection *connection)
+{
+ DBusList *link;
+ BusDeferredMessage *message;
+ BusConnectionData *d = BUS_CONNECTION_DATA(connection);
+
+ _dbus_assert (d != NULL);
+
+ link =_dbus_list_get_first_link(&d->deferred_messages);
+ if (link != NULL)
+ {
+ message = link->data;
+ if (!bus_deferred_message_waits_for_check(message))
+ {
+ _dbus_list_remove_link(&d->deferred_messages, link);
+ return message;
+ }
+ }
+
+ return NULL;
+}
+
+dbus_bool_t
+bus_connection_putback_deferred_message (DBusConnection *connection, BusDeferredMessage *message)
+{
+ BusConnectionData *d = BUS_CONNECTION_DATA(connection);
+
+ _dbus_assert (d != NULL);
+
+ if (_dbus_list_prepend(&d->deferred_messages, message))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+bus_connection_clear_deferred_messages (DBusConnection *connection)
+{
+ BusConnectionData *d = BUS_CONNECTION_DATA(connection);
+ DBusList *link;
+ DBusList *next;
+ BusDeferredMessage *message;
+
+ _dbus_assert (d != NULL);
+
+ link =_dbus_list_get_first_link(&d->deferred_messages);
+ while (link != NULL)
+ {
+ next = _dbus_list_get_next_link (&d->deferred_messages, link);
+ message = link->data;
+
+ bus_deferred_message_unref(message);
+ _dbus_list_remove_link(&d->deferred_messages, link);
+
+ link = next;
+ }
+}
+
+void
+bus_connection_remove_deferred_message (DBusConnection *connection,
+ BusDeferredMessage *message)
+{
+ BusConnectionData *d = BUS_CONNECTION_DATA(connection);
+
+ _dbus_assert (d != NULL);
+
+ if (_dbus_list_remove(&d->deferred_messages, message))
+ bus_deferred_message_unref(message);
+}
+
int
bus_connections_get_n_active (BusConnections *connections)
{
BusConnectionData *d;
d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert(d != NULL);
+
return d->peak_match_rules;
}
BusConnectionData *d;
d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert(d != NULL);
+
return d->peak_bus_names;
}
+
+int bus_connection_get_n_pending_replies (DBusConnection *connection)
+{
+ BusConnectionData *d;
+ DBusList *link;
+ BusPendingReply *pending;
+ int count = 0;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert(d != NULL);
+
+ link = bus_expire_list_get_first_link(d->connections->pending_replies);
+ while (link != NULL)
+ {
+ pending = link->data;
+ link = bus_expire_list_get_next_link(d->connections->pending_replies,
+ link);
+ if (pending->will_get_reply == connection)
+ ++count;
+ }
+
+ return count;
+}
+
+int
+bus_connection_get_peak_pending_replies (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ return d->peak_pending_replies;
+}
#endif /* DBUS_ENABLE_STATS */
+
+dbus_bool_t
+bus_connection_is_monitor (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert(d != NULL);
+
+ return d->link_in_monitors != NULL;
+}
+
+static dbus_bool_t
+bcd_add_monitor_rules (BusConnectionData *d,
+ DBusConnection *connection,
+ DBusList **rules)
+{
+ BusMatchmaker *mm = d->connections->monitor_matchmaker;
+ DBusList *iter;
+
+ if (mm == NULL)
+ {
+ mm = bus_matchmaker_new ();
+
+ if (mm == NULL)
+ return FALSE;
+
+ d->connections->monitor_matchmaker = mm;
+ }
+
+ for (iter = _dbus_list_get_first_link (rules);
+ iter != NULL;
+ iter = _dbus_list_get_next_link (rules, iter))
+ {
+ if (!bus_matchmaker_add_rule (mm, iter->data))
+ {
+ bus_matchmaker_disconnected (mm, connection);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+bcd_drop_monitor_rules (BusConnectionData *d,
+ DBusConnection *connection)
+{
+ BusMatchmaker *mm = d->connections->monitor_matchmaker;
+
+ if (mm != NULL)
+ bus_matchmaker_disconnected (mm, connection);
+}
+
+dbus_bool_t
+bus_connection_be_monitor (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusList **rules,
+ DBusError *error)
+{
+ BusConnectionData *d;
+ DBusList *link;
+ DBusList *tmp;
+ DBusList *iter;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ link = _dbus_list_alloc_link (connection);
+
+ if (link == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bcd_add_monitor_rules (d, connection, rules))
+ {
+ _dbus_list_free_link (link);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* release all its names */
+ if (!_dbus_list_copy (&d->services_owned, &tmp))
+ {
+ bcd_drop_monitor_rules (d, connection);
+ _dbus_list_free_link (link);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ for (iter = _dbus_list_get_first_link (&tmp);
+ iter != NULL;
+ iter = _dbus_list_get_next_link (&tmp, iter))
+ {
+ BusService *service = iter->data;
+
+ /* This call is transactional: if there isn't enough memory to
+ * do everything, then the service gets all its names back when
+ * the transaction is cancelled due to OOM. */
+ if (!bus_service_remove_owner (service, connection, transaction, error))
+ {
+ bcd_drop_monitor_rules (d, connection);
+ _dbus_list_free_link (link);
+ _dbus_list_clear (&tmp);
+ return FALSE;
+ }
+ }
+
+ /* We have now done everything that can fail, so there is no problem
+ * with doing the irrevocable stuff. */
+
+ _dbus_list_clear (&tmp);
+
+ bus_context_log (transaction->context, DBUS_SYSTEM_LOG_INFO,
+ "Connection %s (%s) became a monitor.", d->name,
+ d->cached_loginfo_string);
+
+ if (d->n_match_rules > 0)
+ {
+ BusMatchmaker *mm;
+
+ mm = bus_context_get_matchmaker (d->connections->context);
+ bus_matchmaker_disconnected (mm, connection);
+ }
+
+ /* flag it as a monitor */
+ d->link_in_monitors = link;
+ _dbus_list_append_link (&d->connections->monitors, link);
+
+ /* it isn't allowed to reply, and it is no longer relevant whether it
+ * receives replies */
+ bus_connection_drop_pending_replies (d->connections, connection);
+
+ return TRUE;
+}