[v18,11/12] config: Make configuration file locations configurable
diff mbox series

Message ID 20250912142915.53949-13-mzamazal@redhat.com
State New
Headers show
Series
  • Add global configuration file
Related show

Commit Message

Milan Zamazal Sept. 12, 2025, 2:29 p.m. UTC
To support easy switching of configurations, let's introduce
LIBCAMERA_CONFIG_DIR and LIBCAMERA_CONFIG_NAME environment variables.

LIBCAMERA_CONFIG_DIR environment variable specifies a directory or a
list of directories separated by colons where to look for the libcamera
configuration file before trying the standard locations.  This override
can be useful in a meson devenv.

LIBCAMERA_CONFIG_NAME
- specifies the path of the configuration file to load if it is an
  absolute path (useful to quickly try some configuration); or
- prevents any configuration file from loading if it is defined and
  empty (useful to disable all configuration files); or
- specifies the path of the configuration file to load, relative to
  the configuration directories (useful to quickly select between
  different configurations).

If a configuration file specified by those environment variables doesn't
exist, no configuration file is loaded.

Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 Documentation/runtime_configuration.rst | 13 +++++++
 src/libcamera/global_configuration.cpp  | 50 +++++++++++++++++--------
 2 files changed, 47 insertions(+), 16 deletions(-)

Comments

Laurent Pinchart Sept. 21, 2025, 5:09 a.m. UTC | #1
Hi Milan,

Thank you for the patch.

On Fri, Sep 12, 2025 at 04:29:13PM +0200, Milan Zamazal wrote:
> To support easy switching of configurations, let's introduce
> LIBCAMERA_CONFIG_DIR and LIBCAMERA_CONFIG_NAME environment variables.
> 
> LIBCAMERA_CONFIG_DIR environment variable specifies a directory or a
> list of directories separated by colons where to look for the libcamera
> configuration file before trying the standard locations.  This override
> can be useful in a meson devenv.
> 
> LIBCAMERA_CONFIG_NAME
> - specifies the path of the configuration file to load if it is an
>   absolute path (useful to quickly try some configuration); or
> - prevents any configuration file from loading if it is defined and
>   empty (useful to disable all configuration files); or
> - specifies the path of the configuration file to load, relative to
>   the configuration directories (useful to quickly select between
>   different configurations).

This last part I'm not thrilled about. Do we really need such a complex
scheme ? Wouldn't a single variable that overrides the configuration
file lookup be enough ?

I'll merge the rest of the series already without this patch while we
discuss the issue.

