2005-11-15 Robert McQueen <robot101@debian.org>
authorRobert McQueen <robot101@debian.org>
Tue, 15 Nov 2005 17:19:19 +0000 (17:19 +0000)
committerRobert McQueen <robot101@debian.org>
Tue, 15 Nov 2005 17:19:19 +0000 (17:19 +0000)
* bus/driver.c, bus/services.c, bus/services.h: Add a ReleaseName
method to org.freedesktop.DBus to release a bus name or give up
waiting in the queue for it.

* dbus/dbus-bus.c, dbus/dbus-bus.h, dbus/dbus-shared.h: Add a
dbus_bus_release_name method to send the ReleaseName method calls.
Add constants for the return values to dbus/dbus-shared.h.

* doc/dbus-specification.xml: Document the new ReleaseName method
in the specification.

* python/dbus_bindings.pyx: Add a low-level python binding for the
release name method.

* python/exceptions.py, python/service.py: Make freeing BusName
objects release the name. Add a NameExistsException, and fix a
bug with creating UnknownMethodException.

* test/python/test-client.py: Add tests for freeing BusName
objects causing names to be released.

12 files changed:
ChangeLog
bus/driver.c
bus/services.c
bus/services.h
dbus/dbus-bus.c
dbus/dbus-bus.h
dbus/dbus-shared.h
doc/dbus-specification.xml
python/dbus_bindings.pyx
python/exceptions.py
python/service.py
test/python/test-client.py

index ee86d05..39e6783 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,28 @@
 2005-11-15  Robert McQueen  <robot101@debian.org>
 
+       * bus/driver.c, bus/services.c, bus/services.h: Add a ReleaseName
+       method to org.freedesktop.DBus to release a bus name or give up
+       waiting in the queue for it.
+
+       * dbus/dbus-bus.c, dbus/dbus-bus.h, dbus/dbus-shared.h: Add a
+       dbus_bus_release_name method to send the ReleaseName method calls.
+       Add constants for the return values to dbus/dbus-shared.h.
+
+       * doc/dbus-specification.xml: Document the new ReleaseName method
+       in the specification.
+
+       * python/dbus_bindings.pyx: Add a low-level python binding for the
+       release name method.
+
+       * python/exceptions.py, python/service.py: Make freeing BusName
+       objects release the name. Add a NameExistsException, and fix a
+       bug with creating UnknownMethodException.
+
+       * test/python/test-client.py: Add tests for freeing BusName
+       objects causing names to be released.
+
+2005-11-14  Robert McQueen  <robot101@debian.org>
+
        * python/service.py: Include the traceback in the error reply when we
        send an exception over the bus. _BEST_ _PATCH_ _EVER_
 
