From patchwork Thu Apr 23 23:00:55 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 26555 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 231FCBDCB5 for ; Thu, 23 Apr 2026 23:02:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A34EA62FDD; Fri, 24 Apr 2026 01:02:05 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="aO9tfmWP"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D876362F6C for ; Fri, 24 Apr 2026 01:01:46 +0200 (CEST) Received: from killaraus.ideasonboard.com (2001-14ba-703d-e500--2a1.rev.dnainternet.fi [IPv6:2001:14ba:703d:e500::2a1]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5185B19A8 for ; Fri, 24 Apr 2026 01:00:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1776985207; bh=45pUlmggSMulllW/YOeKF1iAAsL4nW4JplvthcCldwg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=aO9tfmWPL8a9IJBWJr6YxQw/zN5Fq6zYlU86JHqRJe2vu3dLuveEjFy/ul6vGZ5a5 CHeCy7nfvwXg2MSHTjpcbfY/6VIVyxHcEC4PaH6sBw9CxvgM9fO5Pzaxz/B2XVKwHN /zTtiBNEtq4SHYR2RFINiyDBeMz/gC6PMnls+Q84= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Subject: [PATCH v3 33/37] libcamera: global_configuration: Override options with environment variables Date: Fri, 24 Apr 2026 02:00:55 +0300 Message-ID: <20260423230059.3180987-34-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> References: <20260423230059.3180987-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" libcamera supports several environment variables that can override configuration options. This requires components that use such overrides to call the special envOption() and envListOption() functions, spreading knowledge of the overrides through the code base. This will hinder future enhancements to the global configuration, such as implementing per-camera options that will override pipeline handler-level options. To prepare for that, move handling of the environment variables to the GlobalConfiguration class. Signed-off-by: Laurent Pinchart Reviewed-by: Isaac Scott --- Changes since v1: - Avoid copy in range-based loop - Drop unneeded constructor - Erase existing node before overriding - Move override code to constructor --- .../libcamera/internal/global_configuration.h | 7 - src/libcamera/camera_manager.cpp | 4 +- src/libcamera/global_configuration.cpp | 180 +++++++++++------- src/libcamera/ipa_manager.cpp | 9 +- src/libcamera/ipa_proxy.cpp | 6 +- src/libcamera/software_isp/software_isp.cpp | 4 +- 6 files changed, 124 insertions(+), 86 deletions(-) diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h index 5eb646e33fc2..0d2ccb8a1808 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 f774bd84291b..0dd4e0c590a1 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 62b9762d3e31..46a285c3c4ea 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 dd1f483beec3..a351f4f7b581 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 6c8780a012d5..bc8ff090fa86 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 cd0e9d06a1e1..d227bd8e325f 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;