2004-07-22 Olivier Andrieu <oliv__a@users.sourceforge.net>
[platform/upstream/dbus.git] / dbus / dbus-bus.c
index a38b4a2..4dd2eaf 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2003  CodeFactory AB
  * Copyright (C) 2003  Red Hat, Inc.
  *
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.0
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * @ingroup DBus
  * @brief Functions for communicating with the message bus
  *
- *
- * @todo get rid of most of these; they should be done
- * with DBusGProxy and the Qt equivalent, i.e. the same
- * way any other interface would be used.
+ * @todo right now the default address of the system bus is hardcoded,
+ * so if you change it in the global config file suddenly you have to
+ * set DBUS_SYSTEM_BUS_ADDRESS env variable.  Might be nice if the
+ * client lib somehow read the config file, or if the bus on startup
+ * somehow wrote out its address to a well-known spot, but might also
+ * not be worth it.
  */
 
-
 /**
  * @defgroup DBusBusInternals Message bus APIs internals
  * @ingroup DBusInternals
  * Block of message-bus-related data we attach to each
  * #DBusConnection used with these convenience functions.
  *
+ *
+ * @todo get rid of most of these; they should be done
+ * with DBusGProxy and the Qt equivalent, i.e. the same
+ * way any other interface would be used.
  */
 typedef struct
 {
@@ -134,48 +139,86 @@ init_connections_unlocked (void)
 
       /* Don't init these twice, we may run this code twice if
        * init_connections_unlocked() fails midway through.
+       * In practice, each block below should contain only one
+       * "return FALSE" or running through twice may not
+       * work right.
        */
       
        if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
          {
+           _dbus_verbose ("Filling in system bus address...\n");
+           
            if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SYSTEM],
                               "DBUS_SYSTEM_BUS_ADDRESS"))
              return FALSE;
-           
+         }
+
+                  
+       if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
+         {
+           /* Use default system bus address if none set in environment */
+           bus_connection_addresses[DBUS_BUS_SYSTEM] =
+             _dbus_strdup (DBUS_SYSTEM_BUS_DEFAULT_ADDRESS);
            if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
-             {
-               /* Use default system bus address if none set in environment */
-               bus_connection_addresses[DBUS_BUS_SYSTEM] =
-                 _dbus_strdup ("unix:path=" DBUS_SYSTEM_BUS_PATH);
-               if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
-                 return FALSE;
-             }
+             return FALSE;
+           
+           _dbus_verbose ("  used default system bus \"%s\"\n",
+                          bus_connection_addresses[DBUS_BUS_SYSTEM]);
          }
+       else
+         _dbus_verbose ("  used env var system bus \"%s\"\n",
+                        bus_connection_addresses[DBUS_BUS_SYSTEM]);
           
       if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
         {
+          _dbus_verbose ("Filling in session bus address...\n");
+          
           if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SESSION],
                              "DBUS_SESSION_BUS_ADDRESS"))
             return FALSE;
+          _dbus_verbose ("  \"%s\"\n", bus_connection_addresses[DBUS_BUS_SESSION] ?
+                         bus_connection_addresses[DBUS_BUS_SESSION] : "none set");
         }
 
       if (bus_connection_addresses[DBUS_BUS_ACTIVATION] == NULL)
         {
+          _dbus_verbose ("Filling in activation bus address...\n");
+          
           if (!get_from_env (&bus_connection_addresses[DBUS_BUS_ACTIVATION],
                              "DBUS_ACTIVATION_ADDRESS"))
             return FALSE;
+          
+          _dbus_verbose ("  \"%s\"\n", bus_connection_addresses[DBUS_BUS_ACTIVATION] ?
+                         bus_connection_addresses[DBUS_BUS_ACTIVATION] : "none set");
         }
 
-      s = _dbus_getenv ("DBUS_ACTIVATION_BUS_TYPE");
 
-      if (s != NULL)
+      if (bus_connection_addresses[DBUS_BUS_ACTIVATION] != NULL)
         {
-          if (strcmp (s, "system") == 0)
-            activation_bus_type = DBUS_BUS_SYSTEM;
-          else if (strcmp (s, "session") == 0)
-            activation_bus_type = DBUS_BUS_SESSION;
+          s = _dbus_getenv ("DBUS_ACTIVATION_BUS_TYPE");
+              
+          if (s != NULL)
+            {
+              _dbus_verbose ("Bus activation type was set to \"%s\"\n", s);
+                  
+              if (strcmp (s, "system") == 0)
+                activation_bus_type = DBUS_BUS_SYSTEM;
+              else if (strcmp (s, "session") == 0)
+                activation_bus_type = DBUS_BUS_SESSION;
+            }
         }