> If a configuration file specified by those environment variables doesn't
> exist, no configuration file is loaded.
> 
> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> ---
>  Documentation/runtime_configuration.rst | 13 +++++++
>  src/libcamera/global_configuration.cpp  | 50 +++++++++++++++++--------
>  2 files changed, 47 insertions(+), 16 deletions(-)
> 
> diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
> index 702139544..5307f3645 100644
> --- a/Documentation/runtime_configuration.rst
> +++ b/Documentation/runtime_configuration.rst
> @@ -19,6 +19,19 @@ order:
>  - LIBCAMERA_SYSCONF_DIR/configuration.yaml
>  - LIBCAMERA_DATA_DIR/libcamera/configuration.yaml
>  
> +If LIBCAMERA_CONFIG_DIR environment variable is non-empty then it
> +specifies additional directories where to look for the configuration
> +file, before looking at the standard locations. It can be a single
> +directory or multiple directories separated by colons.
> +
> +The default name of the configuration file, configuration.yaml, can be
> +overridden in LIBCAMERA_CONFIG_NAME environment variable. The variable
> +can specify just an alternative configuration file name to be looked up
> +in the locations above, or a whole absolute path. If an absolute path is
> +specified then it is the only location that is used; if the given file
> +doesn't exist then no configuration file is read. If the variable is
> +defined but empty, no configuration is loaded.
> +
>  The first configuration file found wins, configuration files in other
>  locations are ignored.
>  
> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
> index 592edcf30..8c2670e03 100644
> --- a/src/libcamera/global_configuration.cpp
> +++ b/src/libcamera/global_configuration.cpp
> @@ -23,13 +23,6 @@
>  
>  namespace libcamera {
>  
> -namespace {
> -const std::vector<std::filesystem::path> globalConfigurationFiles = {
> -	std::filesystem::path(LIBCAMERA_SYSCONF_DIR) / "configuration.yaml",
> -	std::filesystem::path(LIBCAMERA_DATA_DIR) / "configuration.yaml",
> -};
> -}
> -
>  LOG_DEFINE_CATEGORY(Configuration)
>  
>  /**
> @@ -80,6 +73,33 @@ bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)
>  
>  void GlobalConfiguration::load()
>  {
> +	const char *libcameraConfigName =
> +		utils::secure_getenv("LIBCAMERA_CONFIG_NAME");
> +	if (libcameraConfigName && libcameraConfigName[0] == '\0')
> +		return;
> +	if (!libcameraConfigName)
> +		libcameraConfigName = "";
> +
> +	std::filesystem::path configName(libcameraConfigName);
> +
> +	if (configName.is_absolute()) {
> +		loadFile(configName);
> +		return;
> +	}
> +
> +	if (configName.empty())
> +		configName = std::filesystem::path("configuration.yaml");
> +
> +	std::vector<std::filesystem::path> configurationDirectories;
> +
> +	const char *configDir = utils::secure_getenv("LIBCAMERA_CONFIG_DIR");
> +	if (configDir) {
> +		for (auto const &path : utils::split(configDir, ":")) {
> +			if (!path.empty())
> +				configurationDirectories.push_back(path);
> +		}
> +	}
> +
>  	std::filesystem::path userConfigurationDirectory;
>  	const char *xdgConfigHome = utils::secure_getenv("XDG_CONFIG_HOME");
>  	if (xdgConfigHome) {
> @@ -90,18 +110,16 @@ void GlobalConfiguration::load()
>  			userConfigurationDirectory =
>  				std::filesystem::path(home) / ".config";
>  	}
> +	if (!userConfigurationDirectory.empty())
> +		configurationDirectories.push_back(
> +			userConfigurationDirectory / "libcamera");
>  
> -	if (!userConfigurationDirectory.empty()) {
> -		std::filesystem::path user_configuration_file =
> -			userConfigurationDirectory / "libcamera" / "configuration.yaml";
> -		if (loadFile(user_configuration_file))
> -			return;
> -	}
> +	configurationDirectories.push_back(LIBCAMERA_SYSCONF_DIR);
> +	configurationDirectories.push_back(LIBCAMERA_DATA_DIR);
>  
> -	for (const auto &path : globalConfigurationFiles) {
> -		if (loadFile(path))
> +	for (const auto &path : configurationDirectories)
> +		if (loadFile(path / configName))
>  			return;
> -	}
>  }
>  
>  /**
Barnabás Pőcze Sept. 22, 2025, 7:52 a.m. UTC | #2
Hi

2025. 09. 21. 7:09 keltezéssel, Laurent Pinchart írta:
> Hi Milan,
> 
> Thank you for the patch.
> 
> On Fri, Sep 12, 2025 at 04:29:13PM +0200, Milan Zamazal wrote:
>> To support easy switching of configurations, let's introduce
>> LIBCAMERA_CONFIG_DIR and LIBCAMERA_CONFIG_NAME environment variables.
>>
>> LIBCAMERA_CONFIG_DIR environment variable specifies a directory or a
>> list of directories separated by colons where to look for the libcamera
>> configuration file before trying the standard locations.  This override
>> can be useful in a meson devenv.
>>
>> LIBCAMERA_CONFIG_NAME
>> - specifies the path of the configuration file to load if it is an
>>    absolute path (useful to quickly try some configuration); or
>> - prevents any configuration file from loading if it is defined and
>>    empty (useful to disable all configuration files); or
>> - specifies the path of the configuration file to load, relative to
>>    the configuration directories (useful to quickly select between
>>    different configurations).
> 
> This last part I'm not thrilled about. Do we really need such a complex
> scheme ? Wouldn't a single variable that overrides the configuration
> file lookup be enough ?

Some of these were my suggestions, so naturally this is my preferred behaviour,
how would that single variable look like? Are there are any concrete concerns?

I feel like this system is relatively flexible and even if it is more complex,
that complexity does not really affect anything else.

I believe the natural progression is to support configuration file merging, i.e.
having fragments in `<main-config-name>.d/` next to the selected main configuration
file and in higher-priority directories, which would allow overwriting single things
(on a per-user, per-system level) without having to copy/paste the whole thing. In
this case having the separation of the configuration directories and the name is
somewhat needed.


Regards,
Barnabás Pőcze


> 
> I'll merge the rest of the series already without this patch while we
> discuss the issue.
> 
>> If a configuration file specified by those environment variables doesn't
>> exist, no configuration file is loaded.
>>
>> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>> ---
>>   Documentation/runtime_configuration.rst | 13 +++++++
>>   src/libcamera/global_configuration.cpp  | 50 +++++++++++++++++--------
>>   2 files changed, 47 insertions(+), 16 deletions(-)
>>
>> diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
>> index 702139544..5307f3645 100644
>> --- a/Documentation/runtime_configuration.rst
>> +++ b/Documentation/runtime_configuration.rst
>> @@ -19,6 +19,19 @@ order:
>>   - LIBCAMERA_SYSCONF_DIR/configuration.yaml
>>   - LIBCAMERA_DATA_DIR/libcamera/configuration.yaml
>>   
>> +If LIBCAMERA_CONFIG_DIR environment variable is non-empty then it
>> +specifies additional directories where to look for the configuration
>> +file, before looking at the standard locations. It can be a single
>> +directory or multiple directories separated by colons.
>> +
>> +The default name of the configuration file, configuration.yaml, can be
>> +overridden in LIBCAMERA_CONFIG_NAME environment variable. The variable
>> +can specify just an alternative configuration file name to be looked up
>> +in the locations above, or a whole absolute path. If an absolute path is
>> +specified then it is the only location that is used; if the given file
>> +doesn't exist then no configuration file is read. If the variable is
>> +defined but empty, no configuration is loaded.
>> +
>>   The first configuration file found wins, configuration files in other
>>   locations are ignored.
>>   
>> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
>> index 592edcf30..8c2670e03 100644
>> --- a/src/libcamera/global_configuration.cpp
>> +++ b/src/libcamera/global_configuration.cpp
>> @@ -23,13 +23,6 @@
>>   
>>   namespace libcamera {
>>   
>> -namespace {
>> -const std::vector<std::filesystem::path> globalConfigurationFiles = {
>> -	std::filesystem::path(LIBCAMERA_SYSCONF_DIR) / "configuration.yaml",
>> -	std::filesystem::path(LIBCAMERA_DATA_DIR) / "configuration.yaml",
>> -};
>> -}
>> -
>>   LOG_DEFINE_CATEGORY(Configuration)
>>   
>>   /**
>> @@ -80,6 +73,33 @@ bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)
>>   
>>   void GlobalConfiguration::load()
>>   {
>> +	const char *libcameraConfigName =
>> +		utils::secure_getenv("LIBCAMERA_CONFIG_NAME");
>> +	if (libcameraConfigName && libcameraConfigName[0] == '\0')
>> +		return;
>> +	if (!libcameraConfigName)
>> +		libcameraConfigName = "";
>> +
>> +	std::filesystem::path configName(libcameraConfigName);
>> +
>> +	if (configName.is_absolute()) {
>> +		loadFile(configName);
>> +		return;
>> +	}
>> +
>> +	if (configName.empty())
>> +		configName = std::filesystem::path("configuration.yaml");
>> +
>> +	std::vector<std::filesystem::path> configurationDirectories;
>> +
>> +	const char *configDir = utils::secure_getenv("LIBCAMERA_CONFIG_DIR");
>> +	if (configDir) {
>> +		for (auto const &path : utils::split(configDir, ":")) {
>> +			if (!path.empty())
>> +				configurationDirectories.push_back(path);
>> +		}
>> +	}
>> +
>>   	std::filesystem::path userConfigurationDirectory;
>>   	const char *xdgConfigHome = utils::secure_getenv("XDG_CONFIG_HOME");
>>   	if (xdgConfigHome) {
>> @@ -90,18 +110,16 @@ void GlobalConfiguration::load()
>>   			userConfigurationDirectory =
>>   				std::filesystem::path(home) / ".config";
>>   	}
>> +	if (!userConfigurationDirectory.empty())
>> +		configurationDirectories.push_back(
>> +			userConfigurationDirectory / "libcamera");
>>   
>> -	if (!userConfigurationDirectory.empty()) {
>> -		std::filesystem::path user_configuration_file =
>> -			userConfigurationDirectory / "libcamera" / "configuration.yaml";
>> -		if (loadFile(user_configuration_file))
>> -			return;
>> -	}
>> +	configurationDirectories.push_back(LIBCAMERA_SYSCONF_DIR);
>> +	configurationDirectories.push_back(LIBCAMERA_DATA_DIR);
>>   
>> -	for (const auto &path : globalConfigurationFiles) {
>> -		if (loadFile(path))
>> +	for (const auto &path : configurationDirectories)
>> +		if (loadFile(path / configName))
>>   			return;
>> -	}
>>   }
>>   
>>   /**
>

Patch
diff mbox series

diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
index 702139544..5307f3645 100644
--- a/Documentation/runtime_configuration.rst
+++ b/Documentation/runtime_configuration.rst
@@ -19,6 +19,19 @@  order:
 - LIBCAMERA_SYSCONF_DIR/configuration.yaml
 - LIBCAMERA_DATA_DIR/libcamera/configuration.yaml
 
+If LIBCAMERA_CONFIG_DIR environment variable is non-empty then it
+specifies additional directories where to look for the configuration
+file, before looking at the standard locations. It can be a single
+directory or multiple directories separated by colons.
+
+The default name of the configuration file, configuration.yaml, can be
+overridden in LIBCAMERA_CONFIG_NAME environment variable. The variable
+can specify just an alternative configuration file name to be looked up
+in the locations above, or a whole absolute path. If an absolute path is
+specified then it is the only location that is used; if the given file
+doesn't exist then no configuration file is read. If the variable is
+defined but empty, no configuration is loaded.
+
 The first configuration file found wins, configuration files in other
 locations are ignored.
 
diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
index 592edcf30..8c2670e03 100644
--- a/src/libcamera/global_configuration.cpp
+++ b/src/libcamera/global_configuration.cpp
@@ -23,13 +23,6 @@ 
 
 namespace libcamera {
 
-namespace {
-const std::vector<std::filesystem::path> globalConfigurationFiles = {
-	std::filesystem::path(LIBCAMERA_SYSCONF_DIR) / "configuration.yaml",
-	std::filesystem::path(LIBCAMERA_DATA_DIR) / "configuration.yaml",
-};
-}
-
 LOG_DEFINE_CATEGORY(Configuration)
 
 /**
@@ -80,6 +73,33 @@  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)
 
 void GlobalConfiguration::load()
 {
+	const char *libcameraConfigName =
+		utils::secure_getenv("LIBCAMERA_CONFIG_NAME");
+	if (libcameraConfigName && libcameraConfigName[0] == '\0')
+		return;
+	if (!libcameraConfigName)
+		libcameraConfigName = "";
+
+	std::filesystem::path configName(libcameraConfigName);
+
+	if (configName.is_absolute()) {
+		loadFile(configName);
+		return;
+	}
+
+	if (configName.empty())
+		configName = std::filesystem::path("configuration.yaml");
+
+	std::vector<std::filesystem::path> configurationDirectories;
+
+	const char *configDir = utils::secure_getenv("LIBCAMERA_CONFIG_DIR");
+	if (configDir) {
+		for (auto const &path : utils::split(configDir, ":")) {
+			if (!path.empty())
+				configurationDirectories.push_back(path);
+		}
+	}
+
 	std::filesystem::path userConfigurationDirectory;
 	const char *xdgConfigHome = utils::secure_getenv("XDG_CONFIG_HOME");
 	if (xdgConfigHome) {
@@ -90,18 +110,16 @@  void GlobalConfiguration::load()
 			userConfigurationDirectory =
 				std::filesystem::path(home) / ".config";
 	}
+	if (!userConfigurationDirectory.empty())
+		configurationDirectories.push_back(
+			userConfigurationDirectory / "libcamera");
 
-	if (!userConfigurationDirectory.empty()) {
-		std::filesystem::path user_configuration_file =
-			userConfigurationDirectory / "libcamera" / "configuration.yaml";
-		if (loadFile(user_configuration_file))
-			return;
-	}
+	configurationDirectories.push_back(LIBCAMERA_SYSCONF_DIR);
+	configurationDirectories.push_back(LIBCAMERA_DATA_DIR);
 
-	for (const auto &path : globalConfigurationFiles) {
-		if (loadFile(path))
+	for (const auto &path : configurationDirectories)
+		if (loadFile(path / configName))
 			return;
-	}
 }
 
 /**