diff --git a/dnf5-plugins/copr_plugin/copr_config.cpp b/dnf5-plugins/copr_plugin/copr_config.cpp index 8714f2813..483207f05 100644 --- a/dnf5-plugins/copr_plugin/copr_config.cpp +++ b/dnf5-plugins/copr_plugin/copr_config.cpp @@ -22,6 +22,8 @@ along with libdnf. If not, see . #include "copr_constants.hpp" +#include "libdnf5/utils/os_release.hpp" + #include @@ -42,6 +44,8 @@ void CoprConfig::load_all_configuration() { // https://github.com/rpm-software-management/dnf5/issues/513 etc_dir = "/etc"; + libdnf5::utils::OSRelease os_release(etc_dir / "os-release"); + load_copr_config_file("/usr/share/dnf/plugins/copr.vendor.conf"); load_copr_config_file(etc_dir / "dnf/plugins/copr.vendor.conf"); load_copr_config_file(etc_dir / "dnf/plugins/copr.conf"); diff --git a/dnf5-plugins/copr_plugin/copr_config.hpp b/dnf5-plugins/copr_plugin/copr_config.hpp index 974d3194f..0fb5c27f1 100644 --- a/dnf5-plugins/copr_plugin/copr_config.hpp +++ b/dnf5-plugins/copr_plugin/copr_config.hpp @@ -20,8 +20,6 @@ along with libdnf. If not, see . #ifndef DNF5_COMMANDS_COPR_COPR_CONFIG_HPP #define DNF5_COMMANDS_COPR_COPR_CONFIG_HPP -#include "os_release.hpp" - #include #include #include @@ -31,7 +29,6 @@ namespace dnf5 { class CoprConfig : public libdnf5::ConfigParser { private: libdnf5::Base & base; - OSRelease os_release; void load_copr_config_file(const std::string & filename); void load_all_configuration(); diff --git a/dnf5-plugins/copr_plugin/os_release.cpp b/dnf5-plugins/copr_plugin/os_release.cpp deleted file mode 100644 index ca74604b4..000000000 --- a/dnf5-plugins/copr_plugin/os_release.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "os_release.hpp" - -#include -#include -#include -#include - - -OSRelease::OSRelease() {} - -std::string OSRelease::get_value(const std::string & key, const std::string & default_value) { - initialize(); - if (map.find(key) == map.end()) - return default_value; - return map[key]; -} - - -void OSRelease::initialize() { - if (initialized_) - return; - - initialized_ = true; - std::filesystem::path filename; - if (const char * pathname = std::getenv("TEST_COPR_CONFIG_DIR")) - filename = pathname; - else - filename = "/etc"; - filename /= "os-release"; - std::ifstream infile(filename); - if (!std::filesystem::exists(filename)) - return; - - const std::regex r_no_quotes("^([A-Z_]+)=(\\w+)"); - const std::regex r_quotes("^([A-Z_]+)=\"([\\w\\s]+)\""); - std::smatch match; - std::string line; - - while (std::getline(infile, line)) { - if (std::regex_match(line, match, r_no_quotes)) { - map[match[1]] = match[2]; - continue; - } - if (std::regex_match(line, match, r_quotes)) { - map[match[1]] = match[2]; - continue; - } - } -} - - -bool OSRelease::initialized_ = false; -std::map OSRelease::map = {}; diff --git a/include/libdnf5/utils/os_release.hpp b/include/libdnf5/utils/os_release.hpp new file mode 100644 index 000000000..205375aba --- /dev/null +++ b/include/libdnf5/utils/os_release.hpp @@ -0,0 +1,61 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#ifndef LIBDNF5_UTILS_OS_RELEASE_HPP +#define LIBDNF5_UTILS_OS_RELEASE_HPP + +#include "libdnf5/defs.h" + +#include +#include +#include +#include + +namespace libdnf5::utils { + +/// Object which loads and exposes values from an os-release file. +class LIBDNF_API OSRelease { +public: + /// Creates an instance of `OSRelease` from the file at `path`. + /// + /// @param path The path to the os-release file to load and parse. + explicit OSRelease(std::filesystem::path path = "/etc/os-release") : path(std::move(path)) {} + + /// Returns the corresponding os-release value for `key`. + /// If the value for `key` isn't set, `default_value` is returned. + /// + /// @param key The os-release key to get the value of. + /// @param default_value Default value to return if the value for `key` isn't set. + std::string get_value(const std::string & key, const std::string & default_value = "UNSET"); + + /// Returns whether the corresponding os-release value is set for `key`. + /// + /// @param key The os-release key to check if set. + bool contains(const std::string & key); + +private: + std::filesystem::path path; + bool initialized_ = false; + std::map map = {}; + void initialize(); +}; + +} // namespace libdnf5::utils + +#endif // LIBDNF5_UTILS_OS_RELEASE_HPP diff --git a/libdnf5/conf/config_main.cpp b/libdnf5/conf/config_main.cpp index 7a54f5a05..69f6915d3 100644 --- a/libdnf5/conf/config_main.cpp +++ b/libdnf5/conf/config_main.cpp @@ -21,14 +21,17 @@ along with libdnf. If not, see . #include "config.h" #include "config_utils.hpp" +#include "utils/system.hpp" #include "libdnf5/common/xdg.hpp" #include "libdnf5/conf/config_parser.hpp" #include "libdnf5/conf/const.hpp" #include "libdnf5/utils/bgettext/bgettext-mark-domain.h" #include "libdnf5/utils/fs/file.hpp" +#include "libdnf5/utils/os_release.hpp" #include +#include #include #include @@ -84,6 +87,23 @@ static int str_to_bytes(const std::string & str) { return static_cast(res); } +static std::string get_user_agent() { + utils::OSRelease os_release; + auto os = utils::get_os(); + auto base_arch = rpm::get_base_arch(utils::detect_arch()); + + if (!(os_release.contains("NAME") && os_release.contains("VERSION_ID") && !os.empty() && !base_arch.empty())) { + return "libdnf"; + } + + return std::format( + "libdnf ({} {}; {}; {}.{})", + os_release.get_value("NAME"), + os_release.get_value("VERSION_ID"), + os_release.get_value("VARIANT_ID", "generic"), + os, + base_arch); +} class ConfigMain::Impl { friend class ConfigMain; @@ -206,7 +226,7 @@ class ConfigMain::Impl { OptionBool module_stream_switch{false}; OptionBool module_obsoletes{false}; - OptionString user_agent{"libdnf"}; // TODO(jrohel): getUserAgent() + OptionString user_agent{get_user_agent()}; OptionBool countme{false}; OptionBool protect_running_kernel{true}; OptionBool build_cache{true}; diff --git a/libdnf5/conf/vars.cpp b/libdnf5/conf/vars.cpp index 4f55ace4b..70a572116 100644 --- a/libdnf5/conf/vars.cpp +++ b/libdnf5/conf/vars.cpp @@ -20,6 +20,7 @@ along with libdnf. If not, see . #include "libdnf5/conf/vars.hpp" #include "rpm/rpm_log_guard.hpp" +#include "utils/system.hpp" #include "libdnf5/base/base.hpp" #include "libdnf5/common/exception.hpp" @@ -53,19 +54,6 @@ namespace libdnf5 { static const std::unordered_set READ_ONLY_VARIABLES = {"releasever_major", "releasever_minor"}; -// ================================================================== -// The following helper functions should be moved e.g. into a library - -static void init_lib_rpm(const char * arch) { - static bool lib_rpm_initiated{false}; - if (!lib_rpm_initiated) { - if (rpmReadConfigFiles(nullptr, arch) != 0) { - throw RuntimeError(M_("failed to read rpm config files")); - } - lib_rpm_initiated = true; - } -} - static constexpr const char * DISTROVERPKGS[] = { "system-release(releasever)", "system-release", @@ -74,51 +62,6 @@ static constexpr const char * DISTROVERPKGS[] = { "redhat-release", "suse-release"}; -/* ARM specific HWCAP defines may be missing on non-ARM devices */ -#ifndef HWCAP_ARM_VFP -#define HWCAP_ARM_VFP (1 << 6) -#endif -#ifndef HWCAP_ARM_NEON -#define HWCAP_ARM_NEON (1 << 12) -#endif - -static std::string detect_arch() { - struct utsname un; - - if (uname(&un) < 0) { - throw RuntimeError(M_("Failed to execute uname()")); - } - - if (!strncmp(un.machine, "armv", 4)) { - /* un.machine is armvXE, where X is version number and E is - * endianness (b or l); we need to add modifiers such as - * h (hardfloat), n (neon). Neon is a requirement of armv8 so - * as far as rpm is concerned armv8l is the equivalent of armv7hnl - * (or 7hnb) so we don't explicitly add 'n' for 8+ as it's expected. */ - char endian = un.machine[strlen(un.machine) - 1]; - char * modifier = un.machine + 5; - while (isdigit(*modifier)) /* keep armv7, armv8, armv9, armv10, armv100, ... */ - modifier++; - if (getauxval(AT_HWCAP) & HWCAP_ARM_VFP) - *modifier++ = 'h'; - if ((atoi(un.machine + 4) == 7) && (getauxval(AT_HWCAP) & HWCAP_ARM_NEON)) - *modifier++ = 'n'; - *modifier++ = endian; - *modifier = 0; - } -#ifdef __MIPSEL__ - // support for little endian MIPS - if (!strcmp(un.machine, "mips")) - strcpy(un.machine, "mipsel"); - else if (!strcmp(un.machine, "mips64")) - strcpy(un.machine, "mips64el"); -#endif - return un.machine; -} - - -// ================================================================== - class Vars::Impl { public: @@ -443,9 +386,9 @@ void Vars::load(const std::string & installroot, const std::vector void Vars::detect_vars(const std::string & installroot) { set_lazy( - "arch", []() -> auto { return std::make_unique(detect_arch()); }, Priority::AUTO); + "arch", []() -> auto { return std::make_unique(utils::detect_arch()); }, Priority::AUTO); - init_lib_rpm(get_value("arch").c_str()); + utils::init_lib_rpm(get_value("arch").c_str()); set_lazy( "basearch", diff --git a/libdnf5/utils/os_release.cpp b/libdnf5/utils/os_release.cpp new file mode 100644 index 000000000..e7b562289 --- /dev/null +++ b/libdnf5/utils/os_release.cpp @@ -0,0 +1,67 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "libdnf5/utils/os_release.hpp" + +#include +#include +#include + +namespace libdnf5::utils { + +std::string OSRelease::get_value(const std::string & key, const std::string & default_value) { + initialize(); + if (!map.contains(key)) + return default_value; + return map[key]; +} + +bool OSRelease::contains(const std::string & key) { + initialize(); + return map.contains(key); +} + + +void OSRelease::initialize() { + if (initialized_) + return; + + initialized_ = true; + std::ifstream infile(path); + if (!std::filesystem::exists(path)) + return; + + const std::regex r_no_quotes("^([A-Z_]+)=(\\w+)"); + const std::regex r_quotes("^([A-Z_]+)=\"(.+)\""); + std::smatch match; + std::string line; + + while (std::getline(infile, line)) { + if (std::regex_match(line, match, r_no_quotes)) { + map[match[1]] = match[2]; + continue; + } + if (std::regex_match(line, match, r_quotes)) { + map[match[1]] = match[2]; + continue; + } + } +} + +} // namespace libdnf5::utils diff --git a/libdnf5/utils/system.cpp b/libdnf5/utils/system.cpp new file mode 100644 index 000000000..89c113004 --- /dev/null +++ b/libdnf5/utils/system.cpp @@ -0,0 +1,89 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 2.1 of the License, or +(at your option) any later version. + +Libdnf is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with libdnf. If not, see . +*/ + +#include "system.hpp" + +#include +#include +#include +#include +#include + +namespace libdnf5::utils { + +void init_lib_rpm(const char * arch) { + static bool lib_rpm_initiated{false}; + if (!lib_rpm_initiated) { + if (rpmReadConfigFiles(nullptr, arch) != 0) { + throw RuntimeError(M_("failed to read rpm config files")); + } + lib_rpm_initiated = true; + } +} + +/* ARM specific HWCAP defines may be missing on non-ARM devices */ +#ifndef HWCAP_ARM_VFP +#define HWCAP_ARM_VFP (1 << 6) +#endif +#ifndef HWCAP_ARM_NEON +#define HWCAP_ARM_NEON (1 << 12) +#endif + +std::string detect_arch() { + struct utsname un; + + if (uname(&un) < 0) { + throw RuntimeError(M_("Failed to execute uname()")); + } + + if (!strncmp(un.machine, "armv", 4)) { + /* un.machine is armvXE, where X is version number and E is + * endianness (b or l); we need to add modifiers such as + * h (hardfloat), n (neon). Neon is a requirement of armv8 so + * as far as rpm is concerned armv8l is the equivalent of armv7hnl + * (or 7hnb) so we don't explicitly add 'n' for 8+ as it's expected. */ + char endian = un.machine[strlen(un.machine) - 1]; + char * modifier = un.machine + 5; + while (isdigit(*modifier)) /* keep armv7, armv8, armv9, armv10, armv100, ... */ + modifier++; + if (getauxval(AT_HWCAP) & HWCAP_ARM_VFP) + *modifier++ = 'h'; + if ((atoi(un.machine + 4) == 7) && (getauxval(AT_HWCAP) & HWCAP_ARM_NEON)) + *modifier++ = 'n'; + *modifier++ = endian; + *modifier = 0; + } +#ifdef __MIPSEL__ + // support for little endian MIPS + if (!strcmp(un.machine, "mips")) + strcpy(un.machine, "mipsel"); + else if (!strcmp(un.machine, "mips64")) + strcpy(un.machine, "mips64el"); +#endif + return un.machine; +} + +std::string get_os() { + const char * value; + init_lib_rpm(detect_arch().c_str()); + rpmGetOsInfo(&value, nullptr); + return value; +} + +} // namespace libdnf5::utils diff --git a/dnf5-plugins/copr_plugin/os_release.hpp b/libdnf5/utils/system.hpp similarity index 65% rename from dnf5-plugins/copr_plugin/os_release.hpp rename to libdnf5/utils/system.hpp index 1aa3c32a5..5cc61b64b 100644 --- a/dnf5-plugins/copr_plugin/os_release.hpp +++ b/libdnf5/utils/system.hpp @@ -17,21 +17,16 @@ You should have received a copy of the GNU Lesser General Public License along with libdnf. If not, see . */ -#ifndef DNF5_COMMANDS_COPR_OS_RELEASE_HPP -#define DNF5_COMMANDS_COPR_OS_RELEASE_HPP - -#include +#ifndef SYSTEM_HPP +#define SYSTEM_HPP #include -class OSRelease { -private: - static bool initialized_; - static std::map map; - void initialize(); +namespace libdnf5::utils { + +void init_lib_rpm(const char * arch); +std::string detect_arch(); +std::string get_os(); -public: - OSRelease(); - std::string get_value(const std::string & key, const std::string & default_value = "UNSET"); -}; +} // namespace libdnf5::utils -#endif // DNF5_COMMANDS_COPR_OS_RELEASE_HPP +#endif // SYSTEM_HPP