From f73e023b8fadd46f2de21c5086d7fbea0e8d0d53 Mon Sep 17 00:00:00 2001 From: Dylan Van Assche Date: Fri, 30 Dec 2022 12:55:38 +0100 Subject: config: add support for conf.d style Distros may want to overwrite various parameters of GeoClue's config file without shipping a complete config file which may result into out-of-sync config files when newer GeoClue versions are released. Support /etc/geoclue/conf.d/ as a config directory in which distros can place overwrites, the config files are loaded in alphabetic order and overwrite the original config file parameters stored in /etc/geoclue/geoclue.conf --- src/gclue-config.c | 362 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 263 insertions(+), 99 deletions(-) diff --git a/src/gclue-config.c b/src/gclue-config.c index 2802484..2cafe24 100644 --- a/src/gclue-config.c +++ b/src/gclue-config.c @@ -22,10 +22,12 @@ #include #include +#include #include "gclue-config.h" #define CONFIG_FILE_PATH SYSCONFDIR "/geoclue/geoclue.conf" +#define CONFIG_D_DIRECTORY SYSCONFDIR "/geoclue/conf.d/" /* This class will be responsible for fetching configuration. */ @@ -103,20 +105,29 @@ gclue_config_class_init (GClueConfigClass *klass) } static void -load_agent_config (GClueConfig *config) +load_agent_config (GClueConfig *config, gboolean initial) { GClueConfigPrivate *priv = config->priv; g_autoptr(GError) error = NULL; + g_auto(GStrv) agents = NULL; + gsize num_agents; - priv->agents = g_key_file_get_string_list (priv->key_file, - "agent", - "whitelist", - &priv->num_agents, - &error); - if (error != NULL) { - g_critical ("Failed to read 'agent/whitelist' key: %s", - error->message); - } + if (!initial && !g_key_file_has_key (priv->key_file, "agent", "whitelist", NULL)) + return; + + agents = g_key_file_get_string_list (priv->key_file, + "agent", + "whitelist", + &num_agents, + &error); + if (error == NULL) { + g_clear_pointer (&priv->agents, g_strfreev); + priv->agents = g_steal_pointer (&agents); + priv->num_agents = num_agents; + + } else + g_warning ("Failed to read 'agent/whitelist' key: %s", + error->message); } static void @@ -134,11 +145,16 @@ load_app_configs (GClueConfig *config) return; for (i = 0; i < num_groups; i++) { - AppConfig *app_config; - int* users; + AppConfig *app_config = NULL; + g_autofree int *users = NULL; + GList *node; gsize num_users = 0, j; gboolean allowed, system; gboolean ignore = FALSE; + gboolean new_app_config = TRUE; + gboolean has_allowed = FALSE; + gboolean has_system = FALSE; + gboolean has_users = FALSE; g_autoptr(GError) error = NULL; for (j = 0; known_groups[j] != NULL; j++) @@ -151,36 +167,64 @@ load_app_configs (GClueConfig *config) if (ignore) continue; + /* Check if entry is new or is overwritten */ + for (node = priv->app_configs; node != NULL; node = node->next) { + if (strcmp (((AppConfig *) node->data)->id, groups[i]) == 0) { + app_config = (AppConfig *) node->data; + new_app_config = FALSE; + + break; + } + } + allowed = g_key_file_get_boolean (priv->key_file, groups[i], "allowed", &error); - if (error != NULL) + has_allowed = (error == NULL); + if (error != NULL && new_app_config) goto error_out; + g_clear_error (&error); system = g_key_file_get_boolean (priv->key_file, groups[i], "system", &error); - if (error != NULL) + has_system = (error == NULL); + if (error != NULL && new_app_config) goto error_out; + g_clear_error (&error); users = g_key_file_get_integer_list (priv->key_file, groups[i], "users", &num_users, &error); - if (error != NULL) + has_users = (error == NULL); + if (error != NULL && new_app_config) goto error_out; + g_clear_error (&error); - app_config = g_slice_new0 (AppConfig); - app_config->id = g_strdup (groups[i]); - app_config->allowed = allowed; - app_config->system = system; - app_config->users = users; - app_config->num_users = num_users; - priv->app_configs = g_list_prepend (priv->app_configs, app_config); + /* New app config, without erroring out above */ + if (new_app_config) { + app_config = g_slice_new0 (AppConfig); + priv->app_configs = g_list_prepend (priv->app_configs, app_config); + app_config->id = g_strdup (groups[i]); + } + + /* New app configs will have all of them, overwrites only some */ + if (has_allowed) + app_config->allowed = allowed; + + if (has_system) + app_config->system = system; + + if (has_users) { + g_free (app_config->users); + app_config->users = g_steal_pointer (&users); + app_config->num_users = num_users; + } continue; error_out: @@ -192,12 +236,18 @@ error_out: static gboolean load_enable_source_config (GClueConfig *config, - const char *source_name) + const char *source_name, + gboolean initial, + gboolean enabled) { GClueConfigPrivate *priv = config->priv; g_autoptr(GError) error = NULL; gboolean enable; + /* Source should be initially enabled by default */ + if (!g_key_file_has_key (priv->key_file, source_name, "enable", NULL)) + return initial? TRUE: enabled; + enable = g_key_file_get_boolean (priv->key_file, source_name, "enable", @@ -208,8 +258,8 @@ load_enable_source_config (GClueConfig *config, source_name, error->message); - /* Source should be enabled by default */ - return TRUE; + /* Keep the previous enable state if something went wrong */ + return enabled; } return enable; @@ -220,137 +270,250 @@ load_enable_source_config (GClueConfig *config, #define DEFAULT_WIFI_SUBMIT_NICK "geoclue" static void -load_wifi_config (GClueConfig *config) +load_wifi_config (GClueConfig *config, gboolean initial) { GClueConfigPrivate *priv = config->priv; g_autoptr(GError) error = NULL; + g_autofree char *wifi_url = NULL; + g_autofree char *wifi_submit_url = NULL; + g_autofree char *wifi_submit_nick = NULL; + guint wifi_submit_nick_length; + + priv->enable_wifi_source = + load_enable_source_config (config, "wifi", initial, + priv->enable_wifi_source); + + if (initial || g_key_file_has_key (priv->key_file, "wifi", "url", NULL)) { + wifi_url = g_key_file_get_string (priv->key_file, + "wifi", + "url", + &error); + if (error == NULL) { + g_clear_pointer (&priv->wifi_url, g_free); + priv->wifi_url = g_steal_pointer (&wifi_url); + } else if (initial) { + g_debug ("Using the default locate URL: %s", error->message); + g_clear_pointer (&priv->wifi_url, g_free); + priv->wifi_url = g_strdup (DEFAULT_WIFI_URL); + } else + g_warning ("Failed to get config \"wifi/url\": %s", error->message); - priv->enable_wifi_source = load_enable_source_config (config, "wifi"); - - priv->wifi_url = g_key_file_get_string (priv->key_file, - "wifi", - "url", - &error); - if (error != NULL) { - g_debug ("Using the default locate URL: %s", - error->message); g_clear_error (&error); - priv->wifi_url = g_strdup (DEFAULT_WIFI_URL); } - priv->wifi_submit = g_key_file_get_boolean (priv->key_file, - "wifi", - "submit-data", - &error); - if (error != NULL) { - g_debug ("Failed to get config \"wifi/submit-data\": %s", - error->message); - - return; + if (initial || g_key_file_has_key (priv->key_file, "wifi", "submit-data", NULL)) { + priv->wifi_submit = g_key_file_get_boolean (priv->key_file, + "wifi", + "submit-data", + &error); + if (error != NULL) { + g_warning ("Failed to get config \"wifi/submit-data\": %s", + error->message); + return; + } + g_clear_error (&error); } - priv->wifi_submit_url = g_key_file_get_string (priv->key_file, - "wifi", - "submission-url", - &error); - if (error != NULL) { - g_debug ("Using the default submit URL: %s", - error->message); + if (initial || g_key_file_has_key (priv->key_file, "wifi", "submission-url", NULL)) { + wifi_submit_url = g_key_file_get_string (priv->key_file, + "wifi", + "submission-url", + &error); + + if (error == NULL) { + g_clear_pointer (&priv->wifi_submit_url, g_free); + priv->wifi_submit_url = g_steal_pointer (&wifi_submit_url); + } else if (initial) { + g_debug ("Using the default submission URL: %s", error->message); + g_clear_pointer (&priv->wifi_submit_url, g_free); + priv->wifi_submit_url = g_strdup (DEFAULT_WIFI_SUBMIT_URL); + } else + g_warning ("Failed to get config \"wifi/submission-url\": %s", error->message); + g_clear_error (&error); - priv->wifi_submit_url = g_strdup (DEFAULT_WIFI_SUBMIT_URL); } - priv->wifi_submit_nick = g_key_file_get_string (priv->key_file, - "wifi", - "submission-nick", - &error); - if (error != NULL) { - g_debug ("Using the default submission nick: %s", - error->message); - priv->wifi_submit_nick = g_strdup (DEFAULT_WIFI_SUBMIT_NICK); + if (initial || g_key_file_has_key (priv->key_file, "wifi", "submission-nick", NULL)) { + wifi_submit_nick = g_key_file_get_string (priv->key_file, + "wifi", + "submission-nick", + &error); + + if (error == NULL) { + /* Submission nickname must be 2-32 characters long */ + wifi_submit_nick_length = strlen (wifi_submit_nick); + if (wifi_submit_nick_length >= 2 && wifi_submit_nick_length <= 32) { + g_clear_pointer (&priv->wifi_submit_nick, g_free); + priv->wifi_submit_nick = g_steal_pointer (&wifi_submit_nick); + } else { + g_warning ("Submission nick must be between 2-32 characters long"); + + if (initial) { + g_debug ("Using the default submission nick: %s", error->message); + g_clear_pointer (&priv->wifi_submit_nick, g_free); + priv->wifi_submit_nick = g_strdup (DEFAULT_WIFI_SUBMIT_NICK); + } + } + } else if (initial) { + g_debug ("Using the default submission nick: %s", error->message); + g_clear_pointer (&priv->wifi_submit_nick, g_free); + priv->wifi_submit_nick = g_strdup (DEFAULT_WIFI_SUBMIT_NICK); + } else + g_warning ("Failed to get config \"wifi/submission-nick\": %s", error->message); } } static void -load_3g_config (GClueConfig *config) +load_3g_config (GClueConfig *config, gboolean initial) { config->priv->enable_3g_source = - load_enable_source_config (config, "3g"); + load_enable_source_config (config, "3g", initial, + config->priv->enable_3g_source); } static void -load_cdma_config (GClueConfig *config) +load_cdma_config (GClueConfig *config, gboolean initial) { config->priv->enable_cdma_source = - load_enable_source_config (config, "cdma"); + load_enable_source_config (config, "cdma", initial, + config->priv->enable_cdma_source); } static void -load_modem_gps_config (GClueConfig *config) +load_modem_gps_config (GClueConfig *config, gboolean initial) { config->priv->enable_modem_gps_source = - load_enable_source_config (config, "modem-gps"); + load_enable_source_config (config, "modem-gps", initial, + config->priv->enable_modem_gps_source); } static void -load_network_nmea_config (GClueConfig *config) +load_network_nmea_config (GClueConfig *config, gboolean initial) { g_autoptr(GError) error = NULL; + g_autofree char* nmea_socket = NULL; config->priv->enable_nmea_source = - load_enable_source_config (config, "network-nmea"); - if (!config->priv->enable_nmea_source) - return; - config->priv->nmea_socket = g_key_file_get_string (config->priv->key_file, - "network-nmea", - "nmea-socket", - &error); - if (error != NULL) { - g_debug ("`nmea-socket` configuration not set."); + load_enable_source_config (config, "network-nmea", initial, + config->priv->enable_nmea_source); + + if (g_key_file_has_key (config->priv->key_file, "network-nmea", "nmea-socket", NULL)) { + nmea_socket = g_key_file_get_string (config->priv->key_file, + "network-nmea", + "nmea-socket", + &error); + if (error == NULL) { + g_clear_pointer (&config->priv->nmea_socket, g_free); + config->priv->nmea_socket = g_steal_pointer (&nmea_socket); + } else + g_warning ("Failed to get config \"nmea-socket\": %s", error->message); } } static void -load_compass_config (GClueConfig *config) +load_compass_config (GClueConfig *config, gboolean initial) { config->priv->enable_compass = - load_enable_source_config (config, "compass"); + load_enable_source_config (config, "compass", initial, + config->priv->enable_compass); } static void -load_static_source_config (GClueConfig *config) +load_static_source_config (GClueConfig *config, gboolean initial) { config->priv->enable_static_source = - load_enable_source_config (config, "static-source"); + load_enable_source_config (config, "static-source", initial, + config->priv->enable_static_source); } static void -gclue_config_init (GClueConfig *config) -{ +load_config_file (GClueConfig *config, const char *path, gboolean initial) { g_autoptr(GError) error = NULL; - config->priv = gclue_config_get_instance_private(config); - config->priv->key_file = g_key_file_new (); + g_debug ("Loading config: %s", path); g_key_file_load_from_file (config->priv->key_file, - CONFIG_FILE_PATH, + path, 0, &error); if (error != NULL) { g_critical ("Failed to load configuration file '%s': %s", - CONFIG_FILE_PATH, error->message); - + path, error->message); return; } - load_agent_config (config); + load_agent_config (config, initial); load_app_configs (config); - load_wifi_config (config); - load_3g_config (config); - load_cdma_config (config); - load_modem_gps_config (config); - load_network_nmea_config (config); - load_compass_config (config); - load_static_source_config (config); + load_wifi_config (config, initial); + load_3g_config (config, initial); + load_cdma_config (config, initial); + load_modem_gps_config (config, initial); + load_network_nmea_config (config, initial); + load_compass_config (config, initial); + load_static_source_config (config, initial); +} + +static void +files_element_clear (void *element) +{ + gchar **file_name = element; + g_free (*file_name); +} + +static gint +sort_files (gconstpointer a, gconstpointer b) +{ + char *str_a = *(char **)a; + char *str_b = *(char **)b; + + return g_strcmp0 (str_a, str_b); +} + +static void +gclue_config_init (GClueConfig *config) +{ + g_autoptr(GDir) dir = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GArray) files = NULL; + char *name; + gsize i; + + config->priv = gclue_config_get_instance_private (config); + config->priv->key_file = g_key_file_new (); + + /* Load config file from default path, log all missing parameters */ + load_config_file (config, CONFIG_FILE_PATH, TRUE); + + /* + * Apply config overwrites from conf.d style config files, + * files are sorted alphabetically, example: '90-config.conf' + * will overwrite '50-config.conf'. + */ + dir = g_dir_open (CONFIG_D_DIRECTORY, 0, &error); + + if (error != NULL) { + g_warning ("Failed to open %s: %s", + CONFIG_D_DIRECTORY, error->message); + return; + } + + files = g_array_new (FALSE, FALSE, sizeof(char *)); + g_array_set_clear_func (files, files_element_clear); + + while ((name = g_strdup (g_dir_read_name (dir)))) { + if (g_str_has_suffix (name, ".conf")) + g_array_append_val (files, name); + } + + g_array_sort (files, sort_files); + + for (i = 0; i < files->len; i++) { + g_autofree char *path = NULL; + + path = g_build_filename (CONFIG_D_DIRECTORY, + g_array_index (files, char *, i), + NULL); + load_config_file (config, path, FALSE); + } } GClueConfig * @@ -480,7 +643,7 @@ void gclue_config_set_wifi_submit_nick (GClueConfig *config, const char *nick) { - + g_clear_pointer (&config->priv->wifi_submit_nick, g_free); config->priv->wifi_submit_nick = g_strdup (nick); } @@ -529,9 +692,10 @@ gclue_config_get_enable_nmea_source (GClueConfig *config) void gclue_config_set_nmea_socket (GClueConfig *config, - const char *nmea_socket) + const char *nmea_socket) { - config->priv->nmea_socket = g_strdup(nmea_socket); + g_clear_pointer (&config->priv->nmea_socket, g_free); + config->priv->nmea_socket = g_strdup (nmea_socket); } gboolean -- cgit v1.2.3