diff options
-rw-r--r-- | doc/stc-docs.xml | 1 | ||||
-rw-r--r-- | doc/stc-sections.txt | 23 | ||||
-rw-r--r-- | doc/stc.types | 1 | ||||
-rw-r--r-- | stc/Makefile.am | 2 | ||||
-rw-r--r-- | stc/stc.c | 75 | ||||
-rw-r--r-- | stc/stc.h | 1 | ||||
-rw-r--r-- | stc/stcconfigerror.c | 229 | ||||
-rw-r--r-- | stc/stcconfigerror.h | 60 | ||||
-rw-r--r-- | stc/stcd.c | 88 | ||||
-rw-r--r-- | stc/stcenums.h | 14 | ||||
-rw-r--r-- | stc/stcmonitor.c | 627 | ||||
-rw-r--r-- | stc/stcmonitor.h | 24 | ||||
-rw-r--r-- | stc/stctypes.h | 3 | ||||
-rw-r--r-- | stc/test.c | 102 |
14 files changed, 875 insertions, 375 deletions
diff --git a/doc/stc-docs.xml b/doc/stc-docs.xml index cd6addb..d023a6a 100644 --- a/doc/stc-docs.xml +++ b/doc/stc-docs.xml @@ -37,6 +37,7 @@ <xi:include href="xml/stcerror.xml"/> <xi:include href="xml/stcitem.xml"/> <xi:include href="xml/stcmonitor.xml"/> + <xi:include href="xml/stcconfigerror.xml"/> <xi:include href="xml/stcoperation.xml"/> </part> diff --git a/doc/stc-sections.txt b/doc/stc-sections.txt index 685f97f..5b853a7 100644 --- a/doc/stc-sections.txt +++ b/doc/stc-sections.txt @@ -2,10 +2,10 @@ <FILE>stcmonitor</FILE> <TITLE>StcMonitor</TITLE> StcMonitor -StcErrorHandlerFunc stc_monitor_new stc_monitor_get_items stc_monitor_get_item_by_id +stc_monitor_get_config_errors <SUBSECTION Standard> STC_TYPE_MONITOR STC_MONITOR @@ -77,3 +77,24 @@ stc_error_quark stc_error_get_type STC_TYPE_ERROR </SECTION> + +<SECTION> +<FILE>stcconfigerror</FILE> +<TITLE>StcConfigError</TITLE> +StcConfigError +StcConfigErrorSeverity +stc_config_error_new +stc_config_error_ref +stc_config_error_unref +stc_config_error_compare +stc_config_error_get_item_id +stc_config_error_get_filename +stc_config_error_get_line_number +stc_config_error_get_severity +stc_config_error_get_message +<SUBSECTION Private> +stc_config_error_get_type +STC_TYPE_CONFIG_ERROR +stc_config_error_severity_get_type +STC_TYPE_CONFIG_ERROR_SEVERITY +</SECTION> diff --git a/doc/stc.types b/doc/stc.types index 15a8430..c15e6ed 100644 --- a/doc/stc.types +++ b/doc/stc.types @@ -1,3 +1,4 @@ stc_item_get_type stc_monitor_get_type stc_operation_get_type +stc_config_error_get_type diff --git a/stc/Makefile.am b/stc/Makefile.am index 1ee3072..41addff 100644 --- a/stc/Makefile.am +++ b/stc/Makefile.am @@ -46,6 +46,7 @@ libstc_1includedir=$(includedir)/stc-1/stc libstc_1include_HEADERS= \ stc.h \ + stcconfigerror.h \ stcenums.h \ stcenumtypes.h \ stcerror.h \ @@ -57,6 +58,7 @@ libstc_1include_HEADERS= \ libstc_1_la_SOURCES = \ $(BUILT_SOURCES) \ stcenums.h \ + stcconfigerror.h stcconfigerror.c \ stcerror.h stcerror.c \ stcitem.h stcitem.c \ stcmonitor.h stcmonitor.c \ @@ -191,30 +191,57 @@ _color_run_pager (void) /* ---------------------------------------------------------------------------------------------------- */ static void -error_handler (StcMonitor *monitor, - const gchar *filename, - gint line_no, - GError *error, - gpointer user_data) +print_config_error (StcMonitor *monitor, + StcConfigError *config_error) { GString *str; + const gchar *item_id; + const gchar *filename; + gint line_number; + StcConfigErrorSeverity severity; + const gchar *message; + + item_id = stc_config_error_get_item_id (config_error); + filename = stc_config_error_get_filename (config_error); + line_number = stc_config_error_get_line_number (config_error); + severity = stc_config_error_get_severity (config_error); + message = stc_config_error_get_message (config_error); str = g_string_new (NULL); g_string_append_printf (str, "%s%s", _color_get (_COLOR_BOLD_ON), _color_get (_COLOR_FG_YELLOW)); if (filename != NULL) { - if (line_no >= 0) - g_string_append_printf (str, "%s:%d: ", filename, line_no); + if (line_number >= 0) + g_string_append_printf (str, "%s:%d: ", filename, line_number); else g_string_append_printf (str, "%s: ", filename); } g_string_append_printf (str, "%s", _color_get (_COLOR_RESET)); - g_string_append_printf (str, "%s%s", _color_get (_COLOR_BOLD_ON), _color_get (_COLOR_FG_RED)); - g_string_append_printf (str, "%s", error->message); - g_string_append_printf (str, "%s", _color_get (_COLOR_RESET)); - g_string_append_printf (str, "%s", _color_get (_COLOR_FG_BLUE)); - g_string_append_printf (str, " (%s, %d)", g_quark_to_string (error->domain), error->code); - g_string_append_printf (str, "%s", _color_get (_COLOR_RESET)); + if (item_id != NULL) + { + g_string_append_printf (str, "%s%s%s: %s", + _color_get (_COLOR_BOLD_ON), + _color_get (_COLOR_FG_BLUE), + item_id, + _color_get (_COLOR_RESET)); + } + switch (severity) + { + case STC_CONFIG_ERROR_SEVERITY_WARNING: + g_string_append_printf (str, "%s%s[WARNING]: %s", + _color_get (_COLOR_BOLD_ON), + _color_get (_COLOR_FG_YELLOW), + _color_get (_COLOR_RESET)); + break; + + case STC_CONFIG_ERROR_SEVERITY_ERROR: + g_string_append_printf (str, "%s%s[ERROR]: %s", + _color_get (_COLOR_BOLD_ON), + _color_get (_COLOR_FG_RED), + _color_get (_COLOR_RESET)); + break; + } + g_string_append_printf (str, "%s", message); if (_color_stdin_is_tty) g_print ("%s\n", str->str); @@ -223,6 +250,22 @@ error_handler (StcMonitor *monitor, g_string_free (str, TRUE); } +static void +print_all_config_errors (StcMonitor *monitor) +{ + GList *config_errors; + GList *l; + + config_errors = stc_monitor_get_config_errors (monitor); + for (l = config_errors; l != NULL; l = l->next) + { + StcConfigError *config_error = l->data; + print_config_error (monitor, config_error); + } + g_list_foreach (config_errors, (GFunc) stc_config_error_unref, NULL); + g_list_free (config_errors); +} + /* ---------------------------------------------------------------------------------------------------- */ static const GOptionEntry command_list_entries[] = @@ -274,7 +317,8 @@ handle_command_list (gint *argc, _color_run_pager (); - monitor = stc_monitor_new (error_handler, NULL); + monitor = stc_monitor_new (); + print_all_config_errors (monitor); items = stc_monitor_get_items (monitor); @@ -877,7 +921,8 @@ handle_command_start_stop (gint *argc, g_print ("--allow-degraded \n"); } - monitor = stc_monitor_new (error_handler, NULL); + monitor = stc_monitor_new (); + print_all_config_errors (monitor); if (complete_ids) { @@ -34,6 +34,7 @@ #include <stc/stcerror.h> #include <stc/stcitem.h> #include <stc/stcmonitor.h> +#include <stc/stcconfigerror.h> #include <stc/stcoperation.h> #undef __STC_INSIDE_STC_H__ diff --git a/stc/stcconfigerror.c b/stc/stcconfigerror.c new file mode 100644 index 0000000..a1774b7 --- /dev/null +++ b/stc/stcconfigerror.c @@ -0,0 +1,229 @@ +/* -*- 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> + */ + +#include "config.h" + +#include <string.h> + +#include "stcconfigerror.h" + +/** + * SECTION:stcconfigerror + * @title: StcConfigError + * @short_description: Details about configuration file errors + * + * Boxed type used for reporting errors in configuration files. + */ + +/** + * StcConfigError: + * + * The #StcConfigError structure contains only private data and should + * only be accessed using the provided API. + */ +struct _StcConfigError +{ + volatile gint ref_count; + gchar *item_id; + gchar *filename; + gint line_number; + StcConfigErrorSeverity severity; + gchar *message; +}; + +G_DEFINE_BOXED_TYPE (StcConfigError, stc_config_error, stc_config_error_ref, stc_config_error_unref); + +/** + * stc_config_error_ref: + * @error: A #StcConfigError. + * + * Increases the reference count on @error. + * + * Returns: The same @error. + */ +StcConfigError * +stc_config_error_ref (StcConfigError *error) +{ + g_atomic_int_inc (&error->ref_count); + return error; +} + +/** + * stc_config_error_unref: + * @error: A #StcConfigError. + * + * Decreases the reference count on @error. When the reference count + * drops to zero, all resources used are freed. + */ +void +stc_config_error_unref (StcConfigError *error) +{ + if (g_atomic_int_dec_and_test (&error->ref_count)) + { + g_free (error->item_id); + g_free (error->filename); + g_free (error->message); + g_slice_free (StcConfigError, error); + } +} + +/** + * stc_config_error_new: + * @item_id: The item the error is about or %NULL if not about a specific item. + * @filename: The filename where the error is. + * @line_number: The line number where the error is or -1 if unknown. + * @severity: The severity of the error. + * @message: A human-readable message describing the error. + * + * Create a new #StcConfigError. + * + * Returns: A newly allocated #StcConfigError structure. Free with stc_config_error_unref(). + */ +StcConfigError * +stc_config_error_new (const gchar *item_id, + const gchar *filename, + gint line_number, + StcConfigErrorSeverity severity, + const gchar *message) +{ + StcConfigError *error; + error = g_slice_new0 (StcConfigError); + error->ref_count = 1; + error->item_id = g_strdup (item_id); + error->filename = g_strdup (filename); + error->line_number = line_number; + error->severity = severity; + error->message = g_strdup (message); + return error; +} + +/** + * stc_config_error_get_item_id: + * @error: A #StcConfigError. + * + * Gets the item the error is for, if any. + * + * Returns: A string owned by @error or %NULL. Do not free. + */ +const gchar * +stc_config_error_get_item_id (StcConfigError *error) +{ + return error->item_id; +} + +/** + * stc_config_error_get_filename: + * @error: A #StcConfigError. + * + * Gets the filename where the error is. + * + * Returns: A string owned by @error. Do not free. + */ +const gchar * +stc_config_error_get_filename (StcConfigError *error) +{ + return error->filename; +} + +/** + * stc_config_error_get_line_number: + * @error: A #StcConfigError. + * + * Gets the line number where the error is. + * + * Returns: The line number or -1 if unknown. + */ +gint +stc_config_error_get_line_number (StcConfigError *error) +{ + return error->line_number; +} + +/** + * stc_config_error_get_severity: + * @error: A #StcConfigError. + * + * Gets the severity of the error. + * + * Returns: A value from the #StcConfigErrorSeverity enumeration. + */ +StcConfigErrorSeverity +stc_config_error_get_severity (StcConfigError *error) +{ + return error->severity; +} + +/** + * stc_config_error_get_message: + * @error: A #StcConfigError. + * + * Gets the human-readable message describing the error. + * + * Returns: A string owned by @error. Do not free. + */ +const gchar * +stc_config_error_get_message (StcConfigError *error) +{ + return error->message; +} + +/** + * stc_config_error_compare: + * @error: A #StcConfigError. + * @other_error: Another #StcConfigError. + * + * Compares @error and @other_error. + * + * Returns: Negative value if @error < @other_error; zero if @error = @other_error; positive value if @error > @other_error. + */ +gint +stc_config_error_compare (StcConfigError *error, + StcConfigError *other_error) +{ + StcConfigError *a; + StcConfigError *b; + gint ret; + + a = error; + b = other_error; + + ret = g_strcmp0 (a->filename, b->filename); + if (ret != 0) + goto out; + + ret = a->line_number - b->line_number; + if (ret != 0) + goto out; + + ret = g_strcmp0 (a->item_id, b->item_id); + if (ret != 0) + goto out; + + ret = a->severity - b->severity; + if (ret != 0) + goto out; + + ret = g_strcmp0 (a->message, b->message); + + out: + return ret; +} diff --git a/stc/stcconfigerror.h b/stc/stcconfigerror.h new file mode 100644 index 0000000..4e56c1d --- /dev/null +++ b/stc/stcconfigerror.h @@ -0,0 +1,60 @@ +/* -*- 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_INSIDE_STC_H__) && !defined (STC_COMPILATION) +#error "Only <stc/stc.h> can be included directly." +#endif + +#ifndef __STC_CONFIG_ERROR_H__ +#define __STC_CONFIG_ERROR_H__ + +#include <stc/stctypes.h> + +G_BEGIN_DECLS + +/** + * STC_TYPE_CONFIG_ERROR: + * + * The #GType for a boxed type holding a #StcConfigError structure. + */ +#define STC_TYPE_CONFIG_ERROR (stc_config_error_get_type ()) + +GType stc_config_error_get_type (void) G_GNUC_CONST; +StcConfigError *stc_config_error_new (const gchar *item_id, + const gchar *filename, + gint line_number, + StcConfigErrorSeverity severity, + const gchar *message); +StcConfigError *stc_config_error_ref (StcConfigError *error); +void stc_config_error_unref (StcConfigError *error); +gint stc_config_error_compare (StcConfigError *error, + StcConfigError *other_error); +const gchar *stc_config_error_get_item_id (StcConfigError *error); +const gchar *stc_config_error_get_filename (StcConfigError *error); +gint stc_config_error_get_line_number (StcConfigError *error); +StcConfigErrorSeverity stc_config_error_get_severity (StcConfigError *error); +const gchar *stc_config_error_get_message (StcConfigError *error); + +G_END_DECLS + +#endif /* __STC_CONFIG_ERROR_H__ */ @@ -163,12 +163,6 @@ _StcDaemon *_stc_daemon_new (void); static void maybe_act_on_item (_StcDaemon *daemon, StcItem *item); -static void error_handler (StcMonitor *monitor, - const gchar *filename, - gint line_no, - GError *error, - gpointer user_data); - static void on_item_added (StcMonitor *monitor, StcItem *item, gpointer user_data); @@ -181,8 +175,13 @@ static void on_item_changed (StcMonitor *monitor, StcItem *item, gpointer user_data); +static void on_config_reloaded (StcMonitor *monitor, + gpointer user_data); + static StcOperation * get_operation (_StcDaemon *daemon); +static void print_all_config_errors (StcMonitor *monitor); + enum { PENDING_ITEMS_CHANGED_SIGNAL, @@ -213,7 +212,9 @@ _stc_daemon_init (_StcDaemon *daemon) daemon->pending_item_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - daemon->monitor = stc_monitor_new (error_handler, NULL); + daemon->monitor = stc_monitor_new (); + print_all_config_errors (daemon->monitor); + g_signal_connect (daemon->monitor, "item-added", G_CALLBACK (on_item_added), @@ -226,6 +227,10 @@ _stc_daemon_init (_StcDaemon *daemon) "item-changed", G_CALLBACK (on_item_changed), daemon); + g_signal_connect (daemon->monitor, + "config-reloaded", + G_CALLBACK (on_config_reloaded), + daemon); /* coldplug */ log_message (LOG_LEVEL_DEBUG, "coldplugging items"); @@ -449,27 +454,66 @@ _stc_daemon_start_serialized (_StcDaemon *daemon, /* ---------------------------------------------------------------------------------------------------- */ static void -error_handler (StcMonitor *monitor, - const gchar *filename, - gint line_no, - GError *error, - gpointer user_data) +print_config_error (StcMonitor *monitor, + StcConfigError *config_error) { GString *str; + const gchar *item_id; + const gchar *filename; + gint line_number; + StcConfigErrorSeverity severity; + const gchar *message; + LogLevel level; + + item_id = stc_config_error_get_item_id (config_error); + filename = stc_config_error_get_filename (config_error); + line_number = stc_config_error_get_line_number (config_error); + severity = stc_config_error_get_severity (config_error); + message = stc_config_error_get_message (config_error); + str = g_string_new (NULL); if (filename != NULL) { - if (line_no >= 0) - g_string_append_printf (str, "%s:%d: ", filename, line_no); + if (line_number >= 0) + g_string_append_printf (str, "%s:%d: ", filename, line_number); else g_string_append_printf (str, "%s: ", filename); } - g_string_append_printf (str, "%s", error->message); - g_string_append_printf (str, " (%s, %d)", g_quark_to_string (error->domain), error->code); - log_message (LOG_LEVEL_WARNING, "Error while parsing %s", str->str); + if (item_id != NULL) + { + g_string_append_printf (str, "%s: ", item_id); + } + switch (severity) + { + case STC_CONFIG_ERROR_SEVERITY_WARNING: + level = LOG_LEVEL_WARNING; + break; + + case STC_CONFIG_ERROR_SEVERITY_ERROR: + level = LOG_LEVEL_ERROR; + break; + } + g_string_append_printf (str, "%s", message); + log_message (level, "%s", str->str); g_string_free (str, TRUE); } +static void +print_all_config_errors (StcMonitor *monitor) +{ + GList *config_errors; + GList *l; + + config_errors = stc_monitor_get_config_errors (monitor); + for (l = config_errors; l != NULL; l = l->next) + { + StcConfigError *config_error = l->data; + print_config_error (monitor, config_error); + } + g_list_foreach (config_errors, (GFunc) stc_config_error_unref, NULL); + g_list_free (config_errors); +} + /* ---------------------------------------------------------------------------------------------------- */ static void @@ -826,6 +870,16 @@ on_item_changed (StcMonitor *monitor, maybe_act_on_item (daemon, item); } +static void +on_config_reloaded (StcMonitor *monitor, + gpointer user_data) +{ + _StcDaemon *daemon = _STC_DAEMON (user_data); + + log_message (LOG_LEVEL_INFO, "Configuration reloaded"); + print_all_config_errors (daemon->monitor); +} + /* ---------------------------------------------------------------------------------------------------- */ static gboolean diff --git a/stc/stcenums.h b/stc/stcenums.h index 5737310..b0ebd7e 100644 --- a/stc/stcenums.h +++ b/stc/stcenums.h @@ -98,6 +98,20 @@ typedef enum STC_ITEM_STATE_DEFUNCT } StcItemState; +/** + * StcConfigErrorSeverity: + * @STC_CONFIG_ERROR_SEVERITY_WARNING: There is a problem but no configuration items was dropped. + * @STC_CONFIG_ERROR_SEVERITY_ERROR: There is a problem including dropping one or more configuration items. + * + * Severity level used in #StcConfigError. + */ +typedef enum +{ + STC_CONFIG_ERROR_SEVERITY_WARNING, + STC_CONFIG_ERROR_SEVERITY_ERROR +} StcConfigErrorSeverity; + + G_END_DECLS #endif /* __STC_ENUMS_H__ */ diff --git a/stc/stcmonitor.c b/stc/stcmonitor.c index db17251..c38d254 100644 --- a/stc/stcmonitor.c +++ b/stc/stcmonitor.c @@ -31,6 +31,7 @@ #include "stcitem.h" #include "stcmount.h" #include "stcmountmonitor.h" +#include "stcconfigerror.h" /** * SECTION:stcmonitor @@ -58,14 +59,16 @@ struct _StcMonitor GMainContext *context; - StcErrorHandlerFunc error_handler_func; - gpointer error_handler_user_data; - GList *items; GHashTable *map_id_to_item; GFileMonitor *conf_file_monitor; GFileMonitor *conf_dir_monitor; + + GList *config_errors; + + gboolean invalidated; + GSource *refresh_config_source; }; typedef struct _StcMonitorClass StcMonitorClass; @@ -80,13 +83,8 @@ struct _StcMonitorClass StcItem *item); void (*item_changed) (StcMonitor *monitor, StcItem *item); -}; -enum -{ - PROP_0, - PROP_ERROR_HANDLER_FUNC, - PROP_ERROR_HANDLER_USER_DATA + void (*config_reloaded) (StcMonitor *monitor); }; enum @@ -94,12 +92,15 @@ enum ITEM_ADDED_SIGNAL, ITEM_REMOVED_SIGNAL, ITEM_CHANGED_SIGNAL, + CONFIG_RELOADED_SIGNAL, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; -static void stc_monitor_update (StcMonitor *monitor); +static void stc_monitor_update_config (StcMonitor *monitor); +static void stc_monitor_invalidate_config (StcMonitor *monitor); +static void stc_monitor_ensure_config (StcMonitor *monitor); static void on_uevent (GUdevClient *client, const gchar *action, @@ -126,18 +127,13 @@ static void on_conf_dir_monitor_changed (GFileMonitor *monitor, GFileMonitorEvent event_type, gpointer user_data); -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); +static void append_config_error (GList **list, + const gchar *item_id, + const gchar *filename, + gint line_number, + StcConfigErrorSeverity severity, + const gchar *message_format, + ...) G_GNUC_PRINTF(6, 7); G_DEFINE_TYPE (StcMonitor, stc_monitor, G_TYPE_OBJECT); @@ -146,6 +142,12 @@ stc_monitor_finalize (GObject *object) { StcMonitor *monitor = STC_MONITOR (object); + if (monitor->refresh_config_source != NULL) + { + g_source_destroy (monitor->refresh_config_source); + monitor->refresh_config_source = NULL; + } + g_signal_handlers_disconnect_by_func (monitor->gudev_client, G_CALLBACK (on_uevent), monitor); @@ -182,54 +184,6 @@ stc_monitor_finalize (GObject *object) } static void -stc_monitor_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - StcMonitor *monitor = STC_MONITOR (object); - - switch (prop_id) - { - case PROP_ERROR_HANDLER_FUNC: - g_value_set_pointer (value, monitor->error_handler_func); - break; - - case PROP_ERROR_HANDLER_USER_DATA: - g_value_set_pointer (value, monitor->error_handler_user_data); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -stc_monitor_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - StcMonitor *monitor = STC_MONITOR (object); - - switch (prop_id) - { - case PROP_ERROR_HANDLER_FUNC: - monitor->error_handler_func = g_value_get_pointer (value); - break; - - case PROP_ERROR_HANDLER_USER_DATA: - monitor->error_handler_user_data = g_value_get_pointer (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void stc_monitor_init (StcMonitor *monitor) { monitor->context = g_main_context_get_thread_default (); @@ -261,10 +215,8 @@ stc_monitor_constructed (GObject *object) g_file_monitor_set_rate_limit (monitor->conf_file_monitor, 50); if (monitor->conf_file_monitor == NULL) { - stc_monitor_emit_gerror (monitor, - path, - -1, - error); + /* use g_warning() since it indicates an installation problem if the stc.conf file is missing */ + g_warning ("Cannot monitor file %s: %s", path, error->message); g_error_free (error); } g_signal_connect (monitor->conf_file_monitor, @@ -281,10 +233,8 @@ stc_monitor_constructed (GObject *object) 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); + /* use g_warning() since it indicates an installation problem if the stc.conf file is missing */ + g_warning ("Cannot monitor directory %s: %s", path, error->message); g_error_free (error); } g_signal_connect (monitor->conf_dir_monitor, @@ -310,7 +260,8 @@ stc_monitor_constructed (GObject *object) G_CALLBACK (on_mount_removed), monitor); - stc_monitor_update (monitor); + /* initial coldplug */ + stc_monitor_update_config (monitor); if (G_OBJECT_CLASS (stc_monitor_parent_class)->constructed) G_OBJECT_CLASS (stc_monitor_parent_class)->constructed (object); @@ -324,39 +275,6 @@ 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:error-handler-func: - * - * Pointer to a #StcErrorHandlerFunc to call on error, or %NULL. - */ - g_object_class_install_property (gobject_class, - PROP_ERROR_HANDLER_FUNC, - g_param_spec_pointer ("error-handler-func", - "Error handler function", - "Function to call on errors", - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - /** - * StcMonitor:error-handler-user-data: - * - * User-data to pass to the function pointer defined in the #StcMonitor:error-handler-func property. - */ - g_object_class_install_property (gobject_class, - PROP_ERROR_HANDLER_USER_DATA, - g_param_spec_pointer ("error-handler-user-data", - "Error handler user data", - "Error handler user data", - G_PARAM_READABLE | - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - /** * StcMonitor::item-added: @@ -399,7 +317,8 @@ stc_monitor_class_init (StcMonitorClass *klass) * @monitor: The #StcMonitor emitting the signal. * @item: The item that changed. * - * Emitted when an item changes. + * Emitted when an item changes (either the configuration of item or + * the state of the item). */ signals[ITEM_CHANGED_SIGNAL] = g_signal_new ("item-changed", G_TYPE_FROM_CLASS (klass), @@ -411,28 +330,36 @@ stc_monitor_class_init (StcMonitorClass *klass) G_TYPE_NONE, 1, STC_TYPE_ITEM); + + /** + * StcMonitor::config-reloaded: + * @monitor: The #StcMonitor emitting the signal. + * + * Emitted when the configuration files has been reloaded. + */ + signals[CONFIG_RELOADED_SIGNAL] = g_signal_new ("config-reloaded", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (StcMonitorClass, config_reloaded), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); } /** * stc_monitor_new: - * @error_handler_func: A function to handle errors or %NULL. - * @error_handler_user_data: The @user_data #gpointer to pass to @error_handler_func. * - * Creates a new #StcMonitor that reads configuration files from the - * default configuration directory - * (e.g. <filename>/etc</filename>). + * Creates a new #StcMonitor. * * Returns: A #StcMonitor. Free with g_object_unref(). */ StcMonitor * -stc_monitor_new (StcErrorHandlerFunc error_handler_func, - gpointer error_handler_user_data) +stc_monitor_new (void) { StcMonitor *monitor; - monitor = STC_MONITOR (g_object_new (STC_TYPE_MONITOR, - "error-handler-func", error_handler_func, - "error-handler-user-data", error_handler_user_data, - NULL)); + monitor = STC_MONITOR (g_object_new (STC_TYPE_MONITOR, NULL)); return monitor; } @@ -453,6 +380,8 @@ stc_monitor_get_items (StcMonitor *monitor) g_return_val_if_fail (STC_IS_MONITOR (monitor), NULL); + stc_monitor_ensure_config (monitor); + ret = g_list_copy (monitor->items); g_list_foreach (ret, (GFunc) g_object_ref, NULL); @@ -522,7 +451,8 @@ check_validity (StcMonitor *monitor, StcItemType item_type, GHashTable *options, const gchar *path, - gint line_no) + gint line_no, + GList **config_errors) { gboolean ret; @@ -535,22 +465,22 @@ check_validity (StcMonitor *monitor, g_hash_table_lookup (options, "FilesystemUUID") != NULL || g_hash_table_lookup (options, "FilesystemLabel") != NULL)) { - stc_monitor_emit_stc_error (monitor, - path, - line_no, - STC_ERROR_FAILED, - "Filesystem item %s needs one of FilesystemDevice, FilesystemUUID and FilesystemLabel options set.", - item_id); + append_config_error (config_errors, + item_id, + path, + line_no, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Filesystem item needs one of FilesystemDevice, FilesystemUUID and FilesystemLabel options set"); goto out; } if (g_hash_table_lookup (options, "FilesystemMountPath") == NULL) { - stc_monitor_emit_stc_error (monitor, - path, - line_no, - STC_ERROR_INSUFFICIENT_DATA, - "Filesystem item %s is missing option FilesystemMountPath.", - item_id); + append_config_error (config_errors, + item_id, + path, + line_no, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Filesystem item is missing option FilesystemMountPath"); goto out; } break; @@ -559,12 +489,12 @@ check_validity (StcMonitor *monitor, if (!(g_hash_table_lookup (options, "RaidUUID") != NULL || g_hash_table_lookup (options, "RaidName") != NULL)) { - stc_monitor_emit_stc_error (monitor, - path, - line_no, - STC_ERROR_FAILED, - "Raid item %s needs one of RaidUUID and RaidName options set.", - item_id); + append_config_error (config_errors, + item_id, + path, + line_no, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Raid item needs one of RaidUUID and RaidName options set"); goto out; } break; @@ -572,23 +502,18 @@ check_validity (StcMonitor *monitor, case STC_ITEM_TYPE_LUKS: if (!(g_hash_table_lookup (options, "LuksUUID") != NULL)) { - stc_monitor_emit_stc_error (monitor, - path, - line_no, - STC_ERROR_FAILED, - "Luks item %s needs the LuksUUID option set.", - item_id); + append_config_error (config_errors, + item_id, + path, + line_no, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Luks item needs the LuksUUID option set"); goto out; } break; default: - stc_monitor_emit_stc_error (monitor, - path, - line_no, - STC_ERROR_FAILED, - "Unsupported item type %d.", - item_type); + g_assert_not_reached (); goto out; } @@ -601,21 +526,33 @@ check_validity (StcMonitor *monitor, static GList * stc_monitor_load_one_file (StcMonitor *monitor, const gchar *path, - GError **error) + GList **config_errors) { GList *ret; GKeyFile *key_file; gchar **groups; guint n, m; gboolean failed; + GError *error; 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; + error = NULL; + if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error)) + { + append_config_error (config_errors, + NULL, + path, + -1, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Error parsing key/value file: %s", + error->message); + g_error_free (error); + goto out; + } groups = g_key_file_get_groups (key_file, NULL); for (n = 0; groups != NULL && groups[n] != NULL; n++) @@ -631,7 +568,6 @@ stc_monitor_load_one_file (StcMonitor *monitor, gchar **depends_strv; const gchar *item_type_as_string; gchar **keys; - GError *local_error; gboolean auto_val; tokens = NULL; @@ -643,25 +579,27 @@ stc_monitor_load_one_file (StcMonitor *monitor, 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); + append_config_error (config_errors, + NULL, + path, + -1, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Error parsing group name `%s'. Expected two tokens, found %d", + group, g_strv_length (tokens)); 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); + append_config_error (config_errors, + NULL, + path, + -1, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Unrecognized item type %s in group `%s'", + tokens[0], + group); goto process_next_item; } item_type_as_string = tokens[0]; @@ -669,14 +607,15 @@ stc_monitor_load_one_file (StcMonitor *monitor, 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); + append_config_error (config_errors, + NULL, + path, + -1, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Invalid item identifer %s in group `%s'. " + "Only characters in [a-zA-Z0-9_] are supported", + item_id, + group); goto process_next_item; } @@ -704,23 +643,22 @@ stc_monitor_load_one_file (StcMonitor *monitor, comment = g_key_file_get_string (key_file, group, "Comment", NULL); /* Auto is optional (but we want to complain if set to an invalid value e.g. 'True') */ - local_error = NULL; - auto_val = g_key_file_get_boolean (key_file, group, "Auto", &local_error); - if (local_error != NULL) + error = NULL; + auto_val = g_key_file_get_boolean (key_file, group, "Auto", &error); + if (error != NULL) { - if (local_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) + if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { - stc_monitor_emit_stc_error (monitor, - path, - -1, - STC_ERROR_PARSE_ERROR, - "Error parsing key Auto for item %s of type %s: %s", - item_id, - tokens[0], - local_error->message); + append_config_error (config_errors, + item_id, + path, + -1, + STC_CONFIG_ERROR_SEVERITY_WARNING, + "Error parsing key Auto: %s", + error->message); } /* not fatal, just continue */ - g_error_free (local_error); + g_error_free (error); } /* Depends is optional */ @@ -732,7 +670,8 @@ stc_monitor_load_one_file (StcMonitor *monitor, item_type, options, path, - -1)) + -1, + config_errors)) { goto process_next_item; } @@ -778,29 +717,49 @@ stc_monitor_load_one_file (StcMonitor *monitor, return ret; } -static GList * -stc_monitor_load_one_file_emit_error (StcMonitor *monitor, - const gchar *path) +static void +diff_sorted_lists (GList *list1, + GList *list2, + GCompareFunc compare, + GList **added, + GList **removed) { - GError *error; - GList *ret; + int order; - error = NULL; - ret = stc_monitor_load_one_file (monitor, path, &error); - if (error != NULL) + *added = *removed = NULL; + + while (list1 != NULL && list2 != NULL) { - g_assert (ret == NULL); - stc_monitor_emit_gerror (monitor, - path, - -1, - error); - g_error_free (error); + 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; + } } - return ret; + 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 gint _p_strcmp (gconstpointer a, gconstpointer b) { @@ -814,7 +773,8 @@ _p_strcmp (gconstpointer a, gconstpointer b) } static GList * -stc_monitor_load_all_files (StcMonitor *monitor) +stc_monitor_load_all_files (StcMonitor *monitor, + GList **out_config_errors) { gchar *path; gchar *dir_path; @@ -829,8 +789,10 @@ stc_monitor_load_all_files (StcMonitor *monitor) GList *ll; GPtrArray *paths; const gchar *const *deps; + GList *config_errors; ret = NULL; + config_errors = NULL; /* hash used to weed out duplicates and check deps */ id_to_item = g_hash_table_new (g_str_hash, g_str_equal); @@ -842,10 +804,13 @@ stc_monitor_load_all_files (StcMonitor *monitor) dir = g_dir_open (dir_path, 0, &error); if (error != NULL) { - stc_monitor_emit_gerror (monitor, - dir_path, - -1, - error); + append_config_error (&config_errors, + NULL, + dir_path, + -1, + STC_CONFIG_ERROR_SEVERITY_ERROR, + "%s", + error->message); g_error_free (error); } else @@ -866,7 +831,7 @@ stc_monitor_load_all_files (StcMonitor *monitor) for (n = 0; n < paths->len; n++) { - loaded_items = stc_monitor_load_one_file_emit_error (monitor, paths->pdata[n]); + loaded_items = stc_monitor_load_one_file (monitor, paths->pdata[n], &config_errors); for (l = loaded_items; l != NULL; l = l->next) { StcItem *item; @@ -877,12 +842,12 @@ stc_monitor_load_all_files (StcMonitor *monitor) id = stc_item_get_id (item); if ((duplicate_item = g_hash_table_lookup (id_to_item, id)) != NULL) { - stc_monitor_emit_stc_error (monitor, - _stc_item_get_path (item), - _stc_item_get_line_no (item), - STC_ERROR_DUPLICATE_ITEM, - "Encountered item with duplicate id `%s'. Ignoring previous items.", - id); + append_config_error (&config_errors, + id, + _stc_item_get_path (item), + _stc_item_get_line_no (item), + STC_CONFIG_ERROR_SEVERITY_ERROR, + "Encountered item with duplicate id. Ignoring previous item"); g_assert (g_list_find (ret, duplicate_item) != NULL); ret = g_list_remove (ret, duplicate_item); } @@ -913,14 +878,14 @@ stc_monitor_load_all_files (StcMonitor *monitor) dep_item = g_hash_table_lookup (id_to_item, dep_id); if (dep_item == NULL) { - stc_monitor_emit_stc_error (monitor, - _stc_item_get_path (item), - _stc_item_get_line_no (item), - STC_ERROR_UNRESOLVED_DEPENDENCY, - "Item %s has unresolved dependency %s.", - id, - dep_id); - /* don't remove the item */ + append_config_error (&config_errors, + id, + _stc_item_get_path (item), + _stc_item_get_line_no (item), + STC_CONFIG_ERROR_SEVERITY_WARNING, + "Item has unresolved dependency on non-existing item %s", + dep_id); + /* Not fatal - don't remove the item */ } } } @@ -929,6 +894,15 @@ stc_monitor_load_all_files (StcMonitor *monitor) ret = g_list_reverse (ret); + if (out_config_errors != NULL) + { + *out_config_errors = config_errors; + } + else + { + g_list_foreach (config_errors, (GFunc) stc_config_error_unref, NULL); + g_list_free (config_errors); + } return ret; } @@ -946,7 +920,7 @@ _item_cmp_func (gconstpointer a, } static void -stc_monitor_update (StcMonitor *monitor) +stc_monitor_update_config (StcMonitor *monitor) { GList *loaded_items; GList *l; @@ -955,6 +929,9 @@ stc_monitor_update (StcMonitor *monitor) GList *items_added; GList *items_removed; GHashTable *loaded_items_ids; + GList *config_errors; + GList *errors_added; + GList *errors_removed; items_changed = NULL; items_added = NULL; @@ -963,7 +940,7 @@ stc_monitor_update (StcMonitor *monitor) loaded_items_ids = g_hash_table_new (g_str_hash, g_str_equal); /* add/change items */ - loaded_items = stc_monitor_load_all_files (monitor); + loaded_items = stc_monitor_load_all_files (monitor, &config_errors); for (l = loaded_items; l != NULL; l = l->next) { StcItem *item = STC_ITEM (l->data); @@ -1018,7 +995,36 @@ stc_monitor_update (StcMonitor *monitor) _stc_item_update_state (item); } - /* emit signals only when our monitor object has been completely updated */ + config_errors = g_list_sort (config_errors, (GCompareFunc) stc_config_error_compare); + diff_sorted_lists (monitor->config_errors, + config_errors, + (GCompareFunc) stc_config_error_compare, + &errors_added, + &errors_removed); + for (l = errors_removed; l != NULL; l = l->next) + { + StcConfigError *config_error = l->data; + g_assert (g_list_find (monitor->config_errors, config_error) != NULL); + monitor->config_errors = g_list_remove (monitor->config_errors, config_error); + stc_config_error_unref (config_error); + } + for (l = errors_added; l != NULL; l = l->next) + { + StcConfigError *config_error = l->data; + monitor->config_errors = g_list_prepend (monitor->config_errors, stc_config_error_ref (config_error)); + } + g_list_foreach (config_errors, (GFunc) stc_config_error_unref, NULL); + g_list_free (config_errors); + g_list_free (errors_removed); + g_list_free (errors_added); + monitor->config_errors = g_list_sort (monitor->config_errors, (GCompareFunc) stc_config_error_compare); + + /* emit signals only when our monitor object has been completely + * updated - otherwise we get infinit loops if someone is calling a + * method that calls ensure() + */ + monitor->invalidated = FALSE; + for (l = items_removed; l != NULL; l = l->next) { StcItem *item = STC_ITEM (l->data); @@ -1041,6 +1047,8 @@ stc_monitor_update (StcMonitor *monitor) g_object_unref (item); } g_list_free (items_changed); + + g_signal_emit (monitor, signals[CONFIG_RELOADED_SIGNAL], 0); } static void @@ -1050,8 +1058,7 @@ on_conf_file_monitor_changed (GFileMonitor *monitor, 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)); + stc_monitor_invalidate_config (STC_MONITOR (user_data)); } static void @@ -1061,45 +1068,7 @@ on_conf_dir_monitor_changed (GFileMonitor *monitor, 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, - 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); - - if (monitor->error_handler_func != NULL) - monitor->error_handler_func (monitor, filename, line_no, error, monitor->error_handler_user_data); -} - -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); + stc_monitor_invalidate_config (STC_MONITOR (user_data)); } GUdevClient * @@ -1132,6 +1101,8 @@ on_uevent (GUdevClient *client, monitor); #endif + stc_monitor_ensure_config (monitor); + /* we don't really know what configuration items this applies to so * we just update all of them */ @@ -1163,6 +1134,8 @@ on_mount_added (_StcMountMonitor *mount_monitor, _stc_mount_get_mount_path (mount)); #endif + stc_monitor_ensure_config (monitor); + /* we don't really know what configuration items this applies to so * we just update all of them */ @@ -1194,6 +1167,8 @@ on_mount_removed (_StcMountMonitor *mount_monitor, _stc_mount_get_mount_path (mount)); #endif + stc_monitor_ensure_config (monitor); + /* we don't really know what configuration items this applies to so * we just update all of them */ @@ -1208,3 +1183,107 @@ on_mount_removed (_StcMountMonitor *mount_monitor, } } +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * stc_monitor_get_config_errors: + * @monitor: A #StcMonitor. + * + * Gets the errors/warning detected when parsing configuration files. + * + * If reporting errors in a log file, you typically want to call this + * method at startup as well as every time the + * #StcMonitor::config-reloaded signal is emitted. + * + * Returns: A list of #StcConfigError structures. The returned list + * must be freed with g_list_free() after each element has been freed + * with stc_config_error_unref(). + */ +GList * +stc_monitor_get_config_errors (StcMonitor *monitor) +{ + GList *ret; + + g_return_val_if_fail (STC_IS_MONITOR (monitor), NULL); + + stc_monitor_ensure_config (monitor); + + ret = g_list_copy (monitor->config_errors); + g_list_foreach (ret, (GFunc) stc_config_error_ref, NULL); + + return ret; +} + +static void +append_config_error (GList **list, + const gchar *item_id, + const gchar *filename, + gint line_number, + StcConfigErrorSeverity severity, + const gchar *message_format, + ...) +{ + gchar *message; + va_list var_args; + + va_start (var_args, message_format); + message = g_strdup_vprintf (message_format, var_args); + va_end (var_args); + + *list = g_list_prepend (*list, + stc_config_error_new (item_id, + filename, + line_number, + severity, + message)); + //g_debug ("message=`%s'", message); + g_free (message); +} + +static gboolean +stc_monitor_invalidate_config_on_refresh_timeout_cb (gpointer user_data) +{ + StcMonitor *monitor = STC_MONITOR (user_data); + //g_debug ("%s %d", G_STRFUNC, monitor->invalidated); + g_assert (monitor->refresh_config_source != NULL); + monitor->refresh_config_source = NULL; + if (monitor->invalidated) + stc_monitor_ensure_config (monitor); + return FALSE; /* remove the source */ +} + +static void +stc_monitor_invalidate_config (StcMonitor *monitor) +{ + //g_debug ("%s %d", G_STRFUNC, monitor->invalidated); + if (!monitor->invalidated) + { + /* reload config items in at least two seconds */ + g_assert (monitor->refresh_config_source == NULL); + monitor->refresh_config_source = g_timeout_source_new_seconds (2); + g_source_set_priority (monitor->refresh_config_source, G_PRIORITY_DEFAULT); + g_source_set_callback (monitor->refresh_config_source, + stc_monitor_invalidate_config_on_refresh_timeout_cb, + monitor, + NULL); /* GDestroyNotify */ + g_source_attach (monitor->refresh_config_source, monitor->context); + g_source_unref (monitor->refresh_config_source); + + monitor->invalidated = TRUE; + } + else + { + g_assert (monitor->refresh_config_source != NULL); + } +} + +static void +stc_monitor_ensure_config (StcMonitor *monitor) +{ + //g_debug ("%s %d", G_STRFUNC, monitor->invalidated); + if (monitor->invalidated) + { + stc_monitor_update_config (monitor); + } +} + diff --git a/stc/stcmonitor.h b/stc/stcmonitor.h index 69be93e..0cdeff5 100644 --- a/stc/stcmonitor.h +++ b/stc/stcmonitor.h @@ -36,32 +36,12 @@ G_BEGIN_DECLS #define STC_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), STC_TYPE_MONITOR, StcMonitor)) #define STC_IS_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), STC_TYPE_MONITOR)) -/** - * StcErrorHandlerFunc: - * @monitor: The #StcMonitor with the condition. - * @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. - * @user_data: User data. - * - * Function pointer for a function that is called whenever @monitor encounters an error condition. - * - * This function is called from the - * <link linkend="g-main-context-push-thread-default">thread-default main loop</link> - * that @monitor was created in. - */ -typedef void (*StcErrorHandlerFunc) (StcMonitor *monitor, - const gchar *filename, - gint line_no, - GError *error, - gpointer user_data); - GType stc_monitor_get_type (void) G_GNUC_CONST; -StcMonitor *stc_monitor_new (StcErrorHandlerFunc error_handler_func, - gpointer error_handler_user_data); +StcMonitor *stc_monitor_new (void); GList *stc_monitor_get_items (StcMonitor *monitor); StcItem *stc_monitor_get_item_by_id (StcMonitor *monitor, const gchar *item_id); +GList *stc_monitor_get_config_errors (StcMonitor *monitor); G_END_DECLS diff --git a/stc/stctypes.h b/stc/stctypes.h index 7756682..1f39de2 100644 --- a/stc/stctypes.h +++ b/stc/stctypes.h @@ -41,6 +41,9 @@ typedef struct _StcMonitor StcMonitor; struct _StcOperation; typedef struct _StcOperation StcOperation; +struct _StcConfigError; +typedef struct _StcConfigError StcConfigError; + G_END_DECLS #endif /* __STC_TYPES_H__ */ @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> +#include <locale.h> #include <glib/gstdio.h> @@ -118,32 +119,40 @@ static void item_append_str (StcItem *item, /* ---------------------------------------------------------------------------------------------------- */ -static void -on_error_append_str (StcMonitor *monitor, - const gchar *filename, - gint line_no, - GError *error, - gpointer user_data) +static gchar * +_stc_monitor_config_errors_to_string (StcMonitor *monitor) { - GString *str = user_data; + GList *errors; + GList *l; + GString *str; - if (filename != NULL) + str = g_string_new (NULL); + errors = stc_monitor_get_config_errors (monitor); + for (l = errors; l != NULL; l = l->next) { - if (line_no >= 0) - g_string_append_printf (str, "%s:%d: ", filename, line_no); - else - g_string_append_printf (str, "%s: ", filename); + StcConfigError *error = l->data; + g_string_append_printf (str, + "%s line=%d item_id=%s sev=%d: %s\n", + stc_config_error_get_filename (error), + stc_config_error_get_line_number (error), + stc_config_error_get_item_id (error) != NULL ? stc_config_error_get_item_id (error) : "-", + stc_config_error_get_severity (error), + stc_config_error_get_message (error)); } + g_list_foreach (errors, (GFunc) stc_config_error_unref, NULL); + g_list_free (errors); - g_string_append_printf (str, "%s (%s, %d)\n", error->message, g_quark_to_string (error->domain), error->code); + return g_string_free (str, FALSE); } +/* ---------------------------------------------------------------------------------------------------- */ + static void test_stc_basic (void) { StcMonitor *monitor; - monitor = stc_monitor_new (NULL, NULL); + monitor = stc_monitor_new (); g_object_unref (monitor); } @@ -151,19 +160,18 @@ static void test_stc_no_conf (void) { StcMonitor *monitor; - GString *str; - - str = g_string_new (NULL); g_setenv ("STC_CONFIG_DIR", SRCDIR "/nonexistant", TRUE); - monitor = stc_monitor_new (on_error_append_str, str); + monitor = stc_monitor_new (); g_object_unref (monitor); +#if 0 /* TODO */ g_assert_cmpstr (str->str, ==, 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); +#endif } static void @@ -171,34 +179,35 @@ test_stc_semi_valid_conf (void) { StcMonitor *monitor; GString *str; - GString *str2; GList *items; GList *l; + gchar *s; - str = g_string_new (NULL); g_setenv ("STC_CONFIG_DIR", SRCDIR "/testdata/semi_valid_conf", TRUE); - monitor = stc_monitor_new (on_error_append_str, str); - g_assert_cmpstr (str->str, ==, - SRCDIR "/testdata/semi_valid_conf/stc.conf: Error parsing key Auto for item BigStorage of type Filesystem: Key file contains key 'Auto' which has value that cannot be interpreted. (stc-error-quark, 1)\n" - SRCDIR "/testdata/semi_valid_conf/stc.conf: Error parsing group name `Blah' (stc-error-quark, 1)\n" - SRCDIR "/testdata/semi_valid_conf/stc.conf: Error parsing item type `XYZType' in group `XYZType ValidIdentifier' (stc-error-quark, 1)\n" - 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: Raid item bar needs one of RaidUUID and RaidName options set. (stc-error-quark, 0)\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" - SRCDIR "/testdata/semi_valid_conf/stc.conf: Item ItemWithUnresolved has unresolved dependency NonExisting. (stc-error-quark, 3)\n" + monitor = stc_monitor_new (); + s = _stc_monitor_config_errors_to_string (monitor); + g_assert_cmpstr (s, ==, + SRCDIR "/testdata/semi_valid_conf/stc.conf line=-1 item_id=- sev=1: Error parsing group name `Blah'. Expected two tokens, found 1\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf line=-1 item_id=- sev=1: Error parsing group name `Filesystem with space'. Expected two tokens, found 3\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf line=-1 item_id=- sev=1: Invalid item identifer with-dash in group `Filesystem with-dash'. Only characters in [a-zA-Z0-9_] are supported\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf line=-1 item_id=- sev=1: Unrecognized item type XYZType in group `XYZType ValidIdentifier'\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf line=-1 item_id=BigStorage sev=0: Error parsing key Auto: Key file contains key 'Auto' which has value that cannot be interpreted.\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf line=-1 item_id=ItemWithUnresolved sev=0: Item has unresolved dependency on non-existing item NonExisting\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf line=-1 item_id=bar sev=1: Raid item needs one of RaidUUID and RaidName options set\n" + SRCDIR "/testdata/semi_valid_conf/stc.conf.d/91.conf line=-1 item_id=multiple_instances_same_id sev=1: Encountered item with duplicate id. Ignoring previous item\n" ); + g_free (s); - str2 = g_string_new (NULL); + str = 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); + item_append_str (item, str); } g_list_foreach (items, (GFunc) g_object_unref, NULL); g_list_free (items); - g_assert_cmpstr (str2->str, ==, + g_assert_cmpstr (str->str, ==, "id `in_pri_10'\n" "comment `(none)'\n" "type filesystem\n" @@ -279,10 +288,9 @@ test_stc_semi_valid_conf (void) "auto 1\n" "\n" ); - g_string_free (str2, TRUE); + g_string_free (str, TRUE); g_object_unref (monitor); - g_string_free (str, TRUE); } /* ---------------------------------------------------------------------------------------------------- */ @@ -314,43 +322,43 @@ typedef enum } _Event; static void -check_state (StcMonitor *monitor, - StcItem *item, - gint state, - _Event event) +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); + g_assert_cmpstr (stc_item_get_id (item), ==, "item1"); break; case 1: - g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); g_assert_cmpint (event, ==, _EVENT_ADDED); + g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); g_assert_cmpstr (stc_item_get_option (item, "FilesystemDevice"), ==, "/dev/sdc"); break; case 2: - g_assert_cmpstr (stc_item_get_id (item), ==, "item1"); g_assert_cmpint (event, ==, _EVENT_REMOVED); + g_assert_cmpstr (stc_item_get_id (item), ==, "item1"); break; case 3: - g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); g_assert_cmpint (event, ==, _EVENT_CHANGED); + g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); g_assert_cmpstr (stc_item_get_option (item, "FilesystemDevice"), ==, "/dev/sda"); break; case 4: - g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); g_assert_cmpint (event, ==, _EVENT_REMOVED); + g_assert_cmpstr (stc_item_get_id (item), ==, "item2"); break; case 5: - g_assert_cmpstr (stc_item_get_id (item), ==, "item3"); g_assert_cmpint (event, ==, _EVENT_ADDED); + g_assert_cmpstr (stc_item_get_id (item), ==, "item3"); break; default: @@ -412,7 +420,7 @@ test_stc_monitoring (void) } g_setenv ("STC_CONFIG_DIR", dirname, TRUE); - monitor = stc_monitor_new (NULL, NULL); + monitor = stc_monitor_new (); /* main timeout */ timeout_id = g_timeout_add_seconds (30, on_timeout, NULL); @@ -527,6 +535,8 @@ main (int argc, g_type_init (); g_test_init (&argc, &argv, NULL); + setlocale (LC_ALL, "C"); + loop = g_main_loop_new (NULL, FALSE); g_test_add_func ("/stc/basic", test_stc_basic); |