diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 5eb646e3..0d2ccb8a 100644 --- a/include/libcamera/internal/global_configuration.h +++ b/include/libcamera/internal/global_configuration.h @@ -42,13 +42,6 @@ public: std::optional> listOption( const std::initializer_list confPath) const; - std::optional envOption( - const char *const envVariable, - const std::initializer_list confPath) const; - std::optional> envListOption( - const char *const envVariable, - const std::initializer_list confPath, - const std::string delimiter = ":") const; private: void load(); diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index f774bd84..0dd4e0c5 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -114,9 +114,7 @@ void CameraManager::Private::createPipelineHandlers() * there is no configuration file. */ const auto pipesList = - configuration().envListOption("LIBCAMERA_PIPELINES_MATCH_LIST", - { "pipelines_match_list" }, - ","); + configuration().listOption({ "pipelines_match_list" }); if (pipesList.has_value()) { /* * When a list of preferred pipelines is defined, iterate diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp index 62b9762d..46a285c3 100644 --- a/src/libcamera/global_configuration.cpp +++ b/src/libcamera/global_configuration.cpp @@ -7,6 +7,7 @@ #include "libcamera/internal/global_configuration.h" +#include #include #include #include @@ -30,6 +31,99 @@ const std::vector globalConfigurationFiles = { std::filesystem::path(LIBCAMERA_DATA_DIR) / "configuration.yaml", }; +class EnvironmentProcessor +{ +public: + virtual ~EnvironmentProcessor() = default; + + virtual void process(ValueNode &node, const char *env) = 0; +}; + +/* A processor that sets a fixed value. */ +template +class EnvironmentFixedProcessor : public EnvironmentProcessor +{ +public: + EnvironmentFixedProcessor(const T &value) + : value_(value) + { + } + + void process(ValueNode &node, [[maybe_unused]] const char *env) override + { + node.set(value_); + } + +private: + T value_; +}; + +/* + * A processor that parses the environment variable as a list of strings with a + * custom delimiter. + */ +class EnvironmentListProcessor : public EnvironmentProcessor +{ +public: + EnvironmentListProcessor(const char *delimiter) + : delimiter_(delimiter) + { + } + + void process(ValueNode &node, const char *env) override + { + for (auto &&value : utils::split(env, delimiter_)) + node.add(std::make_unique(std::move(value))); + } + +private: + const std::string delimiter_; +}; + +/* A processor that copies the value of the environment variable. */ +class EnvironmentValueProcessor : public EnvironmentProcessor +{ +public: + void process(ValueNode &node, const char *env) override + { + node.set(std::string{ env }); + } +}; + +struct EnvironmentOverride { + const char *variable; + std::initializer_list path; + std::unique_ptr processor; +}; + +const std::array environmentOverrides{ { + { + "LIBCAMERA_IPA_CONFIG_PATH", + { "ipa", "config_paths" }, + std::make_unique(":"), + }, { + "LIBCAMERA_IPA_FORCE_ISOLATION", + { "ipa", "force_isolation" }, + std::make_unique>(true), + }, { + "LIBCAMERA_IPA_MODULE_PATH", + { "ipa", "module_paths" }, + std::make_unique(":"), + }, { + "LIBCAMERA_IPA_PROXY_PATH", + { "ipa", "proxy_paths" }, + std::make_unique(":"), + }, { + "LIBCAMERA_PIPELINES_MATCH_LIST", + { "pipelines_match_list" }, + std::make_unique(","), + }, { + "LIBCAMERA_SOFTISP_MODE", + { "software_isp", "mode" }, + std::make_unique(), + }, +} }; + } /* namespace */ LOG_DEFINE_CATEGORY(Configuration) @@ -50,9 +144,9 @@ LOG_DEFINE_CATEGORY(Configuration) * reported and no configuration file is used. This is to prevent libcamera from * using an unintended configuration file. * - * The configuration can be accessed using the provided helpers, namely - * option(), envOption(), listOption() and envListOption() to access individual - * options, or configuration() to access the whole configuration. + * The configuration can be accessed using the provided helpers, namely option() + * and listOption() to access individual options, or configuration() to access + * the whole configuration. */ /** @@ -66,6 +160,25 @@ GlobalConfiguration::GlobalConfiguration() configuration_->add("version", std::make_unique(1)); configuration_->add("configuration", std::make_unique()); } + + /* Process environment variables that override configuration options. */ + ValueNode *cfg = configuration_->at("configuration"); + + for (const EnvironmentOverride &envOverride : environmentOverrides) { + const char *envValue = utils::secure_getenv(envOverride.variable); + if (!envValue || !envValue[0]) + continue; + + std::unique_ptr node = std::make_unique(); + envOverride.processor->process(*node.get(), envValue); + + cfg->erase(envOverride.path); + + if (!cfg->add(envOverride.path, std::move(node))) + LOG(Configuration, Error) + << "Failed to override " + << utils::join(envOverride.path, "/"); + } } void GlobalConfiguration::load() @@ -185,65 +298,4 @@ std::optional> GlobalConfiguration::listOption( return c->get>(); } -/** - * \brief Retrieve the value of environment variable with a fallback on the configuration file - * \param[in] envVariable Environment variable to get the value from - * \param[in] confPath The sequence of YAML section names to fall back on when - * \a envVariable is unavailable - * - * This helper looks first at the given environment variable and if it is - * defined then it returns its value (even if it is empty). Otherwise it looks - * for \a confPath the same way as in GlobalConfiguration::option. Only string - * values are supported. - * - * \note Support for using environment variables to configure libcamera behavior - * is provided here mostly for backward compatibility reasons. Introducing new - * configuration environment variables is discouraged. - * - * \return The value retrieved from the given environment if it is set, - * otherwise the value from the configuration file if it exists, or no value if - * it does not - */ -std::optional GlobalConfiguration::envOption( - const char *envVariable, - const std::initializer_list confPath) const -{ - const char *envValue = utils::secure_getenv(envVariable); - if (envValue) - return std::optional{ std::string{ envValue } }; - return option(confPath); -} - -/** - * \brief Retrieve the value of the configuration option from a file or environment - * \param[in] envVariable Environment variable to get the value from - * \param[in] confPath The same as in GlobalConfiguration::option - * \param[in] delimiter Items separator in the environment variable - * - * This helper looks first at the given environment variable and if it is - * defined (even if it is empty) then it splits its value by semicolons and - * returns the resulting list of strings. Otherwise it looks for \a confPath the - * same way as in GlobalConfiguration::option, value of which must be a list of - * strings. - * - * \note Support for using environment variables to configure libcamera behavior - * is provided here mostly for backward compatibility reasons. Introducing new - * configuration environment variables is discouraged. - * - * \return A vector of strings retrieved from the given environment option or - * configuration file or no value if not found; the vector may be empty - */ -std::optional> GlobalConfiguration::envListOption( - const char *const envVariable, - const std::initializer_list confPath, - const std::string delimiter) const -{ - const char *envValue = utils::secure_getenv(envVariable); - if (envValue) { - auto items = utils::split(envValue, delimiter); - return std::vector(items.begin(), items.end()); - } - return listOption(confPath); -} - } /* namespace libcamera */ diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index dd1f483b..a351f4f7 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -114,18 +114,15 @@ IPAManager::IPAManager(const CameraManager &cm) if (!pubKey_.isValid()) LOG(IPAManager, Warning) << "Public key not valid"; - char *force = utils::secure_getenv("LIBCAMERA_IPA_FORCE_ISOLATION"); - forceIsolation_ = (force && force[0] != '\0') || - (!force && configuration.option({ "ipa", "force_isolation" }) - .value_or(false)); + forceIsolation_ = configuration.option({ "ipa", "force_isolation" }) + .value_or(false); #endif unsigned int ipaCount = 0; /* User-specified paths take precedence. */ const auto modulePaths = - configuration.envListOption( - "LIBCAMERA_IPA_MODULE_PATH", { "ipa", "module_paths" }) + configuration.listOption({ "ipa", "module_paths" }) .value_or(std::vector()); for (const auto &dir : modulePaths) { if (dir.empty()) diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp index 6c8780a0..bc8ff090 100644 --- a/src/libcamera/ipa_proxy.cpp +++ b/src/libcamera/ipa_proxy.cpp @@ -124,11 +124,9 @@ IPAProxy::IPAProxy(IPAModule *ipam, const CameraManager &cm) { const GlobalConfiguration &configuration = cm._d()->configuration(); - configPaths_ = configuration.envListOption("LIBCAMERA_IPA_CONFIG_PATH", - { "ipa", "config_paths" }) + configPaths_ = configuration.listOption({ "ipa", "config_paths" }) .value_or(std::vector()); - execPaths_ = configuration.envListOption("LIBCAMERA_IPA_PROXY_PATH", - { "ipa", "proxy_paths" }) + execPaths_ = configuration.listOption({ "ipa", "proxy_paths" }) .value_or(std::vector()); } diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp index cd0e9d06..d227bd8e 100644 --- a/src/libcamera/software_isp/software_isp.cpp +++ b/src/libcamera/software_isp/software_isp.cpp @@ -106,11 +106,11 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor, #if HAVE_DEBAYER_EGL const GlobalConfiguration &configuration = cm._d()->configuration(); - std::optional softISPMode = configuration.envOption("LIBCAMERA_SOFTISP_MODE", { "software_isp", "mode" }); + std::optional softISPMode = configuration.option({ "software_isp", "mode" }); if (softISPMode) { if (softISPMode != "gpu" && softISPMode != "cpu") { LOG(SoftwareIsp, Error) - << "Invalid LIBCAMERA_SOFTISP_MODE \"" + << "Invalid software ISP mode \"" << softISPMode.value() << "\", must be \"cpu\" or \"gpu\""; return;