index a6c4faf..bc63e7c 100644 (file)
@@ -470,7 +470,7 @@ bus_driver_handle_acquire_service (DBusConnection *connection,
   DBusMessage *reply;
   DBusString service_name;
   const char *name;
-  int service_reply;
+  dbus_uint32_t service_reply;
   dbus_uint32_t flags;
   dbus_bool_t retval;
   BusRegistry *registry;
@@ -526,6 +526,67 @@ bus_driver_handle_acquire_service (DBusConnection *connection,
 } 
 
 static dbus_bool_t
+bus_driver_handle_release_service (DBusConnection *connection,
+                                   BusTransaction *transaction,
+                                   DBusMessage    *message,
+                                   DBusError      *error)
+{
+  DBusMessage *reply;
+  DBusString service_name;
+  const char *name;
+  dbus_uint32_t service_reply;
+  dbus_bool_t retval;
+  BusRegistry *registry;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  registry = bus_connection_get_registry (connection);
+
+  if (!dbus_message_get_args (message, error,
+                              DBUS_TYPE_STRING, &name,
+                              DBUS_TYPE_INVALID))
+    return FALSE;
+
+  _dbus_verbose ("Trying to release name %s\n", name);
+
+  retval = FALSE;
+  reply = NULL;
+
+  _dbus_string_init_const (&service_name, name);
+
+  if (!bus_registry_release_service (registry, connection,
+                                     &service_name, &service_reply,
+                                     transaction, error))
+    goto out;
+
+  reply = dbus_message_new_method_return (message);
+  if (reply == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto out;
+    }
+
+  if (!dbus_message_append_args (reply, DBUS_TYPE_UINT32, &service_reply, DBUS_TYPE_INVALID))
+    {
+      BUS_SET_OOM (error);
+      goto out;
+    }
+
+  if (!bus_transaction_send_from_driver (transaction, connection, reply))
+    {
+      BUS_SET_OOM (error);
+      goto out;
+    }
+
+  retval = TRUE;
+
+ out:
+  if (reply)
+    dbus_message_unref (reply);
+  return retval;
+}
+
+static dbus_bool_t
 bus_driver_handle_service_exists (DBusConnection *connection,
                                   BusTransaction *transaction,
                                   DBusMessage    *message,
@@ -1142,6 +1203,10 @@ struct
     DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_UINT32_AS_STRING,
     DBUS_TYPE_UINT32_AS_STRING,
     bus_driver_handle_acquire_service },
+  { "ReleaseName",
+    DBUS_TYPE_STRING_AS_STRING,
+    DBUS_TYPE_UINT32_AS_STRING,
+    bus_driver_handle_release_service },
   { "StartServiceByName",
     DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_UINT32_AS_STRING,
     DBUS_TYPE_UINT32_AS_STRING,
index 7a22dce..87feab2 100644 (file)
@@ -434,6 +434,70 @@ bus_registry_acquire_service (BusRegistry      *registry,
 }
 
 dbus_bool_t
+bus_registry_release_service (BusRegistry      *registry,
+                              DBusConnection   *connection,
+                              const DBusString *service_name,
+                              dbus_uint32_t    *result,
+                              BusTransaction   *transaction,
+                              DBusError        *error)
+{
+  dbus_bool_t retval;
+  BusService *service;
+
+  retval = FALSE;
+
+  if (!_dbus_validate_bus_name (service_name, 0,
+                                _dbus_string_get_length (service_name)))
+    {
+      dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+                      "Given bus name \"%s\" is not valid",
+                      _dbus_string_get_const_data (service_name));
+
+      _dbus_verbose ("Attempt to release invalid service name\n");
+
+      goto out;
+    }
+
+  if (_dbus_string_get_byte (service_name, 0) == ':')
+    {
+      /* Not allowed; the base service name cannot be created or released */
+      dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+                      "Cannot release a service starting with ':' such as \"%s\"",
+                      _dbus_string_get_const_data (service_name));
+
+      _dbus_verbose ("Attempt to release invalid base service name \"%s\"",
+                     _dbus_string_get_const_data (service_name));
+
+      goto out;
+    }
+
+  service = bus_registry_lookup (registry, service_name);
+
+  if (service == NULL)
+    {
+      *result = DBUS_RELEASE_NAME_REPLY_NON_EXISTENT;
+    }
+  else if (!bus_service_has_owner (service, connection))
+    {
+      *result = DBUS_RELEASE_NAME_REPLY_NOT_OWNER;
+    }
+  else
+    {
+      if (!bus_service_remove_owner (service, connection,
+                                     transaction, error))
+        goto out;
+
+      _dbus_assert (!bus_service_has_owner (service, connection));
+      *result = DBUS_RELEASE_NAME_REPLY_RELEASED;
+    }
+
+  retval = TRUE;
+
+ out:
+  return retval;
+}
+
+dbus_bool_t
 bus_registry_set_service_context_table (BusRegistry   *registry,
                                        DBusHashTable *table)
 {
index f075404..d4b3d83 100644 (file)
@@ -56,6 +56,12 @@ dbus_bool_t  bus_registry_acquire_service (BusRegistry                 *registry
                                            dbus_uint32_t               *result,
                                            BusTransaction              *transaction,
                                            DBusError                   *error);
+dbus_bool_t  bus_registry_release_service (BusRegistry                 *registry,
+                                           DBusConnection              *connection,
+                                           const DBusString            *service_name,
+                                           dbus_uint32_t               *result,
+                                           BusTransaction              *transaction,
+                                           DBusError                   *error);
 dbus_bool_t  bus_registry_set_service_context_table (BusRegistry           *registry,
                                                     DBusHashTable         *table);
 
index 107fde9..9016f1b 100644 (file)
@@ -777,6 +777,71 @@ dbus_bus_request_name (DBusConnection *connection,
   return result;
 }
 
+int
+dbus_bus_release_name (DBusConnection *connection,
+                       const char     *name,
+                       DBusError      *error)
+{
+  DBusMessage *message, *reply;
+  dbus_uint32_t result;
+
+  _dbus_return_val_if_fail (connection != NULL, 0);
+  _dbus_return_val_if_fail (name != NULL, 0);
+  _dbus_return_val_if_fail (_dbus_check_is_valid_bus_name (name), 0);
+  _dbus_return_val_if_error_is_set (error, 0);
+
+  message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                          DBUS_PATH_DBUS,
+                                          DBUS_INTERFACE_DBUS,
+                                          "ReleaseName");
+
+  if (message == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return -1;
+    }
+
+  if (!dbus_message_append_args (message,
+                                 DBUS_TYPE_STRING, &name,
+                                 DBUS_TYPE_INVALID))
+    {
+      dbus_message_unref (message);
+      _DBUS_SET_OOM (error);
+      return -1;
+    }
+
+  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 -1;
+    }
+
+  if (dbus_set_error_from_message (error, reply))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return -1;
+    }
+
+  if (!dbus_message_get_args (reply, error,
+                              DBUS_TYPE_UINT32, &result,
+                              DBUS_TYPE_INVALID))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      dbus_message_unref (reply);
+      return -1;
+    }
+
+  dbus_message_unref (reply);
+
+  return result;
+}
+
 /**
  * Checks whether a certain name has an owner.
  *
index 2447067..2329e13 100644 (file)
@@ -48,6 +48,9 @@ int             dbus_bus_request_name     (DBusConnection *connection,
                                           const char     *name,
                                           unsigned int    flags,
                                           DBusError      *error);
+int             dbus_bus_release_name     (DBusConnection *connection,
+                                          const char     *name,
+                                          DBusError      *error);
 dbus_bool_t     dbus_bus_name_has_owner   (DBusConnection *connection,
                                           const char     *name,
                                           DBusError      *error);
index a1f49a0..a8519c1 100644 (file)
@@ -77,6 +77,11 @@ typedef enum
 #define DBUS_REQUEST_NAME_REPLY_EXISTS         3
 #define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER  4
 
+/* Replies to releasing a name */
+#define DBUS_RELEASE_NAME_REPLY_RELEASED        1
+#define DBUS_RELEASE_NAME_REPLY_NON_EXISTENT    2
+#define DBUS_RELEASE_NAME_REPLY_NOT_OWNER       3
+
 /* Replies to service starts */
 #define DBUS_START_REPLY_SUCCESS         1
 #define DBUS_START_REPLY_ALREADY_RUNNING 2
