[gnome-flashback] monitor-config-store: add way to define config store loading policy



commit 67cccd0015aca1392f32af126e22bf59fe4d29c7
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Mon Mar 7 12:20:23 2022 +0200

    monitor-config-store: add way to define config store loading policy
    
    This adds a way to define a way, at the system level, to define a policy
    of how monitor configuration files are loaded.
    
    The intended use case is to e.g. either prefer system level monitor
    configurations before user levels, or only allow system level
    configurations.
    
    Examples:
    
    Prefer system over user level configurations:
    
        <monitors version="2">
          <policy>
            <stores>
              <store>system</store>
              <store>user</store>
            </stores>
          </policy>
          <configuration>
            ...
          </configuration>
        </monitors>
    
    Only allow system level configurations:
    
        <monitors version="2">
          <policy>
            <stores>
              <store>system</store>
            </stores>
          </policy>
          <configuration>
            ...
          </configuration>
        </monitors>
    
    Based on mutter commit:
    https://gitlab.gnome.org/GNOME/mutter/-/commit/c611b64c53d16fe252d9f276e5c43b18868740c7

 backends/gf-monitor-config-store-private.h |   3 +
 backends/gf-monitor-config-store.c         | 456 +++++++++++++++++++++++------
 2 files changed, 373 insertions(+), 86 deletions(-)
---
diff --git a/backends/gf-monitor-config-store-private.h b/backends/gf-monitor-config-store-private.h
index 573156e..be4bff0 100644
--- a/backends/gf-monitor-config-store-private.h
+++ b/backends/gf-monitor-config-store-private.h
@@ -44,12 +44,15 @@ void                  gf_monitor_config_store_remove              (GfMonitorConf
 gboolean              gf_monitor_config_store_set_custom          (GfMonitorConfigStore  *config_store,
                                                                    const gchar           *read_path,
                                                                    const gchar           *write_path,
+                                                                   GfMonitorsConfigFlag   flags,
                                                                    GError               **error);
 
 gint                  gf_monitor_config_store_get_config_count    (GfMonitorConfigStore  *config_store);
 
 GfMonitorManager     *gf_monitor_config_store_get_monitor_manager (GfMonitorConfigStore  *config_store);
 
+void                  gf_monitor_config_store_reset               (GfMonitorConfigStore  *config_store);
+
 G_END_DECLS
 
 #endif
diff --git a/backends/gf-monitor-config-store.c b/backends/gf-monitor-config-store.c
index a509a93..63d2b02 100644
--- a/backends/gf-monitor-config-store.c
+++ b/backends/gf-monitor-config-store.c
@@ -111,6 +111,9 @@ struct _GfMonitorConfigStore
   GFile            *user_file;
   GFile            *custom_read_file;
   GFile            *custom_write_file;
+
+  gboolean          has_stores_policy;
+  GList            *stores_policy;
 };
 
 enum
@@ -152,13 +155,26 @@ typedef enum
   STATE_MONITOR_MODE_RATE,
   STATE_MONITOR_MODE_FLAG,
   STATE_MONITOR_UNDERSCANNING,
-  STATE_DISABLED
+  STATE_DISABLED,
+  STATE_POLICY,
+  STATE_STORES,
+  STATE_STORE
 } ParserState;
 
