3655499
From 776070012e3d5d2bff5a1b2a9e175ced7122f125 Mon Sep 17 00:00:00 2001
3655499
From: Jannis Pohlmann <jannis@xfce.org>
3655499
Date: Wed, 28 Sep 2011 21:11:57 +0000
3655499
Subject: Fix ownership race conditions when started twice (bug #8001).
3655499
3655499
It can happen that D-Bus activates tumblerd multiple times if the
3655499
activated instance doesn't bring up the service quickly enough. We need
3655499
to detect this in order to exit duplicate instances gracefully (exit
3655499
code 0). Exiting with an error code breaks clients.
3655499
3655499
For more information, see the following bugs:
3655499
3655499
  https://bugzilla.xfce.org/show_bug.cgi?id=8001
3655499
  https://bugs.freedesktop.org/show_bug.cgi?id=41233
3655499
---
3655499
diff --git a/tumblerd/main.c b/tumblerd/main.c
3655499
index f2de4f0..f79049b 100644
3655499
--- a/tumblerd/main.c
3655499
+++ b/tumblerd/main.c
3655499
@@ -65,6 +65,7 @@ main (int    argc,
3655499
   TumblerService          *service;
3655499
   TumblerCacheService     *cache_service;
3655499
   GMainLoop               *main_loop;
3655499
+  gboolean                 already_running = FALSE;
3655499
   GError                  *error = NULL;
3655499
   GList                   *providers;
3655499
   GList                   *thumbnailers;
3655499
@@ -99,22 +100,6 @@ main (int    argc,
3655499
   /* create the lifecycle manager */
3655499
   lifecycle_manager = tumbler_lifecycle_manager_new ();
3655499
 
3655499
-  /* create the thumbnail cache service */
3655499
-  cache_service = tumbler_cache_service_new (connection, lifecycle_manager);
3655499
-
3655499
-  /* try to start the service and exit if that fails */
3655499
-  if (!tumbler_cache_service_start (cache_service, &error))
3655499
-    {
3655499
-      g_warning (_("Failed to start the thumbnail cache service: %s"), error->message);
3655499
-      g_error_free (error);
3655499
-
3655499
-      g_object_unref (cache_service);
3655499
-
3655499
-      dbus_g_connection_unref (connection);
3655499
-
3655499
-      return EXIT_FAILURE;
3655499
-    }
3655499
-
3655499
   /* create the thumbnailer registry */
3655499
   registry = tumbler_registry_new ();
3655499
 
3655499
@@ -152,6 +137,15 @@ main (int    argc,
3655499
   /* update the URI schemes / MIME types supported information */
3655499
   tumbler_registry_update_supported (registry);
3655499
 
3655499
+  /* create the thumbnail cache service */
3655499
+  cache_service = tumbler_cache_service_new (connection, lifecycle_manager);
3655499
+
3655499
+  /* create the thumbnailer manager service */
3655499
+  manager = tumbler_manager_new (connection, lifecycle_manager, registry);
3655499
+
3655499
+  /* create the generic thumbnailer service */
3655499
+  service = tumbler_service_new (connection, lifecycle_manager, registry);
3655499
+
3655499
   /* try to load specialized thumbnailers and exit if that fails */
3655499
   if (!tumbler_registry_load (registry, &error))
3655499
     {
3655499
@@ -159,49 +153,80 @@ main (int    argc,
3655499
                  error->message);
3655499
       g_error_free (error);
3655499
 
3655499
-      g_object_unref (registry);
3655499
+      g_object_unref (service);
3655499
+      g_object_unref (manager);
3655499
       g_object_unref (cache_service);
3655499
+      g_object_unref (registry);
3655499
 
3655499
       dbus_g_connection_unref (connection);
3655499
 
3655499
       return EXIT_FAILURE;
3655499
     }
3655499
 
3655499
-  /* create the thumbnailer manager service */
3655499
-  manager = tumbler_manager_new (connection, lifecycle_manager, registry);
3655499
+  /* try to start the service and exit if that fails */
3655499
+  if (!tumbler_cache_service_start (cache_service, &error))
3655499
+    {
3655499
+      if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_ADDRESS_IN_USE)
3655499
+        already_running = TRUE;
3655499
+
3655499
+      g_warning (_("Failed to start the thumbnail cache service: %s"), error->message);
3655499
+      g_error_free (error);
3655499
+
3655499
+      g_object_unref (service);
3655499
+      g_object_unref (manager);
3655499
+      g_object_unref (cache_service);
3655499
+      g_object_unref (registry);
3655499
+
3655499
+      dbus_g_connection_unref (connection);
3655499
+
3655499
+      if (already_running)
3655499
+        return EXIT_SUCCESS;
3655499
+      else
3655499
+        return EXIT_FAILURE;
3655499
+    }
3655499
 
3655499
   /* try to start the service and exit if that fails */
3655499
   if (!tumbler_manager_start (manager, &error))
3655499
     {
3655499
+      if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_ADDRESS_IN_USE)
3655499
+        already_running = TRUE;
3655499
+
3655499
       g_warning (_("Failed to start the thumbnailer manager: %s"), error->message);
3655499
       g_error_free (error);
3655499
 
3655499
+      g_object_unref (service);
3655499
       g_object_unref (manager);
3655499
-      g_object_unref (registry);
3655499
       g_object_unref (cache_service);
3655499
+      g_object_unref (registry);
3655499
 
3655499
       dbus_g_connection_unref (connection);
3655499
 
3655499
-      return EXIT_FAILURE;
3655499
+      if (already_running)
3655499
+        return EXIT_SUCCESS;
3655499
+      else
3655499
+        return EXIT_FAILURE;
3655499
     }
3655499
 
3655499
-  /* create the generic thumbnailer service */
3655499
-  service = tumbler_service_new (connection, lifecycle_manager, registry);
3655499
-
3655499
   /* try to start the service and exit if that fails */
3655499
   if (!tumbler_service_start (service, &error))
3655499
     {
3655499
+      if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_ADDRESS_IN_USE)
3655499
+        already_running = TRUE;
3655499
+
3655499
       g_warning (_("Failed to start the thumbnailer service: %s"), error->message);
3655499
       g_error_free (error);
3655499
 
3655499
       g_object_unref (service);
3655499
       g_object_unref (manager);
3655499
-      g_object_unref (registry);
3655499
       g_object_unref (cache_service);
3655499
+      g_object_unref (registry);
3655499
 
3655499
       dbus_g_connection_unref (connection);
3655499
 
3655499
-      return EXIT_FAILURE;
3655499
+      if (already_running)
3655499
+        return EXIT_SUCCESS;
3655499
+      else
3655499
+        return EXIT_FAILURE;
3655499
     }
3655499
 
3655499
   /* create a new main loop */
3655499
@@ -220,8 +245,8 @@ main (int    argc,
3655499
   /* shut our services down and release all objects */
3655499
   g_object_unref (service);
3655499
   g_object_unref (manager);
3655499
-  g_object_unref (registry);
3655499
   g_object_unref (cache_service);
3655499
+  g_object_unref (registry);
3655499
   g_object_unref (lifecycle_manager);
3655499
 
3655499
   /* disconnect from the D-Bus session bus */
3655499
diff --git a/tumblerd/tumbler-cache-service.c b/tumblerd/tumbler-cache-service.c
3655499
index f13177c..f6b2dc5 100644
3655499
--- a/tumblerd/tumbler-cache-service.c
3655499
+++ b/tumblerd/tumbler-cache-service.c
3655499
@@ -174,6 +174,15 @@ tumbler_cache_service_constructed (GObject *object)
3655499
                                             service, 1, FALSE, NULL);
3655499
   service->cleanup_pool = g_thread_pool_new (tumbler_cache_service_cleanup_thread, 
3655499
                                              service, 1, FALSE, NULL);
3655499
+
3655499
+  /* everything's fine, install the cache type D-Bus info */
3655499
+  dbus_g_object_type_install_info (G_OBJECT_TYPE (service),
3655499
+                                   &dbus_glib_tumbler_cache_service_object_info);
3655499
+
3655499
+  /* register the cache instance as a handler of the cache interface */
3655499
+  dbus_g_connection_register_g_object (service->connection, 
3655499
+                                       "/org/freedesktop/thumbnails/Cache1",
3655499
+                                       G_OBJECT (service));
3655499
 }