index a30e101..d118465 100644 (file)
         A connection can request additional names to be associated with it using
         the <literal>org.freedesktop.DBus.RequestName</literal> message. <xref
         linkend="message-protocol-names-bus"/> describes the format of a valid
-        name.
+        name. These names can be released again using the
+        <literal>org.freedesktop.DBus.ReleaseName</literal> message.
       </para>
 
       <sect3 id="bus-messages-request-name">
            </tgroup>
          </informaltable>
         </para>
+       </sect3>
+
+       <sect3 id="bus-messages-release-name">
+        <title><literal>org.freedesktop.DBus.ReleaseName</literal></title>
+        <para>
+          As a method:
+          <programlisting>
+            UINT32 ReleaseName (in STRING name)
+          </programlisting>
+          Message arguments:
+          <informaltable>
+            <tgroup cols="3">
+              <thead>
+                <row>
+                  <entry>Argument</entry>
+                  <entry>Type</entry>
+                  <entry>Description</entry>
+                </row>
+              </thead>
+              <tbody>
+                <row>
+                  <entry>0</entry>
+                  <entry>STRING</entry>
+                  <entry>Name to release</entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+          Reply arguments:
+          <informaltable>
+            <tgroup cols="3">
+              <thead>
+                <row>
+                  <entry>Argument</entry>
+                  <entry>Type</entry>
+                  <entry>Description</entry>
+                </row>
+              </thead>
+              <tbody>
+                <row>
+                  <entry>0</entry>
+                  <entry>UINT32</entry>
+                  <entry>Return value</entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+        </para>
+        <para>
+          This method call should be sent to
+          <literal>org.freedesktop.DBus</literal> and asks the message bus to
+          release the method caller's claim to the given name. If the caller is
+          the primary owner, a new primary owner will be selected from the
+          queue if any other owners are waiting. If the caller is waiting in
+          the queue for the name, the caller will removed from the queue and
+          will not be made an owner of the name if it later becomes available.
+          If there are no other owners in the queue for the name, it will be
+          removed from the bus entirely.
+
+          The return code can be one of the following values:
+
+          <informaltable>
+            <tgroup cols="3">
+              <thead>
+                <row>
+                  <entry>Conventional Name</entry>
+                  <entry>Value</entry>
+                  <entry>Description</entry>
+                </row>
+              </thead>
+              <tbody>
+               <row>
+                  <entry>DBUS_RELEASE_NAME_REPLY_RELEASED</entry>
+                  <entry>1</entry> <entry>The caller has released his claim on
+                  the given name. Either the caller was the primary owner of
+                  the name, and the name is now unused or taken by somebody
+                  waiting in the queue for the name, or the caller was waiting
+                  in the queue for the name and has now been removed from the
+                  queue.</entry>
+               </row>
+               <row>
+                 <entry>DBUS_RELEASE_NAME_REPLY_NON_EXISTENT</entry>
+                 <entry>2</entry>
+                 <entry>The given name does not exist on this bus.</entry>
+               </row>
+               <row>
+                 <entry>DBUS_RELEASE_NAME_REPLY_NOT_OWNER</entry>
+                 <entry>3</entry>
+                 <entry>The caller was not the primary owner of this name,
+                  and was also not waiting in the queue to own this name.</entry>
+               </row>
+             </tbody>
+           </tgroup>
+         </informaltable>
+        </para>
       </sect3>
     </sect2>
 
