diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h new file mode 100644 index 00000000..f695498c --- /dev/null +++ b/include/libcamera/internal/global_configuration.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024-2025 Red Hat, inc. + * + * Global configuration handling + */ + +#pragma once + +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace libcamera { + +class GlobalConfiguration +{ +public: + using Configuration = const YamlObject &; + + GlobalConfiguration(); + + unsigned int version() const; + Configuration configuration() const; + +private: + bool loadFile(const std::filesystem::path &fileName); + void load(); + + std::unique_ptr yamlConfiguration_ = + std::make_unique(); +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 5c80a28c..45c299f6 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -23,6 +23,7 @@ libcamera_internal_headers = files([ 'dma_buf_allocator.h', 'formats.h', 'framebuffer.h', + 'global_configuration.h', 'ipa_data_serializer.h', 'ipa_manager.h', 'ipa_module.h', diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp new file mode 100644 index 00000000..22814a4e --- /dev/null +++ b/src/libcamera/global_configuration.cpp @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024-2025 Red Hat, inc. + * + * Global configuration handling + */ + +#include "libcamera/internal/global_configuration.h" + +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace libcamera { + +namespace { +const std::vector globalConfigurationFiles = { + std::filesystem::path(LIBCAMERA_SYSCONF_DIR) / "configuration.yaml", + std::filesystem::path(LIBCAMERA_DATA_DIR) / "configuration.yaml", +}; +} + +LOG_DEFINE_CATEGORY(Configuration) + +/** + * \class GlobalConfiguration + * \brief Support for global libcamera configuration + * + * The configuration file is a YAML file and the configuration itself is stored + * under a `configuration' top-level item. + * + * The configuration file is looked up in the user's home directory first and, + * if it is not found, then in system-wide configuration directories. If + * multiple configuration files exist then only the first one found is used and + * no configuration merging is performed. + * + * If the first found configuration file cannot be opened or parsed, an error is + * reported and no configuration file is used. This is to prevent libcamera from + * using an unintended configuration file. + */ + +bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName) +{ + File file(fileName); + if (!file.open(File::OpenModeFlag::ReadOnly)) { + if (file.error() == -ENOENT) + return false; + + LOG(Configuration, Error) + << "Failed to open configuration file " << fileName; + return true; + } + + std::unique_ptr configuration = YamlParser::parse(file); + if (!configuration) { + LOG(Configuration, Error) + << "Failed to parse configuration file " << fileName; + return true; + } + + yamlConfiguration_ = std::move(configuration); + return true; +} + +void GlobalConfiguration::load() +{ + std::filesystem::path userConfigurationDirectory; + const char *xdgConfigHome = utils::secure_getenv("XDG_CONFIG_HOME"); + if (xdgConfigHome) { + userConfigurationDirectory = xdgConfigHome; + } else { + const char *home = utils::secure_getenv("HOME"); + if (home) + userConfigurationDirectory = + std::filesystem::path(home) / ".config"; + } + + if (!userConfigurationDirectory.empty()) { + std::filesystem::path user_configuration_file = + userConfigurationDirectory / "libcamera" / "configuration.yaml"; + if (loadFile(user_configuration_file)) + return; + } + + for (const auto &path : globalConfigurationFiles) { + if (loadFile(path)) + return; + } +} + +/** + * \brief Initialize the global configuration + */ +GlobalConfiguration::GlobalConfiguration() +{ + load(); +} + +/** + * \typedef GlobalConfiguration::Configuration + * \brief Type representing global libcamera configuration + * + * All code outside GlobalConfiguration must use this type declaration and not + * the underlying type. + */ + +/** + * \brief Retrieve the configuration version + * + * The version is declared in the configuration file in the top-level `version' + * element, alongside `configuration'. This has currently no real use but may be + * needed in future if configuration incompatibilities occur. + * + * \return Configuration version as declared in the configuration file or 0 if + * no global configuration is available + */ +unsigned int GlobalConfiguration::version() const +{ + return (*yamlConfiguration_)["version"].get().value_or(0); +} + +/** + * \brief Retrieve the libcamera global configuration + * + * This returns the whole configuration stored in the top-level section + * `configuration' of the YAML configuration file. + * + * The requested part of the configuration can be accessed using \a YamlObject + * methods. + * + * \note \a YamlObject type itself shouldn't be used in type declarations to + * avoid trouble if we decide to change the underlying data objects in future. + * + * \return The whole configuration section + */ +GlobalConfiguration::Configuration GlobalConfiguration::configuration() const +{ + return (*yamlConfiguration_)["configuration"]; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index b3ca27f2..5b9b86f2 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -31,6 +31,7 @@ libcamera_internal_sources = files([ 'device_enumerator_sysfs.cpp', 'dma_buf_allocator.cpp', 'formats.cpp', + 'global_configuration.cpp', 'ipa_controls.cpp', 'ipa_data_serializer.cpp', 'ipa_interface.cpp',