[{"id":35025,"web_url":"https://patchwork.libcamera.org/comment/35025/","msgid":"<408c7e5e-2d8e-49f4-b5de-7b93ec6104b6@ideasonboard.com>","date":"2025-07-22T14:41:22","subject":"Re: [PATCH v14 01/12] config: Introduce global runtime configuration","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. 07. 15. 10:19 keltezéssel, Milan Zamazal írta:\n> Currently, libcamera can be configured in runtime using several\n> environment variables.  With introducing more and more variables, this\n> mechanism reaches its limits.  It would be simpler and more flexible if\n> it was possible to configure libcamera in a single file.\n> \n> For example, there was a request to define pipeline precedence in\n> runtime.  We want to compile in multiple pipelines, in order to have\n> them accessible within single packages in distributions.  And then being\n> able to select among the pipelines manually as needed based on the\n> particular hardware or operating system environment.  Having the\n> configuration file then allows easy switching between hardware, GPU or\n> CPU IPAs.  The configuration file can also be used to enable or disable\n> experimental features and avoid the need to track local patches changing\n> configuration options hard-wired in the code when working on new\n> features.\n> \n> This patch introduces basic support for configuration files.\n> GlobalConfiguration class reads, stores and accesses the configuration.\n> GlobalConfiguration instances are meant to be stored to and accessed\n> from instances of another class, preferably from a single instance.\n> \n> libcamera configuration can be specified using a system-wide\n> configuration file or a user configuration file.  The user configuration\n> file takes precedence if present.  There is currently no way to merge\n> multiple configuration files, the one found is used as the only\n> configuration file.  If no configuration file is present, nothing\n> changes to the current libcamera behavior (except for some log\n> messages related to configuration file lookup).\n> \n> The configuration file is a YAML file.  We already have a mechanism for\n> handling YAML configuration files in libcamera and the given\n> infrastructure can be reused for the purpose.  However, the\n> configuration type is abstracted to make contingent future change of the\n> underlying class easier while retaining (most of) the original API.\n> \n> The configuration is versioned.  This has currently no particular\n> meaning but is likely to have its purpose in future, especially once\n> configuration validation is introduced.\n> \n> The configuration YAML file looks as follows:\n> \n>    ---\n>    version: 1\n>    configuration:\n>      WHATEVER CONFIGURATION NEEDED\n> \n> This patch introduces just the basic idea.  Actually using the\n> configuration in the corresponding places (everything what is currently\n> configurable via environment variables should be configurable in the\n> file configuration) and other enhancements are implemented in the\n> followup patches.\n> \n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n> ---\n>   .../libcamera/internal/global_configuration.h |  34 ++++\n>   include/libcamera/internal/meson.build        |   1 +\n>   src/libcamera/global_configuration.cpp        | 145 ++++++++++++++++++\n>   src/libcamera/meson.build                     |   1 +\n>   4 files changed, 181 insertions(+)\n>   create mode 100644 include/libcamera/internal/global_configuration.h\n>   create mode 100644 src/libcamera/global_configuration.cpp\n> \n> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\n> new file mode 100644\n> index 000000000..3eaf39c94\n> --- /dev/null\n> +++ b/include/libcamera/internal/global_configuration.h\n> @@ -0,0 +1,34 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024-2025 Red Hat, inc.\n> + *\n> + * Global configuration handling\n> + */\n> +\n> +#pragma once\n> +\n> +#include <filesystem>\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +namespace libcamera {\n> +\n> +class GlobalConfiguration\n> +{\n> +public:\n> +\tusing Configuration = const YamlObject &;\n> +\n> +\tGlobalConfiguration();\n> +\n> +\tunsigned int version() const;\n> +\tConfiguration configuration() const;\n> +\n> +private:\n> +\tbool loadFile(const std::filesystem::path &fileName);\n> +\tbool load();\n> +\n> +\tstd::unique_ptr<YamlObject> yamlConfiguration_ =\n> +\t\tstd::make_unique<YamlObject>();\n> +};\n> +\n> +} /* namespace libcamera */\n> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n> index 5c80a28c4..45c299f6a 100644\n> --- a/include/libcamera/internal/meson.build\n> +++ b/include/libcamera/internal/meson.build\n> @@ -23,6 +23,7 @@ libcamera_internal_headers = files([\n>       'dma_buf_allocator.h',\n>       'formats.h',\n>       'framebuffer.h',\n> +    'global_configuration.h',\n>       'ipa_data_serializer.h',\n>       'ipa_manager.h',\n>       'ipa_module.h',\n> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n> new file mode 100644\n> index 000000000..9a798ba63\n> --- /dev/null\n> +++ b/src/libcamera/global_configuration.cpp\n> @@ -0,0 +1,145 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024-2025 Red Hat, inc.\n> + *\n> + * Global configuration handling\n> + */\n> +\n> +#include \"libcamera/internal/global_configuration.h\"\n> +\n> +#include <filesystem>\n> +#include <string_view>\n> +#include <sys/types.h>\n> +\n> +#include <libcamera/base/file.h>\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/base/utils.h>\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace {\n> +const std::vector<std::filesystem::path> globalConfigurationFiles = {\n> +\tstd::filesystem::path(LIBCAMERA_SYSCONF_DIR) / \"configuration.yaml\",\n> +\tstd::filesystem::path(LIBCAMERA_DATA_DIR) / \"configuration.yaml\",\n> +};\n> +}\n> +\n> +LOG_DEFINE_CATEGORY(Configuration)\n> +\n> +/**\n> + * \\class GlobalConfiguration\n> + * \\brief Support for global libcamera configuration\n> + *\n> + * The configuration file is a YAML file and the configuration itself is stored\n> + * under `configuration' top-level item.\n> + *\n> + * The configuration file is looked up in user's home directory first and if it\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> +\n> +bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n> +{\n> +\tFile file(fileName);\n> +\tif (!file.exists())\n> +\t\treturn false;\n> +\n> +\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n> +\t\tLOG(Configuration, Error)\n> +\t\t\t<< \"Failed to open configuration file \" << fileName;\n> +\t\treturn true;\n> +\t}\n\nCould we do this instead?\n\ndiff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\nindex 553d5f221..d4ab38cfb 100644\n--- a/src/libcamera/global_configuration.cpp\n+++ b/src/libcamera/global_configuration.cpp\n@@ -46,10 +46,10 @@ LOG_DEFINE_CATEGORY(Configuration)\n  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n  {\n         File file(fileName);\n-       if (!file.exists())\n-               return false;\n-\n         if (!file.open(File::OpenModeFlag::ReadOnly)) {\n+               if (file.error() == -ENOENT)\n+                       return false;\n+\n                 LOG(Configuration, Error)\n                         << \"Failed to open configuration file \" << fileName;\n                 return true;\n\n\nOr is there some reason I am missing? Of course now permission errors that would\nbe ignored in `File::exists()` now cause an error message. But I feel like that\nis better, especially if someone sets `LIBCAMERA_CONFIG_{NAME,DIR}` explicitly.\n\n\n\n> +\n> +\tyamlConfiguration_ = YamlParser::parse(file);\n\nSave the return value into a local variable and defer assignment, otherwise\n`yamlConfiguration_` may end up being nullptr. This will requires modifications\nof the `return !!yamlConfiguration_` lines. However, the return value of\n`GlobalConfiguration::load()` is not used anywhere, so maybe it could be\nremoved.\n\n\nRegards,\nBarnabás Pőcze\n\n\n> +\tif (!yamlConfiguration_) {\n> +\t\tLOG(Configuration, Error)\n> +\t\t\t<< \"Failed to parse configuration file \" << fileName;\n> +\t\treturn true;\n> +\t}\n> +\n> +\treturn true;\n> +}\n> +\n> +bool GlobalConfiguration::load()\n> +{\n> +\tstd::filesystem::path userConfigurationDirectory;\n> +\tconst char *xdgConfigHome = utils::secure_getenv(\"XDG_CONFIG_HOME\");\n> +\tif (xdgConfigHome) {\n> +\t\tuserConfigurationDirectory = xdgConfigHome;\n> +\t} else {\n> +\t\tconst char *home = utils::secure_getenv(\"HOME\");\n> +\t\tif (home)\n> +\t\t\tuserConfigurationDirectory =\n> +\t\t\t\tstd::filesystem::path(home) / \".config\";\n> +\t}\n> +\n> +\tif (!userConfigurationDirectory.empty()) {\n> +\t\tstd::filesystem::path user_configuration_file =\n> +\t\t\tuserConfigurationDirectory / \"libcamera\" / \"configuration.yaml\";\n> +\t\tif (loadFile(user_configuration_file))\n> +\t\t\treturn !!yamlConfiguration_;\n> +\t}\n> +\n> +\tfor (const auto &path : globalConfigurationFiles) {\n> +\t\tif (loadFile(path))\n> +\t\t\treturn !!yamlConfiguration_;\n> +\t}\n> +\n> +\treturn true;\n> +}\n> +\n> +/**\n> + * \\brief Initialize the global configuration\n> + */\n> +GlobalConfiguration::GlobalConfiguration()\n> +{\n> +\tload();\n> +}\n> +\n> +/**\n> + * \\typedef GlobalConfiguration::Configuration\n> + * \\brief Type representing global libcamera configuration\n> + *\n> + * All code outside GlobalConfiguration must use this type declaration and not\n> + * the underlying type.\n> + */\n> +\n> +/**\n> + * \\brief Return configuration version\n> + *\n> + * The version is (optionally) declared in the configuration file in the\n> + * top-level section `version', alongside `configuration'. This has currently no\n> + * real use but may be needed in future if configuration incompatibilities\n> + * occur.\n> + *\n> + * \\return Configuration version as declared in the configuration file or 0 if\n> + * no version is declared there\n> + */\n> +unsigned int GlobalConfiguration::version() const\n> +{\n> +\treturn (*yamlConfiguration_)[\"version\"].get<unsigned int>().value_or(0);\n> +}\n> +\n> +/**\n> + * \\brief Return libcamera global configuration\n> + *\n> + * This returns the whole configuration stored in the top-level section\n> + * `configuration' of the YAML configuration file.\n> + *\n> + * The requested part of the configuration can be accessed using \\a YamlObject\n> + * methods.\n> + *\n> + * \\note \\a YamlObject type itself shouldn't be used in type declarations to\n> + * avoid trouble if we decide to change the underlying data objects in future.\n> + *\n> + * \\return The whole configuration section\n> + */\n> +GlobalConfiguration::Configuration GlobalConfiguration::configuration() const\n> +{\n> +\treturn (*yamlConfiguration_)[\"configuration\"];\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index de1eb99b2..275cf5a4b 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -31,6 +31,7 @@ libcamera_internal_sources = files([\n>       'device_enumerator_sysfs.cpp',\n>       'dma_buf_allocator.cpp',\n>       'formats.cpp',\n> +    'global_configuration.cpp',\n>       'ipa_controls.cpp',\n>       'ipa_data_serializer.cpp',\n>       'ipa_interface.cpp',","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 5AD83BDCC1\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 22 Jul 2025 14:41:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F09BF69038;\n\tTue, 22 Jul 2025 16:41:29 +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 5CB7A68F93\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 22 Jul 2025 16:41:28 +0200 (CEST)","from [192.168.33.11] (185.221.140.39.nat.pool.zt.hu\n\t[185.221.140.39])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5F04279E6;\n\tTue, 22 Jul 2025 16:40:50 +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=\"Y23qZnnq\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1753195250;\n\tbh=NviwvMLOMl54SsaRhm0p5IQRQU82zriqtHF6U/DPjc4=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=Y23qZnnqMQbYRBLkkoL4c3gc93BeDJwMtJ6sn+10Hhf8Yj5H1Llz/Ul8q1+P6ga7c\n\tIdMfNQB1Na5dVEviQBn4nnGrS2/IhgHMZ9uR0aUL6St/5q6MWjg4YfpQfiLaI39jY/\n\tr7J8UyBNCjhGQgqzJMYzv+SSEtD6yDrlZOAl0P0A=","Message-ID":"<408c7e5e-2d8e-49f4-b5de-7b93ec6104b6@ideasonboard.com>","Date":"Tue, 22 Jul 2025 16:41:22 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v14 01/12] config: Introduce global runtime configuration","To":"Milan Zamazal <mzamazal@redhat.com>, libcamera-devel@lists.libcamera.org","Cc":"Kieran Bingham <kieran.bingham@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20250715081921.5522-1-mzamazal@redhat.com>\n\t<20250715081921.5522-2-mzamazal@redhat.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20250715081921.5522-2-mzamazal@redhat.com>","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":35088,"web_url":"https://patchwork.libcamera.org/comment/35088/","msgid":"<85ms8ulzti.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2025-07-24T09:11:04","subject":"Re: [PATCH v14 01/12] config: Introduce global runtime configuration","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Hi Barnabás,\n\nthank you for review.\n\nBarnabás Pőcze <barnabas.pocze@ideasonboard.com> writes:\n\n> Hi\n>\n> 2025. 07. 15. 10:19 keltezéssel, Milan Zamazal írta:\n>> Currently, libcamera can be configured in runtime using several\n>> environment variables.  With introducing more and more variables, this\n>> mechanism reaches its limits.  It would be simpler and more flexible if\n>> it was possible to configure libcamera in a single file.\n>> For example, there was a request to define pipeline precedence in\n>> runtime.  We want to compile in multiple pipelines, in order to have\n>> them accessible within single packages in distributions.  And then being\n>> able to select among the pipelines manually as needed based on the\n>> particular hardware or operating system environment.  Having the\n>> configuration file then allows easy switching between hardware, GPU or\n>> CPU IPAs.  The configuration file can also be used to enable or disable\n>> experimental features and avoid the need to track local patches changing\n>> configuration options hard-wired in the code when working on new\n>> features.\n>> This patch introduces basic support for configuration files.\n>> GlobalConfiguration class reads, stores and accesses the configuration.\n>> GlobalConfiguration instances are meant to be stored to and accessed\n>> from instances of another class, preferably from a single instance.\n>> libcamera configuration can be specified using a system-wide\n>> configuration file or a user configuration file.  The user configuration\n>> file takes precedence if present.  There is currently no way to merge\n>> multiple configuration files, the one found is used as the only\n>> configuration file.  If no configuration file is present, nothing\n>> changes to the current libcamera behavior (except for some log\n>> messages related to configuration file lookup).\n>> The configuration file is a YAML file.  We already have a mechanism for\n>> handling YAML configuration files in libcamera and the given\n>> infrastructure can be reused for the purpose.  However, the\n>> configuration type is abstracted to make contingent future change of the\n>> underlying class easier while retaining (most of) the original API.\n>> The configuration is versioned.  This has currently no particular\n>> meaning but is likely to have its purpose in future, especially once\n>> configuration validation is introduced.\n>> The configuration YAML file looks as follows:\n>>    ---\n>>    version: 1\n>>    configuration:\n>>      WHATEVER CONFIGURATION NEEDED\n>> This patch introduces just the basic idea.  Actually using the\n>> configuration in the corresponding places (everything what is currently\n>> configurable via environment variables should be configurable in the\n>> file configuration) and other enhancements are implemented in the\n>> followup patches.\n>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n>> ---\n>>   .../libcamera/internal/global_configuration.h |  34 ++++\n>>   include/libcamera/internal/meson.build        |   1 +\n>>   src/libcamera/global_configuration.cpp        | 145 ++++++++++++++++++\n>>   src/libcamera/meson.build                     |   1 +\n>>   4 files changed, 181 insertions(+)\n>>   create mode 100644 include/libcamera/internal/global_configuration.h\n>>   create mode 100644 src/libcamera/global_configuration.cpp\n>> diff --git a/include/libcamera/internal/global_configuration.h\n>> b/include/libcamera/internal/global_configuration.h\n>> new file mode 100644\n>> index 000000000..3eaf39c94\n>> --- /dev/null\n>> +++ b/include/libcamera/internal/global_configuration.h\n>> @@ -0,0 +1,34 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024-2025 Red Hat, inc.\n>> + *\n>> + * Global configuration handling\n>> + */\n>> +\n>> +#pragma once\n>> +\n>> +#include <filesystem>\n>> +\n>> +#include \"libcamera/internal/yaml_parser.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +class GlobalConfiguration\n>> +{\n>> +public:\n>> +\tusing Configuration = const YamlObject &;\n>> +\n>> +\tGlobalConfiguration();\n>> +\n>> +\tunsigned int version() const;\n>> +\tConfiguration configuration() const;\n>> +\n>> +private:\n>> +\tbool loadFile(const std::filesystem::path &fileName);\n>> +\tbool load();\n>> +\n>> +\tstd::unique_ptr<YamlObject> yamlConfiguration_ =\n>> +\t\tstd::make_unique<YamlObject>();\n>> +};\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\n>> index 5c80a28c4..45c299f6a 100644\n>> --- a/include/libcamera/internal/meson.build\n>> +++ b/include/libcamera/internal/meson.build\n>> @@ -23,6 +23,7 @@ libcamera_internal_headers = files([\n>>       'dma_buf_allocator.h',\n>>       'formats.h',\n>>       'framebuffer.h',\n>> +    'global_configuration.h',\n>>       'ipa_data_serializer.h',\n>>       'ipa_manager.h',\n>>       'ipa_module.h',\n>> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n>> new file mode 100644\n>> index 000000000..9a798ba63\n>> --- /dev/null\n>> +++ b/src/libcamera/global_configuration.cpp\n>> @@ -0,0 +1,145 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2024-2025 Red Hat, inc.\n>> + *\n>> + * Global configuration handling\n>> + */\n>> +\n>> +#include \"libcamera/internal/global_configuration.h\"\n>> +\n>> +#include <filesystem>\n>> +#include <string_view>\n>> +#include <sys/types.h>\n>> +\n>> +#include <libcamera/base/file.h>\n>> +#include <libcamera/base/log.h>\n>> +#include <libcamera/base/utils.h>\n>> +\n>> +#include \"libcamera/internal/yaml_parser.h\"\n>> +\n>> +namespace libcamera {\n>> +\n>> +namespace {\n>> +const std::vector<std::filesystem::path> globalConfigurationFiles = {\n>> +\tstd::filesystem::path(LIBCAMERA_SYSCONF_DIR) / \"configuration.yaml\",\n>> +\tstd::filesystem::path(LIBCAMERA_DATA_DIR) / \"configuration.yaml\",\n>> +};\n>> +}\n>> +\n>> +LOG_DEFINE_CATEGORY(Configuration)\n>> +\n>> +/**\n>> + * \\class GlobalConfiguration\n>> + * \\brief Support for global libcamera configuration\n>> + *\n>> + * The configuration file is a YAML file and the configuration itself is stored\n>> + * under `configuration' top-level item.\n>> + *\n>> + * The configuration file is looked up in user's home directory first and if it\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>> +\n>> +bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n>> +{\n>> +\tFile file(fileName);\n>> +\tif (!file.exists())\n>> +\t\treturn false;\n>> +\n>> +\tif (!file.open(File::OpenModeFlag::ReadOnly)) {\n>> +\t\tLOG(Configuration, Error)\n>> +\t\t\t<< \"Failed to open configuration file \" << fileName;\n>> +\t\treturn true;\n>> +\t}\n>\n> Could we do this instead?\n>\n> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp\n> index 553d5f221..d4ab38cfb 100644\n> --- a/src/libcamera/global_configuration.cpp\n> +++ b/src/libcamera/global_configuration.cpp\n> @@ -46,10 +46,10 @@ LOG_DEFINE_CATEGORY(Configuration)\n>  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)\n>  {\n>         File file(fileName);\n> -       if (!file.exists())\n> -               return false;\n> -\n>         if (!file.open(File::OpenModeFlag::ReadOnly)) {\n> +               if (file.error() == -ENOENT)\n> +                       return false;\n> +\n>                 LOG(Configuration, Error)\n>                         << \"Failed to open configuration file \" << fileName;\n>                 return true;\n>\n>\n> Or is there some reason I am missing? Of course now permission errors that would\n> be ignored in `File::exists()` now cause an error message. But I feel like that\n> is better, especially if someone sets `LIBCAMERA_CONFIG_{NAME,DIR}` explicitly.\n\nYes, I'll change it as you suggest.\n\n>> +\n>> +\tyamlConfiguration_ = YamlParser::parse(file);\n>\n> Save the return value into a local variable and defer assignment, otherwise\n> `yamlConfiguration_` may end up being nullptr. \n\nOK.\n\n> This will requires modifications of the `return !!yamlConfiguration_`\n> lines. However, the return value of `GlobalConfiguration::load()` is\n> not used anywhere, so maybe it could be removed.\n\nYes, the return value is not used any more, so I'll change the method to\nvoid.\n\n> Regards,\n> Barnabás Pőcze\n>\n>\n>> +\tif (!yamlConfiguration_) {\n>> +\t\tLOG(Configuration, Error)\n>> +\t\t\t<< \"Failed to parse configuration file \" << fileName;\n>> +\t\treturn true;\n>> +\t}\n>> +\n>> +\treturn true;\n>> +}\n>> +\n>> +bool GlobalConfiguration::load()\n>> +{\n>> +\tstd::filesystem::path userConfigurationDirectory;\n>> +\tconst char *xdgConfigHome = utils::secure_getenv(\"XDG_CONFIG_HOME\");\n>> +\tif (xdgConfigHome) {\n>> +\t\tuserConfigurationDirectory = xdgConfigHome;\n>> +\t} else {\n>> +\t\tconst char *home = utils::secure_getenv(\"HOME\");\n>> +\t\tif (home)\n>> +\t\t\tuserConfigurationDirectory =\n>> +\t\t\t\tstd::filesystem::path(home) / \".config\";\n>> +\t}\n>> +\n>> +\tif (!userConfigurationDirectory.empty()) {\n>> +\t\tstd::filesystem::path user_configuration_file =\n>> +\t\t\tuserConfigurationDirectory / \"libcamera\" / \"configuration.yaml\";\n>> +\t\tif (loadFile(user_configuration_file))\n>> +\t\t\treturn !!yamlConfiguration_;\n>> +\t}\n>> +\n>> +\tfor (const auto &path : globalConfigurationFiles) {\n>> +\t\tif (loadFile(path))\n>> +\t\t\treturn !!yamlConfiguration_;\n>> +\t}\n>> +\n>> +\treturn true;\n>> +}\n>> +\n>> +/**\n>> + * \\brief Initialize the global configuration\n>> + */\n>> +GlobalConfiguration::GlobalConfiguration()\n>> +{\n>> +\tload();\n>> +}\n>> +\n>> +/**\n>> + * \\typedef GlobalConfiguration::Configuration\n>> + * \\brief Type representing global libcamera configuration\n>> + *\n>> + * All code outside GlobalConfiguration must use this type declaration and not\n>> + * the underlying type.\n>> + */\n>> +\n>> +/**\n>> + * \\brief Return configuration version\n>> + *\n>> + * The version is (optionally) declared in the configuration file in the\n>> + * top-level section `version', alongside `configuration'. This has currently no\n>> + * real use but may be needed in future if configuration incompatibilities\n>> + * occur.\n>> + *\n>> + * \\return Configuration version as declared in the configuration file or 0 if\n>> + * no version is declared there\n>> + */\n>> +unsigned int GlobalConfiguration::version() const\n>> +{\n>> +\treturn (*yamlConfiguration_)[\"version\"].get<unsigned int>().value_or(0);\n>> +}\n>> +\n>> +/**\n>> + * \\brief Return libcamera global configuration\n>> + *\n>> + * This returns the whole configuration stored in the top-level section\n>> + * `configuration' of the YAML configuration file.\n>> + *\n>> + * The requested part of the configuration can be accessed using \\a YamlObject\n>> + * methods.\n>> + *\n>> + * \\note \\a YamlObject type itself shouldn't be used in type declarations to\n>> + * avoid trouble if we decide to change the underlying data objects in future.\n>> + *\n>> + * \\return The whole configuration section\n>> + */\n>> +GlobalConfiguration::Configuration GlobalConfiguration::configuration() const\n>> +{\n>> +\treturn (*yamlConfiguration_)[\"configuration\"];\n>> +}\n>> +\n>> +} /* namespace libcamera */\n>> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n>> index de1eb99b2..275cf5a4b 100644\n>> --- a/src/libcamera/meson.build\n>> +++ b/src/libcamera/meson.build\n>> @@ -31,6 +31,7 @@ libcamera_internal_sources = files([\n>>       'device_enumerator_sysfs.cpp',\n>>       'dma_buf_allocator.cpp',\n>>       'formats.cpp',\n>> +    'global_configuration.cpp',\n>>       'ipa_controls.cpp',\n>>       'ipa_data_serializer.cpp',\n>>       'ipa_interface.cpp',","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 5F18AC3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 24 Jul 2025 09:11:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E6A0C690C4;\n\tThu, 24 Jul 2025 11:11:11 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3F042690BC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 24 Jul 2025 11:11:10 +0200 (CEST)","from mail-ej1-f71.google.com (mail-ej1-f71.google.com\n\t[209.85.218.71]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-295-eLOgXjEnNGinCMNcwUXVVw-1; Thu, 24 Jul 2025 05:11:07 -0400","by mail-ej1-f71.google.com with SMTP id\n\ta640c23a62f3a-ae3c8477f1fso77698466b.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 24 Jul 2025 02:11:07 -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\ta640c23a62f3a-af479228a3fsm85918866b.0.2025.07.24.02.11.04\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 24 Jul 2025 02:11:05 -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=\"akP3/HBu\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1753348269;\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=GB0rphSa/OGDME4Br+/xT8RxsVzWjrxlLDDJG1rllS8=;\n\tb=akP3/HBuWGmub3D7n/NZ6UfMO5YQcX52+vKpHTJnynFEsL9l5gAvkBqf8OoiAuCaH+Ekd0\n\tlPIszDRbyAatdFAjLfWdUWVptDlyQfU1SgOeKagMrQea+pFfZwQFp9CLWNMlnEFIn5IPb2\n\t679XJxHl/fshSoFPq4AqHKCUxIG879g=","X-MC-Unique":"eLOgXjEnNGinCMNcwUXVVw-1","X-Mimecast-MFC-AGG-ID":"eLOgXjEnNGinCMNcwUXVVw_1753348266","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1753348266; x=1753953066;\n\th=content-transfer-encoding:mime-version:user-agent:date:references\n\t:message-id:in-reply-to:subject:cc:to:from:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=ddL9PRejuG90jM1xcJDB4aUeMDVPaD7/wa+h4u3xPp8=;\n\tb=MGWe6gSzkmMbKHq52Jxn3zoKv8KWSTAcCe8HE5eZyJWbg4iGIIHpEenhZFkXJeVONA\n\t0Lacd39cWW1A8cgDLij0w2dpbr1SDQ8l9NPFaOZn6pRa5iCYp7sX3T00ZFjCIfFOiOQe\n\t9MdNThrcKN3d+iZ7aUM+trM2hATOg9HL1H+4j85hvXU3orfmzOiy/J8W5yyODbpLuFIh\n\t+mjJLbfEQog4vYG8OFEfeG6RUP+1fK0Z2oFzKOZTYf/VaQMOJPdDN+JCrxLRkT2lerUE\n\tN+3TTfBqyB5ui8xPSujlm0/sfYiNMjj7Lq+dngFxNZT3oA0vY+QCr8QogU2qv94SzJSE\n\t9QfQ==","X-Gm-Message-State":"AOJu0Yz+Dk1GX0Y1OL3+95iJcpFf6ybpHwVR9tYf7zRB6t3A8zrLl+cU\n\tfxBxFnHA0l91uVOdVyVM5ahj4C3aTj+Q4KiYiyARAhInOh12Q/yH/yoRkWAk1313qN3IQ6Mbvwd\n\tJqMq/eRJfnuA22A9DwbsizIXyxW76XiA3ClEEQgIpEFYiGSa9iM24MGoUo6WyhZVYrWCHxhSEGA\n\tc=","X-Gm-Gg":"ASbGncs5mWO3MpFd0sDTL6ZhNAvKLxNX4Id33/LQwQ/8uHDMmMcBQEZRLaFVvsVWhpY\n\tHYnIEsGiZEnNYJBCIPwhguZpVHKDN2wdNJh6jkAGSy5zVATz1PPlPItrn7jxpt69E9btCFhSioD\n\tq6uCLBOW+ps4q2fLXmdZV50CbX1z8sZTdhm82AX5whaNeRswkVEhkWvaj2PpxtEc+IzvURFof+b\n\tgloBQ7k9Ez+ONlla/Okfj2z5B00fJ8kXmAcUnbpbEmKmcsqZJ3x/CxSZAOK2o9tP/jwTPwq1c6T\n\theRJYQKkWS1H6QAhbu9BInZ8wYO/ejrG+znX7YRpB8mLehwYEGReF/DRj392GQaHKYJU38et4GT\n\tX7HC5Ss7Gbh4BAWyT","X-Received":["by 2002:a17:906:99c4:b0:ae0:ad5c:4185 with SMTP id\n\ta640c23a62f3a-af2f91791e9mr669898066b.57.1753348266013; \n\tThu, 24 Jul 2025 02:11:06 -0700 (PDT)","by 2002:a17:906:99c4:b0:ae0:ad5c:4185 with SMTP id\n\ta640c23a62f3a-af2f91791e9mr669894366b.57.1753348265465; \n\tThu, 24 Jul 2025 02:11:05 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IEalCNnIco5rly6ytOR8RzP8k7PsvVgi79X7GKVhzGjHteLk07G0WU85nrhNPJXWEfdPjgihw==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,  Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>,  Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>","Subject":"Re: [PATCH v14 01/12] config: Introduce global runtime configuration","In-Reply-To":"<408c7e5e-2d8e-49f4-b5de-7b93ec6104b6@ideasonboard.com> (\n\t=?utf-8?b?IkJhcm5hYsOhcyBQxZFjemUiJ3M=?= message of \"Tue,\n\t22 Jul 2025  16:41:22 +0200\")","Message-ID":"<85ms8ulzti.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","References":"<20250715081921.5522-1-mzamazal@redhat.com>\n\t<20250715081921.5522-2-mzamazal@redhat.com>\n\t<408c7e5e-2d8e-49f4-b5de-7b93ec6104b6@ideasonboard.com>","Date":"Thu, 24 Jul 2025 11:11:04 +0200","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"RMCSuCO9QWp5uGFl-8U_F0LHawUQEA_VDJbVgTA02Zc_1753348266","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>"}}]