1 /* ev-daemon.c
2 * this file is part of atril, a mate document viewer
3 *
4 * Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
5 * Copyright © 2010, 2012 Christian Persch
6 *
7 * Atril is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * Atril is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include "config.h"
23
24 #define G_LOG_DOMAIN "AtrilDaemon"
25 #include <glib.h>
26 #include <glib/gstdio.h>
27 #include <gio/gio.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34
35 #include "ev-daemon-gdbus-generated.h"
36
37 #define EV_DBUS_DAEMON_NAME "org.mate.atril.Daemon"
38 #define EV_DBUS_DAEMON_OBJECT_PATH "/org/mate/atril/Daemon"
39
40 #define EV_DBUS_WINDOW_INTERFACE_NAME "org.mate.atril.Window"
41
42 #define DAEMON_TIMEOUT (30) /* seconds */
43
44 #define LOG g_debug
45
46
47 #define EV_TYPE_DAEMON_APPLICATION (ev_daemon_application_get_type ())
48 #define EV_DAEMON_APPLICATION(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), EV_TYPE_DAEMON_APPLICATION, EvDaemonApplication))
49
50 typedef struct _EvDaemonApplication EvDaemonApplication;
51 typedef struct _EvDaemonApplicationClass EvDaemonApplicationClass;
52
53 struct _EvDaemonApplicationClass {
54 GApplicationClass parent_class;
55 };
56
57 struct _EvDaemonApplication
58 {
59 GApplication parent_instance;
60
61 EvDaemon *daemon;
62 GHashTable *pending_invocations;
63 GList *docs;
64 };
65
66 static GType ev_daemon_application_get_type (void);
67 G_DEFINE_TYPE (EvDaemonApplication, ev_daemon_application, G_TYPE_APPLICATION)
68
69 typedef struct {
70 gchar *dbus_name;
71 gchar *uri;
72 guint watch_id;
73 guint loaded_id;
74 } EvDoc;
75
76 static void
ev_doc_free(EvDoc * doc)77 ev_doc_free (EvDoc *doc)
78 {
79 if (!doc)
80 return;
81
82 g_free (doc->dbus_name);
83 g_free (doc->uri);
84
85 g_bus_unwatch_name (doc->watch_id);
86
87 g_free (doc);
88 }
89
90 static EvDoc *
ev_daemon_application_find_doc(EvDaemonApplication * application,const gchar * uri)91 ev_daemon_application_find_doc (EvDaemonApplication *application,
92 const gchar *uri)
93 {
94 GList *l;
95
96 for (l = application->docs; l != NULL; l = l->next) {
97 EvDoc *doc = (EvDoc *)l->data;
98
99 if (strcmp (doc->uri, uri) == 0)
100 return doc;
101 }
102
103 return NULL;
104 }
105
106 static gboolean
spawn_atril(const gchar * uri)107 spawn_atril (const gchar *uri)
108 {
109 gchar *argv[3];
110 gboolean retval;
111 GError *error = NULL;
112
113 /* TODO Check that the uri exists */
114 argv[0] = g_build_filename (BINDIR, "atril", NULL);
115 argv[1] = (gchar *) uri;
116 argv[2] = NULL;
117
118 retval = g_spawn_async (NULL /* wd */, argv, NULL /* env */,
119 0, NULL, NULL, NULL, &error);
120 if (!retval) {
121 g_printerr ("Error spawning atril for uri %s: %s\n", uri, error->message);
122 g_error_free (error);
123 }
124 g_free (argv[0]);
125
126 return retval;
127 }
128
129 static void
name_appeared_cb(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)130 name_appeared_cb (GDBusConnection *connection,
131 const gchar *name,
132 const gchar *name_owner,
133 gpointer user_data)
134 {
135 LOG ("Watch name'%s' appeared with owner '%s'", name, name_owner);
136 }
137
138 static void
name_vanished_cb(GDBusConnection * connection,const gchar * name,gpointer user_data)139 name_vanished_cb (GDBusConnection *connection,
140 const gchar *name,
141 gpointer user_data)
142 {
143 EvDaemonApplication *application = EV_DAEMON_APPLICATION (user_data);
144 GList *l;
145
146 LOG ("Watch name'%s' disappeared", name);
147
148 for (l = application->docs; l != NULL; l = l->next) {
149 EvDoc *doc = (EvDoc *) l->data;
150
151 if (strcmp (doc->dbus_name, name) != 0)
152 continue;
153
154 LOG ("Watch found URI '%s' for name; removing", doc->uri);
155
156 application->docs = g_list_delete_link (application->docs, l);
157 ev_doc_free (doc);
158
159 g_application_release (G_APPLICATION (application));
160
161 return;
162 }
163 }
164
165 static void
process_pending_invocations(EvDaemonApplication * application,const gchar * uri,const gchar * dbus_name)166 process_pending_invocations (EvDaemonApplication *application,
167 const gchar *uri,
168 const gchar *dbus_name)
169 {
170 GList *l;
171 GList *uri_invocations;
172
173 LOG ("RegisterDocument process pending invocations for URI %s", uri);
174 uri_invocations = g_hash_table_lookup (application->pending_invocations, uri);
175
176 for (l = uri_invocations; l != NULL; l = l->next) {
177 GDBusMethodInvocation *invocation;
178
179 invocation = (GDBusMethodInvocation *)l->data;
180 g_dbus_method_invocation_return_value (invocation,
181 g_variant_new ("(s)", dbus_name));
182 }
183
184 g_list_free (uri_invocations);
185 g_hash_table_remove (application->pending_invocations, uri);
186 }
187
188 static void
document_loaded_cb(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer user_data)189 document_loaded_cb (GDBusConnection *connection,
190 const gchar *sender_name,
191 const gchar *object_path,
192 const gchar *interface_name,
193 const gchar *signal_name,
194 GVariant *parameters,
195 gpointer user_data)
196 {
197 EvDaemonApplication *application = EV_DAEMON_APPLICATION (user_data);
198 const gchar *uri;
199 EvDoc *doc;
200
201 g_variant_get (parameters, "(&s)", &uri);
202 doc = ev_daemon_application_find_doc (application, uri);
203 if (doc != NULL && strcmp (uri, doc->uri) == 0) {
204 process_pending_invocations (application, uri, sender_name);
205 }
206
207 g_dbus_connection_signal_unsubscribe (connection, doc->loaded_id);
208 doc->loaded_id = 0;
209 }
210
211 static gboolean
handle_register_document_cb(EvDaemon * object,GDBusMethodInvocation * invocation,const gchar * uri,EvDaemonApplication * application)212 handle_register_document_cb (EvDaemon *object,
213 GDBusMethodInvocation *invocation,
214 const gchar *uri,
215 EvDaemonApplication *application)
216 {
217 GDBusConnection *connection;
218 const char *sender;
219 EvDoc *doc;
220
221 doc = ev_daemon_application_find_doc (application, uri);
222 if (doc != NULL) {
223 LOG ("RegisterDocument found owner '%s' for URI '%s'", doc->dbus_name, uri);
224 ev_daemon_complete_register_document (object, invocation, doc->dbus_name);
225
226 return TRUE;
227 }
228
229 sender = g_dbus_method_invocation_get_sender (invocation);
230 connection = g_dbus_method_invocation_get_connection (invocation);
231
232 LOG ("RegisterDocument registered owner '%s' for URI '%s'", sender, uri);
233
234 doc = g_new (EvDoc, 1);
235 doc->dbus_name = g_strdup (sender);
236 doc->uri = g_strdup (uri);
237
238 application->docs = g_list_prepend (application->docs, doc);
239
240 doc->loaded_id = g_dbus_connection_signal_subscribe (connection,
241 doc->dbus_name,
242 EV_DBUS_WINDOW_INTERFACE_NAME,
243 "DocumentLoaded",
244 NULL,
245 NULL,
246 0,
247 document_loaded_cb,
248 application, NULL);
249 doc->watch_id = g_bus_watch_name_on_connection (connection,
250 sender,
251 G_BUS_NAME_WATCHER_FLAGS_NONE,
252 name_appeared_cb,
253 name_vanished_cb,
254 application, NULL);
255
256 ev_daemon_complete_register_document (object, invocation, "");
257
258 g_application_hold (G_APPLICATION (application));
259
260 return TRUE;
261 }
262
263 static gboolean
handle_unregister_document_cb(EvDaemon * object,GDBusMethodInvocation * invocation,const gchar * uri,EvDaemonApplication * application)264 handle_unregister_document_cb (EvDaemon *object,
265 GDBusMethodInvocation *invocation,
266 const gchar *uri,
267 EvDaemonApplication *application)
268 {
269 EvDoc *doc;
270 const char *sender;
271
272 LOG ("UnregisterDocument URI '%s'", uri);
273
274 doc = ev_daemon_application_find_doc (application, uri);
275 if (doc == NULL) {
276 LOG ("UnregisterDocument URI was not registered!");
277 g_dbus_method_invocation_return_error_literal (invocation,
278 G_DBUS_ERROR,
279 G_DBUS_ERROR_INVALID_ARGS,
280 "URI not registered");
281 return TRUE;
282 }
283
284 sender = g_dbus_method_invocation_get_sender (invocation);
285 if (strcmp (doc->dbus_name, sender) != 0) {
286 LOG ("UnregisterDocument called by non-owner (owner '%s' sender '%s')",
287 doc->dbus_name, sender);
288
289 g_dbus_method_invocation_return_error_literal (invocation,
290 G_DBUS_ERROR,
291 G_DBUS_ERROR_BAD_ADDRESS,
292 "Only owner can call this method");
293 return TRUE;
294 }
295
296 application->docs = g_list_remove (application->docs, doc);
297
298 if (doc->loaded_id != 0) {
299 g_dbus_connection_signal_unsubscribe (g_dbus_method_invocation_get_connection (invocation),
300 doc->loaded_id);
301 doc->loaded_id = 0;
302 }
303
304 ev_doc_free (doc);
305
306 ev_daemon_complete_unregister_document (object, invocation);
307
308 g_application_release (G_APPLICATION (application));
309
310 return TRUE;
311 }
312
313 static gboolean
handle_find_document_cb(EvDaemon * object,GDBusMethodInvocation * invocation,const gchar * uri,gboolean spawn,EvDaemonApplication * application)314 handle_find_document_cb (EvDaemon *object,
315 GDBusMethodInvocation *invocation,
316 const gchar *uri,
317 gboolean spawn,
318 EvDaemonApplication *application)
319 {
320 EvDoc *doc;
321
322 LOG ("FindDocument URI '%s'", uri);
323
324 doc = ev_daemon_application_find_doc (application, uri);
325 if (doc != NULL) {
326 ev_daemon_complete_find_document (object, invocation, doc->dbus_name);
327
328 return TRUE;
329 }
330
331 if (spawn) {
332 GList *uri_invocations;
333 gboolean ret_val = TRUE;
334
335 uri_invocations = g_hash_table_lookup (application->pending_invocations, uri);
336
337 if (uri_invocations == NULL) {
338 /* Only spawn once. */
339 ret_val = spawn_atril (uri);
340 }
341
342 if (ret_val) {
343 /* Only defer DBUS answer if atril was succesfully spawned */
344 uri_invocations = g_list_prepend (uri_invocations, invocation);
345 g_hash_table_insert (application->pending_invocations,
346 g_strdup (uri),
347 uri_invocations);
348 return TRUE;
349 }
350 }
351
352 LOG ("FindDocument URI '%s' was not registered!", uri);
353 // FIXME: shouldn't this return an error then?
354 ev_daemon_complete_find_document (object, invocation, "");
355
356 return TRUE;
357 }
358
359 /* ------------------------------------------------------------------------- */
360
361 static gboolean
ev_daemon_application_dbus_register(GApplication * gapplication,GDBusConnection * connection,const gchar * object_path,GError ** error)362 ev_daemon_application_dbus_register (GApplication *gapplication,
363 GDBusConnection *connection,
364 const gchar *object_path,
365 GError **error)
366 {
367 EvDaemonApplication *application = EV_DAEMON_APPLICATION (gapplication);
368 EvDaemon *skeleton;
369
370 if (!G_APPLICATION_CLASS (ev_daemon_application_parent_class)->dbus_register (gapplication,
371 connection,
372 object_path,
373 error))
374 return FALSE;
375
376 skeleton = ev_daemon_skeleton_new ();
377 if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
378 connection,
379 EV_DBUS_DAEMON_OBJECT_PATH,
380 error)) {
381 g_object_unref (skeleton);
382 return FALSE;
383 }
384
385 application->daemon = skeleton;
386
387 g_signal_connect (skeleton, "handle-register-document",
388 G_CALLBACK (handle_register_document_cb), application);
389 g_signal_connect (skeleton, "handle-unregister-document",
390 G_CALLBACK (handle_unregister_document_cb), application);
391 g_signal_connect (skeleton, "handle-find-document",
392 G_CALLBACK (handle_find_document_cb), application);
393 return TRUE;
394 }
395
396 static void
ev_daemon_application_dbus_unregister(GApplication * gapplication,GDBusConnection * connection,const gchar * object_path)397 ev_daemon_application_dbus_unregister (GApplication *gapplication,
398 GDBusConnection *connection,
399 const gchar *object_path)
400 {
401 EvDaemonApplication *application = EV_DAEMON_APPLICATION (gapplication);
402
403 if (application->daemon) {
404 g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (application->daemon));
405 g_object_unref (application->daemon);
406 application->daemon = NULL;
407 }
408
409 G_APPLICATION_CLASS (ev_daemon_application_parent_class)->dbus_unregister (gapplication,
410 connection,
411 object_path);
412 }
413
414 static void
ev_daemon_application_init(EvDaemonApplication * application)415 ev_daemon_application_init (EvDaemonApplication *application)
416 {
417 application->pending_invocations = g_hash_table_new_full (g_str_hash,
418 g_str_equal,
419 (GDestroyNotify) g_free,
420 NULL);
421 }
422
423 static void
ev_daemon_application_finalize(GObject * object)424 ev_daemon_application_finalize (GObject *object)
425 {
426 EvDaemonApplication *application = EV_DAEMON_APPLICATION (object);
427
428 g_warn_if_fail (g_hash_table_size (application->pending_invocations) == 0);
429 g_hash_table_destroy (application->pending_invocations);
430
431 g_list_free_full (application->docs, (GDestroyNotify) ev_doc_free);
432
433 G_OBJECT_CLASS (ev_daemon_application_parent_class)->finalize (object);
434 }
435
436 static void
ev_daemon_application_class_init(EvDaemonApplicationClass * klass)437 ev_daemon_application_class_init (EvDaemonApplicationClass *klass)
438 {
439 GObjectClass *object_class = G_OBJECT_CLASS (klass);
440 GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
441
442 object_class->finalize = ev_daemon_application_finalize;
443
444 g_application_class->dbus_register = ev_daemon_application_dbus_register;
445 g_application_class->dbus_unregister = ev_daemon_application_dbus_unregister;
446 }
447
448 /* ------------------------------------------------------------------------- */
449
450 gint
main(gint argc,gchar ** argv)451 main (gint argc, gchar **argv)
452 {
453 GApplication *application;
454 const GApplicationFlags flags = G_APPLICATION_IS_SERVICE;
455 GError *error = NULL;
456 int status;
457
458 g_set_prgname ("atril-daemon");
459
460 application = g_object_new (EV_TYPE_DAEMON_APPLICATION,
461 "application-id", EV_DBUS_DAEMON_NAME,
462 "flags", flags,
463 NULL);
464 g_application_set_inactivity_timeout (application, DAEMON_TIMEOUT);
465
466 if (!g_application_register (application, NULL, &error)) {
467 g_printerr ("Failed to register: %s\n", error->message);
468 g_error_free (error);
469 g_object_unref (application);
470
471 return 1;
472 }
473
474 status = g_application_run (application, 0, NULL);
475 g_object_unref (application);
476
477 return status;
478 }
479