summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2010-10-05 15:10:18 -0400
committerDavid Zeuthen <davidz@redhat.com>2010-10-05 15:10:18 -0400
commit273829916f9969c81d53e5966fa7e671327c7e73 (patch)
tree8b3d755e8656d12e622cb181ff975c9a0c90e213
parent9a6cf5e2f415ddd0d61d380032e34f9cb96e3b1a (diff)
Determine whether a configuration is currently or can be applied
http://people.freedesktop.org/~david/stc-list.png Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r--doc/stc-sections.txt8
-rw-r--r--stc/Makefile.am4
-rw-r--r--stc/stc.c57
-rw-r--r--stc/stcenums.h33
-rw-r--r--stc/stcitem.c429
-rw-r--r--stc/stcitem.h4
-rw-r--r--stc/stcmonitor.c140
-rw-r--r--stc/stcmount.c159
-rw-r--r--stc/stcmount.h46
-rw-r--r--stc/stcmountmonitor.c502
-rw-r--r--stc/stcmountmonitor.h46
-rw-r--r--stc/stcprivate.h19
-rw-r--r--stc/test.c7
13 files changed, 1431 insertions, 23 deletions
diff --git a/doc/stc-sections.txt b/doc/stc-sections.txt
index ebe982b..10f6c6e 100644
--- a/doc/stc-sections.txt
+++ b/doc/stc-sections.txt
@@ -21,15 +21,15 @@ stc_monitor_get_type
<TITLE>StcItem</TITLE>
StcItem
StcItemType
-stc_item_get_dependencies
+StcItemState
stc_item_get_id
stc_item_get_item_type
stc_item_get_nick_name
stc_item_get_target
stc_item_get_option
stc_item_get_option_keys
-stc_item_get_is_applied
-stc_item_get_can_apply
+stc_item_get_dependencies
+stc_item_get_state
<SUBSECTION Standard>
STC_TYPE_ITEM
STC_ITEM
@@ -37,7 +37,9 @@ STC_IS_ITEM
<SUBSECTION Private>
stc_item_get_type
stc_item_type_get_type
+stc_item_state_get_type
STC_TYPE_ITEM_TYPE
+STC_TYPE_ITEM_STATE
</SECTION>
<SECTION>
diff --git a/stc/Makefile.am b/stc/Makefile.am
index b89827b..9ea6aaa 100644
--- a/stc/Makefile.am
+++ b/stc/Makefile.am
@@ -60,6 +60,8 @@ libstc_1_la_SOURCES = \
stcitem.h stcitem.c \
stcmonitor.h stcmonitor.c \
stcprivate.h \
+ stcmount.h stcmount.c \
+ stcmountmonitor.h stcmountmonitor.c \
$(NULL)
libstc_1_la_CPPFLAGS = \
@@ -68,10 +70,12 @@ libstc_1_la_CPPFLAGS = \
libstc_1_la_CFLAGS = \
$(GLIB_CFLAGS) \
+ $(GUDEV_CFLAGS) \
$(NULL)
libstc_1_la_LIBADD = \
$(GLIB_LIBS) \
+ $(GUDEV_LIBS) \
$(NULL)
# ----------------------------------------------------------------------------------------------------
diff --git a/stc/stc.c b/stc/stc.c
index c757f90..e6e8463 100644
--- a/stc/stc.c
+++ b/stc/stc.c
@@ -38,6 +38,52 @@ static GMainLoop *loop = NULL;
/* ---------------------------------------------------------------------------------------------------- */
+static gchar *
+flags_to_str (GType flags_type, guint value)
+{
+ GString *str;
+ GFlagsClass *klass;
+ GFlagsValue *flags_value;
+ guint n;
+
+ str = g_string_new (NULL);
+
+ klass = g_type_class_ref (flags_type);
+ if (klass == NULL)
+ {
+ g_string_append (str, "<Unregistered flags type>");
+ goto out;
+ }
+
+ for (n = 0; n < 32; n++)
+ {
+ if ((value & (1<<n)) != 0)
+ {
+ flags_value = g_flags_get_first_value (klass, (1<<n));
+ if (str->len > 0)
+ g_string_append_c (str, ',');
+ if (flags_value == NULL)
+ g_string_append_printf (str, "<Unregistered bit %d>", n);
+ else
+ g_string_append (str, flags_value->value_nick);
+ }
+ }
+
+ if (str->len == 0)
+ {
+ flags_value = g_flags_get_first_value (klass, 0);
+ if (flags_value == NULL)
+ g_string_append (str, "<No flags set>");
+ else
+ g_string_append (str, flags_value->value_nick);
+ }
+
+ out:
+ if (klass != NULL)
+ g_type_class_unref (klass);
+ return g_string_free (str, FALSE);
+}
+
static const gchar *
item_type_to_str (StcItemType type)
{
@@ -285,6 +331,7 @@ handle_command_list (gint *argc,
const gchar *target;
const gchar *const *options;
const gchar *const *deps;
+ gchar *state_str;
type = stc_item_get_item_type (item);
id = stc_item_get_id (item);
@@ -379,6 +426,16 @@ handle_command_list (gint *argc,
g_print ("\n");
}
+ state_str = flags_to_str (STC_TYPE_ITEM_STATE, stc_item_get_state (item));
+ g_print (" %sState:%s %s%s%s%s\n",
+ _color_get (_COLOR_FG_WHITE),
+ _color_get (_COLOR_RESET),
+ _color_get (_COLOR_FG_YELLOW),
+ _color_get (_COLOR_BOLD_ON),
+ state_str,
+ _color_get (_COLOR_RESET));
+ g_free (state_str);
+
g_print ("\n");
}
g_list_foreach (items, (GFunc) g_object_unref, NULL);
diff --git a/stc/stcenums.h b/stc/stcenums.h
index 715c15b..b736359 100644
--- a/stc/stcenums.h
+++ b/stc/stcenums.h
@@ -67,6 +67,39 @@ typedef enum
STC_ITEM_TYPE_LUKS
} StcItemType;
+/**
+ * StcItemState:
+ * @STC_ITEM_STATE_NONE: No flags are set.
+ * @STC_ITEM_STATE_IS_APPLIED: A device/object matching the configuration item is already configured.
+ * @STC_ITEM_STATE_CAN_APPLY: The configuration can be applied (e.g. all requisite devices are present and all dependencies are fulfilled).
+ * @STC_ITEM_STATE_CAN_APPLY_DEGRADED: The configuration can be applied but the device will be started degraded.
+ *
+ * Flag enumeration describing what state a configuration item currently is in.
+ *
+ * Note that %STC_ITEM_STATE_IS_APPLIED is set for an item even if the
+ * requested options does not exactly match the request options. For
+ * example, a %STC_ITEM_TYPE_FILESYSTEM configuration item may be set
+ * if a filesystem matching the item is mounted but the mount options
+ * doesn't match. On the other hand, for a filesystem, the mount path
+ * needs to match - e.g. if the item has the option
+ * <literal>Filesystem:mount_path</literal> set to
+ * <filename>/mnt/bar</filename> then it won't be tagged with
+ * %STC_ITEM_STATE_IS_APPLIED if the filesystem is mounted at
+ * <filename>/mnt/foo</filename>.
+ *
+ * States %STC_ITEM_STATE_CAN_APPLY and
+ * %STC_ITEM_STATE_CAN_APPLY_DEGRADED may be set even if
+ * %STC_ITEM_STATE_IS_APPLIED is also set - e.g. they are independent
+ * of whether the configuration is actually applied or not.
+ */
+typedef enum
+{
+ STC_ITEM_STATE_NONE = 0,
+ STC_ITEM_STATE_IS_APPLIED = (1<<0),
+ STC_ITEM_STATE_CAN_APPLY = (1<<1),
+ STC_ITEM_STATE_CAN_APPLY_DEGRADED = (1<<2)
+} StcItemState;
+
G_END_DECLS
#endif /* __STC_ENUMS_H__ */
diff --git a/stc/stcitem.c b/stc/stcitem.c
index 224c6ff..ab0b189 100644
--- a/stc/stcitem.c
+++ b/stc/stcitem.c
@@ -24,6 +24,8 @@
#include "stcitem.h"
#include "stcmonitor.h"
+#include "stcmount.h"
+#include "stcmountmonitor.h"
#include "stcprivate.h"
/**
@@ -56,8 +58,7 @@ struct _StcItem
GHashTable *options;
gchar **depends;
- gboolean can_apply;
- gboolean is_applied;
+ StcItemState state;
/* memoized */
gchar **option_keys;
@@ -91,6 +92,9 @@ stc_item_finalize (GObject *object)
{
StcItem *item = STC_ITEM (object);
+ if (item->monitor != NULL)
+ g_object_remove_weak_pointer (G_OBJECT (item->monitor), (gpointer*) &item->monitor);
+
g_free (item->id);
g_free (item->nick_name);
g_free (item->target);
@@ -172,23 +176,16 @@ stc_item_get_target (StcItem *item)
return item->target;
}
-gboolean
-stc_item_get_can_apply (StcItem *item)
-{
- g_return_val_if_fail (STC_IS_ITEM (item), FALSE);
- return item->can_apply;
-}
-
-gboolean
-stc_item_get_is_applied (StcItem *item)
+StcItemState
+stc_item_get_state (StcItem *item)
{
- g_return_val_if_fail (STC_IS_ITEM (item), FALSE);
- return item->is_applied;
+ g_return_val_if_fail (STC_IS_ITEM (item), STC_ITEM_STATE_NONE);
+ return item->state;
}
-
StcItem *
-_stc_item_new (StcItemType type,
+_stc_item_new (StcMonitor *monitor,
+ StcItemType type,
const gchar *id,
const gchar *target,
const gchar *nick_name,
@@ -202,6 +199,8 @@ _stc_item_new (StcItemType type,
item = STC_ITEM (g_object_new (STC_TYPE_ITEM, NULL));
+ item->monitor = monitor;
+ g_object_add_weak_pointer (G_OBJECT (item->monitor), (gpointer*) &item->monitor);
item->id = g_strdup (id);
item->target = g_strdup (target);
item->nick_name = g_strdup (nick_name);
@@ -213,6 +212,8 @@ _stc_item_new (StcItemType type,
item->path = g_strdup (path);
item->line_no = line_no;
+ _stc_item_update_state (item);
+
return item;
}
@@ -313,9 +314,407 @@ _stc_item_update (StcItem *item, StcItem *other)
changed = TRUE;
}
+ if (changed)
+ {
+ _stc_item_update_state (item);
+ }
+
return changed;
}
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* TODO: need something like this in gudev */
+static GList *
+_g_udev_client_query_by_property (GUdevClient *client,
+ const gchar *subsystem,
+ const gchar *key,
+ const gchar *value)
+{
+ GList *ret;
+ GList *devices;
+ GList *l;
+
+ ret = NULL;
+
+ devices = g_udev_client_query_by_subsystem (client, subsystem);
+ for (l = devices; l != NULL; l = l->next)
+ {
+ GUdevDevice *device = G_UDEV_DEVICE (l->data);
+ if (g_strcmp0 (g_udev_device_get_property (device, key), value) == 0)
+ {
+ ret = g_list_prepend (ret, g_object_ref (device));
+ }
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+
+ ret = g_list_reverse (ret);
+ return ret;
+}
+
+static StcItemState
+_stc_item_update_state_filesystem (StcItem *item,
+ GUdevClient *gudev_client,
+ _StcMountMonitor *mount_monitor)
+{
+ StcItemState ret;
+ GUdevDevice *device;
+ GUdevDevice *device_with_fs;
+ GList *devices;
+ GList *l;
+
+ ret = STC_ITEM_STATE_NONE;
+
+ device_with_fs = NULL;
+
+ if (g_str_has_prefix (item->target, "Device="))
+ {
+ device = g_udev_client_query_by_device_file (gudev_client,
+ item->target + sizeof "Device=" - 1);
+ if (device != NULL)
+ {
+ if (g_strcmp0 (g_udev_device_get_property (device, "ID_FS_USAGE"), "filesystem") == 0)
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ device_with_fs = g_object_ref (device);
+ g_object_unref (device);
+ }
+ }
+ else if (g_str_has_prefix (item->target, "UUID="))
+ {
+ devices = _g_udev_client_query_by_property (gudev_client,
+ "block",
+ "ID_FS_UUID",
+ item->target + sizeof "UUID=" - 1);
+ for (l = devices; l != NULL; l = l->next)
+ {
+ device = G_UDEV_DEVICE (l->data);
+ if (g_strcmp0 (g_udev_device_get_property (device, "ID_FS_USAGE"), "filesystem") == 0)
+ {
+ device_with_fs = g_object_ref (device);
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ break;
+ }
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+ }
+ else if (g_str_has_prefix (item->target, "Label="))
+ {
+ devices = _g_udev_client_query_by_property (gudev_client,
+ "block",
+ "ID_FS_LABEL",
+ item->target + sizeof "Label=" - 1);
+ for (l = devices; l != NULL; l = l->next)
+ {
+ device = G_UDEV_DEVICE (l->data);
+ if (g_strcmp0 (g_udev_device_get_property (device, "ID_FS_USAGE"), "filesystem") == 0)
+ {
+ device_with_fs = g_object_ref (device);
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ break;
+ }
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+ }
+ else
+ {
+ g_warning ("Unsupported target type %s for Filesystem", item->target);
+ }
+
+ /* figure out IS_APPLIED */
+ if (device_with_fs != NULL)
+ {
+ GList *mounts;
+ mounts = _stc_mount_monitor_get_mounts_for_dev (mount_monitor,
+ g_udev_device_get_device_number (device_with_fs));
+ if (mounts != NULL)
+ {
+ const gchar *desired_mount_path;
+ desired_mount_path = g_hash_table_lookup (item->options, "Filesystem:mount_path");
+ g_assert (desired_mount_path != NULL);
+ for (l = mounts; l != NULL; l = l->next)
+ {
+ _StcMount *mount = _STC_MOUNT (l->data);
+ if (g_strcmp0 (desired_mount_path, _stc_mount_get_mount_path (mount)) == 0)
+ {
+ ret |= STC_ITEM_STATE_IS_APPLIED;
+ break;
+ }
+ }
+ }
+ g_list_foreach (mounts, (GFunc) g_object_unref, NULL);
+ g_list_free (mounts);
+ g_object_unref (device_with_fs);
+ }
+
+ return ret;
+}
+
+static gchar *
+_strdup_without_dashes (const gchar *s)
+{
+ GString *str;
+ guint n;
+
+ if (s == NULL)
+ return NULL;
+
+ str = g_string_new (NULL);
+ for (n = 0; s[n] != '\0'; n++)
+ {
+ gint c = s[n];
+ if (c == '-')
+ continue;
+ g_string_append_c (str, c);
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+static StcItemState
+_stc_item_update_state_luks (StcItem *item,
+ GUdevClient *gudev_client)
+{
+ StcItemState ret;
+ GUdevDevice *device;
+ GUdevDevice *device_with_crypto;
+ GList *devices;
+ GList *l;
+
+ ret = STC_ITEM_STATE_NONE;
+
+ device_with_crypto = NULL;
+
+ if (g_str_has_prefix (item->target, "Device="))
+ {
+ device = g_udev_client_query_by_device_file (gudev_client,
+ item->target + sizeof "Device=" - 1);
+ if (device != NULL)
+ {
+ if (g_strcmp0 (g_udev_device_get_property (device, "ID_FS_USAGE"), "crypto") == 0 &&
+ g_strcmp0 (g_udev_device_get_property (device, "ID_FS_TYPE"), "crypto_LUKS") == 0)
+ {
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ device_with_crypto = g_object_ref (device);
+ }
+ g_object_unref (device);
+ }
+ }
+ else if (g_str_has_prefix (item->target, "UUID="))
+ {
+ devices = _g_udev_client_query_by_property (gudev_client,
+ "block",
+ "ID_FS_UUID",
+ item->target + sizeof "UUID=" - 1);
+ for (l = devices; l != NULL; l = l->next)
+ {
+ device = G_UDEV_DEVICE (l->data);
+ if (g_strcmp0 (g_udev_device_get_property (device, "ID_FS_USAGE"), "crypto") == 0 &&
+ g_strcmp0 (g_udev_device_get_property (device, "ID_FS_TYPE"), "crypto_LUKS") == 0)
+ {
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ device_with_crypto = g_object_ref (device);
+ break;
+ }
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+ }
+ else
+ {
+ g_warning ("Unsupported target type %s for LUKS", item->target);
+ }
+
+ /* figure out IS_APPLIED */
+ if (device_with_crypto != NULL)
+ {
+ gchar *uuid_prefix;
+ gchar *luks_uuid;
+ /* if a LUKS device is unlocked, then DM_UUID is set to uuid_prefix plus the name - so
+ * just check the prefix
+ */
+ luks_uuid = _strdup_without_dashes (g_udev_device_get_property (device_with_crypto, "ID_FS_UUID"));
+ uuid_prefix = g_strdup_printf ("CRYPT-LUKS1-%s", luks_uuid);
+ devices = g_udev_client_query_by_subsystem (gudev_client, "block");
+ for (l = devices; l != NULL; l = l->next)
+ {
+ const gchar *dm_uuid;
+ device = G_UDEV_DEVICE (l->data);
+ dm_uuid = g_udev_device_get_property (device, "DM_UUID");
+ if (dm_uuid != NULL && g_str_has_prefix (dm_uuid, uuid_prefix))
+ {
+ ret |= STC_ITEM_STATE_IS_APPLIED;
+ break;
+ }
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+ g_free (uuid_prefix);
+ g_object_unref (device_with_crypto);
+ }
+
+ return ret;
+}
+
+static StcItemState
+_stc_item_update_state_md_raid (StcItem *item,
+ GUdevClient *gudev_client)
+{
+ StcItemState ret;
+ GUdevDevice *device;
+ GList *devices;
+ GList *l;
+ gint num_components_seen;
+ gint md_devices;
+ gchar *md_level;
+ gboolean array_seen;
+
+ ret = STC_ITEM_STATE_NONE;
+
+ num_components_seen = 0;
+ array_seen = FALSE;
+ md_devices = 0;
+ md_level = NULL;
+
+ if (g_str_has_prefix (item->target, "Name="))
+ {
+ devices = _g_udev_client_query_by_property (gudev_client,
+ "block",
+ "MD_NAME",
+ item->target + sizeof "Name=" - 1);
+ for (l = devices; l != NULL; l = l->next)
+ {
+ device = G_UDEV_DEVICE (l->data);
+ /* only component devices has MD_EVENTS */
+ if (g_udev_device_has_property (device, "MD_EVENTS") &&
+ g_strcmp0 (g_udev_device_get_property (device, "ID_FS_USAGE"), "raid") == 0 &&
+ g_strcmp0 (g_udev_device_get_property (device, "ID_FS_TYPE"), "linux_raid_member") == 0)
+ {
+ num_components_seen += 1;
+ if (md_devices == 0)
+ md_devices = g_udev_device_get_property_as_int (device, "MD_DEVICES");
+ if (md_level == NULL)
+ md_level = g_strdup (g_udev_device_get_property (device, "MD_LEVEL"));
+ }
+ else if (!g_udev_device_has_property (device, "MD_EVENTS"))
+ {
+ array_seen = TRUE;
+ }
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+ }
+ else if (g_str_has_prefix (item->target, "UUID="))
+ {
+ devices = _g_udev_client_query_by_property (gudev_client,
+ "block",
+ "MD_UUID",
+ item->target + sizeof "UUID=" - 1);
+ for (l = devices; l != NULL; l = l->next)
+ {
+ device = G_UDEV_DEVICE (l->data);
+ /* only component devices has MD_EVENTS */
+ if (g_udev_device_has_property (device, "MD_EVENTS") &&
+ g_strcmp0 (g_udev_device_get_property (device, "ID_FS_USAGE"), "raid") == 0 &&
+ g_strcmp0 (g_udev_device_get_property (device, "ID_FS_TYPE"), "linux_raid_member") == 0)
+ {
+ num_components_seen += 1;
+ if (md_devices == 0)
+ md_devices = g_udev_device_get_property_as_int (device, "MD_DEVICES");
+ if (md_level == NULL)
+ md_level = g_strdup (g_udev_device_get_property (device, "MD_LEVEL"));
+ }
+ else if (!g_udev_device_has_property (device, "MD_EVENTS"))
+ {
+ array_seen = TRUE;
+ }
+ }
+ g_list_foreach (devices, (GFunc) g_object_unref, NULL);
+ g_list_free (devices);
+ }
+ else
+ {
+ g_warning ("Unsupported target type %s for LUKS", item->target);
+ }
+
+ if (g_strcmp0 (md_level, "raid0") == 0)
+ {
+ if (num_components_seen == md_devices)
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ }
+ else if (g_strcmp0 (md_level, "raid1") == 0)
+ {
+ if (num_components_seen >= 1)
+ ret |= STC_ITEM_STATE_CAN_APPLY_DEGRADED;
+ if (num_components_seen == md_devices)
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ }
+ else if (g_strcmp0 (md_level, "raid5") == 0)
+ {
+ if (md_devices - num_components_seen <= 1)
+ ret |= STC_ITEM_STATE_CAN_APPLY_DEGRADED;
+ if (num_components_seen == md_devices)
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ }
+ else if (g_strcmp0 (md_level, "raid6") == 0)
+ {
+ if (md_devices - num_components_seen <= 2)
+ ret |= STC_ITEM_STATE_CAN_APPLY_DEGRADED;
+ if (num_components_seen == md_devices)
+ ret |= STC_ITEM_STATE_CAN_APPLY;
+ }
+ else if (md_level != NULL)
+ {
+ g_warning ("Unhandled md_level string `%s'", md_level);
+ }
+
+ if (array_seen)
+ ret |= STC_ITEM_STATE_IS_APPLIED;
+
+ g_free (md_level);
+
+ return ret;
+}
+
+void
+_stc_item_update_state (StcItem *item)
+{
+ GUdevClient *gudev_client;
+ _StcMountMonitor *mount_monitor;
+ StcItemState state;
+
+ g_return_if_fail (STC_IS_ITEM (item));
+
+ state = STC_ITEM_STATE_NONE;
+ gudev_client = _stc_monitor_get_gudev_client (item->monitor);
+ mount_monitor = _stc_monitor_get_mount_monitor (item->monitor);
+
+ switch (item->type)
+ {
+ case STC_ITEM_TYPE_FILESYSTEM:
+ state = _stc_item_update_state_filesystem (item, gudev_client, mount_monitor);
+ break;
+
+ case STC_ITEM_TYPE_LUKS:
+ state = _stc_item_update_state_luks (item, gudev_client);
+ break;
+
+ case STC_ITEM_TYPE_MD_RAID:
+ state = _stc_item_update_state_md_raid (item, gudev_client);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ item->state = state;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
const gchar *
_stc_item_get_sort_key (StcItem *item)
{
diff --git a/stc/stcitem.h b/stc/stcitem.h
index c993189..a38a4fe 100644
--- a/stc/stcitem.h
+++ b/stc/stcitem.h
@@ -45,9 +45,7 @@ const gchar* const *stc_item_get_option_keys (StcItem *item);
const gchar *stc_item_get_option (StcItem *item,
const gchar *key);
const gchar* const *stc_item_get_dependencies (StcItem *item);
-
-gboolean stc_item_get_can_apply (StcItem *item);
-gboolean stc_item_get_is_applied (StcItem *item);
+StcItemState stc_item_get_state (StcItem *item);
G_END_DECLS
diff --git a/stc/stcmonitor.c b/stc/stcmonitor.c
index ec6b020..694d899 100644
--- a/stc/stcmonitor.c
+++ b/stc/stcmonitor.c
@@ -29,6 +29,8 @@
#include "stcmarshal.h"
#include "stcprivate.h"
#include "stcitem.h"
+#include "stcmount.h"
+#include "stcmountmonitor.h"
/**
* SECTION:stcmonitor
@@ -49,6 +51,9 @@ struct _StcMonitor
{
GObject parent_instance;
+ GUdevClient *gudev_client;
+ _StcMountMonitor *mount_monitor;
+
GMainContext *context;
StcErrorHandlerFunc error_handler_func;
@@ -97,6 +102,19 @@ static guint signals[LAST_SIGNAL] = { 0 };
static void stc_monitor_update (StcMonitor *monitor);
+static void on_uevent (GUdevClient *client,
+ const gchar *action,
+ GUdevDevice *device,
+ gpointer user_data);
+
+static void on_mount_added (_StcMountMonitor *mount_monitor,
+ _StcMount *mount,
+ gpointer user_data);
+
+static void on_mount_removed (_StcMountMonitor *mount_monitor,
+ _StcMount *mount,
+ gpointer user_data);
+
static void on_conf_file_monitor_changed (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
@@ -129,6 +147,19 @@ stc_monitor_finalize (GObject *object)
{
StcMonitor *monitor = STC_MONITOR (object);
+ g_signal_handlers_disconnect_by_func (monitor->gudev_client,
+ G_CALLBACK (on_uevent),
+ monitor);
+ g_object_unref (monitor->gudev_client);
+
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor,
+ G_CALLBACK (on_mount_added),
+ monitor);
+ g_signal_handlers_disconnect_by_func (monitor->mount_monitor,
+ G_CALLBACK (on_mount_removed),
+ monitor);
+ g_object_unref (monitor->mount_monitor);
+
if (monitor->context != NULL)
g_main_context_unref (monitor->context);
if (monitor->conf_file_monitor != NULL)
@@ -225,6 +256,7 @@ stc_monitor_constructed (GObject *object)
GFile *file;
GError *error;
gchar *path;
+ const gchar *subsystems[] = {"block", NULL};
g_return_if_fail (STC_IS_MONITOR (monitor));
@@ -268,6 +300,22 @@ stc_monitor_constructed (GObject *object)
g_object_unref (file);
g_free (path);
+ monitor->gudev_client = g_udev_client_new (subsystems);
+ g_signal_connect (monitor->gudev_client,
+ "uevent",
+ G_CALLBACK (on_uevent),
+ monitor);
+
+ monitor->mount_monitor = _stc_mount_monitor_new ();
+ g_signal_connect (monitor->mount_monitor,
+ "mount-added",
+ G_CALLBACK (on_mount_added),
+ monitor);
+ g_signal_connect (monitor->mount_monitor,
+ "mount-removed",
+ G_CALLBACK (on_mount_removed),
+ monitor);
+
stc_monitor_update (monitor);
if (G_OBJECT_CLASS (stc_monitor_parent_class)->constructed)
@@ -782,7 +830,8 @@ stc_monitor_load_one_file (StcMonitor *monitor,
/* Use <filename>:<group_number> as sort key to maintain same ordering */
sort_key = g_strdup_printf ("%s:%d", path, n);
- item = _stc_item_new (item_type,
+ item = _stc_item_new (monitor,
+ item_type,
item_id,
target,
nick_name,
@@ -1132,3 +1181,92 @@ stc_monitor_emit_stc_error (StcMonitor *monitor,
stc_monitor_emit_gerror (monitor, filename, line_no, error);
g_error_free (error);
}
+
+GUdevClient *
+_stc_monitor_get_gudev_client (StcMonitor *monitor)
+{
+ g_return_val_if_fail (STC_IS_MONITOR (monitor), NULL);
+ return monitor->gudev_client;
+}
+
+_StcMountMonitor *
+_stc_monitor_get_mount_monitor (StcMonitor *monitor)
+{
+ g_return_val_if_fail (STC_IS_MONITOR (monitor), NULL);
+ return monitor->mount_monitor;
+}
+
+static void
+on_uevent (GUdevClient *client,
+ const gchar *action,
+ GUdevDevice *device,
+ gpointer user_data)
+{
+ StcMonitor *monitor = STC_MONITOR (user_data);
+ GList *l;
+
+ g_print ("in on_uevent() for action %s on %s for %p\n",
+ action,
+ g_udev_device_get_sysfs_path (device),
+ monitor);
+
+ /* we don't really know what configuration items this applies to so
+ * we just update all of them
+ */
+ for (l = monitor->items; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ _stc_item_update_state (item);
+ }
+}
+
+static void
+on_mount_added (_StcMountMonitor *mount_monitor,
+ _StcMount *mount,
+ gpointer user_data)
+{
+ StcMonitor *monitor = STC_MONITOR (user_data);
+ GList *l;
+ dev_t dev;
+
+ dev = _stc_mount_get_dev (mount);
+ g_print ("in on_mount_added() for mount %d:%d at %s\n",
+ major (dev),
+ minor (dev),
+ _stc_mount_get_mount_path (mount));
+
+ /* we don't really know what configuration items this applies to so
+ * we just update all of them
+ */
+ for (l = monitor->items; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ _stc_item_update_state (item);
+ }
+}
+
+static void
+on_mount_removed (_StcMountMonitor *mount_monitor,
+ _StcMount *mount,
+ gpointer user_data)
+{
+ StcMonitor *monitor = STC_MONITOR (user_data);
+ GList *l;
+ dev_t dev;
+
+ dev = _stc_mount_get_dev (mount);
+ g_print ("in on_mount_removed() for mount %d:%d at %s\n",
+ major (dev),
+ minor (dev),
+ _stc_mount_get_mount_path (mount));
+
+ /* we don't really know what configuration items this applies to so
+ * we just update all of them
+ */
+ for (l = monitor->items; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ _stc_item_update_state (item);
+ }
+}
+
diff --git a/stc/stcmount.c b/stc/stcmount.c
new file mode 100644
index 0000000..1187fcd
--- /dev/null
+++ b/stc/stcmount.c
@@ -0,0 +1,159 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
+ *
+ * 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 licence, 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: David Zeuthen <zeuthen@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <mntent.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "stcmount.h"
+#include "stcprivate.h"
+
+/**
+ * _StcMount:
+ *
+ * The #_StcMount structure contains only private data and should
+ * only be accessed using the provided API.
+ */
+struct __StcMount
+{
+ GObject parent_instance;
+
+ gchar *mount_path;
+ dev_t dev;
+};
+
+typedef struct __StcMountClass _StcMountClass;
+
+struct __StcMountClass
+{
+ GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (_StcMount, _stc_mount, G_TYPE_OBJECT);
+
+static void
+_stc_mount_finalize (GObject *object)
+{
+ _StcMount *mount = _STC_MOUNT (object);
+
+ g_free (mount->mount_path);
+
+ if (G_OBJECT_CLASS (_stc_mount_parent_class)->finalize)
+ G_OBJECT_CLASS (_stc_mount_parent_class)->finalize (object);
+}
+
+static void
+_stc_mount_init (_StcMount *mount)
+{
+}
+
+static void
+_stc_mount_class_init (_StcMountClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = _stc_mount_finalize;
+}
+
+_StcMount *
+__stc_mount_new (dev_t dev,
+ const gchar *mount_path)
+{
+ _StcMount *mount;
+
+ mount = _STC_MOUNT (g_object_new (_STC_TYPE_MOUNT, NULL));
+ mount->dev = dev;
+ mount->mount_path = g_strdup (mount_path);
+
+ return mount;
+}
+
+/**
+ * _stc_mount_get_mount_path:
+ * @mount: A #_StcMount
+ *
+ * Gets the mount path for @mount.
+ *
+ * Returns: A string owned by @mount. Do not free.
+ */
+const gchar *
+_stc_mount_get_mount_path (_StcMount *mount)
+{
+ g_return_val_if_fail (_STC_IS_MOUNT (mount), NULL);
+ return mount->mount_path;
+}
+
+/**
+ * _stc_mount_get_dev:
+ * @mount: A #_StcMount.
+ *
+ * Gets the device number for @mount.
+ *
+ * Returns: A #dev_t.
+ */
+dev_t
+_stc_mount_get_dev (_StcMount *mount)
+{
+ g_return_val_if_fail (_STC_IS_MOUNT (mount), 0);
+ return mount->dev;
+}
+
+/**
+ * _stc_mount_compare:
+ * @mount: A #_StcMount
+ * @other_mount: Another #_StcMount.
+ *
+ * Comparison function for comparing two #_StcMount objects.
+ *
+ * Returns: Negative value if @mount < @other_mount; zero if @mount = @other_mount; positive value if @mount > @other_mount.
+ */
+gint
+_stc_mount_compare (_StcMount *mount,
+ _StcMount *other_mount)
+{
+ gint ret;
+
+ g_return_val_if_fail (_STC_IS_MOUNT (mount), 0);
+ g_return_val_if_fail (_STC_IS_MOUNT (other_mount), 0);
+
+ ret = g_strcmp0 (other_mount->mount_path, mount->mount_path);
+ if (ret != 0)
+ goto out;
+
+ ret = (mount->dev - other_mount->dev);
+
+ out:
+ return ret;
+}
diff --git a/stc/stcmount.h b/stc/stcmount.h
new file mode 100644
index 0000000..ce8a0c3
--- /dev/null
+++ b/stc/stcmount.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
+ *
+ * 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 licence, 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: David Zeuthen <zeuthen@gmail.com>
+ */
+
+#if !defined (STC_COMPILATION)
+#error "stcmountmonitor.h is a private header file."
+#endif
+
+#ifndef ___STC_MOUNT_H__
+#define ___STC_MOUNT_H__
+
+#include "stcprivate.h"
+
+G_BEGIN_DECLS
+
+#define _STC_TYPE_MOUNT (_stc_mount_get_type ())
+#define _STC_MOUNT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), _STC_TYPE_MOUNT, _StcMount))
+#define _STC_IS_MOUNT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), _STC_TYPE_MOUNT))
+
+GType _stc_mount_get_type (void) G_GNUC_CONST;
+const gchar *_stc_mount_get_mount_path (_StcMount *mount);
+dev_t _stc_mount_get_dev (_StcMount *mount);
+gint _stc_mount_compare (_StcMount *mount,
+ _StcMount *other_mount);
+
+G_END_DECLS
+
+#endif /* ___STC_MOUNT_H__ */
diff --git a/stc/stcmountmonitor.c b/stc/stcmountmonitor.c
new file mode 100644
index 0000000..22f2bce
--- /dev/null
+++ b/stc/stcmountmonitor.c
@@ -0,0 +1,502 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
+ *
+ * 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 licence, 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: David Zeuthen <zeuthen@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <mntent.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "stcmountmonitor.h"
+#include "stcmount.h"
+
+/**
+ * _StcMountMonitor:
+ *
+ * The #_StcMountMonitor structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct __StcMountMonitor
+{
+ GObject parent_instance;
+
+ GIOChannel *mounts_channel;
+ GSource *watch_source;
+ gboolean have_data;
+ GList *mounts;
+};
+
+typedef struct __StcMountMonitorClass _StcMountMonitorClass;
+
+struct __StcMountMonitorClass
+{
+ GObjectClass parent_class;
+
+ void (*mount_added) (_StcMountMonitor *monitor,
+ _StcMount *mount);
+ void (*mount_removed) (_StcMountMonitor *monitor,
+ _StcMount *mount);
+};
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+enum
+ {
+ MOUNT_ADDED_SIGNAL,
+ MOUNT_REMOVED_SIGNAL,
+ LAST_SIGNAL,
+ };
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (_StcMountMonitor, _stc_mount_monitor, G_TYPE_OBJECT)
+
+static void _stc_mount_monitor_ensure (_StcMountMonitor *monitor);
+static void _stc_mount_monitor_invalidate (_StcMountMonitor *monitor);
+static void _stc_mount_monitor_constructed (GObject *object);
+
+static void
+_stc_mount_monitor_finalize (GObject *object)
+{
+ _StcMountMonitor *monitor = _STC_MOUNT_MONITOR (object);
+
+ if (monitor->mounts_channel != NULL)
+ g_io_channel_unref (monitor->mounts_channel);
+
+ if (monitor->watch_source != NULL)
+ g_source_destroy (monitor->watch_source);
+
+ g_list_foreach (monitor->mounts, (GFunc) g_object_unref, NULL);
+ g_list_free (monitor->mounts);
+
+ if (G_OBJECT_CLASS (_stc_mount_monitor_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (_stc_mount_monitor_parent_class)->finalize (object);
+}
+
+static void
+_stc_mount_monitor_init (_StcMountMonitor *monitor)
+{
+ monitor->mounts = NULL;
+}
+
+static void
+_stc_mount_monitor_class_init (_StcMountMonitorClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->finalize = _stc_mount_monitor_finalize;
+ gobject_class->constructed = _stc_mount_monitor_constructed;
+
+ /**
+ * _StcMountMonitor::mount-added
+ * @monitor: A #_StcMountMonitor.
+ * @mount: The #_StcMount that was added.
+ *
+ * Emitted when a mount is added.
+ *
+ * This signal is emitted in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * that @monitor was created in.
+ */
+ signals[MOUNT_ADDED_SIGNAL] = g_signal_new ("mount-added",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (_StcMountMonitorClass, mount_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ _STC_TYPE_MOUNT);
+
+ /**
+ * _StcMountMonitor::mount-removed
+ * @monitor: A #_StcMountMonitor.
+ * @mount: The #_StcMount that was removed.
+ *
+ * Emitted when a mount is removed.
+ *
+ * This signal is emitted in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * that @monitor was created in.
+ */
+ signals[MOUNT_REMOVED_SIGNAL] = g_signal_new ("mount-removed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+ G_STRUCT_OFFSET (_StcMountMonitorClass, mount_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ _STC_TYPE_MOUNT);
+}
+
+static void
+diff_sorted_lists (GList *list1,
+ GList *list2,
+ GCompareFunc compare,
+ GList **added,
+ GList **removed)
+{
+ int order;
+
+ *added = *removed = NULL;
+
+ while (list1 != NULL && list2 != NULL)
+ {
+ order = (*compare) (list1->data, list2->data);
+ if (order < 0)
+ {
+ *removed = g_list_prepend (*removed, list1->data);
+ list1 = list1->next;
+ }
+ else if (order > 0)
+ {
+ *added = g_list_prepend (*added, list2->data);
+ list2 = list2->next;
+ }
+ else
+ { /* same item */
+ list1 = list1->next;
+ list2 = list2->next;
+ }
+ }
+
+ while (list1 != NULL)
+ {
+ *removed = g_list_prepend (*removed, list1->data);
+ list1 = list1->next;
+ }
+ while (list2 != NULL)
+ {
+ *added = g_list_prepend (*added, list2->data);
+ list2 = list2->next;
+ }
+}
+
+static gboolean
+mounts_changed_event (GIOChannel *channel,
+ GIOCondition cond,
+ gpointer user_data)
+{
+ _StcMountMonitor *monitor = _STC_MOUNT_MONITOR (user_data);
+ GList *old_mounts;
+ GList *cur_mounts;
+ GList *added;
+ GList *removed;
+ GList *l;
+
+ if (cond & ~G_IO_ERR)
+ goto out;
+
+ _stc_mount_monitor_ensure (monitor);
+
+ old_mounts = g_list_copy (monitor->mounts);
+ g_list_foreach (old_mounts, (GFunc) g_object_ref, NULL);
+
+ _stc_mount_monitor_invalidate (monitor);
+ _stc_mount_monitor_ensure (monitor);
+
+ cur_mounts = g_list_copy (monitor->mounts);
+
+ old_mounts = g_list_sort (old_mounts, (GCompareFunc) _stc_mount_compare);
+ cur_mounts = g_list_sort (cur_mounts, (GCompareFunc) _stc_mount_compare);
+ diff_sorted_lists (old_mounts, cur_mounts, (GCompareFunc) _stc_mount_compare, &added, &removed);
+
+ for (l = removed; l != NULL; l = l->next)
+ {
+ _StcMount *mount = _STC_MOUNT (l->data);
+ g_signal_emit (monitor, signals[MOUNT_REMOVED_SIGNAL], 0, mount);
+ }
+
+ for (l = added; l != NULL; l = l->next)
+ {
+ _StcMount *mount = _STC_MOUNT (l->data);
+ g_signal_emit (monitor, signals[MOUNT_ADDED_SIGNAL], 0, mount);
+ }
+
+ g_list_foreach (old_mounts, (GFunc) g_object_unref, NULL);
+ g_list_free (old_mounts);
+ g_list_free (cur_mounts);
+ g_list_free (removed);
+ g_list_free (added);
+
+ out:
+ return TRUE;
+}
+
+static void
+_stc_mount_monitor_constructed (GObject *object)
+{
+ _StcMountMonitor *monitor = _STC_MOUNT_MONITOR (object);
+ GError *error;
+
+ error = NULL;
+ monitor->mounts_channel = g_io_channel_new_file ("/proc/self/mountinfo", "r", &error);
+ if (monitor->mounts_channel != NULL)
+ {
+ monitor->watch_source = g_io_create_watch (monitor->mounts_channel, G_IO_ERR);
+ g_source_set_callback (monitor->watch_source, (GSourceFunc) mounts_changed_event, monitor, NULL);
+ g_source_attach (monitor->watch_source, g_main_context_get_thread_default ());
+ g_source_unref (monitor->watch_source);
+ }
+ else
+ {
+ g_error ("No /proc/self/mountinfo file: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (G_OBJECT_CLASS (_stc_mount_monitor_parent_class)->constructed != NULL)
+ (*G_OBJECT_CLASS (_stc_mount_monitor_parent_class)->constructed) (object);
+}
+
+/**
+ * _stc_mount_monitor_new:
+ *
+ * Creates a new #_StcMountMonitor object.
+ *
+ * Signals are emitted in the <link
+ * linkend="g-main-context-push-thread-default">thread-default main
+ * loop</link> that this function is called from.
+ *
+ * Returns: A #_StcMountMonitor. Free with g_object_unref().
+ */
+_StcMountMonitor *
+_stc_mount_monitor_new (void)
+{
+ return _STC_MOUNT_MONITOR (g_object_new (_STC_TYPE_MOUNT_MONITOR, NULL));
+}
+
+static void
+_stc_mount_monitor_invalidate (_StcMountMonitor *monitor)
+{
+ monitor->have_data = FALSE;
+
+ g_list_foreach (monitor->mounts, (GFunc) g_object_unref, NULL);
+ g_list_free (monitor->mounts);
+ monitor->mounts = NULL;
+}
+
+static gboolean
+have_mount (_StcMountMonitor *monitor,
+ dev_t dev,
+ const gchar *mount_point)
+{
+ GList *l;
+ gboolean ret;
+
+ ret = FALSE;
+
+ for (l = monitor->mounts; l != NULL; l = l->next)
+ {
+ _StcMount *mount = _STC_MOUNT (l->data);
+ if (_stc_mount_get_dev (mount) == dev &&
+ g_strcmp0 (_stc_mount_get_mount_path (mount), mount_point) == 0)
+ {
+ ret = TRUE;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void
+_stc_mount_monitor_ensure (_StcMountMonitor *monitor)
+{
+ gchar *contents;
+ gchar **lines;
+ GError *error;
+ guint n;
+
+ contents = NULL;
+ lines = NULL;
+
+ if (monitor->have_data)
+ goto out;
+
+ error = NULL;
+ if (!g_file_get_contents ("/proc/self/mountinfo", &contents, NULL, &error))
+ {
+ g_warning ("Error reading /proc/self/mountinfo: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* See Documentation/filesystems/proc.txt for the format of /proc/self/mountinfo
+ *
+ * Note that things like space are encoded as \020.
+ */
+
+ lines = g_strsplit (contents, "\n", 0);
+ for (n = 0; lines[n] != NULL; n++)
+ {
+ guint mount_id;
+ guint parent_id;
+ guint major, minor;
+ gchar encoded_root[PATH_MAX];
+ gchar encoded_mount_point[PATH_MAX];
+ gchar *mount_point;
+ dev_t dev;
+
+ if (strlen (lines[n]) == 0)
+ continue;
+
+ if (sscanf (lines[n],
+ "%d %d %d:%d %s %s",
+ &mount_id,
+ &parent_id,
+ &major,
+ &minor,
+ encoded_root,
+ encoded_mount_point) != 6)
+ {
+ g_warning ("Error parsing line '%s'", lines[n]);
+ continue;
+ }
+
+ /* ignore mounts where only a subtree of a filesystem is mounted */
+ if (g_strcmp0 (encoded_root, "/") != 0)
+ continue;
+
+ /* Temporary work-around for btrfs, see
+ *
+ * https://bugzilla.redhat.com/show_bug.cgi?id=495152#c31
+ * http://article.gmane.org/gmane.comp.file-systems.btrfs/2851
+ *
+ * for details.
+ */
+ if (major == 0)
+ {
+ const gchar *sep;
+ sep = strstr (lines[n], " - ");
+ if (sep != NULL)
+ {
+ gchar fstype[PATH_MAX];
+ gchar mount_source[PATH_MAX];
+ struct stat statbuf;
+
+ if (sscanf (sep + 3, "%s %s", fstype, mount_source) != 2)
+ {
+ g_warning ("Error parsing things past - for '%s'", lines[n]);
+ continue;
+ }
+
+ if (g_strcmp0 (fstype, "btrfs") != 0)
+ continue;
+
+ if (!g_str_has_prefix (mount_source, "/dev/"))
+ continue;
+
+ if (stat (mount_source, &statbuf) != 0)
+ {
+ g_warning ("Error statting %s: %m", mount_source);
+ continue;
+ }
+
+ if (!S_ISBLK (statbuf.st_mode))
+ {
+ g_warning ("%s is not a block device", mount_source);
+ continue;
+ }
+
+ dev = statbuf.st_rdev;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else
+ {
+ dev = makedev (major, minor);
+ }
+
+ mount_point = g_strcompress (encoded_mount_point);
+
+ /* TODO: we can probably use a hash table or something if this turns out to be slow */
+ if (!have_mount (monitor, dev, mount_point))
+ {
+ _StcMount *mount;
+ mount = __stc_mount_new (dev, mount_point);
+ monitor->mounts = g_list_prepend (monitor->mounts, mount);
+ //g_debug ("SUP ADDING %d:%d on %s", major, minor, mount_point);
+ }
+
+ g_free (mount_point);
+ }
+
+ monitor->have_data = TRUE;
+
+ out:
+ g_free (contents);
+ g_strfreev (lines);
+}
+
+/**
+ * _stc_mount_monitor_get_mounts_for_dev:
+ * @monitor: A #_StcMountMonitor.
+ * @dev: A #dev_t device number.
+ *
+ * Gets all #_StcMount objects for @dev.
+ *
+ * Returns: A #GList of #_StcMount objects. The returned list must
+ * be freed with g_list_free() after each element has been freed with
+ * g_object_unref().
+ */
+GList *
+_stc_mount_monitor_get_mounts_for_dev (_StcMountMonitor *monitor,
+ dev_t dev)
+{
+ GList *ret;
+ GList *l;
+
+ ret = NULL;
+
+ _stc_mount_monitor_ensure (monitor);
+
+ for (l = monitor->mounts; l != NULL; l = l->next)
+ {
+ _StcMount *mount = _STC_MOUNT (l->data);
+
+ if (_stc_mount_get_dev (mount) == dev)
+ {
+ ret = g_list_prepend (ret, g_object_ref (mount));
+ }
+ }
+
+ /* Sort the list to ensure that shortest mount paths appear first */
+ ret = g_list_sort (ret, (GCompareFunc) _stc_mount_compare);
+
+ return ret;
+}
diff --git a/stc/stcmountmonitor.h b/stc/stcmountmonitor.h
new file mode 100644
index 0000000..2b6905c
--- /dev/null
+++ b/stc/stcmountmonitor.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
+ *
+ * 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 licence, 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: David Zeuthen <zeuthen@gmail.com>
+ */
+
+
+#if !defined (STC_COMPILATION)
+#error "stcmountmonitor.h is a private header file."
+#endif
+
+#ifndef ___STC_MOUNT_MONITOR_H__
+#define ___STC_MOUNT_MONITOR_H__
+
+#include "stcprivate.h"
+
+G_BEGIN_DECLS
+
+#define _STC_TYPE_MOUNT_MONITOR (_stc_mount_monitor_get_type ())
+#define _STC_MOUNT_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), _STC_TYPE_MOUNT_MONITOR, _StcMountMonitor))
+#define _STC_IS_MOUNT_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), _STC_TYPE_MOUNT_MONITOR))
+
+GType _stc_mount_monitor_get_type (void) G_GNUC_CONST;
+_StcMountMonitor *_stc_mount_monitor_new (void);
+GList *_stc_mount_monitor_get_mounts_for_dev (_StcMountMonitor *monitor,
+ dev_t dev);
+
+G_END_DECLS
+
+#endif /* ___STC_MOUNT_MONITOR_H__ */
diff --git a/stc/stcprivate.h b/stc/stcprivate.h
index 0e100ac..f9e76e0 100644
--- a/stc/stcprivate.h
+++ b/stc/stcprivate.h
@@ -29,10 +29,22 @@
#define __STC_PRIVATE_H__
#include <stc/stctypes.h>
+#include <gudev/gudev.h>
G_BEGIN_DECLS
-StcItem *_stc_item_new (StcItemType type,
+struct __StcMount;
+typedef struct __StcMount _StcMount;
+
+struct __StcMountMonitor;
+typedef struct __StcMountMonitor _StcMountMonitor;
+
+_StcMount * __stc_mount_new (dev_t dev,
+ const gchar *mount_path);
+
+
+StcItem *_stc_item_new (StcMonitor *monitor,
+ StcItemType type,
const gchar *id,
const gchar *target,
const gchar *nick_name,
@@ -47,6 +59,11 @@ const gchar *_stc_item_get_path (StcItem *item);
gint _stc_item_get_line_no (StcItem *item);
gboolean _stc_item_update (StcItem *item, StcItem *other);
+void _stc_item_update_state (StcItem *item);
+
+GUdevClient *_stc_monitor_get_gudev_client (StcMonitor *monitor);
+
+_StcMountMonitor *_stc_monitor_get_mount_monitor (StcMonitor *monitor);
G_END_DECLS
diff --git a/stc/test.c b/stc/test.c
index 712e81d..e541667 100644
--- a/stc/test.c
+++ b/stc/test.c
@@ -199,6 +199,8 @@ test_stc_semi_valid_conf (void)
StcItem *item = STC_ITEM (l->data);
item_append_str (item, str2);
}
+ g_list_foreach (items, (GFunc) g_object_unref, NULL);
+ g_list_free (items);
g_assert_cmpstr (str2->str, ==,
"id `in_pri_10'\n"
"target `Device=/dev/sda'\n"
@@ -527,6 +529,11 @@ main (int argc,
g_test_add_func ("/stc/semi_valid_conf", test_stc_semi_valid_conf);
g_test_add_func ("/stc/monitoring", test_stc_monitoring);
+ /* it would be nice if we could easily run tests to check the
+ * returned StcItemState value. That would require returning fake
+ * udev and /proc/self/mountinfo data...
+ */
+
ret = g_test_run();
g_main_loop_unref (loop);