updated changelog
[platform/upstream/evolution-data-server.git] / libebackend / e-dbus-server.c
1 /*
2  * e-dbus-server.c
3  *
4  * This library is free software you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library; if not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17
18 /**
19  * SECTION: e-dbus-server
20  * @include: libebackend/libebackend.h
21  * @short_description: An abstract base class for a D-Bus server
22  **/
23
24 #include "e-dbus-server.h"
25
26 #include <config.h>
27
28 #ifdef G_OS_UNIX
29 #include <glib-unix.h>
30 #endif
31
32 #include <libedataserver/libedataserver.h>
33
34 #include <libebackend/e-module.h>
35 #include <libebackend/e-extensible.h>
36 #include <libebackend/e-backend-enumtypes.h>
37
38 #define E_DBUS_SERVER_GET_PRIVATE(obj) \
39         (G_TYPE_INSTANCE_GET_PRIVATE \
40         ((obj), E_TYPE_DBUS_SERVER, EDBusServerPrivate))
41
42 #define INACTIVITY_TIMEOUT 10  /* seconds */
43
44 struct _EDBusServerPrivate {
45         GMainLoop *main_loop;
46         guint bus_owner_id;
47         guint hang_up_id;
48         guint terminate_id;
49
50         guint inactivity_timeout_id;
51         guint use_count;
52         gboolean wait_for_client;
53         EDBusServerExitCode exit_code;
54 };
55
56 enum {
57         BUS_ACQUIRED,
58         BUS_NAME_ACQUIRED,
59         BUS_NAME_LOST,
60         RUN_SERVER,
61         QUIT_SERVER,
62         LAST_SIGNAL
63 };
64
65 static guint signals[LAST_SIGNAL];
66
67 static GHashTable *directories_loaded;
68 G_LOCK_DEFINE_STATIC (directories_loaded);
69
70 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
71         EDBusServer, e_dbus_server, G_TYPE_OBJECT,
72         G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
73
74 static void
75 dbus_server_bus_acquired_cb (GDBusConnection *connection,
76                              const gchar *bus_name,
77                              EDBusServer *server)
78 {
79         g_signal_emit (server, signals[BUS_ACQUIRED], 0, connection);
80 }
81
82 static void
83 dbus_server_name_acquired_cb (GDBusConnection *connection,
84                               const gchar *bus_name,
85                               EDBusServer *server)
86 {
87         g_signal_emit (server, signals[BUS_NAME_ACQUIRED], 0, connection);
88 }
89
90 static void
91 dbus_server_name_lost_cb (GDBusConnection *connection,
92                           const gchar *bus_name,
93                           EDBusServer *server)
94 {
95         g_signal_emit (server, signals[BUS_NAME_LOST], 0, connection);
96 }
97
98 static gboolean
99 dbus_server_inactivity_timeout_cb (gpointer user_data)
100 {
101         EDBusServer *server = E_DBUS_SERVER (user_data);
102
103         e_dbus_server_quit (server, E_DBUS_SERVER_EXIT_NORMAL);
104
105         return FALSE;
106 }
107
108 #ifdef G_OS_UNIX
109 static gboolean
110 dbus_server_hang_up_cb (gpointer user_data)
111 {
112         EDBusServer *server = E_DBUS_SERVER (user_data);
113
114         g_print ("Received hang up signal.\n");
115         e_dbus_server_quit (server, E_DBUS_SERVER_EXIT_RELOAD);
116
117         return FALSE;
118 }
119
120 static gboolean
121 dbus_server_terminate_cb (gpointer user_data)
122 {
123         EDBusServer *server = E_DBUS_SERVER (user_data);
124
125         g_print ("Received terminate signal.\n");
126         e_dbus_server_quit (server, E_DBUS_SERVER_EXIT_NORMAL);
127
128         return FALSE;
129 }
130 #endif
131
132 static void
133 dbus_server_finalize (GObject *object)
134 {
135         EDBusServerPrivate *priv;
136
137         priv = E_DBUS_SERVER_GET_PRIVATE (object);
138
139         g_main_loop_unref (priv->main_loop);
140
141         if (priv->bus_owner_id > 0)
142                 g_bus_unown_name (priv->bus_owner_id);
143
144         if (priv->hang_up_id > 0)
145                 g_source_remove (priv->hang_up_id);
146
147         if (priv->terminate_id > 0)
148                 g_source_remove (priv->terminate_id);
149
150         if (priv->inactivity_timeout_id > 0)
151                 g_source_remove (priv->inactivity_timeout_id);
152
153         /* Chain up to parent's finalize() method. */
154         G_OBJECT_CLASS (e_dbus_server_parent_class)->finalize (object);
155 }
156
157 static void
158 dbus_server_constructed (GObject *object)
159 {
160         e_dbus_server_load_modules (E_DBUS_SERVER (object));
161
162         e_extensible_load_extensions (E_EXTENSIBLE (object));
163
164         /* Chain up to parent's constructed() method. */
165         G_OBJECT_CLASS (e_dbus_server_parent_class)->constructed (object);
166 }
167
168 static void
169 dbus_server_bus_acquired (EDBusServer *server,
170                           GDBusConnection *connection)
171 {
172         if (server->priv->use_count == 0 && !server->priv->wait_for_client) {
173                 server->priv->inactivity_timeout_id =
174                         e_named_timeout_add_seconds (
175                                 INACTIVITY_TIMEOUT, (GSourceFunc)
176                                 dbus_server_inactivity_timeout_cb,
177                                 server);
178         }
179 }
180
181 static void
182 dbus_server_bus_name_acquired (EDBusServer *server,
183                                GDBusConnection *connection)
184 {
185         EDBusServerClass *class;
186
187         class = E_DBUS_SERVER_GET_CLASS (server);
188         g_return_if_fail (class->bus_name != NULL);
189
190         g_print ("Bus name '%s' acquired.\n", class->bus_name);
191 }
192
193 static void
194 dbus_server_bus_name_lost (EDBusServer *server,
195                            GDBusConnection *connection)
196 {
197         EDBusServerClass *class;
198
199         class = E_DBUS_SERVER_GET_CLASS (server);
200         g_return_if_fail (class->bus_name != NULL);
201
202         g_print ("Bus name '%s' lost.\n", class->bus_name);
203
204         e_dbus_server_quit (server, E_DBUS_SERVER_EXIT_NORMAL);
205 }
206
207 static EDBusServerExitCode
208 dbus_server_run_server (EDBusServer *server)
209 {
210         EDBusServerClass *class;
211
212         /* Try to acquire the well-known bus name. */
213
214         class = E_DBUS_SERVER_GET_CLASS (server);
215         g_return_val_if_fail (
216                 class->bus_name != NULL,
217                 E_DBUS_SERVER_EXIT_NONE);
218
219         server->priv->bus_owner_id = g_bus_own_name (
220                 G_BUS_TYPE_SESSION,
221                 class->bus_name,
222                 G_BUS_NAME_OWNER_FLAGS_REPLACE |
223                 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
224                 (GBusAcquiredCallback) dbus_server_bus_acquired_cb,
225                 (GBusNameAcquiredCallback) dbus_server_name_acquired_cb,
226                 (GBusNameLostCallback) dbus_server_name_lost_cb,
227                 g_object_ref (server),
228                 (GDestroyNotify) g_object_unref);
229
230         g_main_loop_run (server->priv->main_loop);
231
232         return server->priv->exit_code;
233 }
234
235 static void
236 dbus_server_quit_server (EDBusServer *server,
237                          EDBusServerExitCode code)
238 {
239         /* If we're reloading, voluntarily relinquish our bus
240          * name to avoid triggering a "bus-name-lost" signal. */
241         if (code == E_DBUS_SERVER_EXIT_RELOAD) {
242                 g_bus_unown_name (server->priv->bus_owner_id);
243                 server->priv->bus_owner_id = 0;
244         }
245
246         server->priv->exit_code = code;
247         g_main_loop_quit (server->priv->main_loop);
248 }
249
250 static void
251 ignore_log (const gchar *log_domain,
252             GLogLevelFlags log_level,
253             const gchar *message,
254             gpointer user_data)
255 {
256         /* Avoid printing of trivial messages while running test
257          * cases.  Only print warnings, criticals and errors. */
258         if ((log_level & (G_LOG_FLAG_FATAL     |
259                           G_LOG_LEVEL_ERROR    |
260                           G_LOG_LEVEL_CRITICAL |
261                           G_LOG_LEVEL_WARNING)) != 0)
262                 g_log_default_handler (log_domain, log_level, message, user_data);
263 }
264
265 static void
266 e_dbus_server_class_init (EDBusServerClass *class)
267 {
268         GObjectClass *object_class;
269
270         g_type_class_add_private (class, sizeof (EDBusServerPrivate));
271
272         object_class = G_OBJECT_CLASS (class);
273         object_class->finalize = dbus_server_finalize;
274         object_class->constructed = dbus_server_constructed;
275
276         class->bus_acquired = dbus_server_bus_acquired;
277         class->bus_name_acquired = dbus_server_bus_name_acquired;
278         class->bus_name_lost = dbus_server_bus_name_lost;
279         class->run_server = dbus_server_run_server;
280         class->quit_server = dbus_server_quit_server;
281
282         /**
283          * EDBusServer::bus-acquired:
284          * @server: the #EDBusServer which emitted the signal
285          * @connection: the #GDBusConnection to the session bus
286          *
287          * Emitted when @server acquires a connection to the session bus.
288          **/
289         signals[BUS_ACQUIRED] = g_signal_new (
290                 "bus-acquired",
291                 G_OBJECT_CLASS_TYPE (object_class),
292                 G_SIGNAL_RUN_LAST,
293                 G_STRUCT_OFFSET (EDBusServerClass, bus_acquired),
294                 NULL, NULL, NULL,
295                 G_TYPE_NONE, 1,
296                 G_TYPE_DBUS_CONNECTION);
297
298         /**
299          * EDBusServer::bus-name-acquired:
300          * @server: the #EDBusServer which emitted the signal
301          * @connection: the #GDBusConnection to the session bus
302          *
303          * Emitted when @server acquires its well-known session bus name.
304          **/
305         signals[BUS_NAME_ACQUIRED] = g_signal_new (
306                 "bus-name-acquired",
307                 G_OBJECT_CLASS_TYPE (object_class),
308                 G_SIGNAL_RUN_LAST,
309                 G_STRUCT_OFFSET (EDBusServerClass, bus_name_acquired),
310                 NULL, NULL, NULL,
311                 G_TYPE_NONE, 1,
312                 G_TYPE_DBUS_CONNECTION);
313
314         /**
315          * EDBusServer::bus-name-lost:
316          * @server: the #EDBusServer which emitted the signal
317          * @connection: the #GDBusconnection to the session bus,
318          *              or %NULL if the connection has been closed
319          *
320          * Emitted when @server loses its well-known session bus name
321          * or the session bus connection has been closed.
322          **/
323         signals[BUS_NAME_LOST] = g_signal_new (
324                 "bus-name-lost",
325                 G_OBJECT_CLASS_TYPE (object_class),
326                 G_SIGNAL_RUN_LAST,
327                 G_STRUCT_OFFSET (EDBusServerClass, bus_name_lost),
328                 NULL, NULL, NULL,
329                 G_TYPE_NONE, 1,
330                 G_TYPE_DBUS_CONNECTION);
331
332         /**
333          * EDBusServer::run-server:
334          * @server: the #EDBusServer which emitted the signal
335          *
336          * Emitted to request that @server start its main loop and
337          * attempt to acquire its well-known session bus name.
338          *
339          * Returns: an #EDBusServerExitCode
340          **/
341         signals[RUN_SERVER] = g_signal_new (
342                 "run-server",
343                 G_OBJECT_CLASS_TYPE (object_class),
344                 G_SIGNAL_RUN_LAST,
345                 G_STRUCT_OFFSET (EDBusServerClass, run_server),
346                 NULL, NULL, NULL,
347                 E_TYPE_DBUS_SERVER_EXIT_CODE, 0);
348
349         /**
350          * EDBusServer::quit-server:
351          * @server: the #EDBusServer which emitted the signal
352          * @code: an #EDBusServerExitCode
353          *
354          * Emitted to request that @server quit its main loop.
355          **/
356         signals[QUIT_SERVER] = g_signal_new (
357                 "quit-server",
358                 G_OBJECT_CLASS_TYPE (object_class),
359                 G_SIGNAL_RUN_LAST,
360                 G_STRUCT_OFFSET (EDBusServerClass, quit_server),
361                 NULL, NULL, NULL,
362                 G_TYPE_NONE, 1,
363                 E_TYPE_DBUS_SERVER_EXIT_CODE);
364
365         if (g_getenv ("EDS_TESTING") != NULL)
366                 g_log_set_default_handler (ignore_log, NULL);
367 }
368
369 static void
370 e_dbus_server_init (EDBusServer *server)
371 {
372         server->priv = E_DBUS_SERVER_GET_PRIVATE (server);
373         server->priv->main_loop = g_main_loop_new (NULL, FALSE);
374         server->priv->wait_for_client = FALSE;
375
376 #ifdef G_OS_UNIX
377         server->priv->hang_up_id = g_unix_signal_add (
378                 SIGHUP, dbus_server_hang_up_cb, server);
379         server->priv->terminate_id = g_unix_signal_add (
380                 SIGTERM, dbus_server_terminate_cb, server);
381 #endif
382 }
383
384 /**
385  * e_dbus_server_run:
386  * @server: an #EDBusServer
387  * @wait_for_client: continue running until a client connects
388  *
389  * Emits the #EDBusServer::run signal.
390  *
391  * By default the @server will start its main loop and attempt to acquire
392  * its well-known session bus name.  If the @server's main loop is already
393  * running, the function will immediately return #E_DBUS_SERVER_EXIT_NONE.
394  * Otherwise the function blocks until e_dbus_server_quit() is called.
395  *
396  * If @wait_for_client is %TRUE, the @server will continue running until
397  * the first client connection is made instead of quitting on its own if
398  * no client connection is made within the first few seconds.
399  *
400  * Returns: the exit code passed to e_dbus_server_quit()
401  *
402  * Since: 3.4
403  **/
404 EDBusServerExitCode
405 e_dbus_server_run (EDBusServer *server,
406                    gboolean wait_for_client)
407 {
408         EDBusServerExitCode exit_code;
409
410         g_return_val_if_fail (
411                 E_IS_DBUS_SERVER (server),
412                 E_DBUS_SERVER_EXIT_NONE);
413
414         server->priv->wait_for_client = wait_for_client;
415
416         if (g_main_loop_is_running (server->priv->main_loop))
417                 return E_DBUS_SERVER_EXIT_NONE;
418
419         g_signal_emit (server, signals[RUN_SERVER], 0, &exit_code);
420
421         return exit_code;
422 }
423
424 /**
425  * e_dbus_server_quit:
426  * @server: an #EDBusServer
427  * @code: an #EDBusServerExitCode
428  *
429  * Emits the #EDBusServer::quit signal with the given @code.
430  *
431  * By default the @server will quit its main loop and cause
432  * e_dbus_server_run() to return @code.
433  *
434  * Since: 3.4
435  **/
436 void
437 e_dbus_server_quit (EDBusServer *server,
438                     EDBusServerExitCode code)
439 {
440         g_return_if_fail (E_IS_DBUS_SERVER (server));
441
442         g_signal_emit (server, signals[QUIT_SERVER], 0, code);
443 }
444
445 /**
446  * e_dbus_server_hold:
447  * @server: an #EDBusServer
448  *
449  * Increases the use count of @server.
450  *
451  * Use this function to indicate that the server has a reason to continue
452  * to run.  To cancel the hold, call e_dbus_server_release().
453  *
454  * Since: 3.4
455  **/
456 void
457 e_dbus_server_hold (EDBusServer *server)
458 {
459         g_return_if_fail (E_IS_DBUS_SERVER (server));
460
461         if (server->priv->inactivity_timeout_id > 0) {
462                 g_source_remove (server->priv->inactivity_timeout_id);
463                 server->priv->inactivity_timeout_id = 0;
464         }
465
466         server->priv->use_count++;
467 }
468
469 /**
470  * e_dbus_server_release:
471  * @server: an #EDBusServer
472  *
473  * Decreates the use count of @server.
474  *
475  * When the use count reaches zero, the server will stop running.
476  *
477  * Never call this function except to cancel the effect of a previous call
478  * to e_dbus_server_hold().
479  *
480  * Since: 3.4
481  **/
482 void
483 e_dbus_server_release (EDBusServer *server)
484 {
485         g_return_if_fail (E_IS_DBUS_SERVER (server));
486         g_return_if_fail (server->priv->use_count > 0);
487
488         server->priv->use_count--;
489
490         if (server->priv->use_count == 0) {
491                 server->priv->inactivity_timeout_id =
492                         e_named_timeout_add_seconds (
493                                 INACTIVITY_TIMEOUT, (GSourceFunc)
494                                 dbus_server_inactivity_timeout_cb,
495                                 server);
496         }
497 }
498
499 /**
500  * e_dbus_server_load_modules:
501  * @server: an #EDBusServer
502  *
503  * This function should be called once during @server initialization to
504  * load all available library modules to extend the @server's functionality.
505  *
506  * Since: 3.4
507  **/
508 void
509 e_dbus_server_load_modules (EDBusServer *server)
510 {
511         EDBusServerClass *class;
512         gboolean already_loaded;
513         const gchar *directory;
514         GList *list;
515
516         g_return_if_fail (E_IS_DBUS_SERVER (server));
517
518         class = E_DBUS_SERVER_GET_CLASS (server);
519         g_return_if_fail (class->module_directory != NULL);
520
521         /* This ensures a module directory is only loaded once. */
522         G_LOCK (directories_loaded);
523         if (directories_loaded == NULL)
524                 directories_loaded = g_hash_table_new (NULL, NULL);
525         directory = g_intern_string (class->module_directory);
526         already_loaded = g_hash_table_contains (directories_loaded, directory);
527         g_hash_table_add (directories_loaded, (gpointer) directory);
528         G_UNLOCK (directories_loaded);
529
530         if (already_loaded)
531                 return;
532
533         list = e_module_load_all_in_directory (class->module_directory);
534         g_list_free_full (list, (GDestroyNotify) g_type_module_unuse);
535 }