+typedef enum
+{
+  GF_CONFIG_STORE_NONE,
+  GF_CONFIG_STORE_SYSTEM,
+  GF_CONFIG_STORE_USER,
+} GfConfigStore;
+
 typedef struct
 {
   ParserState             state;
   GfMonitorConfigStore   *config_store;
+  GFile                  *file;
+
+  GHashTable             *pending_configs;
 
   ParserState             monitor_spec_parent_state;
 
@@ -172,6 +188,11 @@ typedef struct
   GfLogicalMonitorConfig *current_logical_monitor_config;
   GList                  *current_disabled_monitor_specs;
 
+  gboolean                seen_policy;
+  gboolean                seen_stores;
+  GfConfigStore           pending_store;
+  GList                  *stores;
+
   ParserState             unknown_state_root;
   int                     unknown_level;
 
@@ -197,6 +218,24 @@ static GParamSpec *config_store_properties[LAST_PROP] = { NULL };
 
 G_DEFINE_TYPE (GfMonitorConfigStore, gf_monitor_config_store, G_TYPE_OBJECT)
 
+static void
+replace_configs (GfMonitorConfigStore *config_store,
+                 GHashTable           *configs)
+{
+  GHashTableIter iter;
+  GfMonitorsConfigKey *key;
+  GfMonitorsConfig *config;
+
+  g_hash_table_iter_init (&iter, configs);
+  while (g_hash_table_iter_next (&iter,
+                                 (gpointer *) &key,
+                                 (gpointer *) &config))
+    {
+      g_hash_table_iter_steal (&iter);
+      g_hash_table_replace (config_store->configs, key, config);
+    }
+}
+
 static gboolean
 is_system_config (GfMonitorsConfig *config)
 {
@@ -282,7 +321,25 @@ handle_start_element (GMarkupParseContext  *context,
 
       case STATE_MONITORS:
         {
-          if (!g_str_equal (element_name, "configuration"))
+          if (g_str_equal (element_name, "configuration"))
+            {
+              parser->state = STATE_CONFIGURATION;
+              parser->current_was_migrated = FALSE;
+            }
+          else if (g_str_equal (element_name, "policy"))
+            {
+              if (parser->seen_policy)
+                {
+                  g_set_error (error,
+                               G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                               "Multiple policy definitions");
+                  return;
+                }
+
+              parser->seen_policy = TRUE;
+              parser->state = STATE_POLICY;
+            }
+          else
             {
               enter_unknown_element (parser,
                                      element_name,
@@ -291,9 +348,6 @@ handle_start_element (GMarkupParseContext  *context,
               return;
             }
 
-          parser->state = STATE_CONFIGURATION;
-          parser->current_was_migrated = FALSE;
-
           return;
         }
 
@@ -544,6 +598,62 @@ handle_start_element (GMarkupParseContext  *context,
           return;
         }
 
+      case STATE_POLICY:
+        {
+          if (!(parser->extra_config_flags & GF_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG))
+            {
+              g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                           "Policy can only be defined in system level configurations");
+              return;
+            }
+
+          if (g_str_equal (element_name, "stores"))
+            {
+              if (parser->seen_stores)
+                {
+                  g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                               "Multiple stores elements under policy");
+                  return;
+                }
+
+              parser->seen_stores = TRUE;
+              parser->state = STATE_STORES;
+            }
+          else
+            {
+              enter_unknown_element (parser,
+                                     element_name,
+                                     "policy",
+                                     STATE_POLICY);
+            }
+
+          return;
+        }
+
+      case STATE_STORES:
+        {
+          if (g_str_equal (element_name, "store"))
+            {
+              parser->state = STATE_STORE;
+            }
+          else
+            {
+              enter_unknown_element (parser,
+                                     element_name,
+                                     "stores",
+                                     STATE_STORES);
+            }
+
+          return;
+        }
+
+      case STATE_STORE:
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                       "Invalid store sub element '%s'", element_name);
+          return;
+        }
+
       default:
         break;
     }
@@ -653,6 +763,9 @@ finish_monitor_spec (ConfigParser *parser)
       case STATE_MONITOR_MODE_RATE:
       case STATE_MONITOR_MODE_FLAG:
       case STATE_MONITOR_UNDERSCANNING:
+      case STATE_POLICY:
+      case STATE_STORES:
+      case STATE_STORE:
       default:
         g_assert_not_reached ();
         break;
@@ -863,13 +976,65 @@ handle_end_element (GMarkupParseContext  *context,
               return;
             }
 
-          g_hash_table_replace (parser->config_store->configs,
+          g_hash_table_replace (parser->pending_configs,
                                 config->key, config);
 
           parser->state = STATE_MONITORS;
           return;
         }
 
