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