3655499
 
3655499
 
3655499
@@ -400,7 +409,19 @@ tumbler_cache_service_start (TumblerCacheService *service,
3655499
                                   DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error);
3655499
 
3655499
   /* check if that failed */
3655499
-  if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
3655499
+  if (result == DBUS_REQUEST_NAME_REPLY_EXISTS)
3655499
+    {
3655499
+      if (error != NULL)
3655499
+        {
3655499
+          g_set_error (error, DBUS_GERROR, DBUS_GERROR_ADDRESS_IN_USE,
3655499
+                       _("Another thumbnail cache service is already running"));
3655499
+        }
3655499
+
3655499
+      g_mutex_unlock (service->mutex);
3655499
+
3655499
+      return FALSE;
3655499
+    }
3655499
+  else if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
3655499
     {
3655499
       /* propagate the D-Bus error */
3655499
       if (dbus_error_is_set (&dbus_error))
3655499
@@ -421,15 +442,6 @@ tumbler_cache_service_start (TumblerCacheService *service,
3655499
       return FALSE;
3655499
     }
3655499
   
3655499
-  /* everything's fine, install the cache type D-Bus info */
3655499
-  dbus_g_object_type_install_info (G_OBJECT_TYPE (service),
3655499
-                                   &dbus_glib_tumbler_cache_service_object_info);
3655499
-
3655499
-  /* register the cache instance as a handler of the cache interface */
3655499
-  dbus_g_connection_register_g_object (service->connection, 
3655499
-                                       "/org/freedesktop/thumbnails/Cache1",
3655499
-                                       G_OBJECT (service));
3655499
-
3655499
   g_mutex_unlock (service->mutex);
3655499
 
3655499
   return TRUE;
3655499
diff --git a/tumblerd/tumbler-manager.c b/tumblerd/tumbler-manager.c
3655499
index 73e6778..feb2943 100644
3655499
--- a/tumblerd/tumbler-manager.c
3655499
+++ b/tumblerd/tumbler-manager.c
3655499
@@ -63,6 +63,7 @@ typedef struct _ThumbnailerInfo ThumbnailerInfo;
3655499
 
3655499
 
3655499
 
3655499
+static void             tumbler_manager_constructed       (GObject          *object);
3655499
 static void             tumbler_manager_finalize          (GObject          *object);
3655499
 static void             tumbler_manager_get_property      (GObject          *object,
3655499
                                                            guint             prop_id,
3655499
@@ -158,6 +159,7 @@ tumbler_manager_class_init (TumblerManagerClass *klass)
3655499
   GObjectClass *gobject_class;
3655499
 
3655499
   gobject_class = G_OBJECT_CLASS (klass);
3655499
+  gobject_class->constructed = tumbler_manager_constructed;
3655499
   gobject_class->finalize = tumbler_manager_finalize; 
3655499
   gobject_class->get_property = tumbler_manager_get_property;
3655499
   gobject_class->set_property = tumbler_manager_set_property;
3655499
@@ -198,6 +200,25 @@ tumbler_manager_init (TumblerManager *manager)
3655499
 
3655499
 
3655499
 
3655499
+
3655499
+
3655499
+static void
3655499
+tumbler_manager_constructed (GObject *object)
3655499
+{
3655499
+  TumblerManager *manager = TUMBLER_MANAGER (object);
3655499
+
3655499
+  /* everything's fine, install the manager type D-Bus info */
3655499
+  dbus_g_object_type_install_info (G_OBJECT_TYPE (manager), 
3655499
+                                   &dbus_glib_tumbler_manager_object_info);
3655499
+
3655499
+  /* register the manager instance as a handler of the manager interface */
3655499
+  dbus_g_connection_register_g_object (manager->connection, 
3655499
+                                       "/org/freedesktop/thumbnails/Manager1", 
3655499
+                                       G_OBJECT (manager));
3655499
+}
3655499
+
3655499
+
3655499
+
3655499
 static void
3655499
 tumbler_manager_finalize (GObject *object)
3655499
 {
3655499
@@ -1838,7 +1859,19 @@ tumbler_manager_start (TumblerManager *manager,
3655499
                                   DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error);
3655499
 
3655499
   /* check if that failed */
3655499
-  if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
3655499
+  if (result == DBUS_REQUEST_NAME_REPLY_EXISTS)
3655499
+    {
3655499
+      if (error != NULL)
3655499
+        {
3655499
+          g_set_error (error, DBUS_GERROR, DBUS_GERROR_ADDRESS_IN_USE,
3655499
+                       _("Another thumbnail cache service is already running"));
3655499
+        }
3655499
+
3655499
+      g_mutex_unlock (manager->mutex);
3655499
+
3655499
+      return FALSE;
3655499
+    }
3655499
+  else if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
3655499
     {
3655499
       /* propagate the D-Bus error */
3655499
       if (dbus_error_is_set (&dbus_error))
3655499
@@ -1860,15 +1893,6 @@ tumbler_manager_start (TumblerManager *manager,
3655499
       return FALSE;
3655499
     }
3655499
 
3655499
-  /* everything's fine, install the manager type D-Bus info */
3655499
-  dbus_g_object_type_install_info (G_OBJECT_TYPE (manager), 
3655499
-                                   &dbus_glib_tumbler_manager_object_info);
3655499
-
3655499
-  /* register the manager instance as a handler of the manager interface */
3655499
-  dbus_g_connection_register_g_object (manager->connection, 
3655499
-                                       "/org/freedesktop/thumbnails/Manager1", 
3655499
-                                       G_OBJECT (manager));
3655499
-
3655499
   g_mutex_unlock (manager->mutex);
3655499
 
3655499
   /* load thumbnailers installed into the system permanently */
3655499
diff --git a/tumblerd/tumbler-service.c b/tumblerd/tumbler-service.c
3655499
index 92ab7ac..8214a45 100644
3655499
--- a/tumblerd/tumbler-service.c
3655499
+++ b/tumblerd/tumbler-service.c
3655499
@@ -295,6 +295,15 @@ tumbler_service_constructed (GObject *object)
3655499
   scheduler = tumbler_group_scheduler_new ("background");
3655499
   tumbler_service_add_scheduler (service, scheduler);
3655499
   g_object_unref (scheduler);
3655499
+
3655499
+  /* everything is fine, install the generic thumbnailer D-Bus info */
3655499
+  dbus_g_object_type_install_info (G_OBJECT_TYPE (service),
3655499
+                                   &dbus_glib_tumbler_service_object_info);
3655499
+
3655499
+  /* register the service instance as a handler of this interface */
3655499
+  dbus_g_connection_register_g_object (service->connection, 
3655499
+                                       THUMBNAILER_PATH, 
3655499
+                                       G_OBJECT (service));
3655499
 }
3655499
 
3655499
 
3655499
@@ -744,7 +753,19 @@ tumbler_service_start (TumblerService *service,
3655499
                                   DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error);
3655499
 
3655499
   /* check if that failed */
3655499
-  if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
3655499
+  if (result == DBUS_REQUEST_NAME_REPLY_EXISTS)
3655499
+    {
3655499
+      if (error != NULL)
3655499
+        {
3655499
+          g_set_error (error, DBUS_GERROR, DBUS_GERROR_ADDRESS_IN_USE,
3655499
+                       _("Another thumbnail cache service is already running"));
3655499
+        }
3655499
+
3655499
+      g_mutex_unlock (service->mutex);
3655499
+
3655499
+      return FALSE;
3655499
+    }
3655499
+  else if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
3655499
     {
3655499
       /* propagate the D-Bus error */
3655499
       if (dbus_error_is_set (&dbus_error))
3655499
@@ -765,15 +786,6 @@ tumbler_service_start (TumblerService *service,
3655499
       return FALSE;
3655499
     }
3655499
 
3655499
-  /* everything is fine, install the generic thumbnailer D-Bus info */
3655499
-  dbus_g_object_type_install_info (G_OBJECT_TYPE (service),
3655499
-                                   &dbus_glib_tumbler_service_object_info);
3655499
-
3655499
-  /* register the service instance as a handler of this interface */
3655499
-  dbus_g_connection_register_g_object (service->connection, 
3655499
-                                       THUMBNAILER_PATH, 
3655499
-                                       G_OBJECT (service));
3655499
-
3655499
   g_mutex_unlock (service->mutex);
3655499
 
3655499
   return TRUE;
3655499
--
3655499
cgit