+      case STATE_STORE:
+        g_assert (g_str_equal (element_name, "store"));
+
+        if (parser->pending_store == GF_CONFIG_STORE_NONE)
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                         "Got an empty store");
+            return;
+          }
+
+        if (g_list_find (parser->stores,
+                         GINT_TO_POINTER (parser->pending_store)))
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                         "Multiple identical stores in policy");
+            return;
+          }
+
+        parser->stores =
+          g_list_append (parser->stores,
+                         GINT_TO_POINTER (parser->pending_store));
+        parser->pending_store = GF_CONFIG_STORE_NONE;
+
+        parser->state = STATE_STORES;
+        return;
+
+      case STATE_STORES:
+        g_assert (g_str_equal (element_name, "stores"));
+
+        if (parser->config_store->has_stores_policy)
+          {
+            g_warning ("Ignoring stores policy from '%s', "
+                       "it has already been configured",
+                       g_file_peek_path (parser->file));
+            g_clear_pointer (&parser->stores, g_list_free);
+          }
+        else
+          {
+            parser->config_store->has_stores_policy = TRUE;
+            parser->config_store->stores_policy = parser->stores;
+            parser->stores = NULL;
+          }
+
+        parser->state = STATE_POLICY;
+        return;
+
+      case STATE_POLICY:
+        g_assert (g_str_equal (element_name, "policy"));
+
+        parser->state = STATE_MONITORS;
+        return;
+
       case STATE_UNKNOWN:
         {
           parser->unknown_level--;
@@ -1015,6 +1180,8 @@ handle_text (GMarkupParseContext  *context,
       case STATE_MONITOR_MODE:
       case STATE_TRANSFORM:
       case STATE_DISABLED:
+      case STATE_POLICY:
+      case STATE_STORES:
         {
           if (!is_all_whitespace (text, text_len))
             g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
@@ -1165,6 +1332,36 @@ handle_text (GMarkupParseContext  *context,
           return;
         }
 
+      case STATE_STORE:
+        {
+          GfConfigStore store;
+
+          if (parser->pending_store != GF_CONFIG_STORE_NONE)
+            {
+              g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                           "Multiple store strings");
+              return;
+            }
+
+          if (text_equals (text, text_len, "system"))
+            {
+              store = GF_CONFIG_STORE_SYSTEM;
+            }
+          else if (text_equals (text, text_len, "user"))
+            {
+              store = GF_CONFIG_STORE_USER;
+            }
+          else
+            {
+              g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                           "Invalid store %.*s", (int) text_len, text);
+              return;
+            }
+
+          parser->pending_store = store;
+          return;
+        }
+
       default:
         break;
     }
