mirror of
https://gitlab.freedesktop.org/upower/upower.git
synced 2026-02-03 10:10:33 +01:00
Add support for conf.d style dirs (UPower.conf.d)
This change adds the feature to read config from conf.d style directories (UPower.conf.d), commonly supported by other tools, as an extension of the main config file. This is useful and convenient in several situations, for example: - distributions can set different values from the defaults shipped upstream without having to modify the main UPower.conf - different packages or config-management tools can change config just by adding, removing or modifying files in that directory The main config file, e.g. '/etc/UPower/UPower.conf', will be processed first, and then files in the UPower.conf.d dir, if existing. The directory to use is derived automatically, e.g. '/etc/UPower/UPower.conf.d/' if the main config file is '/etc/UPower/UPower.conf'. Only files within that directory are considered, and only those with valid config-group 'UPower' and with the filename format: starting with '00-' to '99-', ending in '.conf' and with alphanumeric characters, dash or underscore in between. The candidate files within the given directory are sorted (with g_strcmp0(), so the ordering will be as with strcmp()). The configuration in the files being processed later will override previous config, in particular the main config, but also the one from previous files processed, if the Group and Key coincide. Add also relevant integration test: 'test_conf_d_support'
This commit is contained in:
parent
5f572ffd9a
commit
46bbc8a602
3 changed files with 297 additions and 4 deletions
|
|
@ -30,7 +30,7 @@ cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
|
|||
cdata.set_quoted('VERSION', meson.project_version())
|
||||
cdata.set_quoted('PACKAGE_SYSCONF_DIR', get_option('sysconfdir'))
|
||||
|
||||
glib_min_version = '2.66'
|
||||
glib_min_version = '2.76'
|
||||
|
||||
glib_version_def = 'GLIB_VERSION_@0@_@1@'.format(
|
||||
glib_min_version.split('.')[0], glib_min_version.split('.')[1])
|
||||
|
|
|
|||
|
|
@ -5904,6 +5904,144 @@ class Tests(dbusmock.DBusTestCase):
|
|||
# client.get_devices_async(None, get_devices_cb)
|
||||
# ml.run()
|
||||
|
||||
def test_conf_d_support(self):
|
||||
"""Ensure support for conf.d style directories"""
|
||||
|
||||
base_dir = tempfile.TemporaryDirectory(delete=False, prefix="UPower-")
|
||||
conf_d_dir_path = os.path.join(base_dir.name, "UPower.conf.d")
|
||||
|
||||
# Low, Critical and Action are all needed to avoid fallback to defaults
|
||||
config = tempfile.NamedTemporaryFile(
|
||||
delete=False, mode="w", dir=base_dir.name, suffix=".conf"
|
||||
)
|
||||
config.write("[UPower]\n")
|
||||
config.write("PercentageLow=20.0\n")
|
||||
config.write("PercentageCritical=3.0\n")
|
||||
config.write("PercentageAction=2.0\n")
|
||||
config.write("AllowRiskyCriticalPowerAction=false\n")
|
||||
config.write("CriticalPowerAction=HybridSleep\n")
|
||||
config.close()
|
||||
|
||||
# UPower.conf.d directory does not exist
|
||||
self.start_daemon(cfgfile=config.name)
|
||||
self.daemon_log.check_line(
|
||||
"failed to find files in 'UPower.conf.d': Error opening directory",
|
||||
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
|
||||
)
|
||||
self.stop_daemon()
|
||||
|
||||
# empty UPower.conf.d directory
|
||||
conf_d_dir = os.mkdir(path=conf_d_dir_path)
|
||||
self.start_daemon(cfgfile=config.name)
|
||||
self.stop_daemon()
|
||||
|
||||
# override config (in a UPower.conf.d/10-*.conf file)
|
||||
config2 = tempfile.NamedTemporaryFile(
|
||||
delete=False, mode="w", dir=conf_d_dir_path, prefix="10-", suffix=".conf"
|
||||
)
|
||||
config2.write("[UPower]\n")
|
||||
config2.write("AllowRiskyCriticalPowerAction=true\n")
|
||||
config2.write("CriticalPowerAction=Suspend\n")
|
||||
config2.close()
|
||||
|
||||
# check warning message when CriticalPowerAction=Suspend and
|
||||
# AllowRiskyCriticalPowerAction=true, meaning that support of
|
||||
# UPower.conf.d dir is working
|
||||
self.start_daemon(cfgfile=config.name, warns=True)
|
||||
self.daemon_log.check_line(
|
||||
'The "Suspend" CriticalPowerAction setting is considered risky:',
|
||||
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
|
||||
)
|
||||
self.stop_daemon()
|
||||
|
||||
# override config (in a UPower.conf.d/90-*.conf file)
|
||||
config3 = tempfile.NamedTemporaryFile(
|
||||
delete=False, mode="w", dir=conf_d_dir_path, prefix="90-", suffix=".conf"
|
||||
)
|
||||
config3.write("[UPower]\n")
|
||||
config3.write("CriticalPowerAction=Ignore\n")
|
||||
config3.close()
|
||||
|
||||
# check warning message when CriticalPowerAction=Ignore and
|
||||
# AllowRiskyCriticalPowerAction=true, meaning that support of
|
||||
# UPower.conf.d dir is working
|
||||
self.start_daemon(cfgfile=config.name, warns=True)
|
||||
self.daemon_log.check_line(
|
||||
'The "Ignore" CriticalPowerAction setting is considered risky:',
|
||||
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
|
||||
)
|
||||
self.stop_daemon()
|
||||
|
||||
# config with invalid name 99-*.conf~ file (not actually overriding)
|
||||
config_inv1 = tempfile.NamedTemporaryFile(
|
||||
delete=False, mode="w", dir=conf_d_dir_path, prefix="99-", suffix=".conf~"
|
||||
)
|
||||
config_inv1.write("[UPower]\n")
|
||||
config_inv1.write("CriticalPowerAction=HybridSleep\n")
|
||||
config_inv1.close()
|
||||
|
||||
# check warning message when CriticalPowerAction=Ignore and
|
||||
# AllowRiskyCriticalPowerAction=true, because the config file
|
||||
# (config_inv1) setting it to the safe 'HybridSleep' is not activated
|
||||
# due to wrong name
|
||||
self.start_daemon(cfgfile=config.name, warns=True)
|
||||
self.daemon_log.check_line(
|
||||
'The "Ignore" CriticalPowerAction setting is considered risky:',
|
||||
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
|
||||
)
|
||||
self.stop_daemon()
|
||||
|
||||
# config with invalid name 999-*.conf file (not actually overriding)
|
||||
config_inv2 = tempfile.NamedTemporaryFile(
|
||||
delete=False, mode="w", dir=conf_d_dir_path, prefix="999-", suffix=".conf"
|
||||
)
|
||||
config_inv2.write("[UPower]\n")
|
||||
config_inv2.write("CriticalPowerAction=HybridSleep\n")
|
||||
config_inv2.close()
|
||||
|
||||
# check warning message when CriticalPowerAction=Ignore and
|
||||
# AllowRiskyCriticalPowerAction=true, because the config files
|
||||
# (config_inv1 and config_inv2) setting it to the safe 'HybridSleep' are
|
||||
# not activated due to wrong names
|
||||
self.start_daemon(cfgfile=config.name, warns=True)
|
||||
self.daemon_log.check_line(
|
||||
'The "Ignore" CriticalPowerAction setting is considered risky:',
|
||||
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
|
||||
)
|
||||
self.stop_daemon()
|
||||
|
||||
# config with invalid name 99-badname+*.conf file (not actually overriding)
|
||||
config_inv3 = tempfile.NamedTemporaryFile(
|
||||
delete=False,
|
||||
mode="w",
|
||||
dir=conf_d_dir_path,
|
||||
prefix="99-badname+",
|
||||
suffix=".conf",
|
||||
)
|
||||
config_inv3.write("[UPower]\n")
|
||||
config_inv3.write("CriticalPowerAction=HybridSleep\n")
|
||||
config_inv3.close()
|
||||
|
||||
# check warning message when CriticalPowerAction=Ignore and
|
||||
# AllowRiskyCriticalPowerAction=true, because the config files
|
||||
# (config_inv1, 2 and 3) setting it to the safe 'HybridSleep' are not
|
||||
# activated due to wrong names
|
||||
self.start_daemon(cfgfile=config.name, warns=True)
|
||||
self.daemon_log.check_line(
|
||||
'The "Ignore" CriticalPowerAction setting is considered risky:',
|
||||
timeout=UP_DAEMON_ACTION_DELAY + 0.5,
|
||||
)
|
||||
self.stop_daemon()
|
||||
|
||||
os.unlink(config_inv3.name)
|
||||
os.unlink(config_inv2.name)
|
||||
os.unlink(config_inv1.name)
|
||||
os.unlink(config3.name)
|
||||
os.unlink(config2.name)
|
||||
os.unlink(config.name)
|
||||
os.rmdir(conf_d_dir_path)
|
||||
os.rmdir(base_dir.name)
|
||||
|
||||
#
|
||||
# Helper methods
|
||||
#
|
||||
|
|
|
|||
161
src/up-config.c
161
src/up-config.c
|
|
@ -104,6 +104,142 @@ up_config_class_init (UpConfigClass *klass)
|
|||
object_class->finalize = up_config_finalize;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_config_list_compare_files:
|
||||
**/
|
||||
static gint
|
||||
up_config_list_compare_files (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
return g_strcmp0 ((const gchar*)a, (const gchar*)b);
|
||||
}
|
||||
|
||||
/**
|
||||
* up_config_list_confd_files:
|
||||
*
|
||||
* The format of the filename should be '^([0-9][0-9])-([a-zA-Z0-9-_])*\.conf$',
|
||||
* that is, starting with "00-" to "99-", ending in ".conf", and with a mix of
|
||||
* alphanumeric characters with dashes and underscores in between. For example:
|
||||
* '01-upower-override.conf'.
|
||||
*
|
||||
* Files named differently, or containing invalid groups (currently only
|
||||
* 'UPower' is valid), will not be considered.
|
||||
*
|
||||
* The candidate files within the given directory are sorted (with g_strcmp0(),
|
||||
* so the ordering will be as with strcmp()). The configuration in the files
|
||||
* being processed later will override previous config, in particular the main
|
||||
* config, but also the one from previous files processed, if the Group and Key
|
||||
* coincide.
|
||||
*
|
||||
* For example, consider 'UPower.conf' that contains the defaults:
|
||||
* PercentageLow=20.0
|
||||
* PercentageCritical=5.0
|
||||
* PercentageAction=2.0
|
||||
*
|
||||
* and there is a file 'UPower.conf.d/70-change-percentages.conf'
|
||||
* containing settings for all 'Percentage*' keys:
|
||||
* [UPower]
|
||||
* PercentageLow=15.0
|
||||
* PercentageCritical=10.0
|
||||
* PercentageAction=5.0
|
||||
*
|
||||
* and another 'UPower.conf.d/99-change-percentages-local.conf'
|
||||
* containing settings only for 'PercentageAction':
|
||||
* [UPower]
|
||||
* PercentageAction=7.5
|
||||
*
|
||||
* First the main 'UPower.conf' will be processed, then
|
||||
* 'UPower.conf.d/70-change-percentages.conf' overriding the defaults
|
||||
* of all percentages from the main config file with the given values,
|
||||
* and finally 'UPower.conf.d/99-change-percentages-local.conf'
|
||||
* overriding once again only 'PercentageAction'. The final, effective
|
||||
* values are:
|
||||
* PercentageLow=15.0
|
||||
* PercentageCritical=10.0
|
||||
* PercentageAction=7.5
|
||||
**/
|
||||
static GPtrArray*
|
||||
up_config_list_confd_files (const gchar* conf_d_path, GError** error)
|
||||
{
|
||||
g_autoptr (GPtrArray) ret_conf_d_files = NULL;
|
||||
GDir *dir = NULL;
|
||||
const gchar *filename = NULL;
|
||||
const char *regex_pattern = "^([0-9][0-9])-([a-zA-Z0-9-_])*\\.conf$";
|
||||
g_autoptr (GRegex) regex = NULL;
|
||||
|
||||
dir = g_dir_open (conf_d_path, 0, error);
|
||||
if (dir == NULL)
|
||||
return NULL;
|
||||
|
||||
regex = g_regex_new (regex_pattern, G_REGEX_DEFAULT, G_REGEX_MATCH_DEFAULT, NULL);
|
||||
g_assert (regex != NULL);
|
||||
|
||||
ret_conf_d_files = g_ptr_array_new_full (0, g_free);
|
||||
|
||||
while ((filename = g_dir_read_name (dir)) != NULL) {
|
||||
g_autofree gchar *file_path = NULL;
|
||||
g_autoptr (GFile) file = NULL;
|
||||
g_autoptr (GFileInfo) file_info = NULL;
|
||||
|
||||
if (!g_regex_match (regex, filename, G_REGEX_MATCH_DEFAULT, NULL))
|
||||
continue;
|
||||
|
||||
file_path = g_build_filename (conf_d_path, filename, NULL);
|
||||
file = g_file_new_for_path (file_path);
|
||||
file_info = g_file_query_info (file,
|
||||
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
NULL,
|
||||
NULL);
|
||||
if (file_info != NULL) {
|
||||
g_debug ("Will consider additional config file '%s'", file_path);
|
||||
g_ptr_array_add (ret_conf_d_files, g_strdup (file_path));
|
||||
}
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
|
||||
g_ptr_array_sort_values (ret_conf_d_files, up_config_list_compare_files);
|
||||
|
||||
return g_ptr_array_ref (ret_conf_d_files);
|
||||
}
|
||||
|
||||
/**
|
||||
* up_config_override_from_confd:
|
||||
**/
|
||||
static void
|
||||
up_config_override_from_confd (GKeyFile *key_file, const gchar* new_config_path)
|
||||
{
|
||||
g_autoptr (GKeyFile) new_keyfile = NULL;
|
||||
gchar **keys = NULL;
|
||||
gsize keys_size = 0;
|
||||
|
||||
new_keyfile = g_key_file_new();
|
||||
if (!g_key_file_load_from_file (new_keyfile, new_config_path, G_KEY_FILE_NONE, NULL))
|
||||
return;
|
||||
|
||||
if (!g_key_file_has_group (new_keyfile, "UPower"))
|
||||
return;
|
||||
|
||||
keys = g_key_file_get_keys (new_keyfile, "UPower", &keys_size, NULL);
|
||||
if (keys == NULL)
|
||||
return;
|
||||
|
||||
for (gsize i = 0; i < keys_size; i++) {
|
||||
g_autofree gchar *value = NULL;
|
||||
g_autofree gchar *old_value = NULL;
|
||||
|
||||
value = g_key_file_get_value (new_keyfile, "UPower", keys[i], NULL);
|
||||
if (value == NULL)
|
||||
continue;
|
||||
|
||||
old_value = g_key_file_get_value (key_file, "UPower", keys[i], NULL);
|
||||
|
||||
if (old_value != NULL)
|
||||
g_key_file_set_value (key_file, "UPower", keys[i], value);
|
||||
}
|
||||
g_strfreev (keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* up_config_init:
|
||||
**/
|
||||
|
|
@ -112,16 +248,24 @@ up_config_init (UpConfig *config)
|
|||
{
|
||||
gboolean allow_risky_critical_action = FALSE;
|
||||
g_autofree gchar *critical_action = NULL;
|
||||
GError *error = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autofree gchar *filename = NULL;
|
||||
gboolean ret;
|
||||
g_autofree gchar *conf_dir = NULL;
|
||||
g_autofree gchar *conf_d_path = NULL;
|
||||
g_autoptr (GPtrArray) conf_d_files = NULL;
|
||||
|
||||
config->priv = up_config_get_instance_private (config);
|
||||
config->priv->keyfile = g_key_file_new ();
|
||||
|
||||
filename = g_strdup (g_getenv ("UPOWER_CONF_FILE_NAME"));
|
||||
if (filename == NULL)
|
||||
if (filename == NULL) {
|
||||
filename = g_build_filename (PACKAGE_SYSCONF_DIR,"UPower", "UPower.conf", NULL);
|
||||
conf_d_path = g_build_filename (PACKAGE_SYSCONF_DIR, "UPower", "UPower.conf.d", NULL);
|
||||
} else {
|
||||
conf_dir = g_path_get_dirname (filename);
|
||||
conf_d_path = g_build_filename (conf_dir, "UPower.conf.d", NULL);
|
||||
}
|
||||
|
||||
/* load */
|
||||
ret = g_key_file_load_from_file (config->priv->keyfile,
|
||||
|
|
@ -132,7 +276,18 @@ up_config_init (UpConfig *config)
|
|||
if (!ret) {
|
||||
g_warning ("failed to load config file '%s': %s",
|
||||
filename, error->message);
|
||||
g_error_free (error);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
conf_d_files = up_config_list_confd_files (conf_d_path, &error);
|
||||
if (conf_d_files != NULL) {
|
||||
for (guint i = 0; i < conf_d_files->len; i++) {
|
||||
const gchar* conf_d_file = (const gchar*)(g_ptr_array_index (conf_d_files, i));
|
||||
up_config_override_from_confd (config->priv->keyfile,
|
||||
conf_d_file);
|
||||
}
|
||||
} else {
|
||||
g_debug ("failed to find files in 'UPower.conf.d': %s", error->message);
|
||||
}
|
||||
|
||||
/* Warn for any dangerous configurations */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue