Show a patch.

GET /api/1.1/patches/22748/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 22748,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/22748/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/22748/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20250206161607.50738-3-mzamazal@redhat.com>",
    "date": "2025-02-06T16:15:53",
    "name": "[v7,02/15] config: Introduce global runtime configuration",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "21ab490e11d748ab35d7ab4d16e232493655b9d6",
    "submitter": {
        "id": 177,
        "url": "https://patchwork.libcamera.org/api/1.1/people/177/?format=api",
        "name": "Milan Zamazal",
        "email": "mzamazal@redhat.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/22748/mbox/",
    "series": [
        {
            "id": 4993,
            "url": "https://patchwork.libcamera.org/api/1.1/series/4993/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=4993",
            "date": "2025-02-06T16:15:51",
            "name": "Add global configuration file",
            "version": 7,
            "mbox": "https://patchwork.libcamera.org/series/4993/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/22748/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/22748/checks/",
    "tags": {},
    "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 3D889C32EF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu,  6 Feb 2025 16:16:29 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E8266685F3;\n\tThu,  6 Feb 2025 17:16:28 +0100 (CET)",
            "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 EE7B2685E2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu,  6 Feb 2025 17:16:26 +0100 (CET)",
            "from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com\n\t(ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63])\n\tby relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n\tcipher=TLS_AES_256_GCM_SHA384) id us-mta-365-SIvyeIVsNDaAoRR_k1qpdQ-1;\n\tThu, 06 Feb 2025 11:16:22 -0500",
            "from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com\n\t(mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com\n\t[10.30.177.111])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\tkey-exchange X25519 server-signature RSA-PSS (2048 bits)\n\tserver-digest SHA256) (No client certificate requested)\n\tby mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTPS id 8B658195608C; Thu,  6 Feb 2025 16:16:21 +0000 (UTC)",
            "from mzamazal-thinkpadp1gen3.tpbc.com (unknown [10.43.17.31])\n\tby mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix)\n\twith ESMTP id 95E4D180056F; Thu,  6 Feb 2025 16:16:19 +0000 (UTC)"
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"A1ZErgYq\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1738858585;\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=r0voiutZmYG0jQOgx3w7Gx/pVfXgzwzPgwABLbqbJ6A=;\n\tb=A1ZErgYqa87ryJSX3upwpJkeVXzxv+GJFhsIxtKMh8V1AmW6hlHCUQmzdKYobRy9F2H9GS\n\tUxs9rXbKPuXiIEynFH1zgyGARXUmeAdkEFHvjBjQU4WYwsOi8dfAgp2HBRsMsFmnFW5vBR\n\t7u1nNmU1hKHpa0Y064BNJBQzSxxUcVU=",
        "X-MC-Unique": "SIvyeIVsNDaAoRR_k1qpdQ-1",
        "X-Mimecast-MFC-AGG-ID": "SIvyeIVsNDaAoRR_k1qpdQ",
        "From": "Milan Zamazal <mzamazal@redhat.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Cc": "Milan Zamazal <mzamazal@redhat.com>, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, Naushir Patuck\n\t<naush@raspberrypi.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<pobrn@protonmail.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>",
        "Subject": "[PATCH v7 02/15] config: Introduce global runtime configuration",
        "Date": "Thu,  6 Feb 2025 17:15:53 +0100",
        "Message-ID": "<20250206161607.50738-3-mzamazal@redhat.com>",
        "In-Reply-To": "<20250206161607.50738-1-mzamazal@redhat.com>",
        "References": "<20250206161607.50738-1-mzamazal@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.111",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-MFC-PROC-ID": "wDLt85emHmTZc5OLDMT0bg_uAzccw1AC5S7J6xi3B4U_1738858581",
        "X-Mimecast-Originator": "redhat.com",
        "Content-Transfer-Encoding": "8bit",
        "content-type": "text/plain; charset=\"US-ASCII\"; x-default=true",
        "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>"
    },
    "content": "Currently, libcamera can be configured in runtime using several\nenvironment variables.  With introducing more and more variables, this\nmechanism reaches its limits.  It would be simpler and more flexible if\nit was possible to configure libcamera in a single file.\n\nFor example, there have been a request for defining pipeline precedence\nin runtime.  We want to compile in multiple pipelines, in order to have\nthem accessible within single packages in distributions.  And then being\nable to select among the pipelines manually as needed based on the\nparticular hardware or operating system environment.  Having the\nconfiguration file then allows easy switching between hardware, GPU or\nCPU IPAs.  Another possible use case is tuning image output, especially\nwith software ISP, to user liking.  For example, some users may prefer\nhigher contrast without the need to use the corresponding knobs, if\npresent at all, in every application.  The configuration file can also\nbe used to enable or disable experimental features and avoid the need to\ntrack local patches changing configuration options hard-wired in the\ncode when working on new features.\n\nThis patch introduces basic support for configuration files.  It reads,\nstores and accesses the configuration.  The configuration is meant to\nuse as a hidden singleton with public access methods.  It is implemented\nas GlobalConfiguration namespace exposing the public functions, with\nprivate functions and the configuration data hidden in an anonymous\nnamespace.  This model suits well the purpose of having a hidden\nsingleton wrapped by public access functions.\n\nlibcamera configuration can be specified using a system-wide\nconfiguration file or a user configuration file.  The user configuration\nfile takes precedence if present.  There is currently no way to merge\nmultiple configuration files, the one found is used as the only\nconfiguration file.  If no configuration file is present, nothing\nchanges to the current libcamera behavior (except for some log\nmessages related to configuration file lookup).\n\nThe configuration file is a YAML file.  We already have a mechanism for\nhandling YAML configuration files in libcamera and the given\ninfrastructure can be reused for the purpose.  However, the\nconfiguration type is abstracted to make contingent future change of the\nunderlying class easier while retaining (most of) the original API.\n\nThe configuration is versioned.  This has currently no particular\nmeaning but is likely to have its purpose in future, especially once\nconfiguration validation is introduced.\n\nThe configuration YAML file looks as follows:\n\n  ---\n  version: 1\n  configuration:\n    WHATEVER CONFIGURATION NEEDED\n\nThere is no logging about reading the configuration file and contingent\nerrors.  This is on purpose because logging will query configuration,\nwhich can lead to various problems when done during configuration\ninitialization.  Reporting the errors will be added later.\n\nA complication arises from the fact that when the configuration is\nloaded, logging may be called and logging will ask for the\nconfiguration.  This is error-prone and may lead to subtle problems.\nFor this reason, the global configuration is instantiated to a pointer,\nwith an empty configuration initially.  The real configuration will be\ncreated through initialize() method.  It will be clearer how it helps in\nthe followup patch introducing logging configuration.\n\nLogging is also the most notable component from base that uses global\nconfiguration.  In order to be able to do it, the global configuration\nmust be put to base.\n\nThis patch introduces just the basic idea.  Actually using the\nconfiguration in the corresponding places (everything what is currently\nconfigurable via environment variables should be configurable in the\nfile configuration) and other enhancements are implemented in the\nfollowup patches.\n\nSigned-off-by: Milan Zamazal <mzamazal@redhat.com>\n---\n .../libcamera/internal/global_configuration.h |  25 +++\n include/libcamera/internal/meson.build        |   1 +\n src/libcamera/base/global_configuration.cpp   | 153 ++++++++++++++++++\n src/libcamera/base/meson.build                |   1 +\n 4 files changed, 180 insertions(+)\n create mode 100644 include/libcamera/internal/global_configuration.h\n create mode 100644 src/libcamera/base/global_configuration.cpp",
    "diff": "diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\nnew file mode 100644\nindex 00000000..bed88279\n--- /dev/null\n+++ b/include/libcamera/internal/global_configuration.h\n@@ -0,0 +1,25 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024 Red Hat, inc.\n+ *\n+ * Global configuration handling\n+ */\n+\n+#pragma once\n+\n+#include \"libcamera/internal/yaml_parser.h\"\n+\n+namespace libcamera {\n+\n+namespace GlobalConfiguration {\n+\n+using Configuration = const YamlObject &;\n+\n+void initialize();\n+\n+unsigned int version();\n+Configuration configuration();\n+\n+} /* namespace GlobalConfiguration */\n+\n+} /* namespace libcamera */\ndiff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\nindex 7d6aa8b7..cf1765bf 100644\n--- a/include/libcamera/internal/meson.build\n+++ b/include/libcamera/internal/meson.build\n@@ -22,6 +22,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',\ndiff --git a/src/libcamera/base/global_configuration.cpp b/src/libcamera/base/global_configuration.cpp\nnew file mode 100644\nindex 00000000..3350a26a\n--- /dev/null\n+++ b/src/libcamera/base/global_configuration.cpp\n@@ -0,0 +1,153 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2024 Red Hat, inc.\n+ *\n+ * Global configuration handling\n+ */\n+\n+#include \"libcamera/internal/global_configuration.h\"\n+\n+#include <filesystem>\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 GlobalConfiguration {\n+\n+namespace {\n+\n+std::unique_ptr<YamlObject> yamlConfiguration = std::make_unique<YamlObject>();\n+\n+bool loadFile(const std::filesystem::path &fileName)\n+{\n+\tFile file(fileName);\n+\tif (!file.exists()) {\n+\t\treturn false;\n+\t}\n+\n+\tif (!file.open(File::OpenModeFlag::ReadOnly))\n+\t\treturn true;\n+\n+\tauto root = YamlParser::parse(file);\n+\tif (!root)\n+\t\treturn true;\n+\tyamlConfiguration = std::move(root);\n+\n+\treturn true;\n+}\n+\n+const std::vector<std::filesystem::path>\n+\tglobalConfigurationFiles = {\n+\t\tstd::filesystem::path(LIBCAMERA_SYSCONF_DIR) / \"configuration.yaml\",\n+\t\tstd::filesystem::path(\"/etc/libcamera/configuration.yaml\"),\n+\t};\n+\n+void load()\n+{\n+\tstd::filesystem::path userConfigurationDirectory;\n+\tchar *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;\n+\t}\n+\n+\tfor (const auto &path : globalConfigurationFiles)\n+\t\tif (loadFile(path))\n+\t\t\treturn;\n+}\n+\n+Configuration get()\n+{\n+\treturn *yamlConfiguration;\n+}\n+\n+} /* namespace */\n+\n+/**\n+ * \\brief Initialize the global configuration\n+ *\n+ * This must be called before global configuration is accessed.\n+ */\n+void initialize()\n+{\n+\tload();\n+}\n+\n+/**\n+ * \\namespace 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+ * The configuration can be accessed using GlobalConfiguration::configuration().\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 version()\n+{\n+\treturn get()[\"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+Configuration configuration()\n+{\n+\treturn get()[\"configuration\"];\n+}\n+\n+} /* namespace GlobalConfiguration */\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build\nindex 94843eb9..4c003284 100644\n--- a/src/libcamera/base/meson.build\n+++ b/src/libcamera/base/meson.build\n@@ -16,6 +16,7 @@ libcamera_base_internal_sources = files([\n     'event_dispatcher_poll.cpp',\n     'event_notifier.cpp',\n     'file.cpp',\n+    'global_configuration.cpp',\n     'log.cpp',\n     'memfd.cpp',\n     'message.cpp',\n",
    "prefixes": [
        "v7",
        "02/15"
    ]
}