From b5e4d26adec0a9ec37a1bae7aeb5a68344b78ebf Mon Sep 17 00:00:00 2001 From: Robert McQueen Date: Tue, 15 Nov 2005 17:19:19 +0000 Subject: [PATCH] 2005-11-15 Robert McQueen * 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. --- ChangeLog | 23 +++++++++++ bus/driver.c | 67 ++++++++++++++++++++++++++++++- bus/services.c | 64 ++++++++++++++++++++++++++++++ bus/services.h | 6 +++ dbus/dbus-bus.c | 65 ++++++++++++++++++++++++++++++ dbus/dbus-bus.h | 3 ++ dbus/dbus-shared.h | 5 +++ doc/dbus-specification.xml | 98 +++++++++++++++++++++++++++++++++++++++++++++- python/dbus_bindings.pyx | 33 +++++++++++++--- python/exceptions.py | 10 +++-- python/service.py | 11 +++--- test/python/test-client.py | 9 +++++ 12 files changed, 377 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index ee86d05..39e6783 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,28 @@ 2005-11-15 Robert McQueen + * 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 + * python/service.py: Include the traceback in the error reply when we send an exception over the bus. _BEST_ _PATCH_ _EVER_ diff --git a/bus/driver.c b/bus/driver.c index a6c4faf..bc63e7c 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -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, diff --git a/bus/services.c b/bus/services.c index 7a22dce..87feab2 100644 --- a/bus/services.c +++ b/bus/services.c @@ -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) { diff --git a/bus/services.h b/bus/services.h index f075404..d4b3d83 100644 --- a/bus/services.h +++ b/bus/services.h @@ -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); diff --git a/dbus/dbus-bus.c b/dbus/dbus-bus.c index 107fde9..9016f1b 100644 --- a/dbus/dbus-bus.c +++ b/dbus/dbus-bus.c @@ -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. * diff --git a/dbus/dbus-bus.h b/dbus/dbus-bus.h index 2447067..2329e13 100644 --- a/dbus/dbus-bus.h +++ b/dbus/dbus-bus.h @@ -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); diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h index a1f49a0..a8519c1 100644 --- a/dbus/dbus-shared.h +++ b/dbus/dbus-shared.h @@ -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 diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index a30e101..d118465 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -2567,7 +2567,8 @@ A connection can request additional names to be associated with it using the org.freedesktop.DBus.RequestName message. describes the format of a valid - name. + name. These names can be released again using the + org.freedesktop.DBus.ReleaseName message. @@ -2700,6 +2701,101 @@ + + + + <literal>org.freedesktop.DBus.ReleaseName</literal> + + As a method: + + UINT32 ReleaseName (in STRING name) + + Message arguments: + + + + + Argument + Type + Description + + + + + 0 + STRING + Name to release + + + + + Reply arguments: + + + + + Argument + Type + Description + + + + + 0 + UINT32 + Return value + + + + + + + This method call should be sent to + org.freedesktop.DBus 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: + + + + + + Conventional Name + Value + Description + + + + + DBUS_RELEASE_NAME_REPLY_RELEASED + 1 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. + + + DBUS_RELEASE_NAME_REPLY_NON_EXISTENT + 2 + The given name does not exist on this bus. + + + DBUS_RELEASE_NAME_REPLY_NOT_OWNER + 3 + The caller was not the primary owner of this name, + and was also not waiting in the queue to own this name. + + + + + diff --git a/python/dbus_bindings.pyx b/python/dbus_bindings.pyx index 75e448e..8b1b221 100644 --- a/python/dbus_bindings.pyx +++ b/python/dbus_bindings.pyx @@ -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) diff --git a/python/exceptions.py b/python/exceptions.py index 5cb8d5a..2b01b96 100644 --- a/python/exceptions.py +++ b/python/exceptions.py @@ -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) diff --git a/python/service.py b/python/service.py index 6c3561a..e5a7002 100644 --- a/python/service.py +++ b/python/service.py @@ -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): diff --git a/test/python/test-client.py b/test/python/test-client.py index ab70350..e972f44 100755 --- a/test/python/test-client.py +++ b/test/python/test-client.py @@ -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() -- 2.7.4