-
+      else
+        {
+          /* Default to the session bus instead if available */
+          if (bus_connection_addresses[DBUS_BUS_SESSION] != NULL)
+            {
+              bus_connection_addresses[DBUS_BUS_ACTIVATION] =
+                _dbus_strdup (bus_connection_addresses[DBUS_BUS_SESSION]);
+              if (bus_connection_addresses[DBUS_BUS_ACTIVATION] == NULL)
+                return FALSE;
+            }
+        }
+      
       /* If we return FALSE we have to be sure that restarting
        * the above code will work right
        */
@@ -306,10 +349,12 @@ dbus_bus_get (DBusBusType  type,
   address_type = type;
   
   /* Use the real type of the activation bus for getting its
-   * connection. (If the activating bus isn't a well-known
-   * bus then activation_bus_type == DBUS_BUS_ACTIVATION)
+   * connection, but only if the real type's address is available. (If
+   * the activating bus isn't a well-known bus then
+   * activation_bus_type == DBUS_BUS_ACTIVATION)
    */
-  if (type == DBUS_BUS_ACTIVATION)
+  if (type == DBUS_BUS_ACTIVATION &&
+      bus_connection_addresses[activation_bus_type] != NULL)
     type = activation_bus_type;
   
   if (bus_connections[type] != NULL)
@@ -338,6 +383,12 @@ dbus_bus_get (DBusBusType  type,
       _DBUS_UNLOCK (bus);
       return NULL;
     }
+
+  /* By default we're bound to the lifecycle of
+   * the message bus.
+   */
+  dbus_connection_set_exit_on_disconnect (connection,
+                                          TRUE);
   
   if (!dbus_bus_register (connection, error))
     {
@@ -495,6 +546,79 @@ dbus_bus_get_base_service (DBusConnection *connection)
 }
 
 /**
+ * Asks the bus to return the uid of a service.
+ *
+ * @param connection the connection
+ * @param service_name the service name
+ * @param error location to store the error
+ * @returns a result code, -1 if error is set
+ */ 
+unsigned long
+dbus_bus_get_unix_user (DBusConnection *connection,
+                        const char     *service,
+                        DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  dbus_uint32_t uid;
+
+  _dbus_return_val_if_fail (connection != NULL, DBUS_UID_UNSET);
+  _dbus_return_val_if_fail (service != NULL, DBUS_UID_UNSET);
+  _dbus_return_val_if_error_is_set (error, DBUS_UID_UNSET);
+  
+  message = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+                                          DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+                                          DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+                                          "GetConnectionUnixUser");
+
+  if (message == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return DBUS_UID_UNSET;
+    }
+  if (!dbus_message_append_args (message,
+                                DBUS_TYPE_STRING, service,
+                                DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (message);
+      _DBUS_SET_OOM (error);
+      return DBUS_UID_UNSET;
+    }
+  
+  reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
+                                                     error);
+  
+  dbus_message_unref (message);
+  
+  if (reply == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      return DBUS_UID_UNSET;
+    }  
+
+  if (dbus_set_error_from_message (error, reply))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return DBUS_UID_UNSET;
+    }
+  
+  if (!dbus_message_get_args (reply, error,
+                              DBUS_TYPE_UINT32, &uid,
+                              DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return DBUS_UID_UNSET;
+    }
+
+  dbus_message_unref (reply);
+  
+  return (unsigned long) uid;
+}
+
+
+/**
  * Asks the bus to try to acquire a certain service.
  *
  * @todo these docs are not complete, need to document the
@@ -578,8 +702,6 @@ dbus_bus_acquire_service (DBusConnection *connection,
 /**
  * Checks whether a certain service exists.
  *
- * @todo the SERVICE_EXISTS message should use BOOLEAN not UINT32
- *
  * @param connection the connection
  * @param service_name the service name
  * @param error location to store any errors
@@ -591,7 +713,7 @@ dbus_bus_service_exists (DBusConnection *connection,
                          DBusError      *error)
 {
   DBusMessage *message, *reply;
-  unsigned int exists;
+  dbus_bool_t exists;
 
   _dbus_return_val_if_fail (connection != NULL, FALSE);
   _dbus_return_val_if_fail (service_name != NULL, FALSE);
@@ -626,14 +748,16 @@ dbus_bus_service_exists (DBusConnection *connection,
     }
 
   if (!dbus_message_get_args (reply, error,
-                              DBUS_TYPE_UINT32, &exists,
+                              DBUS_TYPE_BOOLEAN, &exists,
                               DBUS_TYPE_INVALID))
     {
       _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
       return FALSE;
     }
   
-  return (exists != FALSE);
+  dbus_message_unref (reply);
+  return exists;
 }
 
 /**
@@ -675,7 +799,7 @@ dbus_bus_activate_service (DBusConnection *connection,
     }
 
   reply = dbus_connection_send_with_reply_and_block (connection, msg,
-                                                        -1, error);
+                                                     -1, error);
   dbus_message_unref (msg);
 
   if (reply == NULL)
@@ -704,5 +828,125 @@ dbus_bus_activate_service (DBusConnection *connection,
   return TRUE;
 }
 
+static void
+send_no_return_values (DBusConnection *connection,
+                       DBusMessage    *msg,
+                       DBusError      *error)
+{
+  if (error)
+    {
+      /* Block to check success codepath */
+      DBusMessage *reply;
+      
+      reply = dbus_connection_send_with_reply_and_block (connection, msg,
+                                                         -1, error);
+      
+      if (reply == NULL)
+        {
+          _DBUS_ASSERT_ERROR_IS_SET (error);
+          return;
+        }
+
+      if (dbus_set_error_from_message (error, reply))
+        {
+          _DBUS_ASSERT_ERROR_IS_SET (error);
+          dbus_message_unref (reply);
+          return;
+        }
+
+      dbus_message_unref (reply);
+    }
+  else
+    {
+      /* Silently-fail nonblocking codepath */
+      if (!dbus_connection_send (connection, msg, NULL))
+        return;
+    }
+}
+
+/**
+ * Adds a match rule to match messages going through the message bus.
+ * The "rule" argument is the string form of a match rule.
+ *
+ * If you pass #NULL for the error, this function will not
+ * block; the match thus won't be added until you flush the
+ * connection, and if there's an error adding the match
+ * (only possible error is lack of resources in the bus),
+ * you won't find out about it.
+ *
+ * If you pass non-#NULL for the error this function will
+ * block until it gets a reply.
+ *
+ * Normal API conventions would have the function return
+ * a boolean value indicating whether the error was set,
+ * but that would require blocking always to determine
+ * the return value.
+ * 
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_add_match (DBusConnection *connection,
+                    const char     *rule,
+                    DBusError      *error)
+{
+  DBusMessage *msg;
+
+  msg = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+                                      DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+                                      DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+                                      "AddMatch");
+
+  if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, rule,
+                                 DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (msg);
+      _DBUS_SET_OOM (error);
+      return;
+    }
+
+  send_no_return_values (connection, msg, error);
+
+  dbus_message_unref (msg);
+}
+
+/**
+ * Removes a previously-added match rule "by value" (the most
+ * recently-added identical rule gets removed).  The "rule" argument
+ * is the string form of a match rule.
+ *
+ * If you pass #NULL for the error, this function will not
+ * block; otherwise it will. See detailed explanation in
+ * docs for dbus_bus_add_match().
+ * 
+ * @param connection connection to the message bus
+ * @param rule textual form of match rule
+ * @param error location to store any errors
+ */
+void
+dbus_bus_remove_match (DBusConnection *connection,
+                       const char     *rule,
+                       DBusError      *error)
+{
+  DBusMessage *msg;
+
+  msg = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+                                      DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+                                      DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+                                      "RemoveMatch");
+
+  if (!dbus_message_append_args (msg, DBUS_TYPE_STRING, rule,
+                                 DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (msg);
+      _DBUS_SET_OOM (error);
+      return;
+    }
+
+  send_no_return_values (connection, msg, error);
+
+  dbus_message_unref (msg);
+}
 
 /** @} */