diff options
author | David Zeuthen <davidz@redhat.com> | 2010-10-05 15:10:18 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2010-10-05 15:10:18 -0400 |
commit | 273829916f9969c81d53e5966fa7e671327c7e73 (patch) | |
tree | 8b3d755e8656d12e622cb181ff975c9a0c90e213 | |
parent | 9a6cf5e2f415ddd0d61d380032e34f9cb96e3b1a (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.txt | 8 | ||||
-rw-r--r-- | stc/Makefile.am | 4 | ||||
-rw-r--r-- | stc/stc.c | 57 | ||||
-rw-r--r-- | stc/stcenums.h | 33 | ||||
-rw-r--r-- | stc/stcitem.c | 429 | ||||
-rw-r--r-- | stc/stcitem.h | 4 | ||||
-rw-r--r-- | stc/stcmonitor.c | 140 | ||||
-rw-r--r-- | stc/stcmount.c | 159 | ||||
-rw-r--r-- | stc/stcmount.h | 46 | ||||
-rw-r--r-- | stc/stcmountmonitor.c | 502 | ||||
-rw-r--r-- | stc/stcmountmonitor.h | 46 | ||||
-rw-r--r-- | stc/stcprivate.h | 19 | ||||
-rw-r--r-- | stc/test.c | 7 |
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) # ---------------------------------------------------------------------------------------------------- @@ -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 @@ -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); |