@@ -1181,6 +1378,7 @@ static gboolean
 read_config_file (GfMonitorConfigStore  *config_store,
                   GFile                 *file,
                   GfMonitorsConfigFlag   extra_config_flags,
+                  GHashTable           **out_configs,
                   GError               **error)
 {
   gchar *buffer;
@@ -1196,9 +1394,15 @@ read_config_file (GfMonitorConfigStore  *config_store,
 
   parser = (ConfigParser) {
     .state = STATE_INITIAL,
+    .file = file,
     .config_store = config_store,
+    .pending_configs = g_hash_table_new_full (gf_monitors_config_key_hash,
+                                              gf_monitors_config_key_equal,
+                                              NULL,
+                                              g_object_unref),
     .extra_config_flags = extra_config_flags,
-    .unknown_state_root = -1
+    .unknown_state_root = -1,
+    .pending_store = GF_CONFIG_STORE_NONE
   };
 
   parse_context = g_markup_parse_context_new (&config_parser,
@@ -1218,12 +1422,18 @@ read_config_file (GfMonitorConfigStore  *config_store,
       g_clear_pointer (&parser.current_logical_monitor_config,
                        gf_logical_monitor_config_free);
 
+      g_list_free (parser.stores);
+      g_hash_table_unref (parser.pending_configs);
+
       g_markup_parse_context_free (parse_context);
       g_free (buffer);
 
       return FALSE;
     }
 
+  *out_configs = parser.pending_configs;
+  parser.pending_configs = NULL;
+
   g_markup_parse_context_free (parse_context);
   g_free (buffer);
 
@@ -1555,85 +1765,12 @@ static void
 gf_monitor_config_store_constructed (GObject *object)
 {
   GfMonitorConfigStore *config_store;
-  const char * const *system_dirs;
-  char *user_file_path;
-  GError *error;
 
   G_OBJECT_CLASS (gf_monitor_config_store_parent_class)->constructed (object);
 
   config_store = GF_MONITOR_CONFIG_STORE (object);
-  error = NULL;
-
-  for (system_dirs = g_get_system_config_dirs ();
-       system_dirs && *system_dirs;
-       system_dirs++)
-    {
-      char *system_file_path;
-
-      system_file_path = g_build_filename (*system_dirs, "monitors.xml", NULL);
-
-      if (g_file_test (system_file_path, G_FILE_TEST_EXISTS))
-        {
-          GFile *system_file;
 
-          system_file = g_file_new_for_path (system_file_path);
-
-          if (!read_config_file (config_store,
-                                 system_file,
-                                 GF_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG,
-                                 &error))
-            {
-              if (g_error_matches (error,
-                                   GF_MONITOR_CONFIG_STORE_ERROR,
-                                   GF_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION))
-                g_warning ("System monitor configuration file (%s) is "
-                           "incompatible; ask your administrator to migrate "
-                           "the system monitor configuration.",
-                           system_file_path);
-              else
-                g_warning ("Failed to read monitors config file '%s': %s",
-                           system_file_path, error->message);
-
-              g_clear_error (&error);
-            }
-
-          g_object_unref (system_file);
-        }
-
-      g_free (system_file_path);
-    }
-
-  user_file_path = g_build_filename (g_get_user_config_dir (), "monitors.xml", NULL);
-  config_store->user_file = g_file_new_for_path (user_file_path);
-
-  if (g_file_test (user_file_path, G_FILE_TEST_EXISTS))
-    {
-      if (!read_config_file (config_store,
-                             config_store->user_file,
-                             GF_MONITORS_CONFIG_FLAG_NONE,
-                             &error))
-        {
-          if (error->domain == GF_MONITOR_CONFIG_STORE_ERROR &&
-              error->code == GF_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)
-            {
-              g_clear_error (&error);
-              if (!gf_migrate_old_user_monitors_config (config_store, &error))
-                {
-                  g_warning ("Failed to migrate old monitors config file: %s",
-                             error->message);
-                  g_error_free (error);
-                }
-            }
-          else
-            {
-              g_warning ("Failed to read monitors config file '%s': %s",
-                         user_file_path, error->message);
-              g_error_free (error);
-            }
-        }
-    }
-
-  g_free (user_file_path);
+  gf_monitor_config_store_reset (config_store);
 }
 
 static void
@@ -1658,6 +1795,7 @@ gf_monitor_config_store_dispose (GObject *object)
   g_clear_object (&config_store->user_file);
   g_clear_object (&config_store->custom_read_file);
   g_clear_object (&config_store->custom_write_file);
+  g_clear_pointer (&config_store->stores_policy, g_list_free);
 
   G_OBJECT_CLASS (gf_monitor_config_store_parent_class)->dispose (object);
 }
@@ -1778,20 +1916,34 @@ gboolean
 gf_monitor_config_store_set_custom (GfMonitorConfigStore  *config_store,
                                     const gchar           *read_path,
                                     const gchar           *write_path,
+                                    GfMonitorsConfigFlag   config_flags,
                                     GError               **error)
 {
+  GHashTable *new_configs;
+
+  new_configs = NULL;
+
   g_clear_object (&config_store->custom_read_file);
   g_clear_object (&config_store->custom_write_file);
-  g_hash_table_remove_all (config_store->configs);
 
   config_store->custom_read_file = g_file_new_for_path (read_path);
   if (write_path)
     config_store->custom_write_file = g_file_new_for_path (write_path);
 
-  return read_config_file (config_store,
-                           config_store->custom_read_file,
-                           GF_MONITORS_CONFIG_FLAG_NONE,
-                           error);
+  g_clear_pointer (&config_store->stores_policy, g_list_free);
+  config_store->has_stores_policy = FALSE;
+
+  if (!read_config_file (config_store,
+                         config_store->custom_read_file,
+                         config_flags,
+                         &new_configs,
+                         error))
+    return FALSE;
+
+  g_clear_pointer (&config_store->configs, g_hash_table_unref);
+  config_store->configs = new_configs;
+
+  return TRUE;
 }
 
 gint
@@ -1805,3 +1957,135 @@ gf_monitor_config_store_get_monitor_manager (GfMonitorConfigStore *config_store)
 {
   return config_store->monitor_manager;
 }
+
+void
+gf_monitor_config_store_reset (GfMonitorConfigStore  *config_store)
+{
+  GHashTable *system_configs;
+  GHashTable *user_configs;
+  const char * const *system_dirs;
+  char *user_file_path;
+  GError *error;
+
+  system_configs = NULL;
+  user_configs = NULL;
+  error = NULL;
+
+  g_clear_object (&config_store->user_file);
+  g_clear_object (&config_store->custom_read_file);
+  g_clear_object (&config_store->custom_write_file);
+  g_hash_table_remove_all (config_store->configs);
+
+  for (system_dirs = g_get_system_config_dirs ();
+       system_dirs && *system_dirs;
+       system_dirs++)
+    {
+      char *system_file_path;
+
+      system_file_path = g_build_filename (*system_dirs, "monitors.xml", NULL);
+
+      if (g_file_test (system_file_path, G_FILE_TEST_EXISTS))
+        {
+          GFile *system_file;
+
+          system_file = g_file_new_for_path (system_file_path);
+
+          if (!read_config_file (config_store,
+                                 system_file,
+                                 GF_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG,
+                                 &system_configs,
+                                 &error))
+            {
+              if (g_error_matches (error,
+                                   GF_MONITOR_CONFIG_STORE_ERROR,
+                                   GF_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION))
+                g_warning ("System monitor configuration file (%s) is "
+                           "incompatible; ask your administrator to migrate "
+                           "the system monitor configuration.",
+                           system_file_path);
+              else
+                g_warning ("Failed to read monitors config file '%s': %s",
+                           system_file_path, error->message);
+
+              g_clear_error (&error);
+            }
+
+          g_object_unref (system_file);
+        }
+
+      g_free (system_file_path);
+    }
+
+  user_file_path = g_build_filename (g_get_user_config_dir (), "monitors.xml", NULL);
+  config_store->user_file = g_file_new_for_path (user_file_path);
+
+  if (g_file_test (user_file_path, G_FILE_TEST_EXISTS))
+    {
+      if (!read_config_file (config_store,
+                             config_store->user_file,
+                             GF_MONITORS_CONFIG_FLAG_NONE,
+                             &user_configs,
+                             &error))
+        {
+          if (error->domain == GF_MONITOR_CONFIG_STORE_ERROR &&
+              error->code == GF_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)
+            {
+              g_clear_error (&error);
+              if (!gf_migrate_old_user_monitors_config (config_store, &error))
+                {
+                  g_warning ("Failed to migrate old monitors config file: %s",
+                             error->message);
+                  g_error_free (error);
+                }
+            }
+          else
+            {
+              g_warning ("Failed to read monitors config file '%s': %s",
+                         user_file_path, error->message);
+              g_error_free (error);
+            }
+        }
+    }
+
+  if (config_store->has_stores_policy)
+    {
+      GList *l;
+
+      for (l = g_list_last (config_store->stores_policy); l; l = l->prev)
+        {
+          GfConfigStore store;
+
+          store = GPOINTER_TO_INT (l->data);
+
+          switch (store)
+            {
+              case GF_CONFIG_STORE_SYSTEM:
+                if (system_configs)
+                  replace_configs (config_store, system_configs);
+                break;
+
+              case GF_CONFIG_STORE_USER:
+                if (user_configs)
+                  replace_configs (config_store, user_configs);
+                break;
+
+              case GF_CONFIG_STORE_NONE:
+              default:
+                g_assert_not_reached ();
+                break;
+            }
+        }
+    }
+  else
+    {
+      if (system_configs)
+        replace_configs (config_store, system_configs);
+
+      if (user_configs)
+        replace_configs (config_store, user_configs);
+    }
+
+  g_clear_pointer (&system_configs, g_hash_table_unref);
+  g_clear_pointer (&user_configs, g_hash_table_unref);
+  g_free (user_file_path);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]