2 * this file is part of evince, a gnome document viewer
4 * Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
5 * Copyright © 2010 Christian Persch
7 * Evince 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.
12 * Evince 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.
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.
25 #include <glib/gstdio.h>
28 #include <sys/types.h>
34 #define EV_DBUS_DAEMON_NAME "org.gnome.evince.Daemon"
35 #define EV_DBUS_DAEMON_INTERFACE_NAME "org.gnome.evince.Daemon"
36 #define EV_DBUS_DAEMON_OBJECT_PATH "/org/gnome/evince/Daemon"
38 #define DAEMON_TIMEOUT (30) /* seconds */
40 #define LOG g_printerr
42 static GList *ev_daemon_docs = NULL;
43 static guint kill_timer_id;
44 static GHashTable *pending_invocations = NULL;
53 ev_doc_free (EvDoc *doc)
58 g_free (doc->dbus_name);
61 g_bus_unwatch_name (doc->watch_id);
67 ev_daemon_find_doc (const gchar *uri)
71 for (l = ev_daemon_docs; l != NULL; l = l->next) {
72 EvDoc *doc = (EvDoc *)l->data;
74 if (strcmp (doc->uri, uri) == 0)
82 ev_daemon_stop_killtimer (void)
84 if (kill_timer_id != 0)
85 g_source_remove (kill_timer_id);
90 ev_daemon_shutdown (gpointer user_data)
92 GMainLoop *loop = (GMainLoop *) user_data;
94 LOG ("Timeout; exiting daemon.\n");
96 if (g_main_loop_is_running (loop))
97 g_main_loop_quit (loop);
103 ev_daemon_maybe_start_killtimer (gpointer data)
105 ev_daemon_stop_killtimer ();
106 if (ev_daemon_docs != NULL)
109 kill_timer_id = g_timeout_add_seconds (DAEMON_TIMEOUT,
110 (GSourceFunc) ev_daemon_shutdown,
115 convert_metadata (const gchar *metadata)
120 GFileAttributeInfoList *namespaces;
121 gboolean supported = FALSE;
122 GError *error = NULL;
125 /* If metadata is not supported for a local file
126 * is likely because and old gvfs version is running.
128 file = g_file_new_for_path (metadata);
129 namespaces = g_file_query_writable_namespaces (file, NULL, NULL);
133 for (i = 0; i < namespaces->n_infos; i++) {
134 if (strcmp (namespaces->infos[i].name, "metadata") == 0) {
139 g_file_attribute_info_list_unref (namespaces);
142 g_warning ("GVFS metadata not supported. "
143 "Evince will run without metadata support.\n");
144 g_object_unref (file);
147 g_object_unref (file);
149 argv[0] = g_build_filename (LIBEXECDIR, "evince-convert-metadata", NULL);
150 argv[1] = (char *) metadata;
153 retval = g_spawn_sync (NULL /* wd */, argv, NULL /* env */,
154 0, NULL, NULL, NULL, NULL,
155 &exit_status, &error);
159 g_printerr ("Error migrating metadata: %s\n", error->message);
160 g_error_free (error);
163 return retval && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0;
167 ev_migrate_metadata (void)
172 const gchar *userdir;
174 userdir = g_getenv ("GNOME22_USER_DIR");
176 dot_dir = g_build_filename (userdir, "evince", NULL);
178 dot_dir = g_build_filename (g_get_home_dir (),
184 updated = g_build_filename (dot_dir, "migrated-to-gvfs", NULL);
185 if (g_file_test (updated, G_FILE_TEST_EXISTS)) {
186 /* Already migrated */
192 metadata = g_build_filename (dot_dir, "ev-metadata.xml", NULL);
193 if (g_file_test (metadata, G_FILE_TEST_EXISTS)) {
194 if (convert_metadata (metadata)) {
197 fd = g_creat (updated, 0600);
210 spawn_evince (const gchar *uri)
214 GError *error = NULL;
216 /* TODO Check that the uri exists */
217 argv[0] = g_build_filename (BINDIR, "evince", NULL);
218 argv[1] = (gchar *) uri;
221 retval = g_spawn_async (NULL /* wd */, argv, NULL /* env */,
222 0, NULL, NULL, NULL, &error);
224 g_printerr ("Error spawning evince for uri %s: %s\n", uri, error->message);
225 g_error_free (error);
233 name_appeared_cb (GDBusConnection *connection,
235 const gchar *name_owner,
238 LOG ("Watch name'%s' appeared with owner '%s'\n", name, name_owner);
242 name_vanished_cb (GDBusConnection *connection,
248 LOG ("Watch name'%s' disappeared\n", name);
250 for (l = ev_daemon_docs; l != NULL; l = l->next) {
251 EvDoc *doc = (EvDoc *) l->data;
253 if (strcmp (doc->dbus_name, name) != 0)
256 LOG ("Watch found URI '%s' for name; removing\n", doc->uri);
258 ev_daemon_docs = g_list_delete_link (ev_daemon_docs, l);
261 ev_daemon_maybe_start_killtimer (user_data);
267 process_pending_invocations (const gchar *uri,
268 const gchar *dbus_name)
271 GList *uri_invocations;
273 LOG ("RegisterDocument process pending invocations for URI %s\n", uri);
274 uri_invocations = g_hash_table_lookup (pending_invocations, uri);
276 for (l = uri_invocations; l != NULL; l = l->next) {
277 GDBusMethodInvocation *invocation;
279 invocation = (GDBusMethodInvocation *)l->data;
280 g_dbus_method_invocation_return_value (invocation,
281 g_variant_new ("(s)", dbus_name));
284 g_list_free (uri_invocations);
285 g_hash_table_remove (pending_invocations, uri);
289 method_call_cb (GDBusConnection *connection,
291 const gchar *object_path,
292 const gchar *interface_name,
293 const gchar *method_name,
294 GVariant *parameters,
295 GDBusMethodInvocation *invocation,
298 if (g_strcmp0 (interface_name, EV_DBUS_DAEMON_INTERFACE_NAME) != 0)
301 if (g_strcmp0 (method_name, "RegisterDocument") == 0) {
305 g_variant_get (parameters, "(&s)", &uri);
307 doc = ev_daemon_find_doc (uri);
309 LOG ("RegisterDocument found owner '%s' for URI '%s'\n", doc->dbus_name, uri);
310 g_dbus_method_invocation_return_value (invocation,
311 g_variant_new ("(s)", doc->dbus_name));
315 ev_daemon_stop_killtimer ();
317 doc = g_new (EvDoc, 1);
318 doc->dbus_name = g_strdup (sender);
319 doc->uri = g_strdup (uri);
320 process_pending_invocations (doc->uri, doc->dbus_name);
322 doc->watch_id = g_bus_watch_name_on_connection (connection,
324 G_BUS_NAME_WATCHER_FLAGS_NONE,
329 LOG ("RegisterDocument registered owner '%s' for URI '%s'\n", doc->dbus_name, uri);
330 ev_daemon_docs = g_list_prepend (ev_daemon_docs, doc);
332 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ""));
333 } else if (g_strcmp0 (method_name, "UnregisterDocument") == 0) {
337 g_variant_get (parameters, "(&s)", &uri);
339 LOG ("UnregisterDocument URI '%s'\n", uri);
341 doc = ev_daemon_find_doc (uri);
343 LOG ("UnregisterDocument URI was not registered!\n");
344 g_dbus_method_invocation_return_error_literal (invocation,
346 G_DBUS_ERROR_INVALID_ARGS,
347 "URI not registered");
351 if (strcmp (doc->dbus_name, sender) != 0) {
352 LOG ("UnregisterDocument called by non-owner (owner '%s' sender '%s')\n",
353 doc->dbus_name, sender);
355 g_dbus_method_invocation_return_error_literal (invocation,
357 G_DBUS_ERROR_BAD_ADDRESS,
358 "Only owner can call this method");
362 ev_daemon_docs = g_list_remove (ev_daemon_docs, doc);
364 ev_daemon_maybe_start_killtimer (user_data);
366 g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
367 } else if (g_strcmp0 (method_name, "FindDocument") == 0) {
372 g_variant_get (parameters, "(&sb)", &uri, &spawn);
374 LOG ("FindDocument URI '%s' \n", uri);
376 doc = ev_daemon_find_doc (uri);
378 g_dbus_method_invocation_return_value (invocation,
379 g_variant_new ("(s)", doc->dbus_name));
384 GList *uri_invocations;
385 gboolean ret_val = TRUE;
387 uri_invocations = g_hash_table_lookup (pending_invocations, uri);
389 if (uri_invocations == NULL) {
390 /* Only spawn once. */
391 ret_val = spawn_evince (uri);
395 /* Only defer DBUS answer if evince was succesfully spawned */
396 uri_invocations = g_list_prepend (uri_invocations, invocation);
397 g_hash_table_insert (pending_invocations,
404 LOG ("FindDocument URI '%s' was not registered!\n", uri);
405 g_dbus_method_invocation_return_value (invocation,
406 g_variant_new ("(s)",""));
410 static const char introspection_xml[] =
412 "<interface name='org.gnome.evince.Daemon'>"
413 "<method name='RegisterDocument'>"
414 "<arg type='s' name='uri' direction='in'/>"
415 "<arg type='s' name='owner' direction='out'/>"
417 "<method name='UnregisterDocument'>"
418 "<arg type='s' name='uri' direction='in'/>"
420 "<method name='FindDocument'>"
421 "<arg type='s' name='uri' direction='in'/>"
422 "<arg type='b' name='spawn' direction='in'/>"
423 "<arg type='s' name='owner' direction='out'/>"
428 static const GDBusInterfaceVTable interface_vtable = {
434 static GDBusNodeInfo *introspection_data;
437 bus_acquired_cb (GDBusConnection *connection,
441 GMainLoop *loop = (GMainLoop *) user_data;
442 guint registration_id;
443 GError *error = NULL;
445 if (!introspection_data)
446 introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
448 registration_id = g_dbus_connection_register_object (connection,
449 EV_DBUS_DAEMON_OBJECT_PATH,
450 introspection_data->interfaces[0],
452 g_main_loop_ref (loop),
453 (GDestroyNotify) g_main_loop_unref,
455 if (registration_id == 0) {
456 g_printerr ("Failed to register object: %s\n", error->message);
457 g_error_free (error);
459 if (g_main_loop_is_running (loop))
460 g_main_loop_quit (loop);
465 name_acquired_cb (GDBusConnection *connection,
469 ev_migrate_metadata ();
471 ev_daemon_maybe_start_killtimer (user_data);
475 name_lost_cb (GDBusConnection *connection,
479 GMainLoop *loop = (GMainLoop *) user_data;
481 /* Failed to acquire the name; exit daemon */
482 if (g_main_loop_is_running (loop))
483 g_main_loop_quit (loop);
487 main (gint argc, gchar **argv)
492 g_set_prgname ("evince-daemon");
496 loop = g_main_loop_new (NULL, FALSE);
498 pending_invocations = g_hash_table_new_full (g_str_hash,
500 (GDestroyNotify)g_free,
503 owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
505 G_BUS_NAME_OWNER_FLAGS_NONE,
509 g_main_loop_ref (loop),
510 (GDestroyNotify) g_main_loop_unref);
512 g_main_loop_run (loop);
514 g_bus_unown_name (owner_id);
516 g_main_loop_unref (loop);
517 if (introspection_data)
518 g_dbus_node_info_unref (introspection_data);
519 g_list_foreach (ev_daemon_docs, (GFunc)ev_doc_free, NULL);
520 g_list_free (ev_daemon_docs);
521 g_hash_table_destroy (pending_invocations);