index 75e448e..8b1b221 100644 (file)
@@ -1710,10 +1710,10 @@ def bus_get_unix_user(Connection connection, service_name):
 
     return retval
 
-#These are defines, not enums so they aren't auto generated
-DBUS_START_REPLY_SUCCESS = 0 
-DBUS_START_REPLY_ALREADY_RUNNING = 1 
-    
+# these are defines, not enums, so they aren't auto generated
+DBUS_START_REPLY_SUCCESS = 0
+DBUS_START_REPLY_ALREADY_RUNNING = 1
+
 def bus_start_service_by_name(Connection connection, service_name, flags=0):
     cdef DBusError error
     dbus_error_init(&error)
@@ -1771,9 +1771,30 @@ def bus_request_name(Connection connection, service_name, flags=0):
         errormsg = error.message
         dbus_error_free(&error)
         raise DBusException, errormsg
-        
+
     return retval
-    
+
+RELEASE_NAME_REPLY_RELEASED = 1
+RELEASE_NAME_REPLY_NON_EXISTENT = 2
+RELEASE_NAME_REPLY_NOT_OWNER = 3
+
+def bus_release_name(Connection connection, service_name):
+    cdef DBusError error
+    dbus_error_init(&error)
+    cdef int retval
+    cdef DBusConnection *conn
+
+    conn = connection._get_conn()
+    retval = dbus_bus_release_name(conn,
+                                   service_name,
+                                   &error)
+    if dbus_error_is_set(&error):
+        errormsg = error.message
+        dbus_error_free(&error)
+        raise DBusException, errormsg
+
+    return retval
+
 def bus_name_has_owner(Connection connection, service_name):
     cdef DBusError error
     dbus_error_init(&error)
index 5cb8d5a..2b01b96 100644 (file)
@@ -17,9 +17,13 @@ class ValidationException(DBusException):
 
 class IntrospectionParserException(DBusException):
     def __init__(self, msg=''):
-            DBusException.__init__(self, "Error parsing introspect data: %s"%msg)
+        DBusException.__init__(self, "Error parsing introspect data: %s"%msg)
 
 class UnknownMethodException(DBusException):
-    def __init__(self, msg=''):
-        DBusException.__init__("Unknown method: %s"%msg)
+    def __init__(self, method):
+        DBusException.__init__(self, "Unknown method: %s"%method)
+
+class NameExistsException(DBusException):
+    def __init__(self, name):
+        DBusException.__init__(self, "Bus name already exists: %s"%name)
 
index 6c3561a..e5a7002 100644 (file)
@@ -1,9 +1,9 @@
-
-import dbus_bindings 
+import dbus_bindings
 import _dbus
 import operator
 import traceback
 
+from exceptions import NameExistsException
 from exceptions import UnknownMethodException
 from decorators import method
 from decorators import signal
@@ -31,13 +31,13 @@ class BusName(object):
             # because you can't put flags in, but... who knows?
             pass
         elif retval == dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
-            raise dbus_bindings.DBusException('requested name %s already exists' % name)
+            raise NameExistsException(name)
         elif retval == dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
             # if this is a shared bus which is being used by someone
             # else in this process, this can happen legitimately
             pass
         else:
-            raise dbus_bindings.DBusException('requesting name %s returned unexpected value %s' % (name, retval))
+            raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval))
 
         # and create the object
         bus_name = object.__new__(cls)
@@ -57,8 +57,7 @@ class BusName(object):
     # we can delete the low-level name here because these objects
     # are guaranteed to exist only once for each bus name
     def __del__(self):
-        # FIXME: we don't have this function yet :)
-        #dbus_bindings.bus_release_name(self._bus.get_connection(), self._named_service)
+        dbus_bindings.bus_release_name(self._bus.get_connection(), self._name)
         pass
 
     def get_bus(self):
index ab70350..e972f44 100755 (executable)
@@ -252,8 +252,17 @@ class TestDBusBindings(unittest.TestCase):
                 else:
                     names[name] = busname
 
+                del busname
+
             print
 
+        del names
+
+        bus = dbus.Bus()
+        ret = dbus.dbus_bindings.bus_name_has_owner(bus._connection, 'org.freedesktop.DBus.Python.TestName')
+        self.assert_(not ret, 'deleting reference failed to release BusName org.freedesktop.DBus.Python.TestName')
+
+
 class TestDBusPythonToGLibBindings(unittest.TestCase):
     def setUp(self):
         self.bus = dbus.SessionBus()