diff options
Diffstat (limited to 'stc/stcmonitor.c')
-rw-r--r-- | stc/stcmonitor.c | 450 |
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); +} |