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)
error.h error.c \
main.c \
linuxdaemon.h linuxdaemon.c \
+ linuxdevice.h linuxdevice.c \
private.h \
profile.h \
$(BUILT_SOURCES) \
#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);
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
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)
--- /dev/null
+/* -*- 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);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
--- /dev/null
+/* -*- 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__ */
g_assert (the_daemon == NULL);
the_daemon = g_object_new (TYPE_LINUX_DAEMON,
"daemon-version", PACKAGE_VERSION,
+ "connection", connection,
NULL);
error = NULL;
G_BEGIN_DECLS
typedef struct _LinuxDaemon LinuxDaemon;
+typedef struct _LinuxDevice LinuxDevice;
typedef struct Mount Mount;
typedef struct MountMonitor MountMonitor;