[gvfs] Add gvfsd-metadata update daemon



commit bb7cc90200c7eccce9a6b6622b191548b5e26586
Author: Alexander Larsson <alexl redhat com>
Date:   Sat Jun 20 21:32:45 2009 +0200

    Add gvfsd-metadata update daemon
    
    This daemon singleton handles updates to metadata stores.
    All clients that wishes to write metadata should talk to
    it via dbus.
    
    This way all writes are serialized to avoid data loss on
    concurrent writes.

 common/gvfsdaemonprotocol.h       |    8 +
 metadata/Makefile.am              |   17 +-
 metadata/gvfs-metadata.service.in |    3 +
 metadata/meta-daemon.c            |  573 +++++++++++++++++++++++++++++++++++++
 4 files changed, 600 insertions(+), 1 deletions(-)
---
diff --git a/common/gvfsdaemonprotocol.h b/common/gvfsdaemonprotocol.h
index ce8b11e..9dc61eb 100644
--- a/common/gvfsdaemonprotocol.h
+++ b/common/gvfsdaemonprotocol.h
@@ -94,6 +94,14 @@ G_BEGIN_DECLS
 #define G_VFS_DBUS_MONITOR_CLIENT_INTERFACE "org.gtk.vfs.MonitorClient"
 #define G_VFS_DBUS_MONITOR_CLIENT_OP_CHANGED "Changed"
 
+/* The well known name of the metadata daemon */
+#define G_VFS_DBUS_METADATA_NAME "org.gtk.vfs.Metadata"
+#define G_VFS_DBUS_METADATA_PATH "/org/gtk/vfs/metadata"
+#define G_VFS_DBUS_METADATA_INTERFACE "org.gtk.vfs.Metadata"
+#define G_VFS_DBUS_METADATA_OP_SET "Set"
+#define G_VFS_DBUS_METADATA_OP_UNSET "Unset"
+#define G_VFS_DBUS_METADATA_OP_REMOVE "Remove"
+#define G_VFS_DBUS_METADATA_OP_MOVE "Move"
 
 /* Mounts time out in 10 minutes, since they can be slow, with auth, etc */
 #define G_VFS_DBUS_MOUNT_TIMEOUT_MSECS (1000*60*10)
diff --git a/metadata/Makefile.am b/metadata/Makefile.am
index 76ab4b8..3116f0c 100644
--- a/metadata/Makefile.am
+++ b/metadata/Makefile.am
@@ -11,10 +11,14 @@ endif
 
 noinst_PROGRAMS = $(APPS)
 
+libexec_PROGRAMS =\
+	gvfsd-metadata	\
+	$(NULL)
 
-INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/gvfs \
+INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/common \
 	$(LIBXML_CFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) \
 	$(UDEV_CFLAGS) \
+	-DGVFS_LOCALEDIR=\""$(localedir)"\"     \
 	-DG_LOG_DOMAIN=\"GVFS\" -DG_DISABLE_DEPRECATED \
 	-DDBUS_API_SUBJECT_TO_CHANGE
 
@@ -28,3 +32,14 @@ libmetadata_la_LIBADD = $(GLIB_LIBS) $(UDEV_LIBS)
 
 convert_nautilus_metadata_LDADD = libmetadata.la $(LIBXML_LIBS)
 convert_nautilus_metadata_SOURCES = metadata-nautilus.c
