Updates
authorDavid Zeuthen <davidz@redhat.com>
Tue, 8 Jun 2010 19:46:33 +0000 (15:46 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Tue, 8 Jun 2010 19:46:33 +0000 (15:46 -0400)
src/Makefile.am
src/linuxdaemon.c
src/linuxdevice.c [new file with mode: 0644]
src/linuxdevice.h [new file with mode: 0644]
src/main.c
src/types.h

index 7f1a780..6b54b5b 100644 (file)
@@ -34,6 +34,7 @@ BUILT_SOURCES =                                                               \
        generated-bindings.h                                            \
        generated-daemon.h              generated-daemon.c              \
        generated-device.h              generated-device.c              \
+       generated-marshallers.list                                      \
        generated-marshallers.h         generated-marshallers.c         \
        generated-typemappers.h                                         \
        $(NULL)
@@ -45,6 +46,7 @@ udisks_daemon_SOURCES =                                               \
        error.h                         error.c                         \
                                        main.c                          \
        linuxdaemon.h                   linuxdaemon.c                   \
+       linuxdevice.h                   linuxdevice.c                   \
        private.h                                                       \
        profile.h                                                       \
        $(BUILT_SOURCES)                                                \
index 8af5adb..685673d 100644 (file)
 #include <gudev/gudev.h>
 
 #include "linuxdaemon.h"
+#include "linuxdevice.h"
 
 struct _LinuxDaemonPrivate
 {
+  GDBusConnection *connection;
+
   GUdevClient *udev_client;
+
+  GHashTable *map_sysfs_path_to_object;
+};
+
+enum
+{
+  PROP_0,
+  PROP_CONNECTION,
 };
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void linux_daemon_coldplug (LinuxDaemon *daemon);
+
+static void on_uevent (GUdevClient  *client,
+                       const gchar  *action,
+                       GUdevDevice  *device,
+                       gpointer      user_data);
+
 static void daemon_iface_init (DaemonIface *iface);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 G_DEFINE_TYPE_WITH_CODE (LinuxDaemon, linux_daemon, TYPE_DAEMON_STUB,
                          G_IMPLEMENT_INTERFACE (TYPE_DAEMON, daemon_iface_init));
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+linux_daemon_get_property (GObject      *object,
+                           guint         prop_id,
+                           GValue       *value,
+                           GParamSpec   *pspec)
+{
+  LinuxDaemon *daemon = LINUX_DAEMON (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONNECTION:
+      g_value_set_object (value, daemon->priv->connection);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+linux_daemon_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  LinuxDaemon *daemon = LINUX_DAEMON (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONNECTION:
+      daemon->priv->connection = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
 
 static void
 linux_daemon_finalize (GObject *object)
 {
   LinuxDaemon *daemon = LINUX_DAEMON (object);
 
+  g_object_unref (daemon->priv->connection);
   g_object_unref (daemon->priv->udev_client);
+  g_hash_table_unref (daemon->priv->map_sysfs_path_to_object);
 
   if (G_OBJECT_CLASS (linux_daemon_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (linux_daemon_parent_class)->finalize (object);
@@ -52,7 +118,27 @@ linux_daemon_init (LinuxDaemon *daemon)
 
   daemon->priv = G_TYPE_INSTANCE_GET_PRIVATE (daemon, TYPE_LINUX_DAEMON, LinuxDaemonPrivate);
 
+  daemon->priv->map_sysfs_path_to_object = g_hash_table_new_full (g_str_hash,
+                                                                  g_str_equal,
+                                                                  g_free,
+                                                                  g_object_unref);
+
   daemon->priv->udev_client = g_udev_client_new (subsystems);
+  g_signal_connect (daemon->priv->udev_client,
+                    "uevent",
+                    G_CALLBACK (on_uevent),
+                    daemon);
+}
+
+static void
+linux_daemon_constructed (GObject *object)
+{
+  LinuxDaemon *daemon = LINUX_DAEMON (object);
+
+  linux_daemon_coldplug (daemon);
+
+  if (G_OBJECT_CLASS (linux_daemon_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (linux_daemon_parent_class)->constructed (object);
 }
 
 static void
@@ -61,13 +147,189 @@ linux_daemon_class_init (LinuxDaemonClass *klass)
   GObjectClass *gobject_class;
 
   gobject_class = G_OBJECT_CLASS (klass);
-  gobject_class->finalize = linux_daemon_finalize;
+  gobject_class->get_property = linux_daemon_get_property;
+  gobject_class->set_property = linux_daemon_set_property;
+  gobject_class->finalize     = linux_daemon_finalize;
+  gobject_class->constructed  = linux_daemon_constructed;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_CONNECTION,
+                                   g_param_spec_object ("connection",
+                                                        "Connection",
+                                                        "The GDBusConnection to use",
+                                                        G_TYPE_DBUS_CONNECTION,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
 
   g_type_class_add_private (klass, sizeof (LinuxDaemonPrivate));
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+/* returns TRUE if a change was made */
+static gboolean
+maybe_export_unexport_object (LinuxDaemon *daemon,
+                              LinuxDevice *device,
+                              gboolean     visible)
+{
+  guint daemon_export_id;
+  gboolean ret;
+
+  ret = FALSE;
+
+  daemon_export_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), "daemon-export-id"));
+  if (visible)
+    {
+      if (daemon_export_id == 0)
+        {
+          GError *error;
+          guint id;
+          error = NULL;
+          id = device_register_object (DEVICE (device),
+                                       daemon->priv->connection,
+                                       linux_device_get_object_path (device),
+                                       &error);
+          if (id == 0)
+            {
+              g_printerr ("Error registering object: %s\n",
+                          error->message);
+              g_error_free (error);
+            }
+          g_object_set_data (G_OBJECT (device), "daemon-export-id", GUINT_TO_POINTER (id));
+          ret = TRUE;
+        }
+      else
+        {
+          /* all good, is already exported */
+        }
+    }
+  else
+    {
+      if (daemon_export_id > 0)
+        {
+          g_dbus_connection_unregister_object (daemon->priv->connection, daemon_export_id);
+          g_object_set_data (G_OBJECT (device), "daemon-export-id", GUINT_TO_POINTER (0));
+          ret = TRUE;
+        }
+      else
+        {
+          /* all good, wasn't previously exported */
+        }
+    }
+
+  return ret;
+}
+
+static void
+emit_added (LinuxDaemon  *daemon,
+            LinuxDevice  *device)
+{
+  daemon_emit_device_added (DAEMON (daemon), linux_device_get_object_path (device));
+}
+
+static void
+emit_removed (LinuxDaemon  *daemon,
+              LinuxDevice  *device)
+{
+  daemon_emit_device_removed (DAEMON (daemon), linux_device_get_object_path (device));
+}
+
+static void
+handle_device_uevent (LinuxDaemon  *daemon,
+                      const gchar  *action,
+                      GUdevDevice  *udev_device)
+{
+  LinuxDevice *device;
+  const gchar *sysfs_path;
+
+  sysfs_path = g_udev_device_get_sysfs_path (udev_device);
+  if (g_strcmp0 (action, "remove") == 0)
+    {
+      device = g_hash_table_lookup (daemon->priv->map_sysfs_path_to_object, sysfs_path);
+      if (device != NULL)
+        {
+          if (maybe_export_unexport_object (daemon, device, FALSE))
+            emit_removed (daemon, device);
+
+          g_hash_table_remove (daemon->priv->map_sysfs_path_to_object, sysfs_path);
+          g_print ("removed object with sysfs path `%s'\n", sysfs_path);
+        }
+    }
+  else
+    {
+      device = g_hash_table_lookup (daemon->priv->map_sysfs_path_to_object, sysfs_path);
+      if (device != NULL)
+        {
+          gboolean visible;
+
+          linux_device_set_udev_device (device, udev_device);
+          linux_device_update (device);
+          visible = linux_device_get_visible (device);
+          if (maybe_export_unexport_object (daemon, device, visible))
+            {
+              if (visible)
+                emit_added (daemon, device);
+              else
+                emit_removed (daemon, device);
+            }
+          g_print ("handled %s uevent for object with sysfs path `%s'\n", action, sysfs_path);
+        }
+      else
+        {
+          const gchar *object_path;
+          gboolean visible;
+
+          device = linux_device_new (udev_device);
+          object_path = linux_device_get_object_path (device);
+          visible = linux_device_get_visible (device);
+
+          g_hash_table_insert (daemon->priv->map_sysfs_path_to_object,
+                               g_strdup (sysfs_path),
+                               device);
+
+          if (maybe_export_unexport_object (daemon, device, visible))
+            emit_added (daemon, device);
+
+          /* TODO: connect to notify:visible to handle changes */
+
+          g_print ("added object with sysfs path `%s'\n", sysfs_path);
+        }
+    }
+}
+
+/* Called on startup */
+static void
+linux_daemon_coldplug (LinuxDaemon *daemon)
+{
+  GList *devices;
+  GList *l;
+
+  /* TODO: maybe do two loops to properly handle dependency SNAFU? */
+  devices = g_udev_client_query_by_subsystem (daemon->priv->udev_client, "block");
+  for (l = devices; l != NULL; l = l->next)
+    handle_device_uevent (daemon, "add", G_UDEV_DEVICE (l->data));
+  g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+  g_list_free (devices);
+}
+
+/* Called when an uevent happens on a device */
+static void
+on_uevent (GUdevClient  *client,
+           const gchar  *action,
+           GUdevDevice  *device,
+           gpointer      user_data)
+{
+  LinuxDaemon *daemon = LINUX_DAEMON (user_data);
+  handle_device_uevent (daemon, action, device);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static gboolean
 handle_enumerate_device_files (Daemon                *_daemon,
                                GDBusMethodInvocation *invocation)
diff --git a/src/linuxdevice.c b/src/linuxdevice.c
new file mode 100644 (file)
index 0000000..1da8918
--- /dev/null
@@ -0,0 +1,287 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gudev/gudev.h>
+
+#include "linuxdevice.h"
+
+struct _LinuxDevicePrivate
+{
+  GUdevDevice *udev_device;
+  gboolean visible;
+  gchar *object_path;
+};
+
+enum
+{
+  PROP_0,
+  PROP_UDEV_DEVICE,
+  PROP_VISIBLE,
+  PROP_OBJECT_PATH,
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void device_iface_init (DeviceIface *iface);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+G_DEFINE_TYPE_WITH_CODE (LinuxDevice, linux_device, TYPE_DEVICE_STUB,
+                         G_IMPLEMENT_INTERFACE (TYPE_DEVICE, device_iface_init));
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+linux_device_get_property (GObject      *object,
+                           guint         prop_id,
+                           GValue       *value,
+                           GParamSpec   *pspec)
+{
+  LinuxDevice *device = LINUX_DEVICE (object);
+
+  switch (prop_id)
+    {
+    case PROP_UDEV_DEVICE:
+      g_value_set_object (value, linux_device_get_udev_device (device));
+      break;
+
+    case PROP_VISIBLE:
+      g_value_set_boolean (value, linux_device_get_visible (device));
+      break;
+
+    case PROP_OBJECT_PATH:
+      g_value_set_string (value, linux_device_get_object_path (device));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+linux_device_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  LinuxDevice *device = LINUX_DEVICE (object);
+
+  switch (prop_id)
+    {
+    case PROP_UDEV_DEVICE:
+      linux_device_set_udev_device (device, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+linux_device_finalize (GObject *object)
+{
+  LinuxDevice *device = LINUX_DEVICE (object);
+
+  if (device->priv->udev_device != NULL)
+    g_object_unref (device->priv->udev_device);
+  g_free (device->priv->object_path);
+
+  if (G_OBJECT_CLASS (linux_device_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (linux_device_parent_class)->finalize (object);
+}
+
+static void
+linux_device_init (LinuxDevice *device)
+{
+  device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, TYPE_LINUX_DEVICE, LinuxDevicePrivate);
+}
+
+static void
+linux_device_constructed (GObject *object)
+{
+  LinuxDevice *device = LINUX_DEVICE (object);
+
+  linux_device_update (device);
+
+  if (G_OBJECT_CLASS (linux_device_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (linux_device_parent_class)->constructed (object);
+}
+
+static void
+linux_device_class_init (LinuxDeviceClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->get_property = linux_device_get_property;
+  gobject_class->set_property = linux_device_set_property;
+  gobject_class->constructed  = linux_device_constructed;
+  gobject_class->finalize     = linux_device_finalize;
+
+  g_type_class_add_private (klass, sizeof (LinuxDevicePrivate));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_UDEV_DEVICE,
+                                   g_param_spec_object ("udev-device",
+                                                        "UDev Device",
+                                                        "The underlying GUdevDevice",
+                                                        G_UDEV_TYPE_DEVICE,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_VISIBLE,
+                                   g_param_spec_boolean ("visible",
+                                                         "Visible",
+                                                         "Whether the device should be exported on D-Bus",
+                                                         TRUE,
+                                                         G_PARAM_READABLE |
+                                                         G_PARAM_STATIC_NAME |
+                                                         G_PARAM_STATIC_BLURB |
+                                                         G_PARAM_STATIC_NICK));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT_PATH,
+                                   g_param_spec_string ("object-path",
+                                                        "Object Path",
+                                                        "The D-Bus object path to use for exporting the device",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+LinuxDevice *
+linux_device_new (GUdevDevice *udev_device)
+{
+  return LINUX_DEVICE (g_object_new (TYPE_LINUX_DEVICE,
+                                     "udev-device", udev_device,
+                                     NULL));
+}
+
+GUdevDevice *
+linux_device_get_udev_device (LinuxDevice *device)
+{
+  g_return_val_if_fail (IS_LINUX_DEVICE (device), NULL);
+  return device->priv->udev_device;
+}
+
+void
+linux_device_set_udev_device (LinuxDevice *device,
+                              GUdevDevice *udev_device)
+{
+  g_return_if_fail (IS_LINUX_DEVICE (device));
+  g_return_if_fail (G_UDEV_IS_DEVICE (udev_device));
+
+  if (device->priv->udev_device != NULL)
+    g_object_unref (device->priv->udev_device);
+  device->priv->udev_device = g_object_ref (udev_device);
+}
+
+gboolean
+linux_device_get_visible (LinuxDevice *device)
+{
+  g_return_val_if_fail (IS_LINUX_DEVICE (device), FALSE);
+  return device->priv->visible;
+}
+
+const gchar *
+linux_device_get_object_path (LinuxDevice *device)
+{
+  g_return_val_if_fail (IS_LINUX_DEVICE (device), NULL);
+  return device->priv->object_path;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+device_iface_init (DeviceIface *iface)
+{
+  /* TODO */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *
+util_compute_object_path (const gchar *path)
+{
+  const gchar *basename;
+  GString *s;
+  guint n;
+
+  g_return_val_if_fail (path != NULL, NULL);
+
+  basename = strrchr (path, '/');
+  if (basename != NULL)
+    basename++;
+  else
+    basename = path;
+
+  s = g_string_new ("/org/freedesktop/UDisks/devices/");
+  for (n = 0; basename[n] != '\0'; n++)
+    {
+      gint c = basename[n];
+
+      /* D-Bus spec sez:
+       *
+       * Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_"
+       */
+      if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
+        {
+          g_string_append_c (s, c);
+        }
+      else
+        {
+          /* Escape bytes not in [A-Z][a-z][0-9] as _<hex-with-two-digits> */
+          g_string_append_printf (s, "_%02x", c);
+        }
+    }
+
+  return g_string_free (s, FALSE);
+}
+
+void
+linux_device_update (LinuxDevice *device)
+{
+  g_return_if_fail (IS_LINUX_DEVICE (device));
+
+  device_set_native_path (DEVICE (device), (gchar *) g_udev_device_get_sysfs_path (device->priv->udev_device));
+
+  /* TODO */
+  device->priv->visible = TRUE;
+  device->priv->object_path = util_compute_object_path (device_get_native_path (DEVICE (device)));
+
+  device_set_device_detection_time (DEVICE (device), device_get_device_detection_time (DEVICE (device)) + 1);
+  device_set_device_media_detection_time (DEVICE (device), device_get_device_media_detection_time (DEVICE (device)) + 2);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/linuxdevice.h b/src/linuxdevice.h
new file mode 100644 (file)
index 0000000..4ca2273
--- /dev/null
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __LINUX_DEVICE_H__
+#define __LINUX_DEVICE_H__
+
+#include "types.h"
+#include <gudev/gudev.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_LINUX_DEVICE         (linux_device_get_type ())
+#define LINUX_DEVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TYPE_LINUX_DEVICE, LinuxDevice))
+#define LINUX_DEVICE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), TYPE_DEVICE, LinuxDeviceClass))
+#define IS_LINUX_DEVICE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TYPE_LINUX_DEVICE))
+#define IS_LINUX_DEVICE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TYPE_LINUX_DEVICE))
+#define LINUX_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_LINUX_DEVICE, LinuxDeviceClass))
+
+typedef struct _LinuxDeviceClass   LinuxDeviceClass;
+typedef struct _LinuxDevicePrivate LinuxDevicePrivate;
+
+struct _LinuxDevice
+{
+  DeviceStub parent;
+  LinuxDevicePrivate *priv;
+};
+
+struct _LinuxDeviceClass
+{
+  DeviceStubClass parent_class;
+};
+
+GType        linux_device_get_type        (void) G_GNUC_CONST;
+LinuxDevice *linux_device_new             (GUdevDevice  *udev_device);
+GUdevDevice *linux_device_get_udev_device (LinuxDevice  *device);
+void         linux_device_set_udev_device (LinuxDevice  *device,
+                                           GUdevDevice  *udev_device);
+void         linux_device_update          (LinuxDevice  *device);
+gboolean     linux_device_get_visible     (LinuxDevice  *device);
+const gchar *linux_device_get_object_path (LinuxDevice  *device);
+
+G_END_DECLS
+
+#endif /* __LINUX_DEVICE_H__ */
index ee74327..857c3ce 100644 (file)
@@ -49,6 +49,7 @@ on_bus_acquired (GDBusConnection *connection,
   g_assert (the_daemon == NULL);
   the_daemon = g_object_new (TYPE_LINUX_DAEMON,
                              "daemon-version", PACKAGE_VERSION,
+                             "connection", connection,
                              NULL);
 
   error = NULL;
index e93ee8e..9e08f3c 100644 (file)
@@ -27,6 +27,7 @@
 G_BEGIN_DECLS
 
 typedef struct _LinuxDaemon  LinuxDaemon;
+typedef struct _LinuxDevice  LinuxDevice;
 
 typedef struct Mount        Mount;
 typedef struct MountMonitor MountMonitor;