summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2010-10-04 12:00:38 -0400
committerDavid Zeuthen <davidz@redhat.com>2010-10-04 12:00:38 -0400
commit7620487167d851324cadce8775c4e70baa46d3f3 (patch)
tree05a7d6de0efe6ef9357bea77c2590cdf6ac9a247
parent2693adb92a660c78f5935cc79b6e9d5c0308dc51 (diff)
Monitor configuration files and emit added, remove and changed signals
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r--stc/stcitem.c110
-rw-r--r--stc/stcmonitor.c285
-rw-r--r--stc/stcprivate.h7
-rw-r--r--stc/test.c290
-rw-r--r--stc/testdata/semi_valid_conf/stc.conf.d/10.conf (renamed from stc/testdata/semi_valid_conf/stc.d/10.conf)0
-rw-r--r--stc/testdata/semi_valid_conf/stc.conf.d/50.conf (renamed from stc/testdata/semi_valid_conf/stc.d/50.conf)0
-rw-r--r--stc/testdata/semi_valid_conf/stc.conf.d/50.conf.wrong-extension (renamed from stc/testdata/semi_valid_conf/stc.d/50.conf.wrong-extension)0
-rw-r--r--stc/testdata/semi_valid_conf/stc.conf.d/90.conf (renamed from stc/testdata/semi_valid_conf/stc.d/90.conf)0
-rw-r--r--stc/testdata/semi_valid_conf/stc.conf.d/91.conf (renamed from stc/testdata/semi_valid_conf/stc.d/91.conf)0
9 files changed, 645 insertions, 47 deletions
diff --git a/stc/stcitem.c b/stc/stcitem.c
index f8fa01e..a736429 100644
--- a/stc/stcitem.c
+++ b/stc/stcitem.c
@@ -56,6 +56,9 @@ struct _StcItem
/* memoized */
gchar **option_keys;
+
+ /* private */
+ gchar *sort_key;
};
typedef struct _StcItemClass StcItemClass;
@@ -63,8 +66,17 @@ typedef struct _StcItemClass StcItemClass;
struct _StcItemClass
{
GObjectClass parent_class;
+ void (*changed) (StcItem *item);
+};
+
+enum
+{
+ CHANGED_SIGNAL,
+ LAST_SIGNAL
};
+static guint signals[LAST_SIGNAL] = { 0 };
+
G_DEFINE_TYPE (StcItem, stc_item, G_TYPE_OBJECT);
static void
@@ -78,6 +90,8 @@ stc_item_finalize (GObject *object)
g_hash_table_unref (item->options);
g_free (item->option_keys);
+ g_free (item->sort_key);
+
if (G_OBJECT_CLASS (stc_item_parent_class)->finalize)
G_OBJECT_CLASS (stc_item_parent_class)->finalize (object);
}
@@ -94,6 +108,22 @@ stc_item_class_init (StcItemClass *klass)
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = stc_item_finalize;
+
+ /**
+ * StcItem::changed:
+ * @item: The item that changed.
+ *
+ * Emitted when an item changes.
+ */
+ signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StcItemClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 0);
}
/**
@@ -160,7 +190,8 @@ _stc_item_new (StcItemType type,
const gchar *id,
const gchar *target,
const gchar *nick_name,
- GHashTable *options)
+ GHashTable *options,
+ const gchar *sort_key)
{
StcItem *item;
@@ -171,10 +202,87 @@ _stc_item_new (StcItemType type,
item->nick_name = g_strdup (nick_name);
item->type = type;
item->options = g_hash_table_ref (options);
+ item->sort_key = g_strdup (sort_key);
return item;
}
+static gboolean
+hash_tables_equal (GHashTable *a, GHashTable *b)
+{
+ gboolean ret;
+ GHashTableIter iter;
+ const gchar *key;
+ const gchar *value;
+ const gchar *other_value;
+
+ ret = FALSE;
+
+ g_hash_table_iter_init (&iter, a);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
+ {
+ other_value = g_hash_table_lookup (b, key);
+ if (g_strcmp0 (value, other_value) != 0)
+ goto out;
+ }
+
+ g_hash_table_iter_init (&iter, b);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value))
+ {
+ other_value = g_hash_table_lookup (a, key);
+ if (g_strcmp0 (value, other_value) != 0)
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+gboolean
+_stc_item_update (StcItem *item, StcItem *other)
+{
+ gboolean changed;
+
+ g_return_val_if_fail (STC_IS_ITEM (item), FALSE);
+ g_return_val_if_fail (STC_IS_ITEM (other), FALSE);
+
+ changed = FALSE;
+
+ g_assert_cmpstr (item->id, ==, other->id);
+
+ if (g_strcmp0 (item->target, other->target) != 0)
+ {
+ g_free (item->target);
+ item->target = g_strdup (other->target);
+ changed = TRUE;
+ }
+
+ if (g_strcmp0 (item->nick_name, other->nick_name) != 0)
+ {
+ g_free (item->nick_name);
+ item->nick_name = g_strdup (other->nick_name);
+ changed = TRUE;
+ }
+
+ if (!hash_tables_equal (item->options, other->options))
+ {
+ g_hash_table_unref (item->options);
+ item->options = g_hash_table_ref (other->options);
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
+const gchar *
+_stc_item_get_sort_key (StcItem *item)
+{
+ g_return_val_if_fail (STC_IS_ITEM (item), NULL);
+ return item->sort_key;
+}
+
const gchar* const *
stc_item_get_option_keys (StcItem *item)
{
diff --git a/stc/stcmonitor.c b/stc/stcmonitor.c
index 8f0cc70..8fc5bf6 100644
--- a/stc/stcmonitor.c
+++ b/stc/stcmonitor.c
@@ -58,6 +58,9 @@ struct _StcMonitor
GList *items;
GHashTable *map_id_to_item;
+
+ GFileMonitor *conf_file_monitor;
+ GFileMonitor *conf_dir_monitor;
};
typedef struct _StcMonitorClass StcMonitorClass;
@@ -66,10 +69,12 @@ struct _StcMonitorClass
{
GObjectClass parent_class;
- void (*error) (StcMonitor *monitor,
- const gchar *filename,
- gint line_no,
- GError **error);
+ void (*item_added) (StcMonitor *monitor,
+ StcItem *item);
+ void (*item_removed) (StcMonitor *monitor,
+ StcItem *item);
+ void (*item_changed) (StcMonitor *monitor,
+ StcItem *item);
};
enum
@@ -80,7 +85,29 @@ enum
PROP_ERROR_HANDLER_USER_DATA
};
-static GList *stc_monitor_load_all_files (StcMonitor *monitor);
+enum
+{
+ ITEM_ADDED_SIGNAL,
+ ITEM_REMOVED_SIGNAL,
+ ITEM_CHANGED_SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void stc_monitor_update (StcMonitor *monitor);
+
+static void on_conf_file_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data);
+
+static void on_conf_dir_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data);
static void
stc_monitor_emit_gerror (StcMonitor *monitor,
@@ -104,6 +131,16 @@ stc_monitor_finalize (GObject *object)
if (monitor->context != NULL)
g_main_context_unref (monitor->context);
+ if (monitor->conf_file_monitor != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (monitor->conf_file_monitor, G_CALLBACK (on_conf_file_monitor_changed), monitor);
+ g_object_unref (monitor->conf_file_monitor);
+ }
+ if (monitor->conf_dir_monitor != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (monitor->conf_dir_monitor, G_CALLBACK (on_conf_dir_monitor_changed), monitor);
+ g_object_unref (monitor->conf_dir_monitor);
+ }
g_free (monitor->config_dir);
g_list_foreach (monitor->items, (GFunc) g_object_unref, NULL);
g_list_free (monitor->items);
@@ -181,27 +218,57 @@ stc_monitor_init (StcMonitor *monitor)
monitor->map_id_to_item = g_hash_table_new (g_str_hash, g_str_equal);
}
-
static void
stc_monitor_constructed (GObject *object)
{
StcMonitor *monitor = STC_MONITOR (object);
- GList *loaded_items;
- GList *l;
+ GFile *file;
+ GError *error;
+ gchar *path;
g_return_if_fail (STC_IS_MONITOR (monitor));
- loaded_items = stc_monitor_load_all_files (monitor);
-
- for (l = loaded_items; l != NULL; l = l->next)
+ path = g_strdup_printf ("%s/stc.conf", monitor->config_dir);
+ file = g_file_new_for_path (path);
+ error = NULL;
+ monitor->conf_file_monitor = g_file_monitor_file (file, G_FILE_MONITOR_SEND_MOVED, NULL, &error);
+ g_file_monitor_set_rate_limit (monitor->conf_file_monitor, 50);
+ if (monitor->conf_file_monitor == NULL)
{
- StcItem *item = STC_ITEM (l->data);
- /* TODO: check if we have item already - if so, update existing object */
- monitor->items = g_list_append (monitor->items, g_object_ref (item));
- g_hash_table_insert (monitor->map_id_to_item, (gpointer) stc_item_get_id (item), item);
+ stc_monitor_emit_gerror (monitor,
+ path,
+ -1,
+ error);
+ g_error_free (error);
}
- g_list_foreach (loaded_items, (GFunc) g_object_unref, NULL);
- g_list_free (loaded_items);
+ g_signal_connect (monitor->conf_file_monitor,
+ "changed",
+ G_CALLBACK (on_conf_file_monitor_changed),
+ monitor);
+ g_object_unref (file);
+ g_free (path);
+
+ path = g_strdup_printf ("%s/stc.conf.d", monitor->config_dir);
+ file = g_file_new_for_path (path);
+ error = NULL;
+ monitor->conf_dir_monitor = g_file_monitor_directory (file, G_FILE_MONITOR_SEND_MOVED, NULL, &error);
+ g_file_monitor_set_rate_limit (monitor->conf_dir_monitor, 50);
+ if (monitor->conf_dir_monitor == NULL)
+ {
+ stc_monitor_emit_gerror (monitor,
+ path,
+ -1,
+ error);
+ g_error_free (error);
+ }
+ g_signal_connect (monitor->conf_dir_monitor,
+ "changed",
+ G_CALLBACK (on_conf_dir_monitor_changed),
+ monitor);
+ g_object_unref (file);
+ g_free (path);
+
+ stc_monitor_update (monitor);
if (G_OBJECT_CLASS (stc_monitor_parent_class)->constructed)
G_OBJECT_CLASS (stc_monitor_parent_class)->constructed (object);
@@ -263,6 +330,61 @@ stc_monitor_class_init (StcMonitorClass *klass)
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+
+
+ /**
+ * StcMonitor::item-added:
+ * @monitor: The #StcMonitor emitting the signal.
+ * @item: The item that was added.
+ *
+ * Emitted when an item is added.
+ */
+ signals[ITEM_ADDED_SIGNAL] = g_signal_new ("item-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StcMonitorClass, item_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ STC_TYPE_ITEM);
+
+ /**
+ * StcMonitor::item-removed:
+ * @monitor: The #StcMonitor emitting the signal.
+ * @item: The item that was removed.
+ *
+ * Emitted when an item is removed.
+ */
+ signals[ITEM_REMOVED_SIGNAL] = g_signal_new ("item-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StcMonitorClass, item_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ STC_TYPE_ITEM);
+
+ /**
+ * StcMonitor::item-changed:
+ * @monitor: The #StcMonitor emitting the signal.
+ * @item: The item that changed.
+ *
+ * Emitted when an item changes.
+ */
+ signals[ITEM_CHANGED_SIGNAL] = g_signal_new ("item-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StcMonitorClass, item_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1,
+ STC_TYPE_ITEM);
}
/**
@@ -422,6 +544,7 @@ stc_monitor_load_one_file (StcMonitor *monitor,
const gchar *item_id;
gchar *target;
gchar *nick_name;
+ gchar *sort_key;
gchar *s;
gchar **options_strv;
StcItem *item;
@@ -430,6 +553,7 @@ stc_monitor_load_one_file (StcMonitor *monitor,
tokens = NULL;
target = NULL;
nick_name = NULL;
+ sort_key = NULL;
options = NULL;
tokens = g_strsplit (group, " ", 0);
@@ -534,13 +658,15 @@ stc_monitor_load_one_file (StcMonitor *monitor,
/* NickName is optional */
nick_name = g_key_file_get_string (key_file, group, "NickName", NULL);
- item = _stc_item_new (item_type, item_id, target, nick_name, options);
+ sort_key = g_strdup_printf ("%s:%s", path, item_id);
+ item = _stc_item_new (item_type, item_id, target, nick_name, options, sort_key);
ret = g_list_prepend (ret, item);
process_next_item:
g_strfreev (tokens);
g_free (target);
g_free (nick_name);
+ g_free (sort_key);
if (options != NULL)
g_hash_table_unref (options);
}
@@ -619,7 +745,7 @@ stc_monitor_load_all_files (StcMonitor *monitor)
paths = g_ptr_array_new ();
g_ptr_array_add (paths, g_strdup_printf ("%s/stc.conf", monitor->config_dir));
- dir_path = g_strdup_printf ("%s/stc.d", monitor->config_dir);
+ dir_path = g_strdup_printf ("%s/stc.conf.d", monitor->config_dir);
error = NULL;
dir = g_dir_open (dir_path, 0, &error);
if (error != NULL)
@@ -683,6 +809,127 @@ stc_monitor_load_all_files (StcMonitor *monitor)
return ret;
}
+static gint
+_item_cmp_func (gconstpointer a,
+ gconstpointer b)
+{
+ StcItem *ia;
+ StcItem *ib;
+
+ ia = STC_ITEM ((gpointer) a);
+ ib = STC_ITEM ((gpointer) b);
+
+ return g_strcmp0 (_stc_item_get_sort_key (ia), _stc_item_get_sort_key (ib));
+}
+
+static void
+stc_monitor_update (StcMonitor *monitor)
+{
+ GList *loaded_items;
+ GList *l;
+ GList *ll;
+ GList *items_changed;
+ GList *items_added;
+ GList *items_removed;
+ GHashTable *loaded_items_ids;
+
+ items_changed = NULL;
+ items_added = NULL;
+ items_removed = NULL;
+
+ loaded_items_ids = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* add/change items */
+ loaded_items = stc_monitor_load_all_files (monitor);
+ for (l = loaded_items; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ StcItem *existing_item;
+
+ existing_item = g_hash_table_lookup (monitor->map_id_to_item, stc_item_get_id (item));
+ if (existing_item != NULL)
+ {
+ if (_stc_item_update (existing_item, item))
+ {
+ items_changed = g_list_prepend (items_changed, g_object_ref (existing_item));
+ }
+ }
+ else
+ {
+ monitor->items = g_list_append (monitor->items, g_object_ref (item));
+ g_hash_table_insert (monitor->map_id_to_item, (gpointer) stc_item_get_id (item), item);
+ items_added = g_list_prepend (items_added, g_object_ref (item));
+ }
+
+ g_hash_table_insert (loaded_items_ids, (gpointer) stc_item_get_id (item), GUINT_TO_POINTER (1));
+ }
+ /* remove items that wasn't loaded this time around */
+ for (l = monitor->items; l != NULL; l = ll)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ ll = l->next;
+ if (g_hash_table_lookup (loaded_items_ids, stc_item_get_id (item)) == NULL)
+ {
+ /* item was removed */
+ items_removed = g_list_prepend (items_removed, g_object_ref (item));
+ monitor->items = g_list_delete_link (monitor->items, l);
+ g_object_unref (item);
+ }
+ }
+ g_hash_table_unref (loaded_items_ids);
+ g_list_foreach (loaded_items, (GFunc) g_object_unref, NULL);
+ g_list_free (loaded_items);
+
+ /* make the items appear in the order they were declared */
+ monitor->items = g_list_sort (monitor->items, _item_cmp_func);
+
+ /* emit signals only when our monitor object has been completely updated */
+ for (l = items_removed; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ g_signal_emit (monitor, signals[ITEM_REMOVED_SIGNAL], 0, item);
+ g_object_unref (item);
+ }
+ g_list_free (items_removed);
+ for (l = items_added; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ g_signal_emit (monitor, signals[ITEM_ADDED_SIGNAL], 0, item);
+ g_object_unref (item);
+ }
+ g_list_free (items_added);
+ for (l = items_changed; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ g_signal_emit (monitor, signals[ITEM_CHANGED_SIGNAL], 0, item);
+ g_signal_emit_by_name (item, "changed");
+ g_object_unref (item);
+ }
+ g_list_free (items_changed);
+}
+
+static void
+on_conf_file_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ /* TODO: this is a bit expensive since all config files are reloaded */
+ stc_monitor_update (STC_MONITOR (user_data));
+}
+
+static void
+on_conf_dir_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ /* TODO: this is a bit expensive since all config files are reloaded */
+ stc_monitor_update (STC_MONITOR (user_data));
+}
+
static void
stc_monitor_emit_gerror (StcMonitor *monitor,
const gchar *filename,
diff --git a/stc/stcprivate.h b/stc/stcprivate.h
index f7325cb..f56021b 100644
--- a/stc/stcprivate.h
+++ b/stc/stcprivate.h
@@ -36,7 +36,12 @@ StcItem *_stc_item_new (StcItemType type,
const gchar *id,
const gchar *target,
const gchar *nick_name,
- GHashTable *options);
+ GHashTable *options,
+ const gchar *sort_key);
+
+const gchar *_stc_item_get_sort_key (StcItem *item);
+
+gboolean _stc_item_update (StcItem *item, StcItem *other);
G_END_DECLS
diff --git a/stc/test.c b/stc/test.c
index c3a7f04..9144bc8 100644
--- a/stc/test.c
+++ b/stc/test.c
@@ -20,8 +20,15 @@
#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
#include "stc.h"
+static GMainLoop *loop = NULL;
+
/* ---------------------------------------------------------------------------------------------------- */
static const gchar *
@@ -134,8 +141,9 @@ test_stc_no_conf (void)
monitor = stc_monitor_new_for_config_dir (SRCDIR "/nonexistant", on_error_append_str, str);
g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, SRCDIR "/nonexistant");
g_object_unref (monitor);
+
g_assert_cmpstr (str->str, ==,
- SRCDIR "/nonexistant/stc.d: Error opening directory '" SRCDIR "/nonexistant/stc.d': No such file or directory (g-file-error-quark, 4)\n"
+ SRCDIR "/nonexistant/stc.conf.d: Error opening directory '" SRCDIR "/nonexistant/stc.conf.d': No such file or directory (g-file-error-quark, 4)\n"
SRCDIR "/nonexistant/stc.conf: No such file or directory (g-file-error-quark, 4)\n"
);
g_string_free (str, TRUE);
@@ -159,7 +167,7 @@ test_stc_semi_valid_conf (void)
SRCDIR "/testdata/semi_valid_conf/stc.conf: Invalid item identifer `with-dash' in group `Filesystem with-dash'. Only characters in [a-zA-Z0-9_] are supported. (stc-error-quark, 1)\n"
SRCDIR "/testdata/semi_valid_conf/stc.conf: Error parsing group name `Filesystem with space' (stc-error-quark, 1)\n"
SRCDIR "/testdata/semi_valid_conf/stc.conf: Element 0 of Options, `foo', for item foo of type MDRaid is malformed (no equal sign found). (stc-error-quark, 1)\n"
- SRCDIR "/testdata/semi_valid_conf/stc.d/91.conf: Encountered item with duplicate id `multiple_instances_same_id'. Ignoring previous items. (stc-error-quark, 2)\n"
+ SRCDIR "/testdata/semi_valid_conf/stc.conf.d/91.conf: Encountered item with duplicate id `multiple_instances_same_id'. Ignoring previous items. (stc-error-quark, 2)\n"
);
str2 = g_string_new (NULL);
items = stc_monitor_get_items (monitor);
@@ -169,6 +177,30 @@ test_stc_semi_valid_conf (void)
item_append_str (item, str2);
}
g_assert_cmpstr (str2->str, ==,
+ "id `in_pri_10'\n"
+ "target `Device=/dev/sda'\n"
+ "nick-name `(none)'\n"
+ "type filesystem\n"
+ "options (none)\n"
+ "\n"
+ "id `in_pri_50'\n"
+ "target `Device=/dev/sda'\n"
+ "nick-name `(none)'\n"
+ "type filesystem\n"
+ "options (none)\n"
+ "\n"
+ "id `in_pri_90'\n"
+ "target `Device=/dev/sda'\n"
+ "nick-name `(none)'\n"
+ "type filesystem\n"
+ "options (none)\n"
+ "\n"
+ "id `multiple_instances_same_id'\n"
+ "target `Device=/dev/multiple_instances_same_id/second'\n"
+ "nick-name `(none)'\n"
+ "type filesystem\n"
+ "options (none)\n"
+ "\n"
"id `BigStorage'\n"
"target `Device=/dev/disk/md-uuid-01234:56789'\n"
"nick-name `BigStorage'\n"
@@ -193,30 +225,6 @@ test_stc_semi_valid_conf (void)
"nick-name `My Secret Stuff (Encrypted)'\n"
"type luks\n"
"options LUKS:password -> `xyz123'\n"
- "\n"
- "id `in_pri_10'\n"
- "target `Device=/dev/sda'\n"
- "nick-name `(none)'\n"
- "type filesystem\n"
- "options (none)\n"
- "\n"
- "id `in_pri_50'\n"
- "target `Device=/dev/sda'\n"
- "nick-name `(none)'\n"
- "type filesystem\n"
- "options (none)\n"
- "\n"
- "id `in_pri_90'\n"
- "target `Device=/dev/sda'\n"
- "nick-name `(none)'\n"
- "type filesystem\n"
- "options (none)\n"
- "\n"
- "id `multiple_instances_same_id'\n"
- "target `Device=/dev/multiple_instances_same_id/second'\n"
- "nick-name `(none)'\n"
- "type filesystem\n"
- "options (none)\n"
"\n");
g_string_free (str2, TRUE);
@@ -226,6 +234,231 @@ test_stc_semi_valid_conf (void)
/* ---------------------------------------------------------------------------------------------------- */
+static void
+cleanup_dir (const gchar *dirname)
+{
+ gchar *command;
+
+ g_assert (g_str_has_prefix (dirname, "/tmp/stc-monitoring-test-"));
+ g_assert (strstr (dirname, "\"") == NULL);
+ command = g_strdup_printf ("rm -rf \"%s\"", dirname);
+ g_assert_cmpint (system (command), ==, 0);
+ g_free (command);
+}
+
+static gboolean
+on_timeout (gpointer user_data)
+{
+ g_assert_not_reached ();
+ return FALSE;
+}
+
+typedef enum
+{
+ _EVENT_ADDED,
+ _EVENT_REMOVED,
+ _EVENT_CHANGED
+} _Event;
+
+static void
+check_state (StcMonitor *monitor,
+ StcItem *item,
+ gint state,
+ _Event event)
+{
+ switch (state)
+ {
+ case 0:
+ g_assert_cmpstr (stc_item_get_id (item), ==, "item1");
+ g_assert_cmpint (event, ==, _EVENT_ADDED);
+ break;
+
+ case 1:
+ g_assert_cmpstr (stc_item_get_id (item), ==, "item2");
+ g_assert_cmpint (event, ==, _EVENT_ADDED);
+ g_assert_cmpstr (stc_item_get_target (item), ==, "Device=/dev/sdc");
+ break;
+
+ case 2:
+ g_assert_cmpstr (stc_item_get_id (item), ==, "item1");
+ g_assert_cmpint (event, ==, _EVENT_REMOVED);
+ break;
+
+ case 3:
+ g_assert_cmpstr (stc_item_get_id (item), ==, "item2");
+ g_assert_cmpint (event, ==, _EVENT_CHANGED);
+ g_assert_cmpstr (stc_item_get_target (item), ==, "Device=/dev/sda");
+ break;
+
+ case 4:
+ g_assert_cmpstr (stc_item_get_id (item), ==, "item2");
+ g_assert_cmpint (event, ==, _EVENT_REMOVED);
+ break;
+
+ case 5:
+ g_assert_cmpstr (stc_item_get_id (item), ==, "item3");
+ g_assert_cmpint (event, ==, _EVENT_ADDED);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+
+static void
+on_item_added (StcMonitor *monitor,
+ StcItem *item,
+ gpointer user_data)
+{
+ gint *state = user_data;
+ check_state (monitor, item, *state, _EVENT_ADDED);
+ *state += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+on_item_removed (StcMonitor *monitor,
+ StcItem *item,
+ gpointer user_data)
+{
+ gint *state = user_data;
+ check_state (monitor, item, *state, _EVENT_REMOVED);
+ *state += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+on_item_changed (StcMonitor *monitor,
+ StcItem *item,
+ gpointer user_data)
+{
+ gint *state = user_data;
+ check_state (monitor, item, *state, _EVENT_CHANGED);
+ *state += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+test_stc_monitoring (void)
+{
+ gchar *dirname;
+ StcMonitor *monitor;
+ GError *error;
+ gboolean rc;
+ gchar *filename;
+ guint timeout_id;
+ gint state;
+
+ dirname = g_strdup ("/tmp/stc-monitoring-test-XXXXXX");
+ if (mkdtemp (dirname) == 0)
+ {
+ g_warning ("Error creating temp dir with template %s: %m", dirname);
+ g_assert_not_reached ();
+ }
+
+ monitor = stc_monitor_new_for_config_dir (dirname, NULL, NULL);
+ g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, dirname);
+
+ /* main timeout */
+ timeout_id = g_timeout_add_seconds (30, on_timeout, NULL);
+
+ state = 0;
+ g_signal_connect (monitor,
+ "item-added",
+ G_CALLBACK (on_item_added),
+ &state);
+ g_signal_connect (monitor,
+ "item-removed",
+ G_CALLBACK (on_item_removed),
+ &state);
+ g_signal_connect (monitor,
+ "item-changed",
+ G_CALLBACK (on_item_changed),
+ &state);
+
+ /* 0: check we get the item-added signal */
+ filename = g_strdup_printf ("%s/stc.conf", dirname);
+ error = NULL;
+ rc = g_file_set_contents (filename,
+ "[Filesystem item1]\n"
+ "Device=/dev/sdb\n",
+ -1,
+ &error);
+ g_assert_no_error (error);
+ g_assert (rc);
+ g_free (filename);
+ if (state <= 0)
+ g_main_loop_run (loop);
+
+ /* 1: check we get the item-added signal (again) */
+ filename = g_strdup_printf ("%s/stc.conf", dirname);
+ error = NULL;
+ rc = g_file_set_contents (filename,
+ "[Filesystem item1]\n"
+ "Device=/dev/sdb\n"
+ "\n"
+ "[Filesystem item2]\n"
+ "Device=/dev/sdc\n",
+ -1,
+ &error);
+ g_assert_no_error (error);
+ g_assert (rc);
+ g_free (filename);
+ if (state <= 1)
+ g_main_loop_run (loop);
+
+ /* 2: check we get the item-removed signal */
+ filename = g_strdup_printf ("%s/stc.conf", dirname);
+ error = NULL;
+ rc = g_file_set_contents (filename,
+ "[Filesystem item2]\n"
+ "Device=/dev/sdc\n",
+ -1,
+ &error);
+ g_assert_no_error (error);
+ g_assert (rc);
+ g_free (filename);
+ if (state <= 2)
+ g_main_loop_run (loop);
+
+ /* 3: check we get the item-changed signal */
+ filename = g_strdup_printf ("%s/stc.conf", dirname);
+ error = NULL;
+ rc = g_file_set_contents (filename,
+ "[Filesystem item2]\n"
+ "Device=/dev/sda\n",
+ -1,
+ &error);
+ g_assert_no_error (error);
+ g_assert (rc);
+ g_free (filename);
+ if (state <= 3)
+ g_main_loop_run (loop);
+
+ /* 4+5: will give item-removed then item-added */
+ filename = g_strdup_printf ("%s/stc.conf", dirname);
+ error = NULL;
+ rc = g_file_set_contents (filename,
+ "[Filesystem item3]\n"
+ "Device=/dev/sda\n",
+ -1,
+ &error);
+ g_assert_no_error (error);
+ g_assert (rc);
+ g_free (filename);
+ if (state <= 5)
+ g_main_loop_run (loop);
+
+ g_object_unref (monitor);
+ cleanup_dir (dirname);
+
+ g_source_remove (timeout_id);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
int
main (int argc,
char **argv)
@@ -235,11 +468,16 @@ main (int argc,
g_type_init ();
g_test_init (&argc, &argv, NULL);
+ loop = g_main_loop_new (NULL, FALSE);
+
g_test_add_func ("/stc/basic", test_stc_basic);
g_test_add_func ("/stc/no_conf", test_stc_no_conf);
g_test_add_func ("/stc/semi_valid_conf", test_stc_semi_valid_conf);
+ g_test_add_func ("/stc/monitoring", test_stc_monitoring);
ret = g_test_run();
+ g_main_loop_unref (loop);
+
return ret;
}
diff --git a/stc/testdata/semi_valid_conf/stc.d/10.conf b/stc/testdata/semi_valid_conf/stc.conf.d/10.conf
index 73b0c95..73b0c95 100644
--- a/stc/testdata/semi_valid_conf/stc.d/10.conf
+++ b/stc/testdata/semi_valid_conf/stc.conf.d/10.conf
diff --git a/stc/testdata/semi_valid_conf/stc.d/50.conf b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf
index bad4e88..bad4e88 100644
--- a/stc/testdata/semi_valid_conf/stc.d/50.conf
+++ b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf
diff --git a/stc/testdata/semi_valid_conf/stc.d/50.conf.wrong-extension b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf.wrong-extension
index fae6617..fae6617 100644
--- a/stc/testdata/semi_valid_conf/stc.d/50.conf.wrong-extension
+++ b/stc/testdata/semi_valid_conf/stc.conf.d/50.conf.wrong-extension
diff --git a/stc/testdata/semi_valid_conf/stc.d/90.conf b/stc/testdata/semi_valid_conf/stc.conf.d/90.conf
index 15a3de1..15a3de1 100644
--- a/stc/testdata/semi_valid_conf/stc.d/90.conf
+++ b/stc/testdata/semi_valid_conf/stc.conf.d/90.conf
diff --git a/stc/testdata/semi_valid_conf/stc.d/91.conf b/stc/testdata/semi_valid_conf/stc.conf.d/91.conf
index a97db0f..a97db0f 100644
--- a/stc/testdata/semi_valid_conf/stc.d/91.conf
+++ b/stc/testdata/semi_valid_conf/stc.conf.d/91.conf