summaryrefslogtreecommitdiff
path: root/stc/stcmonitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'stc/stcmonitor.c')
-rw-r--r--stc/stcmonitor.c450
1 files changed, 435 insertions, 15 deletions
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);
+}