Add wrapper for DBusServer.
[dbus-python-phuang.git] / _dbus_bindings / server.c
blob3de7e07f2baf7df35129d6a98fedb91a2223622e
1 /* Implementation of the _dbus_bindings Server type, a Python wrapper
2 * for DBusServer. See also server-methods.c.
4 * Copyright (C) 2008 Huang Peng <phuang@redhat.com> .
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use, copy,
10 * modify, merge, publish, distribute, sublicense, and/or sell copies
11 * of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
27 #include "dbus_bindings-internal.h"
28 #include "server-internal.h"
30 /* Server definition ============================================ */
32 PyDoc_STRVAR(Server_tp_doc,
33 "A D-Bus server.\n"
34 "\n"
35 "::\n"
36 "\n"
37 " Server(address, mainloop=None) -> Server\n"
40 /* D-Bus Server user data slot, containing an owned reference to either
41 * the Server, or a weakref to the Server.
43 static dbus_int32_t _server_python_slot;
45 /* C API for main-loop hooks ======================================== */
47 /* Return a borrowed reference to the DBusServer which underlies this
48 * Server. */
49 DBusServer *
50 DBusPyServer_BorrowDBusServer(PyObject *self)
52 DBusServer *dbs;
54 TRACE(self);
55 if (!DBusPyServer_Check(self)) {
56 PyErr_SetString(PyExc_TypeError, "A dbus.Server is required");
57 return NULL;
59 dbs = ((Server *)self)->server;
60 if (!dbs) {
61 PyErr_SetString(PyExc_RuntimeError, "Server is in an invalid "
62 "state: no DBusServer");
63 return NULL;
65 return dbs;
68 /* Internal C API =================================================== */
70 /* Return a new reference to a Python Server or subclass corresponding
71 * to the DBusServer server. For use in callbacks.
73 * Raises AssertionError if the DBusServer does not have a Server.
75 PyObject *
76 DBusPyServer_ExistingFromDBusServer(DBusServer *server)
78 PyObject *self, *ref;
80 Py_BEGIN_ALLOW_THREADS
81 ref = (PyObject *)dbus_server_get_data(server,
82 _server_python_slot);
83 Py_END_ALLOW_THREADS
84 if (ref) {
85 DBG("(DBusServer *)%p has weak reference at %p", server, ref);
86 self = PyWeakref_GetObject(ref); /* still a borrowed ref */
87 if (self && self != Py_None && DBusPyServer_Check(self)) {
88 DBG("(DBusServer *)%p has weak reference at %p pointing to %p",
89 server, ref, self);
90 TRACE(self);
91 Py_INCREF(self);
92 TRACE(self);
93 return self;
97 PyErr_SetString(PyExc_AssertionError,
98 "D-Bus server does not have a Server "
99 "instance associated with it");
100 return NULL;
103 /* Return a new reference to a Python Server or subclass (given by cls)
104 * corresponding to the DBusServer server, which must have been newly
105 * created. For use by the Server and Bus constructors.
107 * Raises AssertionError if the DBusServer already has a Server.
110 static void
111 Server_new_connection_callback (DBusServer *server, DBusConnection *connection, void * data UNUSED)
113 PyObject *conn = NULL;
114 PyObject *result = NULL;
115 PyObject *args = NULL;
116 Server *self = NULL;
118 self = (Server *)DBusPyServer_ExistingFromDBusServer(server);
119 if (self == NULL)
120 goto error;
122 if (self->new_connection_callback) {
123 conn = DBusPyConnection_NewConsumingDBusConnection(self->new_connection_type, connection, self->mainloop);
124 if (conn == NULL)
125 goto error;
126 dbus_connection_ref (connection);
128 args = Py_BuildValue ("(OO)", self, conn);
129 if (args == NULL)
130 goto error;
131 result = PyObject_Call (self->new_connection_callback, args, NULL);
132 if (result == NULL)
133 goto error;
136 goto out;
137 error:
138 PyErr_Print ();
139 out:
140 Py_XDECREF (conn);
141 Py_XDECREF (result);
142 Py_XDECREF (args);
143 Py_XDECREF ((PyObject *)self);
146 /* Server type-methods ========================================== */
148 /* "Constructor" (the real constructor is Server_NewFromDBusServer,
149 * to which this delegates). */
150 static PyObject *
151 Server_tp_new(PyTypeObject *cls, PyObject *args UNUSED, PyObject *kwargs UNUSED)
153 PyObject *self;
154 self = cls->tp_alloc (cls, 0);
155 return (PyObject *)self;
160 static int
161 Server_tp_init(Server *self, PyObject *args, PyObject *kwargs)
163 PyObject *mainloop = NULL;
164 DBusServer *server = NULL;
165 char *address = NULL;
167 PyObject *ref = NULL;
168 DBusError error;
169 dbus_bool_t ok;
171 static char *argnames[] = {"address", "mainloop", NULL};
173 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", argnames,
174 &address, &mainloop)) {
175 return -1;
178 if (!mainloop || mainloop == Py_None) {
179 mainloop = dbus_py_get_default_main_loop();
180 if (!mainloop) {
181 PyErr_SetString (PyExc_Exception,
182 "Can not get default mainloop.");
183 goto err;
186 else {
187 Py_INCREF (mainloop);
191 dbus_error_init(&error);
193 Py_BEGIN_ALLOW_THREADS
194 server = dbus_server_listen(address, &error);
195 Py_END_ALLOW_THREADS
197 if (!server) {
198 DBusPyException_ConsumeError(&error);
199 goto err;
202 dbus_server_set_new_connection_function (server, Server_new_connection_callback, NULL, NULL);
204 Py_INCREF (mainloop);
205 self->mainloop = mainloop;
206 self->server = server;
207 self->new_connection_callback = NULL;
208 self->new_connection_type = NULL;
210 ref = PyWeakref_NewRef((PyObject *)self, NULL);
211 if (!ref) goto err;
212 DBG("Created weak ref %p to (Server *)%p for (DBusServer *)%p",
213 ref, self, server);
215 Py_BEGIN_ALLOW_THREADS
216 ok = dbus_server_set_data(server, _server_python_slot,
217 (void *)ref,
218 (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
219 Py_END_ALLOW_THREADS
221 if (ok) {
222 DBG("Attached weak ref %p ((Server *)%p) to (DBusServer *)%p",
223 ref, self, server);
224 ref = NULL; /* don't DECREF it - the DBusServer owns it now */
226 else {
227 DBG("Failed to attached weak ref %p ((Server *)%p) to "
228 "(DBusServer *)%p - will dispose of it", ref, self, server);
229 PyErr_NoMemory();
230 goto err;
233 DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(server, err);
234 self->server = server;
235 /* the DBusPyServer will close it now */
236 server = NULL;
238 if (!dbus_py_set_up_server((PyObject *)self, mainloop)) {
239 goto err;
242 Py_DECREF(mainloop);
244 DBG("%s() -> %p", __func__, self);
245 TRACE(self);
246 return 0;
248 err:
249 DBG("Failed to construct Server.");
250 Py_XDECREF(mainloop);
251 Py_XDECREF(ref);
252 if (server) {
253 Py_BEGIN_ALLOW_THREADS
254 dbus_server_unref(server);
255 Py_END_ALLOW_THREADS
257 DBG("%s() fail", __func__);
258 DBG_WHEREAMI;
259 return -1;
263 /* Destructor */
264 static void Server_tp_dealloc(Server *self)
266 DBusServer *server = self->server;
267 PyObject *et, *ev, *etb;
269 /* avoid clobbering any pending exception */
270 PyErr_Fetch(&et, &ev, &etb);
272 if (self->weaklist) {
273 PyObject_ClearWeakRefs((PyObject *)self);
276 TRACE(self);
277 DBG("Deallocating Server at %p (DBusServer at %p)", self, server);
278 DBG_WHEREAMI;
280 DBG("Server at %p: deleting callbacks", self);
282 if (server) {
283 /* Might trigger callbacks if we're unlucky... */
284 DBG("Server at %p has a server, closing it...", self);
285 Py_BEGIN_ALLOW_THREADS
286 Py_END_ALLOW_THREADS
289 /* make sure to do this last to preserve the invariant that
290 * self->server is always non-NULL for any referenced Server
291 * (until the filters and object paths were freed, we might have been
292 * in a reference cycle!)
294 DBG("Server at %p: nulling self->server", self);
295 self->server = server;
297 if (server) {
298 DBG("Server at %p: unreffing server", self);
299 dbus_server_unref(server);
302 Py_XDECREF (self->mainloop);
304 DBG("Server at %p: freeing self", self);
305 PyErr_Restore(et, ev, etb);
306 (self->ob_type->tp_free)((PyObject *)self);
309 /* Server type object =========================================== */
311 PyTypeObject DBusPyServer_Type = {
312 PyObject_HEAD_INIT(NULL)
313 0, /*ob_size*/
314 "_dbus_bindings.Server",/*tp_name*/
315 sizeof(Server), /*tp_basicsize*/
316 0, /*tp_itemsize*/
317 /* methods */
318 (destructor)Server_tp_dealloc,
319 0, /*tp_print*/
320 0, /*tp_getattr*/
321 0, /*tp_setattr*/
322 0, /*tp_compare*/
323 0, /*tp_repr*/
324 0, /*tp_as_number*/
325 0, /*tp_as_sequence*/
326 0, /*tp_as_mapping*/
327 0, /*tp_hash*/
328 0, /*tp_call*/
329 0, /*tp_str*/
330 0, /*tp_getattro*/
331 0, /*tp_setattro*/
332 0, /*tp_as_buffer*/
333 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
334 Server_tp_doc, /*tp_doc*/
335 0, /*tp_traverse*/
336 0, /*tp_clear*/
337 0, /*tp_richcompare*/
338 offsetof(Server, weaklist), /*tp_weaklistoffset*/
339 0, /*tp_iter*/
340 0, /*tp_iternext*/
341 DBusPyServer_tp_methods,/*tp_methods*/
342 0, /*tp_members*/
343 0, /*tp_getset*/
344 0, /*tp_base*/
345 0, /*tp_dict*/
346 0, /*tp_descr_get*/
347 0, /*tp_descr_set*/
348 0, /*tp_dictoffset*/
349 (initproc) Server_tp_init,
350 /*tp_init*/
351 0, /*tp_alloc*/
352 Server_tp_new, /*tp_new*/
353 0, /*tp_free*/
354 0, /*tp_is_gc*/
357 dbus_bool_t
358 dbus_py_init_server_types(void)
360 /* Get a slot to store our weakref on DBus Servers */
361 _server_python_slot = -1;
362 if (!dbus_server_allocate_data_slot(&_server_python_slot))
363 return FALSE;
364 if (PyType_Ready(&DBusPyServer_Type) < 0)
365 return FALSE;
366 return TRUE;
369 dbus_bool_t
370 dbus_py_insert_server_types(PyObject *this_module)
372 if (PyModule_AddObject(this_module, "Server",
373 (PyObject *)&DBusPyServer_Type) < 0) return FALSE;
374 return TRUE;
377 /* vim:set ft=c cino< sw=4 sts=4 et: */