Fix D-Bus timeout handling
[platform/upstream/connman.git] / gdbus / mainloop.c
1 /*
2  *
3  *  D-Bus helper library
4  *
5  *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdint.h>
29
30 #include <glib.h>
31 #include <dbus/dbus.h>
32
33 #ifdef NEED_DBUS_WATCH_GET_UNIX_FD
34 #define dbus_watch_get_unix_fd dbus_watch_get_fd
35 #endif
36
37 #include "gdbus.h"
38
39 #define DISPATCH_TIMEOUT  0
40
41 #define info(fmt...)
42 #define error(fmt...)
43 #define debug(fmt...)
44
45 typedef struct {
46         uint32_t id;
47         DBusTimeout *timeout;
48 } timeout_handler_t;
49
50 struct watch_info {
51         guint watch_id;
52         GIOChannel *io;
53         DBusConnection *conn;
54 };
55
56 struct server_info {
57         guint watch_id;
58         GIOChannel *io;
59         DBusServer *server;
60 };
61
62 struct disconnect_data {
63         GDBusWatchFunction disconnect_cb;
64         void *user_data;
65 };
66
67 static DBusHandlerResult disconnect_filter(DBusConnection *conn,
68                                                 DBusMessage *msg, void *data)
69 {
70         struct disconnect_data *dc_data = data;
71
72         if (dbus_message_is_signal(msg,
73                         DBUS_INTERFACE_LOCAL, "Disconnected") == TRUE) {
74                 error("Got disconnected from the system message bus");
75                 dc_data->disconnect_cb(conn, dc_data->user_data);
76                 dbus_connection_unref(conn);
77         }
78
79         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
80 }
81
82 static gboolean message_dispatch_cb(void *data)
83 {
84         DBusConnection *connection = data;
85
86         dbus_connection_ref(connection);
87
88         /* Dispatch messages */
89         while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
90
91         dbus_connection_unref(connection);
92
93         return FALSE;
94 }
95
96 static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
97 {
98         DBusWatch *watch = data;
99         struct watch_info *info = dbus_watch_get_data(watch);
100         int flags = 0;
101
102         if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
103         if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
104         if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
105         if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
106
107         dbus_watch_handle(watch, flags);
108
109         if (dbus_connection_get_dispatch_status(info->conn) == DBUS_DISPATCH_DATA_REMAINS)
110                 g_timeout_add(DISPATCH_TIMEOUT, message_dispatch_cb, info->conn);
111
112         return TRUE;
113 }
114
115 static dbus_bool_t add_watch(DBusWatch *watch, void *data)
116 {
117         GIOCondition cond = G_IO_HUP | G_IO_ERR;
118         DBusConnection *conn = data;
119         struct watch_info *info;
120         int fd, flags;
121
122         if (!dbus_watch_get_enabled(watch))
123                 return TRUE;
124
125         info = g_new(struct watch_info, 1);
126
127         fd = dbus_watch_get_unix_fd(watch);
128         info->io = g_io_channel_unix_new(fd);
129         info->conn = dbus_connection_ref(conn);
130
131         dbus_watch_set_data(watch, info, NULL);
132
133         flags = dbus_watch_get_flags(watch);
134
135         if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
136         if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
137
138         info->watch_id = g_io_add_watch(info->io, cond, watch_func, watch);
139
140         return TRUE;
141 }
142
143 static void remove_watch(DBusWatch *watch, void *data)
144 {
145         struct watch_info *info = dbus_watch_get_data(watch);
146
147         dbus_watch_set_data(watch, NULL, NULL);
148
149         if (info) {
150                 g_source_remove(info->watch_id);
151                 g_io_channel_unref(info->io);
152                 dbus_connection_unref(info->conn);
153                 g_free(info);
154         }
155 }
156
157 static void watch_toggled(DBusWatch *watch, void *data)
158 {
159         /* Because we just exit on OOM, enable/disable is
160          * no different from add/remove */
161         if (dbus_watch_get_enabled(watch))
162                 add_watch(watch, data);
163         else
164                 remove_watch(watch, data);
165 }
166
167 static gboolean timeout_handler_dispatch(gpointer data)
168 {
169         timeout_handler_t *handler = data;
170
171         /* if not enabled should not be polled by the main loop */
172         if (dbus_timeout_get_enabled(handler->timeout) != TRUE)
173                 return FALSE;
174
175         dbus_timeout_handle(handler->timeout);
176
177         return FALSE;
178 }
179
180 static void timeout_handler_free(void *data)
181 {
182         timeout_handler_t *handler = data;
183         if (!handler)
184                 return;
185
186         if (handler->id > 0) {
187                 g_source_remove(handler->id);
188                 handler->id = 0;
189         }
190
191         g_free(handler);
192 }
193
194 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
195 {
196         timeout_handler_t *handler;
197
198         if (!dbus_timeout_get_enabled(timeout))
199                 return TRUE;
200
201         handler = g_new0(timeout_handler_t, 1);
202
203         handler->timeout = timeout;
204         handler->id = g_timeout_add(dbus_timeout_get_interval(timeout),
205                                         timeout_handler_dispatch, handler);
206
207         dbus_timeout_set_data(timeout, handler, timeout_handler_free);
208
209         return TRUE;
210 }
211
212 static void remove_timeout(DBusTimeout *timeout, void *data)
213 {
214         timeout_handler_t *handler;
215
216         handler = dbus_timeout_get_data(timeout);
217
218         if (!handler)
219                 return;
220
221         if (handler->id > 0) {
222                 g_source_remove(handler->id);
223                 handler->id = 0;
224         }
225 }
226
227 static void timeout_toggled(DBusTimeout *timeout, void *data)
228 {
229         if (dbus_timeout_get_enabled(timeout))
230                 add_timeout(timeout, data);
231         else
232                 remove_timeout(timeout, data);
233 }
234
235 static void dispatch_status_cb(DBusConnection *conn,
236                                 DBusDispatchStatus new_status, void *data)
237 {
238         if (!dbus_connection_get_is_connected(conn))
239                 return;
240
241         if (new_status == DBUS_DISPATCH_DATA_REMAINS)
242                 g_timeout_add(DISPATCH_TIMEOUT, message_dispatch_cb, data);
243 }
244
245 static void setup_dbus_with_main_loop(DBusConnection *conn)
246 {
247         dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
248                                                 watch_toggled, conn, NULL);
249
250         dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
251                                                 timeout_toggled, conn, NULL);
252
253         dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
254                                                                 conn, NULL);
255 }
256
257 DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
258                                                         DBusError *error)
259 {
260         DBusConnection *conn;
261
262         conn = dbus_bus_get(type, error);
263
264         if (error != NULL) {
265                 if (dbus_error_is_set(error) == TRUE)
266                         return NULL;
267         }
268
269         if (conn == NULL)
270                 return NULL;
271
272         if (name != NULL) {
273                 if (dbus_bus_request_name(conn, name,
274                                 DBUS_NAME_FLAG_DO_NOT_QUEUE, error) !=
275                                 DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ) {
276                         dbus_connection_unref(conn);
277                         return NULL;
278                 }
279
280                 if (error != NULL) {
281                         if (dbus_error_is_set(error) == TRUE) {
282                                 dbus_connection_unref(conn);
283                                 return NULL;
284                         }
285                 }
286         }
287
288         setup_dbus_with_main_loop(conn);
289
290         if (dbus_connection_get_dispatch_status(conn) == DBUS_DISPATCH_DATA_REMAINS)
291                 g_timeout_add(DISPATCH_TIMEOUT, message_dispatch_cb, conn);
292
293         return conn;
294 }
295
296 gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
297                                                         DBusError *error)
298 {
299         return TRUE;
300 }
301
302 gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
303                                 GDBusWatchFunction function,
304                                 void *user_data, DBusFreeFunction destroy)
305 {
306         struct disconnect_data *dc_data;
307
308         dc_data = g_new(struct disconnect_data, 1);
309
310         dc_data->disconnect_cb = function;
311         dc_data->user_data = user_data;
312
313         dbus_connection_set_exit_on_disconnect(connection, FALSE);
314
315         if (dbus_connection_add_filter(connection, disconnect_filter,
316                                                 dc_data, g_free) == FALSE) {
317                 error("Can't add D-Bus disconnect filter");
318                 g_free(dc_data);
319                 return FALSE;
320         }
321
322         return TRUE;
323 }