From 2a95db46a0e69ad0e3a3d04d39988f98f34e0d3b Mon Sep 17 00:00:00 2001 From: Huang Peng Date: Wed, 25 Jun 2008 11:33:44 +0800 Subject: [PATCH] Add wrapper for DBusServer. --- Makefile.am | 1 + _dbus_bindings/Makefile.am | 3 + _dbus_bindings/conn-methods.c | 1 + _dbus_bindings/dbus_bindings-internal.h | 8 + _dbus_bindings/mainloop.c | 18 ++ _dbus_bindings/module.c | 2 + _dbus_bindings/server-internal.h | 51 +++++ _dbus_bindings/server-methods.c | 215 ++++++++++++++++++ _dbus_bindings/server.c | 377 ++++++++++++++++++++++++++++++++ dbus/lowlevel.py | 2 + dbus/server.py | 71 ++++++ examples/Makefile.am | 5 +- examples/example-server-client.py | 18 ++ examples/example-server.py | 61 ++++++ include/dbus-python.h | 1 + 15 files changed, 833 insertions(+), 1 deletion(-) create mode 100644 _dbus_bindings/server-internal.h create mode 100644 _dbus_bindings/server-methods.c create mode 100644 _dbus_bindings/server.c create mode 100644 dbus/server.py create mode 100644 examples/example-server-client.py create mode 100644 examples/example-server.py diff --git a/Makefile.am b/Makefile.am index 8f594ad..40ac719 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,6 +35,7 @@ nobase_python_PYTHON = \ dbus/mainloop/glib.py \ dbus/proxies.py \ dbus/service.py \ + dbus/server.py \ dbus/types.py check_py_sources = $(nobase_python_PYTHON) diff --git a/_dbus_bindings/Makefile.am b/_dbus_bindings/Makefile.am index c2e59c9..a747e18 100644 --- a/_dbus_bindings/Makefile.am +++ b/_dbus_bindings/Makefile.am @@ -7,6 +7,9 @@ _dbus_bindings_la_SOURCES = \ abstract.c \ bus.c \ bytes.c \ + server.c \ + server-internal.h \ + server-methods.c \ conn.c \ conn-internal.h \ conn-methods.c \ diff --git a/_dbus_bindings/conn-methods.c b/_dbus_bindings/conn-methods.c index 6fadc31..81c4514 100644 --- a/_dbus_bindings/conn-methods.c +++ b/_dbus_bindings/conn-methods.c @@ -179,6 +179,7 @@ _filter_message(DBusConnection *conn, DBusMessage *message, void *user_data) callable = PyList_GET_ITEM(conn_obj->filters, i); if (callable == user_data) { Py_INCREF(callable); + break; } else { callable = NULL; diff --git a/_dbus_bindings/dbus_bindings-internal.h b/_dbus_bindings/dbus_bindings-internal.h index 1dfd6b2..ea3f2aa 100644 --- a/_dbus_bindings/dbus_bindings-internal.h +++ b/_dbus_bindings/dbus_bindings-internal.h @@ -54,6 +54,12 @@ static inline int type##_Check (PyObject *o) \ return (PyObject_TypeCheck (o, &type##_Type)); \ } +/* server.c */ +extern PyTypeObject DBusPyServer_Type; +DEFINE_CHECK(DBusPyServer) +extern dbus_bool_t dbus_py_init_server_types(void); +extern dbus_bool_t dbus_py_insert_server_types(PyObject *this_module); + /* conn.c */ extern PyTypeObject DBusPyConnection_Type; DEFINE_CHECK(DBusPyConnection) @@ -138,6 +144,8 @@ extern dbus_bool_t dbus_py_init_pending_call(void); extern dbus_bool_t dbus_py_insert_pending_call(PyObject *this_module); /* mainloop.c */ +extern dbus_bool_t dbus_py_set_up_server(PyObject *server, + PyObject *mainloop); extern dbus_bool_t dbus_py_set_up_connection(PyObject *conn, PyObject *mainloop); extern PyObject *dbus_py_get_default_main_loop(void); diff --git a/_dbus_bindings/mainloop.c b/_dbus_bindings/mainloop.c index 3b56ade..c4659fc 100644 --- a/_dbus_bindings/mainloop.c +++ b/_dbus_bindings/mainloop.c @@ -114,6 +114,24 @@ dbus_py_check_mainloop_sanity(PyObject *mainloop) } dbus_bool_t +dbus_py_set_up_server(PyObject *server, PyObject *mainloop) +{ + if (NativeMainLoop_Check(mainloop)) { + /* Native mainloops are allowed to do arbitrary strange things */ + NativeMainLoop *nml = (NativeMainLoop *)mainloop; + DBusServer *dbs = DBusPyServer_BorrowDBusServer(server); + + if (!dbs) { + return FALSE; + } + return (nml->set_up_server_cb)(dbs, nml->data); + } + PyErr_SetString(PyExc_TypeError, + "A dbus.mainloop.NativeMainLoop instance is required"); + return FALSE; +} + +dbus_bool_t dbus_py_set_up_connection(PyObject *conn, PyObject *mainloop) { if (NativeMainLoop_Check(mainloop)) { diff --git a/_dbus_bindings/module.c b/_dbus_bindings/module.c index ddeb1f0..de5966b 100644 --- a/_dbus_bindings/module.c +++ b/_dbus_bindings/module.c @@ -267,6 +267,7 @@ init_dbus_bindings(void) if (!dbus_py_init_message_types()) return; if (!dbus_py_init_pending_call()) return; if (!dbus_py_init_mainloop()) return; + if (!dbus_py_init_server_types()) return; if (!dbus_py_init_conn_types()) return; this_module = Py_InitModule3("_dbus_bindings", module_functions, module_doc); @@ -282,6 +283,7 @@ init_dbus_bindings(void) if (!dbus_py_insert_message_types(this_module)) return; if (!dbus_py_insert_pending_call(this_module)) return; if (!dbus_py_insert_mainloop_types(this_module)) return; + if (!dbus_py_insert_server_types(this_module)) return; if (!dbus_py_insert_conn_types(this_module)) return; if (PyModule_AddStringConstant(this_module, "BUS_DAEMON_NAME", diff --git a/_dbus_bindings/server-internal.h b/_dbus_bindings/server-internal.h new file mode 100644 index 0000000..cde1b1f --- /dev/null +++ b/_dbus_bindings/server-internal.h @@ -0,0 +1,51 @@ +/* _dbus_bindings internal API. For use within _dbus_bindings only. + * + * Copyright (C) 2008 Huang Peng . + * + * 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. + */ + +#ifndef DBUS_BINDINGS_SERVER_H +#define DBUS_BINDINGS_SERVER_H + +#include "dbus_bindings-internal.h" + +typedef struct { + PyObject_HEAD + DBusServer *server; + + PyObject *new_connection_callback; + PyTypeObject *new_connection_type; + /* Weak-references list to make Connections weakly referenceable */ + PyObject *weaklist; + + PyObject *mainloop; +} Server; + +extern struct PyMethodDef DBusPyServer_tp_methods[]; +extern DBusHandlerResult DBusPyServer_HandleMessage(Server *, + PyObject *, + PyObject *); +extern PyObject *DBusPyServer_ExistingFromDBusServer(DBusServer *); + +extern PyObject *DBusPyServer_GetAddress(Server *, PyObject *); + +#endif diff --git a/_dbus_bindings/server-methods.c b/_dbus_bindings/server-methods.c new file mode 100644 index 0000000..1e10d06 --- /dev/null +++ b/_dbus_bindings/server-methods.c @@ -0,0 +1,215 @@ +/* Implementation of normal Python-accessible methods on the _dbus_bindings + * Connection type; separated out to keep the file size manageable. + * + * Copyright (C) 2008 Huang Peng . + * + * 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" +#include "server-internal.h" + +PyDoc_STRVAR(Server_disconnect__doc__, +"disconnect()\n\n" +"Releases the server's address and stops listening for new clients.\n"); +static PyObject * +Server_disconnect (Server *self, PyObject *args UNUSED) +{ + TRACE(self); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server); + Py_BEGIN_ALLOW_THREADS + dbus_server_disconnect(self->server); + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} + + + +PyDoc_STRVAR(Server_get_address__doc__, +"get_address() -> String\n\n" +"Return the address of the server.\n"); +static PyObject * +Server_get_address (Server *self, PyObject *args UNUSED) +{ + char *address; + PyObject *ret; + + 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 + ret = PyString_FromString(address); + dbus_free (address); + return ret; +} + +PyDoc_STRVAR(Server_get_id__doc__, +"get_id() -> String\n\n" +"Return the id of the server.\n"); +static PyObject * +Server_get_id (Server *self, PyObject *args UNUSED) +{ + char *id; + PyObject *ret; + + 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 + + ret = PyString_FromString(id); + dbus_free (id); + + return ret; +} + +PyDoc_STRVAR(Server_get_is_connected__doc__, +"get_is_connected() -> bool\n\n" +"Return True if server is istill listening new connections.\n"); +static PyObject * +Server_get_is_connected (Server *self, PyObject *args UNUSED) +{ + PyObject *ret; + + TRACE(self); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server); + Py_BEGIN_ALLOW_THREADS + if (dbus_server_get_is_connected(self->server)) + ret = Py_True; + else + ret = Py_False; + Py_END_ALLOW_THREADS + + Py_INCREF(ret); + + return ret; +} + + +PyDoc_STRVAR(Server_set_new_connection_function__doc__, +"set_new_connection_function()\n\n" +"Set a function for handling new connections.\n"); +static PyObject * +Server_set_new_connection_function (Server *self, PyObject *args) +{ + + PyObject *callback; + PyTypeObject *conn_type = NULL; + + if (!PyArg_ParseTuple(args, "O|O:set_new_connection_function", + &callback, &conn_type)) { + return NULL; + } + + if (callback != Py_None && ! PyCallable_Check (callback)) { + PyErr_SetString (PyExc_TypeError, + "The first argument must be a callable object"); + return NULL; + } + + if (!conn_type || (PyObject *)conn_type == Py_None) { + conn_type = &DBusPyConnection_Type; + } + + if (!PyType_IsSubtype (conn_type, &DBusPyConnection_Type)) { + PyErr_SetString (PyExc_TypeError, + "The second argument must be None or Subclass of _dbus_bindings_.Connection"); + return NULL; + } + + TRACE(self); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server); + Py_BEGIN_ALLOW_THREADS + Py_XDECREF (self->new_connection_callback); + if (callback != Py_None) { + Py_INCREF (callback); + self->new_connection_callback = callback; + } + else + self->new_connection_callback = NULL; + + Py_XDECREF (self->new_connection_type); + Py_INCREF (conn_type); + self->new_connection_type = conn_type; + + Py_END_ALLOW_THREADS + + Py_INCREF (Py_None); + return Py_None; +} + + +PyDoc_STRVAR(Server_set_auth_mechanisms__doc__, +"set_auth_mechanisms (...)\n\n" +"Set the authentication mechanisms that this server offers to clients.\n"); +static PyObject * +Server_set_auth_mechanisms (Server *self, PyObject *args) +{ + + int len, i; + const char ** _mechanisms = NULL; + + len = PyTuple_Size (args); + + if (len > 0) { + _mechanisms = (const char **)malloc (sizeof (char *) * (len + 1)); + for (i = 0; i < len; i++) { + PyObject *mechanism = PyTuple_GetItem (args, i); + if (!PyString_Check (mechanism)) { + free (_mechanisms); + PyErr_SetString(PyExc_TypeError, + "arguments must be strings"); + return NULL; + } + _mechanisms[i] = PyString_AS_STRING (mechanism); + } + _mechanisms[len] = NULL; + } + + TRACE(self); + DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server); + Py_BEGIN_ALLOW_THREADS + dbus_server_set_auth_mechanisms (self->server, _mechanisms); + Py_END_ALLOW_THREADS + + if (_mechanisms != NULL) + free (_mechanisms); + Py_INCREF (Py_None); + return Py_None; +} + +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), + ENTRY(set_new_connection_function, METH_VARARGS), + ENTRY(set_auth_mechanisms, METH_VARARGS), + {NULL}, +#undef ENTRY +}; + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/_dbus_bindings/server.c b/_dbus_bindings/server.c new file mode 100644 index 0000000..3de7e07 --- /dev/null +++ b/_dbus_bindings/server.c @@ -0,0 +1,377 @@ +/* Implementation of the _dbus_bindings Server type, a Python wrapper + * for DBusServer. See also server-methods.c. + * + * Copyright (C) 2008 Huang Peng . + * + * 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" +#include "server-internal.h" + +/* Server definition ============================================ */ + +PyDoc_STRVAR(Server_tp_doc, +"A D-Bus server.\n" +"\n" +"::\n" +"\n" +" Server(address, mainloop=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 =================================================== */ + +/* 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. + */ +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; +} + +/* 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 and Bus constructors. + * + * Raises AssertionError if the DBusServer already has a Server. + */ + +static void +Server_new_connection_callback (DBusServer *server, DBusConnection *connection, void * data UNUSED) +{ + PyObject *conn = NULL; + PyObject *result = NULL; + PyObject *args = NULL; + Server *self = NULL; + + self = (Server *)DBusPyServer_ExistingFromDBusServer(server); + if (self == NULL) + goto error; + + if (self->new_connection_callback) { + conn = DBusPyConnection_NewConsumingDBusConnection(self->new_connection_type, connection, self->mainloop); + if (conn == NULL) + goto error; + dbus_connection_ref (connection); + + args = Py_BuildValue ("(OO)", self, conn); + if (args == NULL) + goto error; + result = PyObject_Call (self->new_connection_callback, args, NULL); + if (result == NULL) + goto error; + + } + goto out; +error: + PyErr_Print (); +out: + Py_XDECREF (conn); + Py_XDECREF (result); + Py_XDECREF (args); + Py_XDECREF ((PyObject *)self); +} + +/* Server type-methods ========================================== */ + +/* "Constructor" (the real constructor is Server_NewFromDBusServer, + * to which this delegates). */ +static PyObject * +Server_tp_new(PyTypeObject *cls, PyObject *args UNUSED, PyObject *kwargs UNUSED) +{ + PyObject *self; + self = cls->tp_alloc (cls, 0); + return (PyObject *)self; + +} + + +static int +Server_tp_init(Server *self, PyObject *args, PyObject *kwargs) +{ + PyObject *mainloop = NULL; + DBusServer *server = NULL; + char *address = NULL; + + PyObject *ref = NULL; + DBusError error; + dbus_bool_t ok; + + static char *argnames[] = {"address", "mainloop", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames, + &address, &mainloop)) { + return -1; + } + + if (!mainloop || mainloop == Py_None) { + mainloop = dbus_py_get_default_main_loop(); + if (!mainloop) { + PyErr_SetString (PyExc_Exception, + "Can not get default mainloop."); + goto err; + } + } + else { + Py_INCREF (mainloop); + } + + + dbus_error_init(&error); + + Py_BEGIN_ALLOW_THREADS + server = dbus_server_listen(address, &error); + Py_END_ALLOW_THREADS + + if (!server) { + DBusPyException_ConsumeError(&error); + goto err; + } + + dbus_server_set_new_connection_function (server, Server_new_connection_callback, NULL, NULL); + + Py_INCREF (mainloop); + self->mainloop = mainloop; + self->server = server; + self->new_connection_callback = NULL; + self->new_connection_type = 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 (!dbus_py_set_up_server((PyObject *)self, mainloop)) { + goto err; + } + + Py_DECREF(mainloop); + + DBG("%s() -> %p", __func__, self); + TRACE(self); + return 0; + +err: + DBG("Failed to construct Server."); + Py_XDECREF(mainloop); + Py_XDECREF(ref); + if (server) { + Py_BEGIN_ALLOW_THREADS + dbus_server_unref(server); + Py_END_ALLOW_THREADS + } + DBG("%s() fail", __func__); + DBG_WHEREAMI; + return -1; +} + + +/* Destructor */ +static void Server_tp_dealloc(Server *self) +{ + DBusServer *server = self->server; + PyObject *et, *ev, *etb; + + /* 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; + + DBG("Server at %p: deleting callbacks", self); + + if (server) { + /* Might trigger callbacks if we're unlucky... */ + DBG("Server at %p has a server, closing it...", self); + Py_BEGIN_ALLOW_THREADS + Py_END_ALLOW_THREADS + } + + /* make sure to do this last to preserve the invariant that + * self->server is always non-NULL for any referenced Server + * (until the filters and object paths were freed, we might have been + * in a reference cycle!) + */ + DBG("Server at %p: nulling self->server", self); + self->server = server; + + if (server) { + DBG("Server at %p: unreffing server", self); + dbus_server_unref(server); + } + + Py_XDECREF (self->mainloop); + + DBG("Server at %p: freeing self", self); + PyErr_Restore(et, ev, etb); + (self->ob_type->tp_free)((PyObject *)self); +} + +/* Server type object =========================================== */ + +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*/ + (initproc) Server_tp_init, + /*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 Servers */ + _server_python_slot = -1; + if (!dbus_server_allocate_data_slot(&_server_python_slot)) + return FALSE; + if (PyType_Ready(&DBusPyServer_Type) < 0) + return FALSE; + return TRUE; +} + +dbus_bool_t +dbus_py_insert_server_types(PyObject *this_module) +{ + if (PyModule_AddObject(this_module, "Server", + (PyObject *)&DBusPyServer_Type) < 0) return FALSE; + return TRUE; +} + +/* vim:set ft=c cino< sw=4 sts=4 et: */ diff --git a/dbus/lowlevel.py b/dbus/lowlevel.py index eeabcbd..38f763c 100644 --- a/dbus/lowlevel.py +++ b/dbus/lowlevel.py @@ -24,9 +24,11 @@ __all__ = ('PendingCall', 'Message', 'MethodCallMessage', 'MethodReturnMessage', 'ErrorMessage', 'SignalMessage', + 'Server', 'HANDLER_RESULT_HANDLED', 'HANDLER_RESULT_NOT_YET_HANDLED') from _dbus_bindings import PendingCall, Message, MethodCallMessage, \ MethodReturnMessage, ErrorMessage, SignalMessage, \ + Server, \ HANDLER_RESULT_HANDLED, \ HANDLER_RESULT_NOT_YET_HANDLED diff --git a/dbus/server.py b/dbus/server.py new file mode 100644 index 0000000..d31567a --- /dev/null +++ b/dbus/server.py @@ -0,0 +1,71 @@ +# Copyright (C) 2008 Huang Peng +# +# 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. + +import _dbus_bindings +import dbus.connection +import dbus.lowlevel + +class Server (_dbus_bindings.Server): + def __init__ (self, address): + _dbus_bindings.Server.__init__ (self, address) + self._connections = [] + self._objects = {} + self.set_new_connection_function (self._new_connection_cb, dbus.connection.Connection) + + def register_object (self, obj, path): + if path in self._objects: + raise Exception ("%s has been registered", path) + self._objects[path] = obj + + for conn in self._connections: + obj.add_to_connection (conn, path) + + def _new_connection_cb (self, server, new_connection): + self._connections.append (new_connection) + new_connection.add_message_filter (self._message_filter_cb) + + # add all objects to the new connection + for path, obj in self._objects.items (): + obj.add_to_connection (new_connection, path) + + self.new_connection (server, new_connection) + + def _message_filter_cb (self, connection, message): + if message.get_interface() == "org.freedesktop.DBus.Local" and \ + message.get_member() == "Disconnected": + # remove all object from this connection + for path, obj in self._objects.items (): + obj.remove_from_connection (connection, path) + + self.remove_connection (connection) + self._connections.remove (connection) + return dbus.lowlevel.HANDLER_RESULT_HANDLED + + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + + def new_connection (self, server, connection): + pass + + def remove_connection (self, server, connection): + pass + + diff --git a/examples/Makefile.am b/examples/Makefile.am index 316b4bb..866aa04 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -4,5 +4,8 @@ EXTRA_DIST = \ example-service.py \ example-signal-emitter.py \ example-signal-recipient.py \ - list-system-services.py + list-system-services.py \ + example-server.py \ + example-server-client.py + # miss out the gconf examples for now - they don't work diff --git a/examples/example-server-client.py b/examples/example-server-client.py new file mode 100644 index 0000000..693fb46 --- /dev/null +++ b/examples/example-server-client.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import dbus +import dbus.mainloop.glib +import dbus.connection +import gobject +import time + +def main(): + loop = gobject.MainLoop () + conn = dbus.connection.Connection ("unix:abstract=/tmp/example-server") + obj = conn.get_object ("no.need.name", "/org/freedesktop/ExampleObject") + print obj.HelloWorld ("[%s] Hello World" % time.time()) + time.sleep (1) + print obj.Exit () + +if __name__ == "__main__": + dbus.mainloop.glib.DBusGMainLoop (set_as_default=True) + main () diff --git a/examples/example-server.py b/examples/example-server.py new file mode 100644 index 0000000..1668426 --- /dev/null +++ b/examples/example-server.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +import dbus +import dbus.server +import dbus.lowlevel +import dbus.service +import dbus.mainloop.glib +import sys +import gobject + +class ExampleObject (dbus.service.Object): + SUPPORTS_MULTIPLE_CONNECTIONS = True + + @dbus.service.method (dbus_interface="org.freedesktop.ExampleObject", in_signature="s", out_signature="s") + def HelloWorld (self, message): + print message + return "Reply : %s" % message + + @dbus.service.method (dbus_interface="org.freedesktop.ExampleObject", out_signature="s", async_callbacks=("reply_cb", "error_cb")) + def Exit (self, reply_cb, error_cb): + reply_cb ("Exit") + sys.exit () + + +class ExampleServer (dbus.server.Server): + def __init__ (self): + dbus.server.Server.__init__ (self, "unix:abstract=/tmp/example-server") + self.register_object (ExampleObject (), "/org/freedesktop/ExampleObject") + + def new_connection (self, server, connection): + connection.add_message_filter (self.message_filter_cb) + print "New connection" + + def remove_connection (self, connection): + print "Remove connection" + + def message_filter_cb (self, connection, message): + if message.is_signal (dbus.LOCAL_IFACE, "Disconnected"): + print "A connection was closed" + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + + print "Got a Message (%s) : " % message.__class__.__name__ + print "\t From: %s" % message.get_sender () + print "\t To: %s" % message.get_destination () + print "\t Interface: %s" % message.get_interface () + print "\t Path: %s" % message.get_path () + print "\t Member: %s" % message.get_member () + print "\t Arguments:" + i = 0 + for arg in message.get_args_list(): + print "\t\t Arg[%d] : %s" % (i, arg) + i = i + 1 + + return dbus.lowlevel.HANDLER_RESULT_NOT_YET_HANDLED + +if __name__ == "__main__": + dbus.mainloop.glib.DBusGMainLoop (set_as_default = True) + loop = gobject.MainLoop () + bus = ExampleServer () + print "ADDRESS=\"%s\"" % bus.get_address () + loop.run () + diff --git a/include/dbus-python.h b/include/dbus-python.h index ff83e71..b1dd7ff 100644 --- a/include/dbus-python.h +++ b/include/dbus-python.h @@ -46,6 +46,7 @@ typedef void (*_dbus_py_free_func)(void *); #ifdef INSIDE_DBUS_PYTHON_BINDINGS extern DBusConnection *DBusPyConnection_BorrowDBusConnection(PyObject *); +extern DBusServer *DBusPyServer_BorrowDBusServer(PyObject *); extern PyObject *DBusPyNativeMainLoop_New4(_dbus_py_conn_setup_func, _dbus_py_srv_setup_func, _dbus_py_free_func, -- 2.11.4.GIT