e83c6ad4ff48c88c80392e2750b353466317bf48
[framework/uifw/edbus.git] / src / lib / dbus / e_dbus.c
1 #include "E_DBus.h"
2 #include "e_dbus_private.h"
3
4 #include <stdbool.h>
5 #include <unistd.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include <Ecore.h>
11 #include <Ecore_Data.h>
12
13 #define NUM_BUS_TYPES 3
14
15 /*
16  * TODO: 
17  *  listen for disconnected signal and clean up?
18  *  listen for NameOwnerChanged signals for names we have SignalHandler's for
19  *    remap SH to listen for signals from new owner
20  */
21
22 static int connection_slot = -1;
23
24 static int init = 0;
25 EAPI int E_DBUS_EVENT_SIGNAL = 0;
26
27 static E_DBus_Connection *shared_connections[2] = {NULL, NULL};
28
29 typedef struct E_DBus_Handler_Data E_DBus_Handler_Data;
30 typedef struct E_DBus_Timeout_Data E_DBus_Timeout_Data;
31
32
33 struct E_DBus_Handler_Data
34 {
35   int fd;
36   Ecore_Fd_Handler *fd_handler;
37   E_DBus_Connection *cd;
38   DBusWatch *watch;
39   int enabled;
40 };
41
42 struct E_DBus_Timeout_Data
43 {
44   Ecore_Timer *handler;
45   DBusTimeout *timeout;
46   E_DBus_Connection *cd;
47   int interval;
48 };
49
50 static int e_dbus_idler(void *data);
51
52 static int
53 e_dbus_fd_handler(void *data, Ecore_Fd_Handler *fd_handler)
54 {
55   E_DBus_Handler_Data *hd;
56   DBusConnection *conn;
57   unsigned int condition = 0;
58
59   DEBUG(5, "fd handler (%ld)!\n", (long int)fd_handler);
60
61   hd = data;
62
63   if (!hd->enabled) {
64     DEBUG(5, "handler disabled\n");
65     if (hd->fd_handler) ecore_main_fd_handler_del(hd->fd_handler);
66     hd->fd_handler = NULL;
67     return 0;
68   }
69
70   conn = hd->cd->conn;
71
72   if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) condition |= DBUS_WATCH_READABLE;
73   if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE)) condition |= DBUS_WATCH_WRITABLE;
74   if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) condition |= DBUS_WATCH_ERROR;
75
76   if (condition & DBUS_WATCH_ERROR) DEBUG(5, "DBUS watch error\n");
77   dbus_watch_handle(hd->watch, condition);
78   hd = NULL;
79
80   return 1;
81 }
82
83
84 static void
85 e_dbus_fd_handler_add(E_DBus_Handler_Data *hd)
86 {
87   unsigned int dflags;
88   Ecore_Fd_Handler_Flags eflags;
89
90   if (hd->fd_handler) return;
91   DEBUG(5, "fd handler add (%d)\n", hd->fd);
92
93   dflags = dbus_watch_get_flags(hd->watch);
94   eflags = ECORE_FD_ERROR;
95   if (dflags & DBUS_WATCH_READABLE) eflags |= ECORE_FD_READ;
96   if (dflags & DBUS_WATCH_WRITABLE) eflags |= ECORE_FD_WRITE;
97
98
99   hd->fd_handler = ecore_main_fd_handler_add(hd->fd,
100                                              eflags,
101                                              e_dbus_fd_handler,
102                                              hd,
103                                              NULL,
104                                              NULL);
105
106   ecore_list_append(hd->cd->fd_handlers, hd->fd_handler);
107 }
108
109
110 static void
111 e_dbus_handler_data_free(void *data)
112 {
113   E_DBus_Handler_Data *hd = data;
114   
115   DEBUG(5, "e_dbus_handler_data_free\n");
116   if (hd->fd_handler)
117   {
118     if (ecore_list_goto(hd->cd->fd_handlers, hd->fd_handler))
119       ecore_list_remove(hd->cd->fd_handlers);
120     ecore_main_fd_handler_del(hd->fd_handler);
121   }
122   free(hd);
123 }
124
125 static void
126 e_dbus_connection_data_watch_add(E_DBus_Connection *cd, DBusWatch *watch)
127 {
128   E_DBus_Handler_Data *hd;
129
130   hd = calloc(1, sizeof(E_DBus_Handler_Data));
131   dbus_watch_set_data(watch, hd, e_dbus_handler_data_free);
132   hd->cd = cd;
133   hd->watch = watch;
134
135   hd->enabled = dbus_watch_get_enabled(watch);
136   hd->fd = dbus_watch_get_unix_fd(hd->watch);
137   DEBUG(5, "watch add (enabled: %d)\n", hd->enabled);
138   if (hd->enabled) e_dbus_fd_handler_add(hd);
139 }
140
141 static E_DBus_Connection *
142 e_dbus_connection_new(DBusConnection *conn)
143 {
144   E_DBus_Connection *cd;
145   const char *conn_name;
146
147   cd = calloc(1, sizeof(E_DBus_Connection));
148   if (!cd) return NULL;
149
150   cd->conn = conn;
151   conn_name = dbus_bus_get_unique_name(conn);
152   if (conn_name)
153   {
154     DEBUG(1, "Connected! Name: %s\n", conn_name);
155     cd->conn_name = strdup(conn_name);
156   }
157   else
158     DEBUG(1, "Not connected\n");
159
160   cd->shared_type = -1;
161   cd->fd_handlers = ecore_list_new();
162   cd->timeouts = ecore_list_new();
163
164   return cd;
165 }
166
167 static void
168 e_dbus_connection_free(void *data)
169 {
170   E_DBus_Connection *cd = data;
171   Ecore_Fd_Handler *fd_handler;
172   Ecore_Timer *timer;
173   DEBUG(5, "e_dbus_connection free!\n");
174
175   ecore_list_first_goto(cd->fd_handlers);
176   while ((fd_handler = ecore_list_next(cd->fd_handlers)))
177     ecore_main_fd_handler_del(fd_handler);
178   ecore_list_destroy(cd->fd_handlers);
179
180   ecore_list_first_goto(cd->timeouts);
181   while ((timer = ecore_list_next(cd->timeouts)))
182     ecore_timer_del(timer);
183   ecore_list_destroy(cd->timeouts);
184
185   if (cd->shared_type != -1)
186     shared_connections[cd->shared_type] = NULL;
187
188   if (cd->signal_handlers)
189     ecore_list_destroy(cd->signal_handlers);
190
191   if (cd->conn_name) free(cd->conn_name);
192
193   if (cd->idler) ecore_idler_del(cd->idler);
194
195   free(cd);
196 }
197
198
199 static void
200 cb_main_wakeup(void *data)
201 {
202   E_DBus_Connection *cd;
203   DEBUG(5, "wakeup main!\n");
204
205   cd = data;
206
207   if (!cd->idler) cd->idler = ecore_idler_add(e_dbus_idler, cd);
208   else DEBUG(1, "already idling\n");
209 }
210
211 static void
212 cb_dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status, void *data)
213 {
214   E_DBus_Connection *cd;
215
216   DEBUG(5, "dispatch status: %d!\n", new_status);
217   cd = data;
218
219   if (new_status == DBUS_DISPATCH_DATA_REMAINS && !cd->idler) cd->idler = ecore_idler_add(e_dbus_idler, cd);
220
221   else if (new_status != DBUS_DISPATCH_DATA_REMAINS && cd->idler) 
222   {
223     static int dummy_event = 0;
224
225     ecore_idler_del(cd->idler);
226     cd->idler = NULL;
227     /* post a dummy event to get the mainloop back to normal - this is
228      * needed because idlers are very special things that won't re-evaluate
229      * timers and other stuff while idelrs run - idle_exiters and enterers
230      * can do this safely, but not idlers. idelrs were meant to be used
231      * very sparingly for very special cases */
232     if (dummy_event == 0) dummy_event = ecore_event_type_new();
233     ecore_event_add(dummy_event, NULL, NULL, NULL);
234   }
235 }
236
237 static int
238 e_dbus_timeout_handler(void *data)
239 {
240   E_DBus_Timeout_Data *td;
241
242   td = data;
243
244   if (dbus_timeout_get_enabled(td->timeout)) 
245   {
246     DEBUG(5, "timeout_handler (not enabled, ending)\n");
247     td->handler = NULL;
248     return 0;
249   }
250
251   DEBUG(5, "timeout handler!\n");
252   dbus_timeout_handle(td->timeout);
253   return 1;
254 }
255
256 static void
257 e_dbus_timeout_data_free(void *timeout_data)
258 {
259   E_DBus_Timeout_Data *td = timeout_data;
260   DEBUG(5, "e_dbus_timeout_data_free\n");
261   if (td->handler) ecore_timer_del(td->handler);
262   free(td);
263 }
264
265 static dbus_bool_t 
266 cb_timeout_add(DBusTimeout *timeout, void *data)
267 {
268   E_DBus_Connection *cd;
269   E_DBus_Timeout_Data *td;
270   
271   cd = data;
272   DEBUG(5, "timeout add!\n");
273   td = calloc(1, sizeof(E_DBus_Timeout_Data));
274   td->cd = cd;
275   dbus_timeout_set_data(timeout, (void *)td, e_dbus_timeout_data_free);
276
277   td->interval = dbus_timeout_get_interval(timeout);
278   td->timeout = timeout;
279
280   if (dbus_timeout_get_enabled(timeout)) td->handler = ecore_timer_add(td->interval, e_dbus_timeout_handler, td);
281   ecore_list_append(td->cd->timeouts, td->handler);
282
283   return true;
284 }
285
286 static void
287 cb_timeout_del(DBusTimeout *timeout, void *data)
288 {
289   E_DBus_Timeout_Data *td;
290   DEBUG(5, "timeout del!\n");
291
292   td = (E_DBus_Timeout_Data *)dbus_timeout_get_data(timeout);
293
294   if (td->handler) 
295   {
296     if (ecore_list_goto(td->cd->timeouts, td->handler))
297       ecore_list_remove(td->cd->timeouts);
298     ecore_timer_del(td->handler);
299     td->handler = NULL;
300   }
301
302   /* Note: timeout data gets freed when the timeout itself is freed by dbus */
303 }
304
305 static void
306 cb_timeout_toggle(DBusTimeout *timeout, void *data)
307 {
308   E_DBus_Timeout_Data *td;
309   DEBUG(5, "timeout toggle!\n");
310
311   td = (E_DBus_Timeout_Data *)dbus_timeout_get_data(timeout);
312
313   if (dbus_timeout_get_enabled(td->timeout))
314   {
315     td->interval = dbus_timeout_get_interval(timeout);
316     td->handler = ecore_timer_add(td->interval, e_dbus_timeout_handler, td);
317   }
318   else
319   {
320     ecore_timer_del(td->handler);
321     td->handler = NULL;
322   }
323
324
325 }
326
327 static dbus_bool_t 
328 cb_watch_add(DBusWatch *watch, void *data)
329 {
330   E_DBus_Connection *cd;
331   cd = data;
332
333   DEBUG(5, "cb_watch_add\n");
334   e_dbus_connection_data_watch_add(cd, watch);
335
336   return true;
337 }
338
339 static void
340 cb_watch_del(DBusWatch *watch, void *data)
341 {
342   E_DBus_Connection *cd;
343   E_DBus_Handler_Data *hd;
344   cd = data;
345
346   DEBUG(5, "cb_watch_del\n");
347   hd = (E_DBus_Handler_Data *)dbus_watch_get_data(watch);
348
349   if (hd->fd_handler) 
350   {
351     if (ecore_list_goto(hd->cd->fd_handlers, hd->fd_handler))
352       ecore_list_remove(hd->cd->fd_handlers);
353     ecore_main_fd_handler_del(hd->fd_handler);
354     hd->fd_handler = NULL;
355   }
356 }
357
358 static void
359 cb_watch_toggle(DBusWatch *watch, void *data)
360 {
361   E_DBus_Handler_Data *hd;
362
363   DEBUG(5, "cb_watch_toggle\n");
364   hd = dbus_watch_get_data(watch);
365
366   if (!hd) return;
367
368   hd->enabled = dbus_watch_get_enabled(watch);
369
370   if (hd->enabled) e_dbus_fd_handler_add(hd);
371 }
372
373 static void
374 e_dbus_message_free(void *data, void *message)
375 {
376   dbus_message_unref(message);
377 }
378
379 static DBusHandlerResult
380 e_dbus_filter(DBusConnection *conn, DBusMessage *message, void *user_data)
381 {
382   E_DBus_Connection *cd = user_data;
383   DEBUG(3, "-----------------\nMessage!\n\n");
384
385   DEBUG(3, "type: %s\n", dbus_message_type_to_string(dbus_message_get_type(message)));
386   DEBUG(3, "path: %s\n", dbus_message_get_path(message));
387   DEBUG(3, "interface: %s\n", dbus_message_get_interface(message));
388   DEBUG(3, "member: %s\n", dbus_message_get_member(message));
389   DEBUG(3, "sender: %s\n", dbus_message_get_sender(message));
390
391   switch (dbus_message_get_type(message))
392   {
393     case DBUS_MESSAGE_TYPE_METHOD_CALL:
394       DEBUG(3, "signature: %s\n", dbus_message_get_signature(message));
395       break;
396     case DBUS_MESSAGE_TYPE_METHOD_RETURN:
397       DEBUG(3, "reply serial %d\n", dbus_message_get_reply_serial(message));
398       break;
399     case DBUS_MESSAGE_TYPE_ERROR:
400       DEBUG(3, "error: %s\n", dbus_message_get_error_name(message));
401       break;
402     case DBUS_MESSAGE_TYPE_SIGNAL:
403       dbus_message_ref(message);
404
405       if (cd->signal_dispatcher)
406         cd->signal_dispatcher(cd, message);
407
408       ecore_event_add(E_DBUS_EVENT_SIGNAL, message, e_dbus_message_free, NULL);
409       /* don't need to handle signals, they're for everyone who wants them */
410       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
411       break;
412     default:
413       break;
414   }
415   DEBUG(3, "-----------------\n\n");
416
417   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
418 }
419
420 int e_dbus_idler_active = 0;
421
422 static int
423 e_dbus_idler(void *data)
424 {
425   E_DBus_Connection *cd;
426   cd = data;
427
428   if (DBUS_DISPATCH_COMPLETE == dbus_connection_get_dispatch_status(cd->conn))
429   {
430     DEBUG(5, "done dispatching!\n");
431     cd->idler = NULL;
432     return 0;
433   }
434   e_dbus_idler_active++;
435   dbus_connection_ref(cd->conn);
436   DEBUG(5, "dispatch!\n");
437   dbus_connection_dispatch(cd->conn);
438   dbus_connection_unref(cd->conn);
439   e_dbus_signal_handlers_clean(cd);
440   e_dbus_idler_active--;
441   return 1;
442 }
443
444 /**
445  * Retrieve a connection to the bus and integrate it with the ecore main loop.
446  * @param type the type of bus to connect to, e.g. DBUS_BUS_SYSTEM or DBUS_BUS_SESSION
447  */
448 EAPI E_DBus_Connection *
449 e_dbus_bus_get(DBusBusType type)
450 {
451   DBusError err;
452   E_DBus_Connection *econn;
453   DBusConnection *conn;
454
455   /* each app only needs a single connection to either bus */
456   if (type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION)
457   {
458     if (shared_connections[type]) 
459     {
460       e_dbus_connection_ref(shared_connections[type]);
461       return shared_connections[type];
462     }
463   }
464
465   dbus_error_init(&err);
466
467   conn = dbus_bus_get_private(type, &err);
468   if (dbus_error_is_set(&err))
469   {
470     fprintf(stderr, "Error connecting to bus: %s\n", err.message);
471     dbus_error_free(&err);
472     return NULL;
473   }
474
475   econn = e_dbus_connection_setup(conn);
476   if (!econn)
477   {
478     fprintf(stderr, "Error setting up dbus connection.\n");
479     dbus_connection_close(conn);
480     dbus_connection_unref(conn);
481     return NULL;
482   }
483
484   if (type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION)
485   {
486     econn->shared_type = type;
487     shared_connections[type] = econn;
488   }
489   dbus_error_free(&err);
490   e_dbus_connection_ref(econn);
491   return econn;
492 }
493
494 /**
495  * Integrate a DBus connection with the ecore main loop
496  *
497  * @param conn - a dbus connection
498  */
499 EAPI E_DBus_Connection *
500 e_dbus_connection_setup(DBusConnection *conn)
501 {
502   E_DBus_Connection *cd;
503
504   cd = e_dbus_connection_new(conn);
505   if (!cd) return NULL;
506
507   /* connection_setup */
508   dbus_connection_set_exit_on_disconnect(cd->conn, FALSE);
509   dbus_connection_allocate_data_slot(&connection_slot);
510
511   dbus_connection_set_data(cd->conn, connection_slot, (void *)cd, e_dbus_connection_free);
512   dbus_connection_set_watch_functions(cd->conn,
513                                       cb_watch_add,
514                                       cb_watch_del,
515                                       cb_watch_toggle,
516                                       cd,
517                                       NULL);
518
519   dbus_connection_set_timeout_functions(cd->conn,
520                                       cb_timeout_add,
521                                       cb_timeout_del,
522                                       cb_timeout_toggle,
523                                       cd,
524                                       NULL);
525
526   dbus_connection_set_wakeup_main_function(cd->conn, cb_main_wakeup, cd, NULL);
527   dbus_connection_set_dispatch_status_function(cd->conn, cb_dispatch_status, cd, NULL);
528   dbus_connection_add_filter(cd->conn, e_dbus_filter, cd, NULL);
529
530   cb_dispatch_status(cd->conn, dbus_connection_get_dispatch_status(cd->conn), cd);
531
532   return cd;
533 }
534
535
536 /**
537  * Close out a connection retrieved with e_dbus_bus_get()
538  * @param conn the connection to close
539  */
540 EAPI void
541 e_dbus_connection_close(E_DBus_Connection *conn)
542 {
543   DEBUG(5, "e_dbus_connection_close\n");
544
545   if (--(conn->refcount) != 0) return;
546
547   dbus_connection_free_data_slot(&connection_slot);
548   dbus_connection_remove_filter(conn->conn, e_dbus_filter, conn);
549   dbus_connection_set_watch_functions (conn->conn,
550                                        NULL,
551                                        NULL,
552                                        NULL,
553                                        NULL, NULL);
554
555   dbus_connection_set_timeout_functions (conn->conn,
556                                          NULL,
557                                          NULL,
558                                          NULL,
559                                          NULL, NULL);
560
561   dbus_connection_set_dispatch_status_function (conn->conn, NULL, NULL, NULL);
562
563   dbus_connection_close(conn->conn);
564   dbus_connection_unref(conn->conn);
565
566   // Note: the E_DBus_Connection gets freed when the dbus_connection is cleaned up by the previous unref
567 }
568
569 EAPI void
570 e_dbus_connection_ref(E_DBus_Connection *conn)
571 {
572   conn->refcount++;
573 }
574
575 DBusConnection *
576 e_dbus_connection_dbus_connection_get(E_DBus_Connection *conn)
577 {
578   return conn->conn;
579 }
580
581 /**
582  * @brief Initialize e_dbus
583  */
584 EAPI int
585 e_dbus_init(void)
586 {
587   if (++init != 1) return init;
588
589   E_DBUS_EVENT_SIGNAL = ecore_event_type_new();
590   ecore_string_init();
591   e_dbus_object_init();
592   return init;
593 }
594
595 /**
596  * Shutdown e_dbus.
597  */
598 EAPI int
599 e_dbus_shutdown(void)
600 {
601   if (--init) return init;
602   e_dbus_object_shutdown();
603   ecore_string_shutdown();
604   return init;
605 }