[{"id":35720,"web_url":"https://patchwork.libcamera.org/comment/35720/","msgid":"<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>","date":"2025-09-08T10:25:15","subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Milan,\n\nThanks for the patch.\n\nQuoting Milan Zamazal (2025-07-29 16:31:51)\n> Let's add some helpers to make accessing simple configuration values\n> simpler.\n> \n> GlobalConfiguration::option ensures that no value is returned rather\n> than a value of YamlObject::empty.\n> \n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n> ---\n>  .../libcamera/internal/global_configuration.h |  8 +++\n>  src/libcamera/global_configuration.cpp        | 53 +++++++++++++++++++\n>  2 files changed, 61 insertions(+)\n> \n> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n> index f695498c4..78bebff9f 100644\n> --- a/include/libcamera/internal/global_configuration.h\n> +++ b/include/libcamera/internal/global_configuration.h\n> @@ -8,6 +8,9 @@\n>  #pragma once\n>  \n>  #include <filesystem>\n> +#include <optional>\n> +#include <string>\n> +#include <string_view>\n>  \n>  #include \"libcamera/internal/yaml_parser.h\"\n>  \n> @@ -22,6 +25,11 @@ public:\n>  \n>         unsigned int version() const;\n>         Configuration configuration() const;\n> +       std::optional<std::string> option(\n> +               const std::initializer_list<std::string_view> confPath) const;\n> +       std::optional<std::string> envOption(\n> +               const char *const envVariable,\n> +               const std::initializer_list<std::string_view> confPath) const;\n>  \n>  private:\n>         bool loadFile(const std::filesystem::path &fileName);\n> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n> index 50756b859..5be0f0701 100644\n> --- a/src/libcamera/global_configuration.cpp\n> +++ b/src/libcamera/global_configuration.cpp\n> @@ -8,6 +8,8 @@\n>  #include \"libcamera/internal/global_configuration.h\"\n>  \n>  #include <filesystem>\n> +#include <memory>\n> +#include <string>\n>  #include <string_view>\n>  #include <sys/types.h>\n>  \n> @@ -39,6 +41,11 @@ LOG_DEFINE_CATEGORY(Configuration)\n>   * is not found then in system-wide configuration directories. If multiple\n>   * configuration files exist then only the first one found is used and no\n>   * configuration merging is performed.\n> + *\n> + * The configuration can be accessed using the provided helpers. Namely\n> + * GlobalConfiguration::option() or GlobalConfiguration::envOption() to access\n> + * individual options or GlobalConfiguration::configuration() to access the\n> + * whole configuration.\n>   */\n>  \n>  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n> @@ -106,6 +113,52 @@ GlobalConfiguration::GlobalConfiguration()\n>   * the underlying type.\n>   */\n>  \n> +/**\n> + * \\brief Return value of the configuration option identified by \\a confPath\n> + * \\param[in] confPath Sequence of the YAML section names (excluding\n> + * `configuration') leading to the requested option\n> + * \\return A value if an item corresponding to \\a confPath exists in the\n> + * configuration file, no value otherwise\n\nI think:\n\n\\return The value of the configuration item corresponding to \\a confPath if it\nexists in the configuration file, or no value otherwise\n\n> + */\n> +std::optional<std::string> GlobalConfiguration::option(\n> +       const std::initializer_list<std::string_view> confPath) const\n> +{\n> +       const YamlObject *c = &configuration();\n> +       for (auto part : confPath) {\n> +               c = &(*c)[part];\n> +               if (!*c)\n> +                       return {};\n> +       }\n> +       return c->get<std::string>();\n> +}\n> +\n> +/**\n> + * \\brief Return value of the configuration option from a file or environment\n\nMaybe \"Return value of environment variable with a fallback on the\nconfiguration file\" ?\n\nOtherwise it sounds like you can actually get the file from either of your\nchoice.\n\n> + * \\param[in] envVariable Environment variable to get the value from\n> + * \\param[in] confPath The same as in GlobalConfiguration::option\n\nMaybe \"The sequence of YAML section names to fall back on when \\a envVariable\nis unavailable\" ?\n\nI personally think that falling back on the config file is an important concept\nin this function that should be in the documentation.\n\n> + *\n> + * This helper looks first at the given environment variable and if it is\n> + * defined then it returns its value (even if it is empty). Otherwise it looks\n\nI wonder if this (returning empty environment variable) will cause any\nconfusion for users? I did a quick test with setting an environment variable to\nan empty string and the unset command gave me the same result when it was set\nto an empty string as to when it wasn't set at all.\n\n> + * for \\a confPath the same way as in GlobalConfiguration::option. Only string\n> + * values are supported.\n> + *\n> + * \\note Support for using environment variables to configure libcamera behavior\n> + * is provided here mostly for backward compatibility reasons. Introducing new\n> + * configuration environment variables is discouraged.\n> + *\n> + * \\return A value retrieved from the given environment option or configuration\n> + * file or no value if not found\n\nThis is a bit convoluted... maybe \"The value retrieved from the given\nenvironment if it is set, otherwise the value from the configuration file if it\nexists\" ?\n\nI like the idea though.\n\n\nThanks,\n\nPaul\n\n> + */\n> +std::optional<std::string> GlobalConfiguration::envOption(\n> +       const char *envVariable,\n> +       const std::initializer_list<std::string_view> confPath) const\n> +{\n> +       const char *envValue = utils::secure_getenv(envVariable);\n> +       if (envValue)\n> +               return std::optional{ std::string{ envValue } };\n> +       return option(confPath);\n> +}\n> +\n>  /**\n>   * \\brief Return configuration version\n>   *\n> -- \n> 2.50.1\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 2C85BC324E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  8 Sep 2025 10:25:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EDD896934E;\n\tMon,  8 Sep 2025 12:25:26 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 46A5369345\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  8 Sep 2025 12:25:24 +0200 (CEST)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:60ea:3a39:598b:6db8])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 7EEF9C71;\n\tMon,  8 Sep 2025 12:24:11 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"qQCBQId6\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1757327052;\n\tbh=p57lgf1dktFnhVjs3aUwtSxBk8bTewXcYvpjRQdaPwI=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=qQCBQId6tBacPrw4unB/AYBJKdGcxNG9KVJeBq8UCTFjLZq6CKoV7QGvHjJe40heq\n\tvgtfzvuV/+dWYlIh1NBCjlJqM9BH5tUD7/BrGN3Kc4hRUj1mSAAzQsu0mb7uOcF3ZK\n\t5tUzT6cc/+t2PT4wfVCQPVjEhJM+ihvQBr1b98cI=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20250729073201.5369-4-mzamazal@redhat.com>","References":"<20250729073201.5369-1-mzamazal@redhat.com>\n\t<20250729073201.5369-4-mzamazal@redhat.com>","Subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"Milan Zamazal <mzamazal@redhat.com>, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<barnabas.pocze@ideasonboard.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>","To":"Milan Zamazal <mzamazal@redhat.com>, libcamera-devel@lists.libcamera.org","Date":"Mon, 08 Sep 2025 19:25:15 +0900","Message-ID":"<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35723,"web_url":"https://patchwork.libcamera.org/comment/35723/","msgid":"<c128c152-d326-4c36-9687-e5c2e02dec60@ideasonboard.com>","date":"2025-09-08T12:06:43","subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n2025. 09. 08. 12:25 keltezéssel, Paul Elder írta:\n> Hi Milan,\n> \n> Thanks for the patch.\n> \n> Quoting Milan Zamazal (2025-07-29 16:31:51)\n>> Let's add some helpers to make accessing simple configuration values\n>> simpler.\n>>\n>> GlobalConfiguration::option ensures that no value is returned rather\n>> than a value of YamlObject::empty.\n>>\n>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n>> ---\n>>   .../libcamera/internal/global_configuration.h |  8 +++\n>>   src/libcamera/global_configuration.cpp        | 53 +++++++++++++++++++\n>>   2 files changed, 61 insertions(+)\n>>\n>> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n>> index f695498c4..78bebff9f 100644\n>> --- a/include/libcamera/internal/global_configuration.h\n>> +++ b/include/libcamera/internal/global_configuration.h\n>> @@ -8,6 +8,9 @@\n>>   #pragma once\n>>   \n>>   #include <filesystem>\n>> +#include <optional>\n>> +#include <string>\n>> +#include <string_view>\n>>   \n>>   #include \"libcamera/internal/yaml_parser.h\"\n>>   \n>> @@ -22,6 +25,11 @@ public:\n>>   \n>>          unsigned int version() const;\n>>          Configuration configuration() const;\n>> +       std::optional<std::string> option(\n>> +               const std::initializer_list<std::string_view> confPath) const;\n>> +       std::optional<std::string> envOption(\n>> +               const char *const envVariable,\n>> +               const std::initializer_list<std::string_view> confPath) const;\n>>   \n>>   private:\n>>          bool loadFile(const std::filesystem::path &fileName);\n>> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n>> index 50756b859..5be0f0701 100644\n>> --- a/src/libcamera/global_configuration.cpp\n>> +++ b/src/libcamera/global_configuration.cpp\n>> @@ -8,6 +8,8 @@\n>>   #include \"libcamera/internal/global_configuration.h\"\n>>   \n>>   #include <filesystem>\n>> +#include <memory>\n>> +#include <string>\n>>   #include <string_view>\n>>   #include <sys/types.h>\n>>   \n>> @@ -39,6 +41,11 @@ LOG_DEFINE_CATEGORY(Configuration)\n>>    * is not found then in system-wide configuration directories. If multiple\n>>    * configuration files exist then only the first one found is used and no\n>>    * configuration merging is performed.\n>> + *\n>> + * The configuration can be accessed using the provided helpers. Namely\n>> + * GlobalConfiguration::option() or GlobalConfiguration::envOption() to access\n>> + * individual options or GlobalConfiguration::configuration() to access the\n>> + * whole configuration.\n>>    */\n>>   \n>>   bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n>> @@ -106,6 +113,52 @@ GlobalConfiguration::GlobalConfiguration()\n>>    * the underlying type.\n>>    */\n>>   \n>> +/**\n>> + * \\brief Return value of the configuration option identified by \\a confPath\n>> + * \\param[in] confPath Sequence of the YAML section names (excluding\n>> + * `configuration') leading to the requested option\n>> + * \\return A value if an item corresponding to \\a confPath exists in the\n>> + * configuration file, no value otherwise\n> \n> I think:\n> \n> \\return The value of the configuration item corresponding to \\a confPath if it\n> exists in the configuration file, or no value otherwise\n> \n>> + */\n>> +std::optional<std::string> GlobalConfiguration::option(\n>> +       const std::initializer_list<std::string_view> confPath) const\n>> +{\n>> +       const YamlObject *c = &configuration();\n>> +       for (auto part : confPath) {\n>> +               c = &(*c)[part];\n>> +               if (!*c)\n>> +                       return {};\n>> +       }\n>> +       return c->get<std::string>();\n>> +}\n>> +\n>> +/**\n>> + * \\brief Return value of the configuration option from a file or environment\n> \n> Maybe \"Return value of environment variable with a fallback on the\n> configuration file\" ?\n> \n> Otherwise it sounds like you can actually get the file from either of your\n> choice.\n> \n>> + * \\param[in] envVariable Environment variable to get the value from\n>> + * \\param[in] confPath The same as in GlobalConfiguration::option\n> \n> Maybe \"The sequence of YAML section names to fall back on when \\a envVariable\n> is unavailable\" ?\n> \n> I personally think that falling back on the config file is an important concept\n> in this function that should be in the documentation.\n> \n>> + *\n>> + * This helper looks first at the given environment variable and if it is\n>> + * defined then it returns its value (even if it is empty). Otherwise it looks\n> \n> I wonder if this (returning empty environment variable) will cause any\n> confusion for users? I did a quick test with setting an environment variable to\n> an empty string and the unset command gave me the same result when it was set\n> to an empty string as to when it wasn't set at all.\n\nSo that behaviour was my suggestion. The idea was that sometimes you might want\nto explicitly inhibit some kind of behaviour, and you would do that by setting the\nenv var to the empty string. (Also see one of the later patches that uses the same\nlogic to inhibit the loading of any configuration.) I actually had your \"layers\" code\nin mind, e.g. using `LIBCAMERA_LAYERS= cam ...` to inhibit the loading of any\nlayers that would otherwise be loaded based on some configuration, etc. Of course\n`env -u LIBCAMERA_LAYERS cam` would also work, but that seems more convoluted.\n\n\nRegards,\nBarnabás Pőcze\n\n\n\n> \n>> + * for \\a confPath the same way as in GlobalConfiguration::option. Only string\n>> + * values are supported.\n>> + *\n>> + * \\note Support for using environment variables to configure libcamera behavior\n>> + * is provided here mostly for backward compatibility reasons. Introducing new\n>> + * configuration environment variables is discouraged.\n>> + *\n>> + * \\return A value retrieved from the given environment option or configuration\n>> + * file or no value if not found\n> \n> This is a bit convoluted... maybe \"The value retrieved from the given\n> environment if it is set, otherwise the value from the configuration file if it\n> exists\" ?\n> \n> I like the idea though.\n> \n> \n> Thanks,\n> \n> Paul\n> \n>> + */\n>> +std::optional<std::string> GlobalConfiguration::envOption(\n>> +       const char *envVariable,\n>> +       const std::initializer_list<std::string_view> confPath) const\n>> +{\n>> +       const char *envValue = utils::secure_getenv(envVariable);\n>> +       if (envValue)\n>> +               return std::optional{ std::string{ envValue } };\n>> +       return option(confPath);\n>> +}\n>> +\n>>   /**\n>>    * \\brief Return configuration version\n>>    *\n>> -- \n>> 2.50.1\n>>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B28ADC324E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  8 Sep 2025 12:06:51 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A75DA69345;\n\tMon,  8 Sep 2025 14:06:50 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BCD7C69338\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  8 Sep 2025 14:06:48 +0200 (CEST)","from [192.168.33.12] (185.221.142.115.nat.pool.zt.hu\n\t[185.221.142.115])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 9451DC71;\n\tMon,  8 Sep 2025 14:05:35 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"nw0cZpIp\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1757333136;\n\tbh=knRtB9SEledz40iJnrWxPQao4EnU90ytAFmsTaeUM/E=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=nw0cZpIpji3Q3xv/3galpNO3Nyemy2i2+ix/E0JO3LtzJ3+aoyxNVeGoqqQerxlLT\n\tjq9NwBNtvB32EeRLsErrLA/8dyYcAFpWh5Y0bkhJOuf3vm6cfMrwtUeVU6h54z6tex\n\tjO25EcJZNP2ZJ2NF/sG/5GWuq55H5ei6HfWlE3WE=","Message-ID":"<c128c152-d326-4c36-9687-e5c2e02dec60@ideasonboard.com>","Date":"Mon, 8 Sep 2025 14:06:43 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tMilan Zamazal <mzamazal@redhat.com>, libcamera-devel@lists.libcamera.org","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20250729073201.5369-1-mzamazal@redhat.com>\n\t<20250729073201.5369-4-mzamazal@redhat.com>\n\t<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35727,"web_url":"https://patchwork.libcamera.org/comment/35727/","msgid":"<85o6rlt32a.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2025-09-08T14:46:21","subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Hi Paul,\n\nthank you for the wording improvement suggestions, I'll use them when\npreparing v17.\n\nPaul Elder <paul.elder@ideasonboard.com> writes:\n\n> Hi Milan,\n>\n> Thanks for the patch.\n>\n> Quoting Milan Zamazal (2025-07-29 16:31:51)\n>> Let's add some helpers to make accessing simple configuration values\n>> simpler.\n>> \n>> GlobalConfiguration::option ensures that no value is returned rather\n>> than a value of YamlObject::empty.\n>> \n>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n>> ---\n>>  .../libcamera/internal/global_configuration.h |  8 +++\n>>  src/libcamera/global_configuration.cpp        | 53 +++++++++++++++++++\n>>  2 files changed, 61 insertions(+)\n>> \n>> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n>> index f695498c4..78bebff9f 100644\n>> --- a/include/libcamera/internal/global_configuration.h\n>> +++ b/include/libcamera/internal/global_configuration.h\n>> @@ -8,6 +8,9 @@\n>>  #pragma once\n>>  \n>>  #include <filesystem>\n>> +#include <optional>\n>> +#include <string>\n>> +#include <string_view>\n>>  \n>>  #include \"libcamera/internal/yaml_parser.h\"\n>>  \n>> @@ -22,6 +25,11 @@ public:\n>>  \n>>         unsigned int version() const;\n>>         Configuration configuration() const;\n>> +       std::optional<std::string> option(\n>> +               const std::initializer_list<std::string_view> confPath) const;\n>> +       std::optional<std::string> envOption(\n>> +               const char *const envVariable,\n>> +               const std::initializer_list<std::string_view> confPath) const;\n>>  \n>>  private:\n>>         bool loadFile(const std::filesystem::path &fileName);\n>> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n>> index 50756b859..5be0f0701 100644\n>> --- a/src/libcamera/global_configuration.cpp\n>> +++ b/src/libcamera/global_configuration.cpp\n>> @@ -8,6 +8,8 @@\n>>  #include \"libcamera/internal/global_configuration.h\"\n>>  \n>>  #include <filesystem>\n>> +#include <memory>\n>> +#include <string>\n>>  #include <string_view>\n>>  #include <sys/types.h>\n>>  \n>> @@ -39,6 +41,11 @@ LOG_DEFINE_CATEGORY(Configuration)\n>>   * is not found then in system-wide configuration directories. If multiple\n>>   * configuration files exist then only the first one found is used and no\n>>   * configuration merging is performed.\n>> + *\n>> + * The configuration can be accessed using the provided helpers. Namely\n>> + * GlobalConfiguration::option() or GlobalConfiguration::envOption() to access\n>> + * individual options or GlobalConfiguration::configuration() to access the\n>> + * whole configuration.\n>>   */\n>>  \n>>  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n>> @@ -106,6 +113,52 @@ GlobalConfiguration::GlobalConfiguration()\n>>   * the underlying type.\n>>   */\n>>  \n>> +/**\n>> + * \\brief Return value of the configuration option identified by \\a confPath\n>> + * \\param[in] confPath Sequence of the YAML section names (excluding\n>> + * `configuration') leading to the requested option\n>> + * \\return A value if an item corresponding to \\a confPath exists in the\n>> + * configuration file, no value otherwise\n>\n> I think:\n>\n> \\return The value of the configuration item corresponding to \\a confPath if it\n> exists in the configuration file, or no value otherwise\n>\n>> + */\n>> +std::optional<std::string> GlobalConfiguration::option(\n>> +       const std::initializer_list<std::string_view> confPath) const\n>> +{\n>> +       const YamlObject *c = &configuration();\n>> +       for (auto part : confPath) {\n>> +               c = &(*c)[part];\n>> +               if (!*c)\n>> +                       return {};\n>> +       }\n>> +       return c->get<std::string>();\n>> +}\n>> +\n>> +/**\n>> + * \\brief Return value of the configuration option from a file or environment\n>\n> Maybe \"Return value of environment variable with a fallback on the\n> configuration file\" ?\n>\n> Otherwise it sounds like you can actually get the file from either of your\n> choice.\n>\n>> + * \\param[in] envVariable Environment variable to get the value from\n>> + * \\param[in] confPath The same as in GlobalConfiguration::option\n>\n> Maybe \"The sequence of YAML section names to fall back on when \\a envVariable\n> is unavailable\" ?\n>\n> I personally think that falling back on the config file is an important concept\n> in this function that should be in the documentation.\n>\n>> + *\n>> + * This helper looks first at the given environment variable and if it is\n>> + * defined then it returns its value (even if it is empty). Otherwise it looks\n>\n> I wonder if this (returning empty environment variable) will cause any\n> confusion for users? \n\nExplained by Barnabás; I accepted his suggestion as it looked reasonable\nto me.\n\n> I did a quick test with setting an environment variable to an empty\n> string and the unset command gave me the same result when it was set\n> to an empty string as to when it wasn't set at all.\n\nDo you mean it doesn't work as described or it differs from the current\nbehaviour?\n\n>> + * for \\a confPath the same way as in GlobalConfiguration::option. Only string\n>> + * values are supported.\n>> + *\n>> + * \\note Support for using environment variables to configure libcamera behavior\n>> + * is provided here mostly for backward compatibility reasons. Introducing new\n>> + * configuration environment variables is discouraged.\n>> + *\n>> + * \\return A value retrieved from the given environment option or configuration\n>> + * file or no value if not found\n>\n> This is a bit convoluted... maybe \"The value retrieved from the given\n> environment if it is set, otherwise the value from the configuration file if it\n> exists\" ?\n>\n> I like the idea though.\n>\n>\n> Thanks,\n>\n> Paul\n>\n>> + */\n>> +std::optional<std::string> GlobalConfiguration::envOption(\n>> +       const char *envVariable,\n>> +       const std::initializer_list<std::string_view> confPath) const\n>> +{\n>> +       const char *envValue = utils::secure_getenv(envVariable);\n>> +       if (envValue)\n>> +               return std::optional{ std::string{ envValue } };\n>> +       return option(confPath);\n>> +}\n>> +\n>>  /**\n>>   * \\brief Return configuration version\n>>   *\n>> -- \n>> 2.50.1\n>>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B6E03BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  8 Sep 2025 14:46:30 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EAB396935A;\n\tMon,  8 Sep 2025 16:46:29 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 81BDF6934E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  8 Sep 2025 16:46:27 +0200 (CEST)","from mail-ed1-f72.google.com (mail-ed1-f72.google.com\n\t[209.85.208.72]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-520--amFQOG4MkyGxonr1vDT5g-1; Mon, 08 Sep 2025 10:46:25 -0400","by mail-ed1-f72.google.com with SMTP id\n\t4fb4d7f45d1cf-61d2b554a4dso3381998a12.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 08 Sep 2025 07:46:24 -0700 (PDT)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\t4fb4d7f45d1cf-61cfc4e50fbsm21876777a12.38.2025.09.08.07.46.22\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 08 Sep 2025 07:46:22 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"PKmsp1dw\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1757342786;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=CRyJSZMlK66M53RbjbR4jxPIz64kSzvwmOnCtrZ2bXA=;\n\tb=PKmsp1dw9c9Vwv7+FQx5+dO6BAjjXNNba0fUW71EE3akwGuCangMn3eq2pX5RxWgXoaVs8\n\tHs0iME5Io577LFE5zEEPM6dokVu6JiyKtl/Mc1nQQqeJb8nvuJUrqirS4LYYDvfB9seT2U\n\t3RQrLRw0eUk9asPvMDUrePgK6dYF+eo=","X-MC-Unique":"-amFQOG4MkyGxonr1vDT5g-1","X-Mimecast-MFC-AGG-ID":"-amFQOG4MkyGxonr1vDT5g_1757342784","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1757342784; x=1757947584;\n\th=content-transfer-encoding:mime-version:user-agent:message-id:date\n\t:references:in-reply-to:subject:cc:to:from:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=ZeAopR+WXiihCNXcRiq+LMqf/AvcUZqy2F7Z9EFyuKQ=;\n\tb=XawUkLtXCJEEJEHzsTyTk9aBidESG8IPSrRPN1ykt5odPY/FgsDcEhkcxik+HwTWDM\n\tkfUnj5ePRNasGwPw3/fpiwGV7b2DlaWpWnFlwLGBGUQT4pl9qR3hAHvk2d0JWsy4JF5Y\n\tY2nd7bXxDeubNjgRglYNMcHTPAiu2tXjNFGAByCd43jaJkdGjM2w7qe6kLvrWuRbFa5z\n\tuOpq9onD3bZVfJjxJUXsRk8T/ubHRjl9k8WItUppQyQnxXY6KFuGJsIz6GVZiTgx/Xg0\n\tsoRxMSwk2S2BIkJ7LQaaGTCexzqKy/WwM/uU3SedsTF9tLfDfTGsza8ylL5tKM/oBhQ5\n\tw2Ng==","X-Gm-Message-State":"AOJu0YzBFebGV8lsSfx8nI8kr1yZxbB2npTbeanRCo7fzqc9BZe9AwSJ\n\tr8NC/Xdqpttv7xp8YLXfkCK6P4fPRwZ/7s9DT5vPLa8IbX45lF+RNuZ4DYrRywLGhtW5oUn1jFr\n\t6KyobcQFaRg+2QWM0a/FCwIXZcIUnQBxodSHdx1mFvcLDfVtCh5t7/tuHIZ29jxyIPXY59j82to\n\tc=","X-Gm-Gg":"ASbGncvXH2cW24iX6tEOeFDn8bArnMoubPPJiRGEsfP7RRaYssoh+oor2mceACD0NYN\n\tTObRRfhjUo/vA3v/64lIu2x/ufucoQ3IYwjDkZsHOt9Ax4tRCBq7V4eIVe9vv+MHq78AXVTlFmW\n\twq6rQTyMWi3MYneE3TAl8iX5NvAPqhV7dEYPm6iL46wAp0yd4p5wGqat1luIMDklWR0usd7Xtjc\n\tKUJjJsZIV2qEgWxJaqnGhsdg9rTVx4HTRBkg8GrZEM6+7+Z+9HICBOJcjt8fL/+mIUwEAp2+1pe\n\tPJ9BglMnc0QLcUUZESVaZLRe3lMNdis2CrbdT+yce8pG0jNGnJDAnsFdCcoT/kRpAKB91o09KXL\n\tDkJs595N8ITRY4bollw==","X-Received":["by 2002:a05:6402:34d0:b0:621:7d0a:150c with SMTP id\n\t4fb4d7f45d1cf-6237b875737mr7350607a12.28.1757342783568; \n\tMon, 08 Sep 2025 07:46:23 -0700 (PDT)","by 2002:a05:6402:34d0:b0:621:7d0a:150c with SMTP id\n\t4fb4d7f45d1cf-6237b875737mr7350579a12.28.1757342783021; \n\tMon, 08 Sep 2025 07:46:23 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IFlo2J3va9MRIZs9f7w2Gt/jm9NyX7aI5uLuW3FzJh6NwBkiuhzs5jhM0ZsmJnHp8Hrx5tLxA==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?= <barnabas.pocze@ideasonboard.com>,\n\tLaurent Pinchart  <laurent.pinchart@ideasonboard.com>","Subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","In-Reply-To":"<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>\n\t(Paul Elder's message of \"Mon, 08 Sep 2025 19:25:15 +0900\")","References":"<20250729073201.5369-1-mzamazal@redhat.com>\n\t<20250729073201.5369-4-mzamazal@redhat.com>\n\t<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>","Date":"Mon, 08 Sep 2025 16:46:21 +0200","Message-ID":"<85o6rlt32a.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"ZowL3HE0WisLL1npESfP1ub06Zvqsm_hSLYoNPwpM04_1757342784","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"quoted-printable","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35748,"web_url":"https://patchwork.libcamera.org/comment/35748/","msgid":"<175742677331.2127323.1282742448436387148@neptunite.rasen.tech>","date":"2025-09-09T14:06:13","subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Milan,\n\nQuoting Milan Zamazal (2025-09-08 23:46:21)\n> Hi Paul,\n> \n> thank you for the wording improvement suggestions, I'll use them when\n> preparing v17.\n> \n> Paul Elder <paul.elder@ideasonboard.com> writes:\n> \n> > Hi Milan,\n> >\n> > Thanks for the patch.\n> >\n> > Quoting Milan Zamazal (2025-07-29 16:31:51)\n> >> Let's add some helpers to make accessing simple configuration values\n> >> simpler.\n> >> \n> >> GlobalConfiguration::option ensures that no value is returned rather\n> >> than a value of YamlObject::empty.\n> >> \n> >> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n> >> ---\n> >>  .../libcamera/internal/global_configuration.h |  8 +++\n> >>  src/libcamera/global_configuration.cpp        | 53 +++++++++++++++++++\n> >>  2 files changed, 61 insertions(+)\n> >> \n> >> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n> >> index f695498c4..78bebff9f 100644\n> >> --- a/include/libcamera/internal/global_configuration.h\n> >> +++ b/include/libcamera/internal/global_configuration.h\n> >> @@ -8,6 +8,9 @@\n> >>  #pragma once\n> >>  \n> >>  #include <filesystem>\n> >> +#include <optional>\n> >> +#include <string>\n> >> +#include <string_view>\n> >>  \n> >>  #include \"libcamera/internal/yaml_parser.h\"\n> >>  \n> >> @@ -22,6 +25,11 @@ public:\n> >>  \n> >>         unsigned int version() const;\n> >>         Configuration configuration() const;\n> >> +       std::optional<std::string> option(\n> >> +               const std::initializer_list<std::string_view> confPath) const;\n> >> +       std::optional<std::string> envOption(\n> >> +               const char *const envVariable,\n> >> +               const std::initializer_list<std::string_view> confPath) const;\n> >>  \n> >>  private:\n> >>         bool loadFile(const std::filesystem::path &fileName);\n> >> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n> >> index 50756b859..5be0f0701 100644\n> >> --- a/src/libcamera/global_configuration.cpp\n> >> +++ b/src/libcamera/global_configuration.cpp\n> >> @@ -8,6 +8,8 @@\n> >>  #include \"libcamera/internal/global_configuration.h\"\n> >>  \n> >>  #include <filesystem>\n> >> +#include <memory>\n> >> +#include <string>\n> >>  #include <string_view>\n> >>  #include <sys/types.h>\n> >>  \n> >> @@ -39,6 +41,11 @@ LOG_DEFINE_CATEGORY(Configuration)\n> >>   * is not found then in system-wide configuration directories. If multiple\n> >>   * configuration files exist then only the first one found is used and no\n> >>   * configuration merging is performed.\n> >> + *\n> >> + * The configuration can be accessed using the provided helpers. Namely\n> >> + * GlobalConfiguration::option() or GlobalConfiguration::envOption() to access\n> >> + * individual options or GlobalConfiguration::configuration() to access the\n> >> + * whole configuration.\n> >>   */\n> >>  \n> >>  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n> >> @@ -106,6 +113,52 @@ GlobalConfiguration::GlobalConfiguration()\n> >>   * the underlying type.\n> >>   */\n> >>  \n> >> +/**\n> >> + * \\brief Return value of the configuration option identified by \\a confPath\n> >> + * \\param[in] confPath Sequence of the YAML section names (excluding\n> >> + * `configuration') leading to the requested option\n> >> + * \\return A value if an item corresponding to \\a confPath exists in the\n> >> + * configuration file, no value otherwise\n> >\n> > I think:\n> >\n> > \\return The value of the configuration item corresponding to \\a confPath if it\n> > exists in the configuration file, or no value otherwise\n> >\n> >> + */\n> >> +std::optional<std::string> GlobalConfiguration::option(\n> >> +       const std::initializer_list<std::string_view> confPath) const\n> >> +{\n> >> +       const YamlObject *c = &configuration();\n> >> +       for (auto part : confPath) {\n> >> +               c = &(*c)[part];\n> >> +               if (!*c)\n> >> +                       return {};\n> >> +       }\n> >> +       return c->get<std::string>();\n> >> +}\n> >> +\n> >> +/**\n> >> + * \\brief Return value of the configuration option from a file or environment\n> >\n> > Maybe \"Return value of environment variable with a fallback on the\n> > configuration file\" ?\n> >\n> > Otherwise it sounds like you can actually get the file from either of your\n> > choice.\n> >\n> >> + * \\param[in] envVariable Environment variable to get the value from\n> >> + * \\param[in] confPath The same as in GlobalConfiguration::option\n> >\n> > Maybe \"The sequence of YAML section names to fall back on when \\a envVariable\n> > is unavailable\" ?\n> >\n> > I personally think that falling back on the config file is an important concept\n> > in this function that should be in the documentation.\n> >\n> >> + *\n> >> + * This helper looks first at the given environment variable and if it is\n> >> + * defined then it returns its value (even if it is empty). Otherwise it looks\n> >\n> > I wonder if this (returning empty environment variable) will cause any\n> > confusion for users? \n> \n> Explained by Barnabás; I accepted his suggestion as it looked reasonable\n> to me.\n\nAh ok I see.\n\n> \n> > I did a quick test with setting an environment variable to an empty\n> > string and the unset command gave me the same result when it was set\n> > to an empty string as to when it wasn't set at all.\n> \n> Do you mean it doesn't work as described or it differs from the current\n> behaviour?\n\nMaybe it's I don't know how to control my environment variables. Don't worry\nabout it :)\n\n\nPaul\n\n> \n> >> + * for \\a confPath the same way as in GlobalConfiguration::option. Only string\n> >> + * values are supported.\n> >> + *\n> >> + * \\note Support for using environment variables to configure libcamera behavior\n> >> + * is provided here mostly for backward compatibility reasons. Introducing new\n> >> + * configuration environment variables is discouraged.\n> >> + *\n> >> + * \\return A value retrieved from the given environment option or configuration\n> >> + * file or no value if not found\n> >\n> > This is a bit convoluted... maybe \"The value retrieved from the given\n> > environment if it is set, otherwise the value from the configuration file if it\n> > exists\" ?\n> >\n> > I like the idea though.\n> >\n> >\n> > Thanks,\n> >\n> > Paul\n> >\n> >> + */\n> >> +std::optional<std::string> GlobalConfiguration::envOption(\n> >> +       const char *envVariable,\n> >> +       const std::initializer_list<std::string_view> confPath) const\n> >> +{\n> >> +       const char *envValue = utils::secure_getenv(envVariable);\n> >> +       if (envValue)\n> >> +               return std::optional{ std::string{ envValue } };\n> >> +       return option(confPath);\n> >> +}\n> >> +\n> >>  /**\n> >>   * \\brief Return configuration version\n> >>   *\n> >> -- \n> >> 2.50.1\n> >>\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 3AABDBDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  9 Sep 2025 14:06:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 58E616936C;\n\tTue,  9 Sep 2025 16:06:24 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 365046934B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  9 Sep 2025 16:06:22 +0200 (CEST)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:6f3a:4f34:f1fa:8b3])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 7043A605;\n\tTue,  9 Sep 2025 16:05:08 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"HXpuy+0G\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1757426709;\n\tbh=eoNQbYPwkrlLElCCd1tbV+6ftnHa7ZOTBzcK0sP32Sk=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=HXpuy+0GBydr9IcvCwIDCoHsTEKz12jYxxJmR/aBTv9BdIkT+K+t71rNl1v7Dgrj7\n\tH80CUpc0uWVGGzgPSY1iMSOhbIBmqsGfHaZvjXWRxRpOTBV1KBh7U1Rn3swpopLsmM\n\t2e3vuNIItDm18H8I8xinTacWg0nw1frl9nsS2A1A=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<85o6rlt32a.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","References":"<20250729073201.5369-1-mzamazal@redhat.com>\n\t<20250729073201.5369-4-mzamazal@redhat.com>\n\t<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>\n\t<85o6rlt32a.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","Subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<barnabas.pocze@ideasonboard.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>","To":"Milan Zamazal <mzamazal@redhat.com>","Date":"Tue, 09 Sep 2025 23:06:13 +0900","Message-ID":"<175742677331.2127323.1282742448436387148@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":35749,"web_url":"https://patchwork.libcamera.org/comment/35749/","msgid":"<175742684769.2127323.9543226963671517874@neptunite.rasen.tech>","date":"2025-09-09T14:07:27","subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hi Barnabás,\n\nQuoting Barnabás Pőcze (2025-09-08 21:06:43)\n> Hi\n> \n> 2025. 09. 08. 12:25 keltezéssel, Paul Elder írta:\n> > Hi Milan,\n> > \n> > Thanks for the patch.\n> > \n> > Quoting Milan Zamazal (2025-07-29 16:31:51)\n> >> Let's add some helpers to make accessing simple configuration values\n> >> simpler.\n> >>\n> >> GlobalConfiguration::option ensures that no value is returned rather\n> >> than a value of YamlObject::empty.\n> >>\n> >> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n> >> ---\n> >>   .../libcamera/internal/global_configuration.h |  8 +++\n> >>   src/libcamera/global_configuration.cpp        | 53 +++++++++++++++++++\n> >>   2 files changed, 61 insertions(+)\n> >>\n> >> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n> >> index f695498c4..78bebff9f 100644\n> >> --- a/include/libcamera/internal/global_configuration.h\n> >> +++ b/include/libcamera/internal/global_configuration.h\n> >> @@ -8,6 +8,9 @@\n> >>   #pragma once\n> >>   \n> >>   #include <filesystem>\n> >> +#include <optional>\n> >> +#include <string>\n> >> +#include <string_view>\n> >>   \n> >>   #include \"libcamera/internal/yaml_parser.h\"\n> >>   \n> >> @@ -22,6 +25,11 @@ public:\n> >>   \n> >>          unsigned int version() const;\n> >>          Configuration configuration() const;\n> >> +       std::optional<std::string> option(\n> >> +               const std::initializer_list<std::string_view> confPath) const;\n> >> +       std::optional<std::string> envOption(\n> >> +               const char *const envVariable,\n> >> +               const std::initializer_list<std::string_view> confPath) const;\n> >>   \n> >>   private:\n> >>          bool loadFile(const std::filesystem::path &fileName);\n> >> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n> >> index 50756b859..5be0f0701 100644\n> >> --- a/src/libcamera/global_configuration.cpp\n> >> +++ b/src/libcamera/global_configuration.cpp\n> >> @@ -8,6 +8,8 @@\n> >>   #include \"libcamera/internal/global_configuration.h\"\n> >>   \n> >>   #include <filesystem>\n> >> +#include <memory>\n> >> +#include <string>\n> >>   #include <string_view>\n> >>   #include <sys/types.h>\n> >>   \n> >> @@ -39,6 +41,11 @@ LOG_DEFINE_CATEGORY(Configuration)\n> >>    * is not found then in system-wide configuration directories. If multiple\n> >>    * configuration files exist then only the first one found is used and no\n> >>    * configuration merging is performed.\n> >> + *\n> >> + * The configuration can be accessed using the provided helpers. Namely\n> >> + * GlobalConfiguration::option() or GlobalConfiguration::envOption() to access\n> >> + * individual options or GlobalConfiguration::configuration() to access the\n> >> + * whole configuration.\n> >>    */\n> >>   \n> >>   bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n> >> @@ -106,6 +113,52 @@ GlobalConfiguration::GlobalConfiguration()\n> >>    * the underlying type.\n> >>    */\n> >>   \n> >> +/**\n> >> + * \\brief Return value of the configuration option identified by \\a confPath\n> >> + * \\param[in] confPath Sequence of the YAML section names (excluding\n> >> + * `configuration') leading to the requested option\n> >> + * \\return A value if an item corresponding to \\a confPath exists in the\n> >> + * configuration file, no value otherwise\n> > \n> > I think:\n> > \n> > \\return The value of the configuration item corresponding to \\a confPath if it\n> > exists in the configuration file, or no value otherwise\n> > \n> >> + */\n> >> +std::optional<std::string> GlobalConfiguration::option(\n> >> +       const std::initializer_list<std::string_view> confPath) const\n> >> +{\n> >> +       const YamlObject *c = &configuration();\n> >> +       for (auto part : confPath) {\n> >> +               c = &(*c)[part];\n> >> +               if (!*c)\n> >> +                       return {};\n> >> +       }\n> >> +       return c->get<std::string>();\n> >> +}\n> >> +\n> >> +/**\n> >> + * \\brief Return value of the configuration option from a file or environment\n> > \n> > Maybe \"Return value of environment variable with a fallback on the\n> > configuration file\" ?\n> > \n> > Otherwise it sounds like you can actually get the file from either of your\n> > choice.\n> > \n> >> + * \\param[in] envVariable Environment variable to get the value from\n> >> + * \\param[in] confPath The same as in GlobalConfiguration::option\n> > \n> > Maybe \"The sequence of YAML section names to fall back on when \\a envVariable\n> > is unavailable\" ?\n> > \n> > I personally think that falling back on the config file is an important concept\n> > in this function that should be in the documentation.\n> > \n> >> + *\n> >> + * This helper looks first at the given environment variable and if it is\n> >> + * defined then it returns its value (even if it is empty). Otherwise it looks\n> > \n> > I wonder if this (returning empty environment variable) will cause any\n> > confusion for users? I did a quick test with setting an environment variable to\n> > an empty string and the unset command gave me the same result when it was set\n> > to an empty string as to when it wasn't set at all.\n> \n> So that behaviour was my suggestion. The idea was that sometimes you might want\n> to explicitly inhibit some kind of behaviour, and you would do that by setting the\n> env var to the empty string. (Also see one of the later patches that uses the same\n> logic to inhibit the loading of any configuration.) I actually had your \"layers\" code\n> in mind, e.g. using `LIBCAMERA_LAYERS= cam ...` to inhibit the loading of any\n> layers that would otherwise be loaded based on some configuration, etc. Of course\n> `env -u LIBCAMERA_LAYERS cam` would also work, but that seems more convoluted.\n\nAh I see. Hm, that sounds like a good idea. It's certainly better than\nintroducing a disable environment variable.\n\nOk looks good then.\n\n\nThanks,\n\nPaul\n\n> \n> \n> Regards,\n> Barnabás Pőcze\n> \n> \n> \n> > \n> >> + * for \\a confPath the same way as in GlobalConfiguration::option. Only string\n> >> + * values are supported.\n> >> + *\n> >> + * \\note Support for using environment variables to configure libcamera behavior\n> >> + * is provided here mostly for backward compatibility reasons. Introducing new\n> >> + * configuration environment variables is discouraged.\n> >> + *\n> >> + * \\return A value retrieved from the given environment option or configuration\n> >> + * file or no value if not found\n> > \n> > This is a bit convoluted... maybe \"The value retrieved from the given\n> > environment if it is set, otherwise the value from the configuration file if it\n> > exists\" ?\n> > \n> > I like the idea though.\n> > \n> > \n> > Thanks,\n> > \n> > Paul\n> > \n> >> + */\n> >> +std::optional<std::string> GlobalConfiguration::envOption(\n> >> +       const char *envVariable,\n> >> +       const std::initializer_list<std::string_view> confPath) const\n> >> +{\n> >> +       const char *envValue = utils::secure_getenv(envVariable);\n> >> +       if (envValue)\n> >> +               return std::optional{ std::string{ envValue } };\n> >> +       return option(confPath);\n> >> +}\n> >> +\n> >>   /**\n> >>    * \\brief Return configuration version\n> >>    *\n> >> -- \n> >> 2.50.1\n> >>\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 747FEBDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  9 Sep 2025 14:07:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 232476936E;\n\tTue,  9 Sep 2025 16:07:35 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 007F86934B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  9 Sep 2025 16:07:33 +0200 (CEST)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:6f3a:4f34:f1fa:8b3])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 61E93605;\n\tTue,  9 Sep 2025 16:06:20 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"MgUYA2q1\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1757426781;\n\tbh=RA6IrU9tM3rjj9HGODDejd9FMLy+ivQrA16mipgqkx0=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=MgUYA2q1tRCLvCjV8Rklloc0rUdWWAjAti7ceO9tIfTyJj9Me2CNF1xFYPCgc4ZKI\n\tJ9XHfan78Tf6VbhLHYFnWTqyV/ua10upR8PN0XQbxBEUENL6fIIWaF/93h+m/ZPB6+\n\tUc7qnTrbYDf7xpqNM2YktchaGuzSmOwBDs+Z9A9I=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<c128c152-d326-4c36-9687-e5c2e02dec60@ideasonboard.com>","References":"<20250729073201.5369-1-mzamazal@redhat.com>\n\t<20250729073201.5369-4-mzamazal@redhat.com>\n\t<175732711549.2066996.11529412124616698216@neptunite.rasen.tech>\n\t<c128c152-d326-4c36-9687-e5c2e02dec60@ideasonboard.com>","Subject":"Re: [PATCH v16 03/12] config: Add configuration retrieval helpers","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tMilan Zamazal <mzamazal@redhat.com>, libcamera-devel@lists.libcamera.org","Date":"Tue, 09 Sep 2025 23:07:27 +0900","Message-ID":"<175742684769.2127323.9543226963671517874@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]