X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=shell%2Fev-daemon.c;h=fa9d506db83ee6b3e32ca7451583952a8df17908;hb=180cf6fef2318791abb3583f4c3f3bc176a204d8;hp=c5b9c6d2daf44516b5c3d5f1e490a2e1e4f25878;hpb=66e5636dcaace0d9a98845490b09721c12ce3d2b;p=evince.git diff --git a/shell/ev-daemon.c b/shell/ev-daemon.c index c5b9c6d2..fa9d506d 100644 --- a/shell/ev-daemon.c +++ b/shell/ev-daemon.c @@ -1,7 +1,8 @@ -/* ev-metadata.c +/* ev-daemon.c * this file is part of evince, a gnome document viewer * * Copyright (C) 2009 Carlos Garcia Campos + * Copyright © 2010 Christian Persch * * Evince is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by @@ -15,7 +16,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" @@ -26,53 +27,29 @@ #include #include #include +#include #include #include -#include -#include -#define EV_DBUS_DAEMON_NAME "org.gnome.evince.Daemon" -#define EV_DBUS_DAEMON_OBJECT_PATH "/org/gnome/evince/Daemon" +#define EV_DBUS_DAEMON_NAME "org.gnome.evince.Daemon" +#define EV_DBUS_DAEMON_INTERFACE_NAME "org.gnome.evince.Daemon" +#define EV_DBUS_DAEMON_OBJECT_PATH "/org/gnome/evince/Daemon" -#define EV_TYPE_DAEMON (ev_daemon_get_type ()) -#define EV_DAEMON(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_DAEMON, EvDaemon)) -#define EV_DAEMON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_DAEMON, EvDaemonClass)) -#define EV_IS_DAEMON(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_DAEMON)) +#define EV_DBUS_WINDOW_INTERFACE_NAME "org.gnome.evince.Window" -typedef struct _EvDaemon EvDaemon; -typedef struct _EvDaemonClass EvDaemonClass; +#define DAEMON_TIMEOUT (30) /* seconds */ -struct _EvDaemon { - GObject base; +#define LOG g_printerr - DBusGProxy *bus_proxy; - - GList *docs; - guint n_docs; - - guint timer_id; -}; - -struct _EvDaemonClass { - GObjectClass base_class; -}; - -static GType ev_daemon_get_type (void) G_GNUC_CONST; -static gboolean ev_daemon_register_document (EvDaemon *ev_daemon, - const gchar *uri, - DBusGMethodInvocation *context); -static gboolean ev_daemon_unregister_document (EvDaemon *ev_daemon, - const gchar *uri, - DBusGMethodInvocation *context); -#include "ev-daemon-service.h" - -static EvDaemon *ev_daemon = NULL; - -G_DEFINE_TYPE(EvDaemon, ev_daemon, G_TYPE_OBJECT) +static GList *ev_daemon_docs = NULL; +static guint kill_timer_id; +static GHashTable *pending_invocations = NULL; typedef struct { gchar *dbus_name; gchar *uri; + guint watch_id; + guint loaded_id; } EvDoc; static void @@ -84,16 +61,17 @@ ev_doc_free (EvDoc *doc) g_free (doc->dbus_name); g_free (doc->uri); + g_bus_unwatch_name (doc->watch_id); + g_free (doc); } static EvDoc * -ev_daemon_find_doc (EvDaemon *ev_daemon, - const gchar *uri) +ev_daemon_find_doc (const gchar *uri) { GList *l; - for (l = ev_daemon->docs; l; l = g_list_next (l)) { + for (l = ev_daemon_docs; l != NULL; l = l->next) { EvDoc *doc = (EvDoc *)l->data; if (strcmp (doc->uri, uri) == 0) @@ -104,339 +82,375 @@ ev_daemon_find_doc (EvDaemon *ev_daemon, } static void -ev_daemon_finalize (GObject *object) +ev_daemon_stop_killtimer (void) { - EvDaemon *ev_daemon = EV_DAEMON (object); + if (kill_timer_id != 0) + g_source_remove (kill_timer_id); + kill_timer_id = 0; +} - if (ev_daemon->docs) { - g_list_foreach (ev_daemon->docs, (GFunc)ev_doc_free, NULL); - g_list_free (ev_daemon->docs); - ev_daemon->docs = NULL; - } +static gboolean +ev_daemon_shutdown (gpointer user_data) +{ + GMainLoop *loop = (GMainLoop *) user_data; - if (ev_daemon->bus_proxy) { - g_object_unref (ev_daemon->bus_proxy); - ev_daemon->bus_proxy = NULL; - } + LOG ("Timeout; exiting daemon.\n"); - G_OBJECT_CLASS (ev_daemon_parent_class)->finalize (object); -} + if (g_main_loop_is_running (loop)) + g_main_loop_quit (loop); -static void -ev_daemon_init (EvDaemon *ev_daemon) -{ + return FALSE; } static void -ev_daemon_class_init (EvDaemonClass *klass) +ev_daemon_maybe_start_killtimer (gpointer data) { - GObjectClass *g_object_class = G_OBJECT_CLASS (klass); - - g_object_class->finalize = ev_daemon_finalize; + ev_daemon_stop_killtimer (); + if (ev_daemon_docs != NULL) + return; - dbus_g_object_type_install_info (EV_TYPE_DAEMON, - &dbus_glib_ev_daemon_object_info); + kill_timer_id = g_timeout_add_seconds (DAEMON_TIMEOUT, + (GSourceFunc) ev_daemon_shutdown, + data); } static gboolean -ev_daemon_shutdown (EvDaemon *ev_daemon) +spawn_evince (const gchar *uri) { - g_object_unref (ev_daemon); + gchar *argv[3]; + gboolean retval; + GError *error = NULL; - return FALSE; -} + /* TODO Check that the uri exists */ + argv[0] = g_build_filename (BINDIR, "evince", NULL); + argv[1] = (gchar *) uri; + argv[2] = NULL; -static void -ev_daemon_stop_killtimer (EvDaemon *ev_daemon) -{ - if (ev_daemon->timer_id > 0) - g_source_remove (ev_daemon->timer_id); - ev_daemon->timer_id = 0; -} + retval = g_spawn_async (NULL /* wd */, argv, NULL /* env */, + 0, NULL, NULL, NULL, &error); + if (!retval) { + g_printerr ("Error spawning evince for uri %s: %s\n", uri, error->message); + g_error_free (error); + } + g_free (argv[0]); -static void -ev_daemon_start_killtimer (EvDaemon *ev_daemon) -{ - ev_daemon_stop_killtimer (ev_daemon); - ev_daemon->timer_id = - g_timeout_add_seconds (30, - (GSourceFunc) ev_daemon_shutdown, - ev_daemon); + return retval; } static void -ev_daemon_name_owner_changed (DBusGProxy *proxy, - const gchar *name, - const gchar *old_owner, - const gchar *new_owner, - EvDaemon *ev_daemon) +name_appeared_cb (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) { - GList *l, *next = NULL; - - if (*name == ':' && *new_owner == '\0') { - for (l = ev_daemon->docs; l; l = next) { - EvDoc *doc = (EvDoc *)l->data; - - next = l->next; - if (strcmp (doc->dbus_name, name) == 0) { - ev_doc_free (doc); - ev_daemon->docs = g_list_delete_link (ev_daemon->docs, l); - if (--ev_daemon->n_docs == 0) - ev_daemon_start_killtimer (ev_daemon); - } - } - } + LOG ("Watch name'%s' appeared with owner '%s'\n", name, name_owner); } -static EvDaemon * -ev_daemon_get (void) +static void +name_vanished_cb (GDBusConnection *connection, + const gchar *name, + gpointer user_data) { - DBusGConnection *connection; - guint request_name_result; - GError *error = NULL; - - if (ev_daemon) - return ev_daemon; - - connection = dbus_g_bus_get (DBUS_BUS_STARTER, &error); - if (!connection) { - g_printerr ("Failed to connect to the D-BUS daemon: %s\n", error->message); - g_error_free (error); - - return NULL; - } - - ev_daemon = g_object_new (EV_TYPE_DAEMON, NULL); - - ev_daemon->bus_proxy = dbus_g_proxy_new_for_name (connection, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS); - if (!org_freedesktop_DBus_request_name (ev_daemon->bus_proxy, - EV_DBUS_DAEMON_NAME, - DBUS_NAME_FLAG_DO_NOT_QUEUE, - &request_name_result, &error)) { - g_printerr ("Failed to acquire daemon name: %s", error->message); - g_error_free (error); - g_object_unref (ev_daemon); - - return NULL; - } - - switch (request_name_result) { - case DBUS_REQUEST_NAME_REPLY_EXISTS: - g_printerr ("Evince daemon already running, exiting.\n"); - g_object_unref (ev_daemon); - - return NULL; - case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: - dbus_g_connection_register_g_object (connection, - EV_DBUS_DAEMON_OBJECT_PATH, - G_OBJECT (ev_daemon)); - break; - default: - g_printerr ("Not primary owner of the service, exiting.\n"); - g_object_unref (ev_daemon); - - return NULL; - } - - - dbus_g_proxy_add_signal (ev_daemon->bus_proxy, - "NameOwnerChanged", - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_INVALID); - dbus_g_proxy_connect_signal (ev_daemon->bus_proxy, "NameOwnerChanged", - G_CALLBACK (ev_daemon_name_owner_changed), - ev_daemon, NULL); - ev_daemon_start_killtimer (ev_daemon); - - return ev_daemon; -} + GList *l; + LOG ("Watch name'%s' disappeared\n", name); + for (l = ev_daemon_docs; l != NULL; l = l->next) { + EvDoc *doc = (EvDoc *) l->data; -static gboolean -ev_daemon_register_document (EvDaemon *ev_daemon, - const gchar *uri, - DBusGMethodInvocation *method) -{ - EvDoc *doc; - const gchar *owner = NULL; - - doc = ev_daemon_find_doc (ev_daemon, uri); - if (doc) { - /* Already registered */ - owner = doc->dbus_name; - } else { - doc = g_new (EvDoc, 1); - doc->dbus_name = dbus_g_method_get_sender (method); - doc->uri = g_strdup (uri); - ev_daemon->docs = g_list_prepend (ev_daemon->docs, doc); - if (ev_daemon->n_docs++ == 0) - ev_daemon_stop_killtimer (ev_daemon); - } + if (strcmp (doc->dbus_name, name) != 0) + continue; - dbus_g_method_return (method, owner); + LOG ("Watch found URI '%s' for name; removing\n", doc->uri); - return TRUE; + ev_daemon_docs = g_list_delete_link (ev_daemon_docs, l); + ev_doc_free (doc); + + ev_daemon_maybe_start_killtimer (user_data); + return; + } } -static gboolean -ev_daemon_unregister_document (EvDaemon *ev_daemon, - const gchar *uri, - DBusGMethodInvocation *method) +static void +process_pending_invocations (const gchar *uri, + const gchar *dbus_name) { - EvDoc *doc; - gchar *sender; - - doc = ev_daemon_find_doc (ev_daemon, uri); - if (!doc) { - g_warning ("Document %s is not registered\n", uri); - dbus_g_method_return (method); + GList *l; + GList *uri_invocations; - return TRUE; - } + LOG ("RegisterDocument process pending invocations for URI %s\n", uri); + uri_invocations = g_hash_table_lookup (pending_invocations, uri); - sender = dbus_g_method_get_sender (method); - if (strcmp (doc->dbus_name, sender) != 0) { - g_warning ("Failed to unregister document %s: invalid owner %s, expected %s\n", - uri, sender, doc->dbus_name); - g_free (sender); - dbus_g_method_return (method); + for (l = uri_invocations; l != NULL; l = l->next) { + GDBusMethodInvocation *invocation; - return TRUE; + invocation = (GDBusMethodInvocation *)l->data; + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", dbus_name)); } - g_free (sender); - ev_daemon->docs = g_list_remove (ev_daemon->docs, doc); - ev_doc_free (doc); - if (--ev_daemon->n_docs == 0) - ev_daemon_start_killtimer (ev_daemon); - - dbus_g_method_return (method); - - return TRUE; + g_list_free (uri_invocations); + g_hash_table_remove (pending_invocations, uri); } static void -do_exit (GMainLoop *loop, - GObject *object) +document_loaded_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + EvDoc *doc) { - if (g_main_loop_is_running (loop)) - g_main_loop_quit (loop); + const gchar *uri; + + g_variant_get (parameters, "(&s)", &uri); + if (strcmp (uri, doc->uri) == 0) + process_pending_invocations (uri, sender_name); + g_dbus_connection_signal_unsubscribe (connection, doc->loaded_id); } -static gboolean -convert_metadata (const gchar *metadata) +static void +method_call_cb (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) { - GFile *file; - gchar *cmd; - gint exit_status; - GFileAttributeInfoList *namespaces; - gboolean supported = FALSE; - GError *error = NULL; - gboolean retval; + if (g_strcmp0 (interface_name, EV_DBUS_DAEMON_INTERFACE_NAME) != 0) + return; + + if (g_strcmp0 (method_name, "RegisterDocument") == 0) { + EvDoc *doc; + const gchar *uri; + + g_variant_get (parameters, "(&s)", &uri); + + doc = ev_daemon_find_doc (uri); + if (doc != NULL) { + LOG ("RegisterDocument found owner '%s' for URI '%s'\n", doc->dbus_name, uri); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", doc->dbus_name)); + return; + } + + ev_daemon_stop_killtimer (); + + doc = g_new (EvDoc, 1); + doc->dbus_name = g_strdup (sender); + doc->uri = g_strdup (uri); + + doc->loaded_id = g_dbus_connection_signal_subscribe (connection, + doc->dbus_name, + EV_DBUS_WINDOW_INTERFACE_NAME, + "DocumentLoaded", + NULL, + NULL, + 0, + (GDBusSignalCallback) document_loaded_cb, + doc, + NULL); + doc->watch_id = g_bus_watch_name_on_connection (connection, + sender, + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_cb, + name_vanished_cb, + user_data, NULL); + + LOG ("RegisterDocument registered owner '%s' for URI '%s'\n", doc->dbus_name, uri); + ev_daemon_docs = g_list_prepend (ev_daemon_docs, doc); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "")); + } else if (g_strcmp0 (method_name, "UnregisterDocument") == 0) { + EvDoc *doc; + const gchar *uri; + + g_variant_get (parameters, "(&s)", &uri); + + LOG ("UnregisterDocument URI '%s'\n", uri); + + doc = ev_daemon_find_doc (uri); + if (doc == NULL) { + LOG ("UnregisterDocument URI was not registered!\n"); + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "URI not registered"); + return; + } + + if (strcmp (doc->dbus_name, sender) != 0) { + LOG ("UnregisterDocument called by non-owner (owner '%s' sender '%s')\n", + doc->dbus_name, sender); + + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_BAD_ADDRESS, + "Only owner can call this method"); + return; + } + + ev_daemon_docs = g_list_remove (ev_daemon_docs, doc); + ev_doc_free (doc); + ev_daemon_maybe_start_killtimer (user_data); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); + } else if (g_strcmp0 (method_name, "FindDocument") == 0) { + EvDoc *doc; + const gchar *uri; + gboolean spawn; + + g_variant_get (parameters, "(&sb)", &uri, &spawn); + + LOG ("FindDocument URI '%s' \n", uri); + + doc = ev_daemon_find_doc (uri); + if (doc != NULL) { + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", doc->dbus_name)); + return; + } + + if (spawn) { + GList *uri_invocations; + gboolean ret_val = TRUE; - /* If metadata is not supported for a local file - * is likely because and old gvfs version is running. - */ - file = g_file_new_for_path (metadata); - namespaces = g_file_query_writable_namespaces (file, NULL, NULL); - if (namespaces) { - gint i; - - for (i = 0; i < namespaces->n_infos; i++) { - if (strcmp (namespaces->infos[i].name, "metadata") == 0) { - supported = TRUE; - break; + uri_invocations = g_hash_table_lookup (pending_invocations, uri); + + if (uri_invocations == NULL) { + /* Only spawn once. */ + ret_val = spawn_evince (uri); + } + + if (ret_val) { + /* Only defer DBUS answer if evince was succesfully spawned */ + uri_invocations = g_list_prepend (uri_invocations, invocation); + g_hash_table_insert (pending_invocations, + g_strdup (uri), + uri_invocations); + return; } } - g_file_attribute_info_list_unref (namespaces); - } - if (!supported) { - g_warning ("%s\n", - "GVFS metadata not supported, " - "Evince will run without metadata support"); - g_object_unref (file); - return FALSE; + + LOG ("FindDocument URI '%s' was not registered!\n", uri); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)","")); } - g_object_unref (file); +} - cmd = g_strdup_printf ("%s %s", LIBEXECDIR"/evince-convert-metadata", metadata); +static const char introspection_xml[] = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + +static const GDBusInterfaceVTable interface_vtable = { + method_call_cb, + NULL, + NULL +}; - retval = g_spawn_command_line_sync (cmd, NULL, NULL, &exit_status, &error); - g_free (cmd); +static GDBusNodeInfo *introspection_data; - if (!retval) { - g_printerr ("Error migrating metadata: %s\n", error->message); +static void +bus_acquired_cb (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GMainLoop *loop = (GMainLoop *) user_data; + guint registration_id; + GError *error = NULL; + + if (!introspection_data) + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + + registration_id = g_dbus_connection_register_object (connection, + EV_DBUS_DAEMON_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, + g_main_loop_ref (loop), + (GDestroyNotify) g_main_loop_unref, + &error); + if (registration_id == 0) { + g_printerr ("Failed to register object: %s\n", error->message); g_error_free (error); - } - return retval && exit_status == 0; + if (g_main_loop_is_running (loop)) + g_main_loop_quit (loop); + } } static void -ev_migrate_metadata (void) +name_acquired_cb (GDBusConnection *connection, + const gchar *name, + gpointer user_data) { - gchar *updated; - gchar *metadata; - gchar *dot_dir; - - dot_dir = g_build_filename (g_get_home_dir (), - ".gnome2", - "evince", - NULL); - - updated = g_build_filename (dot_dir, "migrated-to-gvfs", NULL); - if (g_file_test (updated, G_FILE_TEST_EXISTS)) { - /* Already migrated */ - g_free (updated); - g_free (dot_dir); - return; - } - - metadata = g_build_filename (dot_dir, "ev-metadata.xml", NULL); - if (g_file_test (metadata, G_FILE_TEST_EXISTS)) { - if (convert_metadata (metadata)) { - gint fd; + ev_daemon_maybe_start_killtimer (user_data); +} - fd = g_creat (updated, 0600); - if (fd != -1) { - close (fd); - } - } - } +static void +name_lost_cb (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GMainLoop *loop = (GMainLoop *) user_data; - g_free (dot_dir); - g_free (updated); - g_free (metadata); + /* Failed to acquire the name; exit daemon */ + if (g_main_loop_is_running (loop)) + g_main_loop_quit (loop); } gint main (gint argc, gchar **argv) { GMainLoop *loop; + guint owner_id; - /* Init glib threads asap */ - if (!g_thread_supported ()) - g_thread_init (NULL); + g_set_prgname ("evince-daemon"); g_type_init (); - if (!ev_daemon_get ()) - return 1; - - ev_migrate_metadata (); - loop = g_main_loop_new (NULL, FALSE); - g_object_weak_ref (G_OBJECT (ev_daemon), - (GWeakNotify) do_exit, - loop); - g_main_loop_run (loop); - g_main_loop_unref (loop); + + pending_invocations = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify)g_free, + NULL); + + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + EV_DBUS_DAEMON_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_cb, + name_acquired_cb, + name_lost_cb, + g_main_loop_ref (loop), + (GDestroyNotify) g_main_loop_unref); + + g_main_loop_run (loop); + + g_bus_unown_name (owner_id); + + g_main_loop_unref (loop); + if (introspection_data) + g_dbus_node_info_unref (introspection_data); + g_list_foreach (ev_daemon_docs, (GFunc)ev_doc_free, NULL); + g_list_free (ev_daemon_docs); + g_hash_table_destroy (pending_invocations); return 0; }