+
+gvfsd_metadata_LDADD = libmetadata.la $(DBUS_LIBS) ../common/libgvfscommon.la
+gvfsd_metadata_SOURCES = meta-daemon.c
+
+# D-BUS service file
+%.service: %.service.in ../config.log
+	sed -e "s|\ libexecdir\@|$(libexecdir)|" $< > $@
+
+servicedir = $(DBUS_SERVICE_DIR)
+service_in_files = gvfs-metadata.service.in
+service_DATA = gvfs-metadata.service
diff --git a/metadata/gvfs-metadata.service.in b/metadata/gvfs-metadata.service.in
new file mode 100644
index 0000000..9893237
--- /dev/null
+++ b/metadata/gvfs-metadata.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gtk.vfs.Metadata
+Exec= libexecdir@/gvfsd-metadata
diff --git a/metadata/meta-daemon.c b/metadata/meta-daemon.c
new file mode 100644
index 0000000..f86e581
--- /dev/null
+++ b/metadata/meta-daemon.c
@@ -0,0 +1,573 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <stdlib.h>
+#include <dbus/dbus.h>
+#include "gdbusutils.h"
+#include "metatree.h"
+#include "gvfsdaemonprotocol.h"
+
+#define WRITEOUT_TIMEOUT_SECS 60
+
+typedef struct {
+  char *filename;
+  MetaTree *tree;
+  guint writeout_timeout;
+} TreeInfo;
+
+static GHashTable *tree_infos = NULL;
+
+static void
+tree_info_free (TreeInfo *info)
+{
+  g_free (info->filename);
+  meta_tree_unref (info->tree);
+  if (info->writeout_timeout)
+    g_source_remove (info->writeout_timeout);
+
+  g_free (info);
+}
+
+static gboolean
+writeout_timeout (gpointer data)
+{
+  TreeInfo *info = data;
+
+  meta_tree_flush (info->tree);
+  info->writeout_timeout = 0;
+
+  return FALSE;
+}
+
+static void
+tree_info_schedule_writeout (TreeInfo *info)
+{
+  if (info->writeout_timeout == 0)
+    info->writeout_timeout =
+      g_timeout_add_seconds (WRITEOUT_TIMEOUT_SECS,
+			     writeout_timeout, info);
+}
+
+static TreeInfo *
+tree_info_new (const char *filename)
+{
+  TreeInfo *info;
+  MetaTree *tree;
+
+  tree = meta_tree_open (filename, TRUE);
+  if (tree == NULL)
+    return NULL;
+
+  info = g_new0 (TreeInfo, 1);
+  info->filename = g_strdup (filename);
+  info->tree = tree;
+  info->writeout_timeout = 0;
+
+  return info;
+}
+
+static TreeInfo *
+tree_info_lookup (const char *filename)
+{
+  TreeInfo *info;
+
+  info = g_hash_table_lookup (tree_infos, filename);
+  if (info)
+    return info;
+
+  info = tree_info_new (filename);
+  if (info)
+    g_hash_table_insert (tree_infos,
+			 info->filename,
+			 info);
+  return info;
+}
+
+static gboolean
+metadata_set (const char *treefile,
+	      const char *path,
+	      DBusMessageIter *iter,
+	      DBusError *derror)
+{
+  TreeInfo *info;
+  const char *str;
+  char **strv;
+  gboolean res;
+  const char *key;
+  int n_elements;
+
+  info = tree_info_lookup (treefile);
+  if (info == NULL)
+    {
+      dbus_set_error (derror,
+		      DBUS_ERROR_FILE_NOT_FOUND,
+		      _("Can't find metadata file %s"),
+		      treefile);
+      return FALSE;
+    }
+
+  res = TRUE;
+  while (dbus_message_iter_get_arg_type (iter) != 0)
+    {
+      if (!_g_dbus_message_iter_get_args (iter, derror,
+					  DBUS_TYPE_STRING, &key,
+					  0))
+	{
+	  res = FALSE;
+	  break;
+	}
+
+      if (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_ARRAY)
+	{
+	  /* stringv */
+	  if (!_g_dbus_message_iter_get_args (iter, derror,
+					      DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &strv, &n_elements,
+					      0))
+	    {
+	      res = FALSE;
+	      break;
+	    }
+	  if (!meta_tree_set_stringv (info->tree, path, key, strv))
+	    {
+	      dbus_set_error (derror,
+			      DBUS_ERROR_FAILED,
+			      _("Unable to set metadata key"));
+	      res = FALSE;
+	    }
+	  g_strfreev (strv);
+	}
+      else
+	{
+	  /* string */
+	  if (!_g_dbus_message_iter_get_args (iter, derror,
+					      DBUS_TYPE_STRING, &str,
+					      0))
+	    {
+	      res = FALSE;
+	      break;
+	    }
+	  if (!meta_tree_set_string (info->tree, path, key, str))
+	    {
+	      dbus_set_error (derror,
+			      DBUS_ERROR_FAILED,
+			      _("Unable to set metadata key"));
+	      res = FALSE;
+	    }
+	}
+    }
+
+  tree_info_schedule_writeout (info);
+
+  return res;
+}
+
+static gboolean
+metadata_unset (const char *treefile,
+		const char *path,
+		const char *key,
+		DBusError *derror)
+{
+  TreeInfo *info;
+
+  info = tree_info_lookup (treefile);
+  if (info == NULL)
+    {
+      dbus_set_error (derror,
+		      DBUS_ERROR_FILE_NOT_FOUND,
+		      _("Can't find metadata file %s"),
+		      treefile);
+      return FALSE;
+    }
+
+  if (!meta_tree_unset (info->tree, path, key))
+    {
+      dbus_set_error (derror,
+		      DBUS_ERROR_FAILED,
+		      _("Unable to unset metadata key"));
+      return FALSE;
+    }
+
+  tree_info_schedule_writeout (info);
+  return TRUE;
+}
+
+static gboolean
+metadata_remove (const char *treefile,
+		 const char *path,
+		 DBusError *derror)
+{
+  TreeInfo *info;
+
+  info = tree_info_lookup (treefile);
+  if (info == NULL)
+    {
+      dbus_set_error (derror,
+		      DBUS_ERROR_FILE_NOT_FOUND,
+		      _("Can't find metadata file %s"),
+		      treefile);
+      return FALSE;
+    }
+
+  if (!meta_tree_remove (info->tree, path))
+    {
+      dbus_set_error (derror,
+		      DBUS_ERROR_FAILED,
+		      _("Unable to remove metadata keys"));
+      return FALSE;
+    }
+
+  tree_info_schedule_writeout (info);
+  return TRUE;
+}
+
+static gboolean
+metadata_move (const char *treefile,
+	       const char *src_path,
+	       const char *dest_path,
+	       DBusError *derror)
+{
+  TreeInfo *info;
+
+  info = tree_info_lookup (treefile);
+  if (info == NULL)
+    {
+      dbus_set_error (derror,
+		      DBUS_ERROR_FILE_NOT_FOUND,
+		      _("Can't find metadata file %s"),
+		      treefile);
+      return FALSE;
+    }
+
+  /* Overwrites any dest */
+  if (!meta_tree_copy (info->tree, src_path, dest_path))
+    {
+      dbus_set_error (derror,
+		      DBUS_ERROR_FAILED,
+		      _("Unable to move metadata keys"));
+      return FALSE;
+    }
+
+  /* Remove source if copy succeeded (ignoring errors) */
+  meta_tree_remove (info->tree, src_path);
+
+  tree_info_schedule_writeout (info);
+  return TRUE;
+}
+
+static gboolean
+register_name (DBusConnection *conn,
+	       gboolean replace)
+{
+  DBusError error;
+  unsigned int flags;
+  int ret;
+
+  flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+  if (replace)
+    flags |= DBUS_NAME_FLAG_REPLACE_EXISTING;
+
+  ret = dbus_bus_request_name (conn, G_VFS_DBUS_METADATA_NAME, flags, &error);
+  if (ret == -1)
+    {
+      g_printerr ("Failed to acquire daemon name: %s", error.message);
+      dbus_error_free (&error);
+      return FALSE;
+    }
+  else if (ret == DBUS_REQUEST_NAME_REPLY_EXISTS)
+    {
+      g_printerr ("VFS daemon already running, exiting.\n");
+      return FALSE;
+    }
+  else if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+    {
+      g_printerr ("Not primary owner of the service, exiting.\n");
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static DBusHandlerResult
+dbus_message_filter_func (DBusConnection *conn,
+			  DBusMessage    *message,
+			  gpointer        data)
+{
+  char *name;
+
+  if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameLost"))
+    {
+      if (dbus_message_get_args (message, NULL,
+				 DBUS_TYPE_STRING, &name,
+				 DBUS_TYPE_INVALID) &&
+	  strcmp (name, G_VFS_DBUS_METADATA_NAME) == 0)
+	{
+	  /* Someone else got the name (i.e. someone used --replace), exit */
+	  exit (1);
+	}
+    }
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+metadata_unregistered (DBusConnection  *connection,
+		       void            *user_data)
+{
+}
+
+static DBusHandlerResult
+metadata_message (DBusConnection  *connection,
+		  DBusMessage     *message,
+		  void            *user_data)
+{
+  DBusMessageIter iter;
+  DBusError derror;
+  DBusMessage *reply;
+  char *treefile;
+  char *path, *dest_path;
+  const char *key;
+
+  reply = NULL;
+  dbus_message_iter_init (message, &iter);
+  dbus_error_init (&derror);
+
+  if (dbus_message_is_method_call (message,
+				   G_VFS_DBUS_METADATA_INTERFACE,
+				   G_VFS_DBUS_METADATA_OP_SET))
+    {
+      treefile = NULL;
+      path = NULL;
+      if (!_g_dbus_message_iter_get_args (&iter, &derror,
+					  G_DBUS_TYPE_CSTRING, &treefile,
+					  G_DBUS_TYPE_CSTRING, &path,
+					  0) ||
+	  !metadata_set (treefile, path, &iter, &derror))
+	{
+	  reply = dbus_message_new_error (message,
+					  derror.name,
+					  derror.message);
+	  dbus_error_free (&derror);
+	}
+      else
+	reply = dbus_message_new_method_return (message);
+
+      g_free (treefile);
+      g_free (path);
+    }
+
+  else if (dbus_message_is_method_call (message,
+				   G_VFS_DBUS_METADATA_INTERFACE,
+				   G_VFS_DBUS_METADATA_OP_UNSET))
+    {
+      treefile = NULL;
+      path = NULL;
+      if (!_g_dbus_message_iter_get_args (&iter, &derror,
+					  G_DBUS_TYPE_CSTRING, &treefile,
+					  G_DBUS_TYPE_CSTRING, &path,
+					  DBUS_TYPE_STRING, &key,
+					  0) ||
+	  !metadata_unset (treefile, path, key, &derror))
+	{
+	  reply = dbus_message_new_error (message,
+					  derror.name,
+					  derror.message);
+	  dbus_error_free (&derror);
+	}
+      else
+	reply = dbus_message_new_method_return (message);
+
+      g_free (treefile);
+      g_free (path);
+    }
+
+  else if (dbus_message_is_method_call (message,
+					G_VFS_DBUS_METADATA_INTERFACE,
+					G_VFS_DBUS_METADATA_OP_REMOVE))
+    {
+      treefile = NULL;
+      path = NULL;
+      if (!_g_dbus_message_iter_get_args (&iter, &derror,
+					  G_DBUS_TYPE_CSTRING, &treefile,
+					  G_DBUS_TYPE_CSTRING, &path,
+					  0) ||
+	  !metadata_remove (treefile, path, &derror))
+	{
+	  reply = dbus_message_new_error (message,
+					  derror.name,
+					  derror.message);
+	  dbus_error_free (&derror);
+	}
+      else
+	reply = dbus_message_new_method_return (message);
+
+      g_free (treefile);
+      g_free (path);
+    }
+
+  else if (dbus_message_is_method_call (message,
+					G_VFS_DBUS_METADATA_INTERFACE,
+					G_VFS_DBUS_METADATA_OP_MOVE))
+    {
+      treefile = NULL;
+      path = NULL;
+      dest_path = NULL;
+      if (!_g_dbus_message_iter_get_args (&iter, &derror,
+					  G_DBUS_TYPE_CSTRING, &treefile,
+					  G_DBUS_TYPE_CSTRING, &path,
+					  G_DBUS_TYPE_CSTRING, &dest_path,
+					  0) ||
+	  !metadata_move (treefile, path, dest_path, &derror))
+	{
+	  reply = dbus_message_new_error (message,
+					  derror.name,
+					  derror.message);
+	  dbus_error_free (&derror);
+	}
+      else
+	reply = dbus_message_new_method_return (message);
+
+      g_free (treefile);
+      g_free (path);
+    }
+
+  if (reply)
+    {
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static struct DBusObjectPathVTable metadata_dbus_vtable = {
+  metadata_unregistered,
+  metadata_message
+};
+
+int
+main (int argc, char *argv[])
+{
+  GMainLoop *loop;
+  DBusConnection *conn;
+  gboolean replace;
+  GError *error;
+  DBusError derror;
+  GOptionContext *context;
+  const GOptionEntry options[] = {
+    { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace,  N_("Replace old daemon."), NULL },
+    { NULL }
+  };
+
+  setlocale (LC_ALL, "");
+
+  bindtextdomain (GETTEXT_PACKAGE, GVFS_LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain (GETTEXT_PACKAGE);
+
+  g_set_application_name (_("GVFS Metadata Daemon"));
+  context = g_option_context_new ("");
+
+  g_option_context_set_summary (context, _("Main daemon for GVFS"));
+
+  g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+
+  replace = FALSE;
+
+  error = NULL;
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      /* Translators: the first %s is the application name, */
+      /* the second %s is the error message                 */
+      g_printerr (_("%s: %s"), g_get_application_name(), error->message);
+      g_printerr ("\n");
+      g_printerr (_("Try \"%s --help\" for more information."),
+		  g_get_prgname ());
+      g_printerr ("\n");
+      g_error_free (error);
+      g_option_context_free (context);
+      return 1;
+    }
+
+  g_option_context_free (context);
+
+  g_type_init ();
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  dbus_error_init (&derror);
+  conn = dbus_bus_get (DBUS_BUS_SESSION, &derror);
+  if (!conn)
+    {
+      g_printerr ("Failed to connect to the D-BUS daemon: %s\n",
+		  derror.message);
+
+      dbus_error_free (&derror);
+      return 1;
+    }
+
+  dbus_bus_add_match (conn,
+		      "type='signal',"
+		      "interface='org.freedesktop.DBus',"
+		      "member='NameOwnerChanged',"
+		      "arg0='"G_VFS_DBUS_METADATA_NAME"'",
+		      &derror);
+  if (dbus_error_is_set (&derror))
+    {
+      g_printerr ("Failed to add dbus match: %s\n", derror.message);
+      dbus_error_free (&derror);
+      return 1;
+    }
+
+  if (!dbus_connection_add_filter (conn,
+				   dbus_message_filter_func, NULL, NULL))
+    {
+      g_printerr ("Failed to add dbus filter\n");
+      return 1;
+    }
+
+  if (!dbus_connection_register_object_path (conn,
+					     G_VFS_DBUS_METADATA_PATH,
+					     &metadata_dbus_vtable, NULL))
+    {
+      g_printerr ("Failed to register object path\n");
+      return 1;
+    }
+
+  if (!register_name (conn, replace))
+    return 1;
+
+  _g_dbus_connection_integrate_with_main (conn);
+
+  tree_infos = g_hash_table_new_full (g_str_hash,
+				      g_str_equal,
+				      NULL,
+				      (GDestroyNotify)tree_info_free);
+
+  g_main_loop_run (loop);
+
+  return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]