summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2010-09-29 17:34:15 -0400
committerDavid Zeuthen <davidz@redhat.com>2010-09-29 17:34:15 -0400
commit69f0ce038868eda535aeaab3aaa6ab8d0e592fa0 (patch)
tree78ebcf5cb531512e4afea8d719193a51eb7600b1
parent22a6d597597477eb14038b9d9b8a21b1b3c908d5 (diff)
Updates
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r--doc/stc-sections.txt2
-rw-r--r--stc/Makefile.am8
-rw-r--r--stc/stc.conf26
-rw-r--r--stc/stcenums.h16
-rw-r--r--stc/stcitem.c53
-rw-r--r--stc/stcitem.h17
-rw-r--r--stc/stcmarshal.list1
-rw-r--r--stc/stcmonitor.c450
-rw-r--r--stc/stcmonitor.h2
-rw-r--r--stc/stcprivate.h43
-rw-r--r--stc/test.c181
11 files changed, 765 insertions, 34 deletions
diff --git a/doc/stc-sections.txt b/doc/stc-sections.txt
index cc1e510..49d81a8 100644
--- a/doc/stc-sections.txt
+++ b/doc/stc-sections.txt
@@ -4,6 +4,8 @@
StcMonitor
stc_monitor_new
stc_monitor_new_for_config_dir
+stc_monitor_get_active
+stc_monitor_activate
stc_monitor_get_config_dir
stc_monitor_get_items
<SUBSECTION Standard>
diff --git a/stc/Makefile.am b/stc/Makefile.am
index 3ed139a..8442330 100644
--- a/stc/Makefile.am
+++ b/stc/Makefile.am
@@ -19,6 +19,12 @@ INCLUDES = \
$(GLIB_CFLAGS) \
$(NULL)
+stcmarshal.h: stcmarshal.list
+ glib-genmarshal $< --prefix=_stc_marshal --header > $@
+
+stcmarshal.c: stcmarshal.list
+ echo "#include \"stcmarshal.h\"" > $@ && glib-genmarshal $< --prefix=_stc_marshal --body >> $@
+
stcenumtypes.h: stcenums.h stcenumtypes.h.template
( top_builddir=`cd $(top_builddir) && pwd`; \
cd $(srcdir) && glib-mkenums --template stcenumtypes.h.template stcenums.h ) > \
@@ -31,6 +37,7 @@ stcenumtypes.c: stcenums.h stcenumtypes.c.template
BUILT_SOURCES = \
stcenumtypes.h stcenumtypes.c \
+ stcmarshal.h stcmarshal.c \
$(NULL)
lib_LTLIBRARIES=libstc-1.la
@@ -52,6 +59,7 @@ libstc_1_la_SOURCES = \
stcerror.h stcerror.c \
stcitem.h stcitem.c \
stcmonitor.h stcmonitor.c \
+ stcprivate.h \
$(NULL)
libstc_1_la_CPPFLAGS = \
diff --git a/stc/stc.conf b/stc/stc.conf
index 1ffec32..acfcd9b 100644
--- a/stc/stc.conf
+++ b/stc/stc.conf
@@ -1,6 +1,10 @@
+# check that we accept valid items...
+#
+
[Filesystem BigStorage]
NickName=BigStorage
Device=/dev/disk/md-uuid-01234:56789
+Filesystem:mount_path=/mnt/BigStorage;
Depends=BigStorage_mdraid
Auto=yes
@@ -11,10 +15,30 @@ UUID=01234:56789
[Filesystem SekritStuff]
NickName=My Secret Stuff
Device=/dev/disk/by-uuid/1234
-Options=Filesystem:options=noatime,dirsync
+Options=Filesystem:mount_path=/mnt/SekritStuff;Filesystem:options=noatime,dirsync
Depends=SekritStuff_LUKS
[LUKS SekritStuff_LUKS]
NickName=My Secret Stuff (Encrypted)
Device=/dev/disk/by-uuid/12345
Options=LUKS:password=xyz123
+
+# ...and that we reject invalid items
+#
+
+# group name invalid (not two tokens)
+[Blah]
+
+# unknown type
+[XYZType ValidIdentifier]
+
+# invalid identifier (only [a-z][A-Z][0-9]_] allowed)
+[Filesystem with-dash]
+
+# another invalid identifier
+[Filesystem with space]
+
+# invalid options (no equal sign)
+[MDRaid foo]
+UUID=01234:56789
+Options=foo
diff --git a/stc/stcenums.h b/stc/stcenums.h
index 49065a2..d608561 100644
--- a/stc/stcenums.h
+++ b/stc/stcenums.h
@@ -33,27 +33,33 @@ G_BEGIN_DECLS
/**
* StcError:
- * @STC_ERROR_FAILED: The operation failed.
+ * @STC_ERROR_FAILED: The operation failed / Generic error.
+ * @STC_ERROR_PARSE_ERROR: A configurtion is invalid.
*
* Error codes for the #STC_ERROR error domain and the
* corresponding D-Bus error names.
*/
typedef enum
{
- STC_ERROR_FAILED
+ STC_ERROR_FAILED,
+ STC_ERROR_PARSE_ERROR
} StcError;
/**
* StcItemType:
* @STC_ITEM_TYPE_INVALID: Unknown or invalid item type.
- * @STC_ITEM_TYPE_FSMOUNT: Filesystem mount.
+ * @STC_ITEM_TYPE_FILESYSTEM: Filesystem mount.
+ * @STC_ITEM_TYPE_MD_RAID: Linux Software RAID.
+ * @STC_ITEM_TYPE_LUKS: Linux Unified Key System.
*
- * Enumeration describing item types.
+ * Enumeration describing configuration item types.
*/
typedef enum
{
STC_ITEM_TYPE_INVALID,
- STC_ITEM_TYPE_FSMOUNT
+ STC_ITEM_TYPE_FILESYSTEM,
+ STC_ITEM_TYPE_MD_RAID,
+ STC_ITEM_TYPE_LUKS
} StcItemType;
G_END_DECLS
diff --git a/stc/stcitem.c b/stc/stcitem.c
index fda895b..7490b0d 100644
--- a/stc/stcitem.c
+++ b/stc/stcitem.c
@@ -23,6 +23,7 @@
#include "config.h"
#include "stcitem.h"
+#include "stcprivate.h"
/**
* SECTION:stcitem
@@ -47,8 +48,12 @@ struct _StcItem
gchar *id;
gchar *nick_name;
gchar *target;
+ GHashTable *options;
gboolean can_apply;
gboolean applied;
+
+ /* memoized */
+ gchar **option_keys;
};
typedef struct _StcItemClass StcItemClass;
@@ -68,6 +73,8 @@ stc_item_finalize (GObject *object)
g_free (item->id);
g_free (item->nick_name);
g_free (item->target);
+ g_hash_table_unref (item->options);
+ g_free (item->option_keys);
if (G_OBJECT_CLASS (stc_item_parent_class)->finalize)
G_OBJECT_CLASS (stc_item_parent_class)->finalize (object);
@@ -146,17 +153,51 @@ stc_item_get_applied (StcItem *item)
}
-#if 0
StcItem *
-_stc_item_new (dev_t dev,
- const gchar *item_path)
+_stc_item_new (StcItemType type,
+ const gchar *id,
+ const gchar *target,
+ const gchar *nick_name,
+ GHashTable *options)
{
StcItem *item;
item = STC_ITEM (g_object_new (STC_TYPE_ITEM, NULL));
- item->dev = dev;
- item->item_path = g_strdup (item_path);
+
+ item->id = g_strdup (id);
+ item->target = g_strdup (target);
+ item->nick_name = g_strdup (nick_name);
+ item->type = type;
+ item->options = g_hash_table_ref (options);
return item;
}
-#endif
+
+const gchar* const *
+stc_item_get_option_keys (StcItem *item)
+{
+ g_return_val_if_fail (STC_IS_ITEM (item), NULL);
+ if (item->option_keys == NULL)
+ {
+ GHashTableIter iter;
+ const gchar *key;
+ GPtrArray *p;
+
+ p = g_ptr_array_new ();
+ g_hash_table_iter_init (&iter, item->options);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL))
+ g_ptr_array_add (p, (gpointer) key);
+ g_ptr_array_add (p, NULL);
+ item->option_keys = (gchar **) g_ptr_array_free (p, FALSE);
+ }
+
+ return (const gchar *const *) item->option_keys;
+}
+
+const gchar *
+stc_item_get_option (StcItem *item,
+ const gchar *key)
+{
+ g_return_val_if_fail (STC_IS_ITEM (item), NULL);
+ return g_hash_table_lookup (item->options, key);
+}
diff --git a/stc/stcitem.h b/stc/stcitem.h
index a666549..287bc65 100644
--- a/stc/stcitem.h
+++ b/stc/stcitem.h
@@ -37,13 +37,16 @@ G_BEGIN_DECLS
#define STC_IS_ITEM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), STC_TYPE_ITEM))
GType stc_item_get_type (void) G_GNUC_CONST;
-StcItemType stc_item_get_item_type (StcItem *item);
-const gchar *stc_item_get_id (StcItem *item);
-const gchar *stc_item_get_nick_name (StcItem *item);
-const gchar *stc_item_get_target (StcItem *item);
-GList *stc_item_get_dependencies (StcItem *item);
-gboolean stc_item_get_can_apply (StcItem *item);
-gboolean stc_item_get_applied (StcItem *item);
+StcItemType stc_item_get_item_type (StcItem *item);
+const gchar *stc_item_get_id (StcItem *item);
+const gchar *stc_item_get_nick_name (StcItem *item);
+const gchar *stc_item_get_target (StcItem *item);
+const gchar* const *stc_item_get_option_keys (StcItem *item);
+const gchar *stc_item_get_option (StcItem *item,
+ const gchar *key);
+GList *stc_item_get_dependencies (StcItem *item);
+gboolean stc_item_get_can_apply (StcItem *item);
+gboolean stc_item_get_applied (StcItem *item);
G_END_DECLS
diff --git a/stc/stcmarshal.list b/stc/stcmarshal.list
new file mode 100644
index 0000000..e0edfb9
--- /dev/null
+++ b/stc/stcmarshal.list
@@ -0,0 +1 @@
+VOID:STRING,INT,BOXED
diff --git a/stc/stcmonitor.c b/stc/stcmonitor.c
index dc1bc0f..f6d4242 100644
--- a/stc/stcmonitor.c
+++ b/stc/stcmonitor.c
@@ -22,7 +22,12 @@
#include "config.h"
+#include <string.h>
+
+#include "stcerror.h"
#include "stcmonitor.h"
+#include "stcmarshal.h"
+#include "stcprivate.h"
/**
* SECTION:stcmonitor
@@ -43,8 +48,12 @@ struct _StcMonitor
{
GObject parent_instance;
+ GMainContext *context;
+
gchar *config_dir;
+ gboolean active;
+
GList *items;
};
@@ -53,14 +62,45 @@ typedef struct _StcMonitorClass StcMonitorClass;
struct _StcMonitorClass
{
GObjectClass parent_class;
+
+ void (*error) (StcMonitor *monitor,
+ const gchar *filename,
+ gint line_no,
+ GError **error);
};
enum
{
PROP_0,
- PROP_CONFIG_DIR
+ PROP_CONFIG_DIR,
+ PROP_ACTIVE
+};
+
+enum
+{
+ ERROR_SIGNAL,
+ LAST_SIGNAL
};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static GList *stc_monitor_load_one_file (StcMonitor *monitor,
+ const gchar *path,
+ GError **error);
+
+static void
+stc_monitor_emit_gerror (StcMonitor *monitor,
+ const gchar *filename,
+ guint line_no,
+ GError *error);
+
+static void stc_monitor_emit_stc_error (StcMonitor *monitor,
+ const gchar *filename,
+ guint line_no,
+ gint error_code,
+ const gchar *format,
+ ...) G_GNUC_PRINTF(5, 6);
+
G_DEFINE_TYPE (StcMonitor, stc_monitor, G_TYPE_OBJECT);
static void
@@ -68,6 +108,8 @@ stc_monitor_finalize (GObject *object)
{
StcMonitor *monitor = STC_MONITOR (object);
+ if (monitor->context != NULL)
+ g_main_context_unref (monitor->context);
g_free (monitor->config_dir);
g_list_foreach (monitor->items, (GFunc) g_object_unref, NULL);
g_list_free (monitor->items);
@@ -90,6 +132,10 @@ stc_monitor_get_property (GObject *object,
g_value_set_string (value, stc_monitor_get_config_dir (monitor));
break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, stc_monitor_get_active (monitor));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -121,16 +167,9 @@ stc_monitor_set_property (GObject *object,
static void
stc_monitor_init (StcMonitor *monitor)
{
-}
-
-
-static void
-stc_monitor_constructed (GObject *object)
-{
- //StcMonitor *monitor = STC_MONITOR (object);
-
- if (G_OBJECT_CLASS (stc_monitor_parent_class)->constructed)
- G_OBJECT_CLASS (stc_monitor_parent_class)->constructed (object);
+ monitor->context = g_main_context_get_thread_default ();
+ if (monitor->context != NULL)
+ g_main_context_ref (monitor->context);
}
static void
@@ -140,15 +179,13 @@ stc_monitor_class_init (StcMonitorClass *klass)
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = stc_monitor_finalize;
- gobject_class->constructed = stc_monitor_constructed;
gobject_class->get_property = stc_monitor_get_property;
gobject_class->set_property = stc_monitor_set_property;
/**
* StcMonitor:config-dir:
*
- * The path for the configuration directory. If set to %NULL at
- * construction time, then the <filename>/etc</filename> directory is used.
+ * The path for the configuration directory.
*/
g_object_class_install_property (gobject_class,
PROP_CONFIG_DIR,
@@ -160,8 +197,61 @@ stc_monitor_class_init (StcMonitorClass *klass)
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
+
+ /**
+ * StcMonitor:active:
+ *
+ * Whether the monitor is currently active.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ "Active",
+ "Whether the monitor is currently active",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * StcMonitor::error:
+ * @monitor: The #StcMonitor emitting the signal
+ * @filename: The path of the file that caused the error or %NULL.
+ * @line_no: The line number the error occured at or -1 if unknown.
+ * @error: The error.
+ *
+ * Emitted whenever a recoverable error (typically file parsing) is
+ * encountered.
+ *
+ * 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[ERROR_SIGNAL] = g_signal_new ("error",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (StcMonitorClass, error),
+ NULL,
+ NULL,
+ _stc_marshal_VOID__STRING_INT_BOXED,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_ERROR);
}
+/**
+ * stc_monitor_new:
+ *
+ * Creates a new #StcMonitor that reads configuration files from the
+ * default configuration directory
+ * (e.g. <filename>/etc</filename>).
+ *
+ * The returned monitor isn't active. Use stc_monitor_activate() to load
+ * and monitor items.
+ *
+ * Returns: A #StcMonitor. Free with g_object_unref().
+ */
StcMonitor *
stc_monitor_new (void)
{
@@ -170,6 +260,17 @@ stc_monitor_new (void)
return monitor;
}
+/**
+ * stc_monitor_new_for_config_dir:
+ * @config_dir: A path to where the <filename>stc.conf</filename> is stored.
+ *
+ * Creates a new #StcMonitor.
+ *
+ * The returned monitor isn't active. Use stc_monitor_activate() to load
+ * and monitor items.
+ *
+ * Returns: A #StcMonitor. Free with g_object_unref().
+ */
StcMonitor *
stc_monitor_new_for_config_dir (const gchar *config_dir)
{
@@ -183,6 +284,63 @@ stc_monitor_new_for_config_dir (const gchar *config_dir)
return monitor;
}
+/**
+ * stc_monitor_activate:
+ * @monitor: A #StcMonitor that isn't active.
+ *
+ * Starts @monitor. If you are interested in reporting errors, connect
+ * to the #StcMonitor::error signal prior to invoking this method.
+ */
+void
+stc_monitor_activate (StcMonitor *monitor)
+{
+ gchar *path;
+ GError *error;
+
+ g_return_if_fail (STC_IS_MONITOR (monitor));
+ g_return_if_fail (!monitor->active);
+
+ path = g_strdup_printf ("%s/stc.conf", monitor->config_dir);
+ error = NULL;
+ monitor->items = stc_monitor_load_one_file (monitor, path, &error);
+ if (error != NULL)
+ {
+ stc_monitor_emit_gerror (monitor,
+ path,
+ -1,
+ error);
+ g_error_free (error);
+ }
+ g_free (path);
+
+ monitor->active = TRUE;
+ g_object_notify (G_OBJECT (monitor), "active");
+}
+
+
+/**
+ * stc_monitor_get_active:
+ * @monitor: A #StcMonitor.
+ *
+ * Gets whether @monitor is active.
+ *
+ * Returns: %TRUE if monitor is active, %FALSE otherwise.
+ */
+gboolean
+stc_monitor_get_active (StcMonitor *monitor)
+{
+ g_return_val_if_fail (STC_IS_MONITOR (monitor), FALSE);
+ return monitor->active;
+}
+
+/**
+ * stc_monitor_get_config_dir:
+ * @monitor: A #StcMonitor.
+ *
+ * Gets the configuration directory used by @monitor.
+ *
+ * Returns: A directory name. Do not free, the string is owned by @monitor.
+ */
const gchar *
stc_monitor_get_config_dir (StcMonitor *monitor)
{
@@ -190,9 +348,271 @@ stc_monitor_get_config_dir (StcMonitor *monitor)
return monitor->config_dir;
}
+/**
+ * stc_monitor_get_items:
+ * @monitor: An active #StcMonitor.
+ *
+ * Gets the configuration items detected by @monitor.
+ *
+ * Returns: A list of configuration items. The returned list must be
+ * with g_list_free() after each element has been unreffed using
+ * g_object_unref().
+ */
GList *
stc_monitor_get_items (StcMonitor *monitor)
{
- return NULL;
+ GList *ret;
+
+ g_return_val_if_fail (STC_IS_MONITOR (monitor), NULL);
+ g_return_val_if_fail (monitor->active, NULL);
+
+ ret = g_list_copy (monitor->items);
+ g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+
+ return ret;
}
+static StcItemType
+item_type_from_string (const gchar *str)
+{
+ StcItemType ret;
+
+ g_return_val_if_fail (str != NULL, STC_ITEM_TYPE_INVALID);
+
+ ret = STC_ITEM_TYPE_INVALID;
+ if (g_strcmp0 (str, "Filesystem") == 0)
+ ret = STC_ITEM_TYPE_FILESYSTEM;
+ else if (g_strcmp0 (str, "MDRaid") == 0)
+ ret = STC_ITEM_TYPE_MD_RAID;
+ else if (g_strcmp0 (str, "LUKS") == 0)
+ ret = STC_ITEM_TYPE_LUKS;
+
+ return ret;
+}
+
+static gboolean
+is_valid_item_id (const gchar *str)
+{
+ guint n;
+ gboolean ret;
+
+ g_return_val_if_fail (str != NULL, FALSE);
+
+ ret = FALSE;
+
+ for (n = 0; str[n] != '\0'; n++)
+ {
+ gint c = str[n];
+ if (!(g_ascii_isalnum (c) || c == '_'))
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ return ret;
+}
+
+static GList *
+stc_monitor_load_one_file (StcMonitor *monitor,
+ const gchar *path,
+ GError **error)
+{
+ GList *ret;
+ GKeyFile *key_file;
+ gchar **groups;
+ guint n;
+ gboolean failed;
+
+ ret = NULL;
+ groups = NULL;
+ failed = TRUE;
+
+ key_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, error))
+ goto out;
+
+ groups = g_key_file_get_groups (key_file, NULL);
+ for (n = 0; groups != NULL && groups[n] != NULL; n++)
+ {
+ const gchar *group = groups[n];
+ gchar **tokens;
+ StcItemType item_type;
+ const gchar *item_id;
+ gchar *target;
+ gchar *nick_name;
+ gchar *s;
+ gchar **options_strv;
+ StcItem *item;
+ GHashTable *options;
+
+ tokens = NULL;
+ target = NULL;
+ nick_name = NULL;
+ options = NULL;
+
+ tokens = g_strsplit (group, " ", 0);
+ if (g_strv_length (tokens) != 2)
+ {
+ stc_monitor_emit_stc_error (monitor,
+ path,
+ -1,
+ STC_ERROR_PARSE_ERROR,
+ "Error parsing group name `%s'",
+ group);
+ goto process_next_item;
+ }
+
+ item_type = item_type_from_string (tokens[0]);
+ if (item_type == STC_ITEM_TYPE_INVALID)
+ {
+ stc_monitor_emit_stc_error (monitor,
+ path,
+ -1,
+ STC_ERROR_PARSE_ERROR,
+ "Error parsing item type `%s' in group `%s'",
+ tokens[0],
+ group);
+ goto process_next_item;
+ }
+
+ item_id = tokens[1];
+ if (!is_valid_item_id (item_id))
+ {
+ stc_monitor_emit_stc_error (monitor,
+ path,
+ -1,
+ STC_ERROR_PARSE_ERROR,
+ "Invalid item identifer `%s' in group `%s'. "
+ "Only characters in [a-zA-Z0-9_] are supported.",
+ item_id,
+ group);
+ goto process_next_item;
+ }
+
+ /* resolve target (TODO: bail if more than one of the allowed keys are present) */
+ if ((s = g_key_file_get_string (key_file, group, "Device", NULL)) != NULL)
+ {
+ target = g_strdup_printf ("Device=%s", s);
+ g_free (s);
+ }
+ else if ((s = g_key_file_get_string (key_file, group, "UUID", NULL)) != NULL)
+ {
+ target = g_strdup_printf ("UUID=%s", s);
+ g_free (s);
+ }
+ else if ((s = g_key_file_get_string (key_file, group, "Label", NULL)) != NULL)
+ {
+ target = g_strdup_printf ("Label=%s", s);
+ g_free (s);
+ }
+ else
+ {
+ stc_monitor_emit_stc_error (monitor,
+ path,
+ -1,
+ STC_ERROR_PARSE_ERROR,
+ "No target key (supported: Device, UUID, Label) keys found "
+ "in item %s of type %s.",
+ item_id,
+ tokens[0]);
+ goto process_next_item;
+ }
+
+ /* Options is optional */
+ options_strv = g_key_file_get_string_list (key_file, group, "Options", NULL, NULL);
+ options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ if (options_strv != NULL)
+ {
+ guint m;
+ for (m = 0; options_strv[m] != NULL; m++)
+ {
+ s = strstr (options_strv[m], "=");
+ if (s == NULL)
+ {
+ stc_monitor_emit_stc_error (monitor,
+ path,
+ -1,
+ STC_ERROR_PARSE_ERROR,
+ "Element %d of Options, `%s', for item %s of type %s is malformed "
+ "(no equal sign found).",
+ m,
+ options_strv[m],
+ item_id,
+ tokens[0]);
+ g_strfreev (options_strv);
+ goto process_next_item;
+ }
+ g_hash_table_insert (options,
+ g_strndup (options_strv[m], s - options_strv[m]),
+ g_strdup (s + 1));
+ }
+ g_strfreev (options_strv);
+ }
+
+ /* 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);
+ ret = g_list_prepend (ret, item);
+
+ process_next_item:
+ g_strfreev (tokens);
+ g_free (target);
+ g_free (nick_name);
+ if (options != NULL)
+ g_hash_table_unref (options);
+ }
+
+ failed = FALSE;
+
+ out:
+ if (failed)
+ {
+ g_list_foreach (ret, (GFunc) g_object_unref, NULL);
+ g_list_free (ret);
+ ret = NULL;
+ }
+ else
+ {
+ ret = g_list_reverse (ret);
+ }
+ g_key_file_free (key_file);
+ g_strfreev (groups);
+ return ret;
+}
+
+static void
+stc_monitor_emit_gerror (StcMonitor *monitor,
+ const gchar *filename,
+ guint line_no,
+ GError *error)
+{
+ /* the following holds because all call-sites are guaranteed to be
+ * in the correct context
+ */
+ g_assert (g_main_context_get_thread_default () == monitor->context);
+ g_signal_emit (monitor, signals[ERROR_SIGNAL], 0, filename, line_no, error);
+}
+
+static void
+stc_monitor_emit_stc_error (StcMonitor *monitor,
+ const gchar *filename,
+ guint line_no,
+ gint error_code,
+ const gchar *format,
+ ...)
+{
+ GError *error;
+ va_list var_args;
+
+ va_start (var_args, format);
+ error = g_error_new_valist (STC_ERROR,
+ error_code,
+ format,
+ var_args);
+ va_end (var_args);
+
+ stc_monitor_emit_gerror (monitor, filename, line_no, error);
+ g_error_free (error);
+}
diff --git a/stc/stcmonitor.h b/stc/stcmonitor.h
index 4cf2e3c..a4ee942 100644
--- a/stc/stcmonitor.h
+++ b/stc/stcmonitor.h
@@ -39,6 +39,8 @@ G_BEGIN_DECLS
GType stc_monitor_get_type (void) G_GNUC_CONST;
StcMonitor *stc_monitor_new (void);
StcMonitor *stc_monitor_new_for_config_dir (const gchar *config_dir);
+void stc_monitor_activate (StcMonitor *monitor);
+gboolean stc_monitor_get_active (StcMonitor *monitor);
const gchar *stc_monitor_get_config_dir (StcMonitor *monitor);
GList *stc_monitor_get_items (StcMonitor *monitor);
diff --git a/stc/stcprivate.h b/stc/stcprivate.h
new file mode 100644
index 0000000..f7325cb
--- /dev/null
+++ b/stc/stcprivate.h
@@ -0,0 +1,43 @@
+/* -*- 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 "stcprivate.h is a private header file."
+#endif
+
+#ifndef __STC_PRIVATE_H__
+#define __STC_PRIVATE_H__
+
+#include <stc/stctypes.h>
+
+G_BEGIN_DECLS
+
+StcItem *_stc_item_new (StcItemType type,
+ const gchar *id,
+ const gchar *target,
+ const gchar *nick_name,
+ GHashTable *options);
+
+G_END_DECLS
+
+#endif /* __STC_PRIVATE_H__ */
diff --git a/stc/test.c b/stc/test.c
index 92bbfa7..f61433a 100644
--- a/stc/test.c
+++ b/stc/test.c
@@ -24,6 +24,92 @@
/* ---------------------------------------------------------------------------------------------------- */
+static const gchar *
+item_type_to_str (StcItemType type)
+{
+ GEnumClass *klass;
+ GEnumValue *value;
+ const gchar *ret;
+
+ ret = "<Unregistered enum type>";
+ klass = g_type_class_ref (STC_TYPE_ITEM_TYPE);
+ if (klass == NULL)
+ goto out;
+
+ ret = "<Invalid enum value>";
+ value = g_enum_get_value (klass, type);
+ if (value == NULL)
+ goto out;
+
+ ret = value->value_nick;
+
+ out:
+ if (klass != NULL)
+ g_type_class_unref (klass);
+ return ret;
+}
+
+static void item_append_str (StcItem *item,
+ GString *str)
+{
+ const gchar* const *keys;
+
+ keys = stc_item_get_option_keys (item);
+
+ g_string_append_printf (str,
+ "id `%s'\n"
+ "target `%s'\n"
+ "nick-name `%s'\n"
+ "type %s\n"
+ "options ",
+ stc_item_get_id (item) != NULL ? stc_item_get_id (item) : "(none)",
+ stc_item_get_target (item) != NULL ? stc_item_get_target (item) : "(none)",
+ stc_item_get_nick_name (item) != NULL ? stc_item_get_nick_name (item) : "(none)",
+ item_type_to_str (stc_item_get_item_type (item)));
+
+ if (g_strv_length ((gchar **) keys) == 0)
+ {
+ g_string_append (str, "(none)");
+ }
+ else
+ {
+ guint n;
+ for (n = 0; keys[n] != NULL; n++)
+ {
+ const gchar *value;
+ if (n > 0)
+ g_string_append (str, "\n ");
+ value = stc_item_get_option (item, keys[n]);
+ g_string_append_printf (str, "%s -> `%s'", keys[n], value);
+ }
+ }
+ g_string_append (str, "\n");
+
+ g_string_append (str, "\n");
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_error_append_str (StcMonitor *monitor,
+ const gchar *filename,
+ gint line_no,
+ GError *error,
+ gpointer user_data)
+{
+ GString *str = user_data;
+
+ if (filename != NULL)
+ {
+ if (line_no >= 0)
+ g_string_append_printf (str, "%s:%d: ", filename, line_no);
+ else
+ g_string_append_printf (str, "%s: ", filename);
+ }
+
+ g_string_append_printf (str, "%s (%s, %d)\n", error->message, g_quark_to_string (error->domain), error->code);
+}
+
static void
test_stc_basic (void)
{
@@ -31,11 +117,104 @@ test_stc_basic (void)
monitor = stc_monitor_new ();
g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, PACKAGE_SYSCONF_DIR);
+ g_assert (!stc_monitor_get_active (monitor));
+ stc_monitor_activate (monitor);
+ g_assert (stc_monitor_get_active (monitor));
+ g_object_unref (monitor);
+
+ monitor = stc_monitor_new_for_config_dir (SRCDIR);
+ g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, SRCDIR);
+ g_assert (!stc_monitor_get_active (monitor));
+ stc_monitor_activate (monitor);
+ g_assert (stc_monitor_get_active (monitor));
+ g_object_unref (monitor);
+}
+
+static void
+test_stc_no_conf (void)
+{
+ StcMonitor *monitor;
+ GString *str;
+
+ str = g_string_new (NULL);
+ monitor = stc_monitor_new_for_config_dir (SRCDIR "/nonexistant");
+ g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, SRCDIR "/nonexistant");
+ g_assert (!stc_monitor_get_active (monitor));
+ g_signal_connect (monitor,
+ "error",
+ G_CALLBACK (on_error_append_str),
+ str);
+ stc_monitor_activate (monitor);
+ g_assert (stc_monitor_get_active (monitor));
g_object_unref (monitor);
+ g_assert_cmpstr (str->str, ==,
+ SRCDIR "/nonexistant/stc.conf: No such file or directory (g-file-error-quark, 4)\n");
+ g_string_free (str, TRUE);
+}
+static void
+test_stc_semi_valid_conf (void)
+{
+ StcMonitor *monitor;
+ GString *str;
+ GString *str2;
+ GList *items;
+ GList *l;
+
+ str = g_string_new (NULL);
monitor = stc_monitor_new_for_config_dir (SRCDIR);
g_assert_cmpstr (stc_monitor_get_config_dir (monitor), ==, SRCDIR);
+ g_assert (!stc_monitor_get_active (monitor));
+ g_signal_connect (monitor,
+ "error",
+ G_CALLBACK (on_error_append_str),
+ str);
+ stc_monitor_activate (monitor);
+ g_assert (stc_monitor_get_active (monitor));
+ g_assert_cmpstr (str->str, ==,
+ SRCDIR "/stc.conf: Error parsing group name `Blah' (stc-error-quark, 1)\n"
+ SRCDIR "/stc.conf: Error parsing item type `XYZType' in group `XYZType ValidIdentifier' (stc-error-quark, 1)\n"
+ SRCDIR "/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 "/stc.conf: Error parsing group name `Filesystem with space' (stc-error-quark, 1)\n"
+ SRCDIR "/stc.conf: Element 0 of Options, `foo', for item foo of type MDRaid is malformed (no equal sign found). (stc-error-quark, 1)\n"
+ );
+ str2 = g_string_new (NULL);
+ items = stc_monitor_get_items (monitor);
+ for (l = items; l != NULL; l = l->next)
+ {
+ StcItem *item = STC_ITEM (l->data);
+ item_append_str (item, str2);
+ }
+ g_assert_cmpstr (str2->str, ==,
+ "id `BigStorage'\n"
+ "target `Device=/dev/disk/md-uuid-01234:56789'\n"
+ "nick-name `BigStorage'\n"
+ "type filesystem\n"
+ "options (none)\n"
+ "\n"
+ "id `BigStorage_mdraid'\n"
+ "target `UUID=01234:56789'\n"
+ "nick-name `BigStorage RAID Array'\n"
+ "type md-raid\n"
+ "options (none)\n"
+ "\n"
+ "id `SekritStuff'\n"
+ "target `Device=/dev/disk/by-uuid/1234'\n"
+ "nick-name `My Secret Stuff'\n"
+ "type filesystem\n"
+ "options Filesystem:mount_path -> `/mnt/SekritStuff'\n"
+ " Filesystem:options -> `noatime,dirsync'\n"
+ "\n"
+ "id `SekritStuff_LUKS'\n"
+ "target `Device=/dev/disk/by-uuid/12345'\n"
+ "nick-name `My Secret Stuff (Encrypted)'\n"
+ "type luks\n"
+ "options LUKS:password -> `xyz123'\n"
+ "\n");
+ g_string_free (str2, TRUE);
+
g_object_unref (monitor);
+ g_string_free (str, TRUE);
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -50,6 +229,8 @@ main (int argc,
g_test_init (&argc, &argv, NULL);
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);
ret = g_test_run();