summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Hasselmann <mathias@openismus.com>2008-01-31 23:26:30 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2008-07-14 14:59:26 +0100
commit9774cdade2306b9bb641162a14645510fc822c86 (patch)
tree18203388623596a0bd7d9fd43dc678a4fb79124c
parentdff98456995c37d964eb32a7de7ca718fc3d48d7 (diff)
downloaddbus-python-9774cdade2306b9bb641162a14645510fc822c86.tar.gz
Initial support for DBusServer class (#14322).
-rw-r--r--_dbus_bindings/server.c535
-rw-r--r--dbus/server.py38
-rwxr-xr-xtest/test-server.py74
3 files changed, 647 insertions, 0 deletions
diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c
new file mode 100644
index 0000000..ec92b3b
--- /dev/null
+++ b/_dbus_bindings/server.c
@@ -0,0 +1,535 @@
+/* Implementation of the _dbus_bindings Server type, a Python wrapper
+ * for DBusServer.
+ *
+ * Copyright (C) 2008 Openismus GmbH <http://openismus.com/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+/* Server definition ================================================ */
+
+typedef struct {
+ PyObject_HEAD
+ DBusServer *server;
+
+ /* Weak-references list to make server weakly referenceable */
+ PyObject *weaklist;
+
+ PyObject *mainloop;
+} Server;
+
+PyDoc_STRVAR(Server_tp_doc,
+"A D-Bus server.\n"
+"\n"
+"::\n"
+"\n"
+" Server(address, mainloop=None, auth_mechanisms=None) -> Server\n"
+);
+
+/* D-Bus Server user data slot, containing an owned reference to either
+ * the Server, or a weakref to the Server.
+ */
+static dbus_int32_t _server_python_slot;
+
+/* C API for main-loop hooks ======================================== */
+
+/* Return a borrowed reference to the DBusServer which underlies this
+ * Server. */
+DBusServer *
+DBusPyServer_BorrowDBusServer(PyObject *self)
+{
+ DBusServer *dbs;
+
+ TRACE(self);
+ if (!DBusPyServer_Check(self)) {
+ PyErr_SetString(PyExc_TypeError, "A dbus.Server is required");
+ return NULL;
+ }
+ dbs = ((Server *)self)->server;
+ if (!dbs) {
+ PyErr_SetString(PyExc_RuntimeError, "Server is in an invalid "
+ "state: no DBusServer");
+ return NULL;
+ }
+ return dbs;
+}
+
+/* Internal C API =================================================== */
+
+static void
+DBusPyServer_set_auth_mechanisms(Server *self,
+ PyObject *auth_mechanisms)
+{
+ const char **list = NULL;
+
+ if (auth_mechanisms) {
+ Py_ssize_t length;
+ Py_ssize_t i;
+
+ if (!PySequence_Check(auth_mechanisms)) {
+ PyErr_SetString(PyExc_TypeError, "Expecting sequence for auth_mechanisms parameter");
+ return;
+ }
+
+ length = PySequence_Fast_GET_SIZE(auth_mechanisms);
+
+ Py_BEGIN_ALLOW_THREADS
+ list = PyMem_New(const char*, length + 1);
+ Py_END_ALLOW_THREADS
+
+ for (i = 0; i < length; ++i) {
+ PyObject* am;
+
+ am = PySequence_Fast_GET_ITEM(auth_mechanisms, i);
+ am = PyObject_Str(am);
+
+ list[i] = PyString_AS_STRING(am);
+ }
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_set_auth_mechanisms(self->server, list);
+ Py_END_ALLOW_THREADS
+
+ PyMem_Free(list);
+}
+
+/* Return a new reference to a Python Server or subclass corresponding
+ * to the DBusServer server. For use in callbacks.
+ *
+ * Raises AssertionError if the DBusServer does not have a Server.
+ */
+static PyObject *
+DBusPyServer_ExistingFromDBusServer(DBusServer *server)
+{
+ PyObject *self, *ref;
+
+ Py_BEGIN_ALLOW_THREADS
+ ref = (PyObject *)dbus_server_get_data(server,
+ _server_python_slot);
+ Py_END_ALLOW_THREADS
+ if (ref) {
+ DBG("(DBusServer *)%p has weak reference at %p", server, ref);
+ self = PyWeakref_GetObject(ref); /* still a borrowed ref */
+ if (self && self != Py_None && DBusPyServer_Check(self)) {
+ DBG("(DBusServer *)%p has weak reference at %p pointing to %p",
+ server, ref, self);
+ TRACE(self);
+ Py_INCREF(self);
+ TRACE(self);
+ return self;
+ }
+ }
+
+ PyErr_SetString(PyExc_AssertionError,
+ "D-Bus server does not have a Server "
+ "instance associated with it");
+ return NULL;
+}
+
+static void
+DBusPyServer_new_connection_cb(DBusServer *server,
+ DBusConnection *conn,
+ void *data UNUSED)
+{
+ PyGILState_STATE gil = PyGILState_Ensure();
+ PyObject *self = NULL, *conn_obj = NULL;
+ PyObject *method = NULL, *result = NULL;
+
+printf("%s:%d\n", __func__, __LINE__);
+ self = DBusPyServer_ExistingFromDBusServer(server);
+ if (!self) goto out;
+ TRACE(self);
+
+ method = PyObject_GetAttrString(self, "_on_new_connection");
+ TRACE(method);
+
+printf("%s:%d\n", __func__, __LINE__);
+ if (method) {
+ conn_obj = DBusPyConnection_NewConsumingDBusConnection(&DBusPyConnection_Type,
+ dbus_connection_ref(conn),
+ ((Server*) self)->mainloop);
+
+ result = PyObject_CallFunctionObjArgs(method, conn_obj, NULL);
+ }
+
+out:
+printf("%s:%d\n", __func__, __LINE__);
+ Py_XDECREF(result);
+ Py_XDECREF(method);
+ Py_XDECREF(conn_obj);
+ Py_XDECREF(self);
+
+ if (PyErr_Occurred())
+ PyErr_Print();
+
+ PyGILState_Release(gil);
+}
+
+/* Return a new reference to a Python Server or subclass (given by cls)
+ * corresponding to the DBusServer server, which must have been newly
+ * created. For use by the Server constructor.
+ *
+ * Raises AssertionError if the DBusServer already has a Server.
+ */
+static PyObject *
+DBusPyServer_NewDBusServer(PyTypeObject *cls,
+ DBusServer *server,
+ PyObject *mainloop,
+ PyObject *auth_mechanisms)
+{
+ Server *self = NULL;
+ PyObject *ref;
+ dbus_bool_t ok;
+
+ DBG("%s(cls=%p, server=%p, mainloop=%p, auth_mechanisms=%p)",
+ __func__, cls, server, mainloop, auth_mechanisms);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(server);
+
+ Py_BEGIN_ALLOW_THREADS
+ ref = (PyObject *)dbus_server_get_data(server,
+ _server_python_slot);
+ Py_END_ALLOW_THREADS
+ if (ref) {
+ self = (Server *)PyWeakref_GetObject(ref);
+ ref = NULL;
+ if (self && (PyObject *)self != Py_None) {
+ self = NULL;
+ PyErr_SetString(PyExc_AssertionError,
+ "Newly created D-Bus server already has a "
+ "Server instance associated with it");
+ DBG("%s() fail - assertion failed, DBusPyServer has a DBusServer already", __func__);
+ DBG_WHEREAMI;
+ return NULL;
+ }
+ }
+ ref = NULL;
+
+ /* Change mainloop from a borrowed reference to an owned reference */
+ if (!mainloop || mainloop == Py_None) {
+ mainloop = dbus_py_get_default_main_loop();
+ if (!mainloop)
+ goto err;
+ }
+ else {
+ Py_INCREF(mainloop);
+ }
+
+ DBG("Constructing Server from DBusServer at %p", server);
+
+ self = (Server *)(cls->tp_alloc(cls, 0));
+ if (!self) goto err;
+ TRACE(self);
+
+ DBG_WHEREAMI;
+
+ self->mainloop = mainloop;
+ self->server = NULL;
+
+ ref = PyWeakref_NewRef((PyObject *)self, NULL);
+ if (!ref) goto err;
+ DBG("Created weak ref %p to (Server *)%p for (DBusServer *)%p",
+ ref, self, server);
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_server_set_data(server, _server_python_slot,
+ (void *)ref,
+ (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
+ Py_END_ALLOW_THREADS
+
+ if (ok) {
+ DBG("Attached weak ref %p ((Server *)%p) to (DBusServer *)%p",
+ ref, self, server);
+
+ ref = NULL; /* don't DECREF it - the DBusServer owns it now */
+ }
+ else {
+ DBG("Failed to attached weak ref %p ((Server *)%p) to "
+ "(DBusServer *)%p - will dispose of it", ref, self, server);
+ PyErr_NoMemory();
+ goto err;
+ }
+
+ DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(server, err);
+ self->server = server;
+ /* the DBusPyServer will close it now */
+ server = NULL;
+
+ if (self->mainloop != Py_None &&
+ !dbus_py_set_up_server((PyObject *)self, self->mainloop))
+ goto err;
+
+ DBusPyServer_set_auth_mechanisms(self, auth_mechanisms);
+
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_set_new_connection_function(self->server,
+ DBusPyServer_new_connection_cb,
+ NULL, NULL);
+ Py_END_ALLOW_THREADS
+
+ DBG("%s() -> %p", __func__, self);
+ TRACE(self);
+ return (PyObject *)self;
+
+err:
+ DBG("Failed to construct Server from DBusServer at %p", server);
+// Py_XDECREF(mainloop);
+// Py_XDECREF(self);
+// Py_XDECREF(ref);
+ if (server) {
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_disconnect(server);
+ dbus_server_unref(server);
+ Py_END_ALLOW_THREADS
+ }
+ DBG("%s() fail", __func__);
+ DBG_WHEREAMI;
+ return NULL;
+}
+
+/* Server type-methods ============================================== */
+
+static PyObject *
+Server_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ DBusServer *server;
+ const char *address;
+ DBusError error;
+ PyObject *self, *mainloop = NULL, *auth_mechanisms = NULL;
+ static char *argnames[] = {"address", "mainloop", "auth_mechanisms", NULL};
+
+printf("%s:%d\n", __func__, __LINE__);
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OO", argnames,
+ &address, &mainloop, &auth_mechanisms)) {
+ return NULL;
+ }
+
+ dbus_error_init(&error);
+
+ Py_BEGIN_ALLOW_THREADS
+ server = dbus_server_listen(address, &error);
+ Py_END_ALLOW_THREADS
+
+ if (!server) {
+ DBusPyException_ConsumeError(&error);
+ return NULL;
+ }
+
+ self = DBusPyServer_NewDBusServer(cls, server, mainloop, auth_mechanisms);
+ TRACE(self);
+
+ return self;
+}
+
+/* Destructor */
+static void Server_tp_dealloc(Server *self)
+{
+ DBusServer *server = self->server;
+ PyObject *et, *ev, *etb;
+
+printf("%s:%d\n", __func__, __LINE__);
+ /* avoid clobbering any pending exception */
+ PyErr_Fetch(&et, &ev, &etb);
+
+ if (self->weaklist) {
+ PyObject_ClearWeakRefs((PyObject *)self);
+ }
+
+ TRACE(self);
+ DBG("Deallocating Server at %p (DBusServer at %p)", self, server);
+ DBG_WHEREAMI;
+
+ if (server) {
+ DBG("Server at %p has a server, disconnecting it...", self);
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_disconnect(server);
+ Py_END_ALLOW_THREADS
+ }
+
+ Py_DECREF(self->mainloop);
+
+ /* make sure to do this last to preserve the invariant that
+ * self->server is always non-NULL for any referenced Server.
+ */
+ DBG("Server at %p: nulling self->server", self);
+ self->server = NULL;
+
+ if (server) {
+ DBG("Server at %p: unreffing server", self);
+ dbus_server_unref(server);
+ }
+
+ DBG("Server at %p: freeing self", self);
+ PyErr_Restore(et, ev, etb);
+ (self->ob_type->tp_free)((PyObject *)self);
+}
+
+PyDoc_STRVAR(Server_disconnect__doc__,
+"disconnect()\n\n"
+"Releases the server's address and stops listening for new clients.\n\n"
+"If called more than once, only the first call has an effect.");
+static PyObject *
+Server_disconnect (Server *self, PyObject *args UNUSED)
+{
+ TRACE(self);
+ if (self->server) {
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_disconnect(self->server);
+ Py_END_ALLOW_THREADS
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Server_get_address__doc__,
+"get_address() -> String\n\n"
+"Returns the address of the server.");
+static PyObject *
+Server_get_address(Server *self, PyObject *args UNUSED)
+{
+ const char *address;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+ Py_BEGIN_ALLOW_THREADS
+ address = dbus_server_get_address(self->server);
+ Py_END_ALLOW_THREADS
+
+ return PyString_FromString(address);
+}
+
+PyDoc_STRVAR(Server_get_id__doc__,
+"get_id() -> String\n\n"
+"Returns the unique ID of the server.");
+static PyObject *
+Server_get_id(Server *self, PyObject *args UNUSED)
+{
+ const char *id;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+ Py_BEGIN_ALLOW_THREADS
+ id = dbus_server_get_id(self->server);
+ Py_END_ALLOW_THREADS
+
+ return PyString_FromString(id);
+}
+
+PyDoc_STRVAR(Server_get_is_connected__doc__,
+"get_is_connected() -> bool\n\n"
+"Return true if this Server is still listening for new connections.\n");
+static PyObject *
+Server_get_is_connected (Server *self, PyObject *args UNUSED)
+{
+ dbus_bool_t ret;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+ Py_BEGIN_ALLOW_THREADS
+ ret = dbus_server_get_is_connected(self->server);
+ Py_END_ALLOW_THREADS
+ return PyBool_FromLong(ret);
+}
+
+/* Server type object =============================================== */
+
+struct PyMethodDef DBusPyServer_tp_methods[] = {
+#define ENTRY(name, flags) {#name, (PyCFunction)Server_##name, flags, Server_##name##__doc__}
+ ENTRY(disconnect, METH_NOARGS),
+ ENTRY(get_address, METH_NOARGS),
+ ENTRY(get_id, METH_NOARGS),
+ ENTRY(get_is_connected, METH_NOARGS),
+ {NULL},
+#undef ENTRY
+};
+
+PyTypeObject DBusPyServer_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "_dbus_bindings.Server",/*tp_name*/
+ sizeof(Server), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)Server_tp_dealloc,
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
+ Server_tp_doc, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ offsetof(Server, weaklist), /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ DBusPyServer_tp_methods,/*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ Server_tp_new, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+dbus_bool_t
+dbus_py_init_server_types(void)
+{
+ /* Get a slot to store our weakref on DBus Server */
+printf("%s:%d\n", __func__, __LINE__);
+ _server_python_slot = -1;
+ if (!dbus_server_allocate_data_slot(&_server_python_slot))
+ return FALSE;
+printf("%s:%d\n", __func__, __LINE__);
+ if (PyType_Ready(&DBusPyServer_Type) < 0)
+ return FALSE;
+printf("%s:%d\n", __func__, __LINE__);
+ return TRUE;
+}
+
+dbus_bool_t
+dbus_py_insert_server_types(PyObject *this_module)
+{
+printf("%s:%d\n", __func__, __LINE__);
+ if (PyModule_AddObject(this_module, "Server",
+ (PyObject *)&DBusPyServer_Type) < 0) return FALSE;
+printf("%s:%d\n", __func__, __LINE__);
+ return TRUE;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus/server.py b/dbus/server.py
new file mode 100644
index 0000000..c7a6184
--- /dev/null
+++ b/dbus/server.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2008 Openismus GmbH <http://openismus.com/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ('Server', )
+__docformat__ = 'reStructuredText'
+
+from _dbus_bindings import Server as _Server
+
+class Server(_Server):
+ """An opaque object representing a server that listens for connections from
+ other applications.
+
+ :Since: 0.82.5
+ """
+
+ address = property(_Server.get_address)
+ id = property(_Server.get_id)
+ is_connected = property(_Server.get_is_connected)
+
diff --git a/test/test-server.py b/test/test-server.py
new file mode 100755
index 0000000..2a9f824
--- /dev/null
+++ b/test/test-server.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+
+from dbus.mainloop.glib import DBusGMainLoop
+
+import dbus
+import dbus.bus
+import dbus.service
+import dbus.server
+
+import gobject
+import os, sys
+
+class TestService(dbus.service.Object):
+ NAME = 'org.freedesktop.dbus.TestService'
+ PATH = '/org/freedesktop/dbus/TestService'
+
+ def __init__(self, conn, path=PATH):
+ super(TestService, self).__init__(conn, path)
+
+ @dbus.service.method(dbus_interface=NAME,
+ out_signature='s',
+ in_signature='s')
+ def reverse(self, text):
+ text = list(text)
+ text.reverse()
+
+ return ''.join(text)
+
+class TestServer(dbus.server.Server):
+ def __init__(self, *args, **kwargs):
+ super(TestServer, self).__init__(*args, **kwargs)
+ self.__connections = list()
+
+ def _on_new_connection(self, conn):
+ print 'new connection: %r' % conn
+ self.__connections.append(conn)
+ TestService(conn)
+
+pin, pout = os.pipe()
+child = os.fork()
+
+if 0 == child:
+ DBusGMainLoop(set_as_default=True)
+ server = TestServer('unix:tmpdir=/tmp')
+
+ os.write(pout, server.address)
+ os.close(pout)
+ os.close(pin)
+
+ print 'server running: %s' % server.address
+ gobject.MainLoop().run()
+
+ print 'server quit'
+
+ print 'done?'
+
+else:
+ os.waitpid(child, os.WNOHANG)
+ os.close(pout)
+
+ address = os.read(pin, 128)
+ os.close(pin)
+
+ client = dbus.bus.Connection(address)
+ proxy = client.get_object(TestService.NAME, TestService.PATH)
+ object = dbus.Interface(proxy, TestService.NAME)
+
+ while True:
+ line = sys.stdin.readline()
+ if not line: break
+
+ text = line.strip()
+ print 'reverse(%s): %s' % (text, object.reverse(text))
+