From 1775663fc5eadfa1d9b365e1fc2958614eaaa314 Mon Sep 17 00:00:00 2001 From: Jaroslav Mracek Date: Mon, 18 Dec 2023 16:19:16 +0100 Subject: [PATCH] Load protected packages from installroot The set of protected packages might differ between systems therefore it would be good to use a relevant set for the current location. --- doc/dnf5.conf.5.rst | 2 +- include/libdnf5/base/base.hpp | 3 +- libdnf5/base/base.cpp | 8 +++ libdnf5/conf/config_main.cpp | 75 +-------------------------- libdnf5/conf/config_utils.cpp | 98 +++++++++++++++++++++++++++++++++++ libdnf5/conf/config_utils.hpp | 14 +++++ 6 files changed, 125 insertions(+), 75 deletions(-) create mode 100644 libdnf5/conf/config_utils.cpp diff --git a/doc/dnf5.conf.5.rst b/doc/dnf5.conf.5.rst index 321102f283..1950d7ae45 100644 --- a/doc/dnf5.conf.5.rst +++ b/doc/dnf5.conf.5.rst @@ -373,7 +373,7 @@ repository configuration file should aside from repo ID consists of baseurl, met ``protected_packages`` :ref:`list ` - List of packages that DNF5 should never completely remove. + This append list option contains names of packages that DNF5 should never completely remove. They are protected via Obsoletes as well as user/plugin removals. diff --git a/include/libdnf5/base/base.hpp b/include/libdnf5/base/base.hpp index 5222c70f5d..c5ff2f1936 100644 --- a/include/libdnf5/base/base.hpp +++ b/include/libdnf5/base/base.hpp @@ -97,7 +97,8 @@ class Base { repo::RepoSackWeakPtr get_repo_sack() { return repo_sack.get_weak_ptr(); } rpm::PackageSackWeakPtr get_rpm_package_sack() { return rpm_package_sack.get_weak_ptr(); } - /// Loads libdnf plugins, vars from environment, varsdirs and installroot (releasever, arch). + /// Loads libdnf plugins, vars from environment, varsdirs and installroot (releasever, arch) and resolves protected + /// packages configuration /// To prevent differences between configuration and internal Base settings, following configurations /// will be locked: installroot, varsdir. /// The method is supposed to be called after configuration is updated, application plugins applied diff --git a/libdnf5/base/base.cpp b/libdnf5/base/base.cpp index 1b2e9ff797..4cdc752df8 100644 --- a/libdnf5/base/base.cpp +++ b/libdnf5/base/base.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "libdnf5/base/base.hpp" +#include "../conf/config_utils.hpp" #include "base_impl.hpp" #include "conf/config.h" #include "module/module_sack_impl.hpp" @@ -173,6 +174,13 @@ void Base::setup() { config.get_logdir_option().set(Option::Priority::INSTALLROOT, full_path); } + // Add protected packages from files from installroot + { + auto & protected_option = config.get_protected_packages_option(); + auto resolved_protected_packages = resolve_path_globs(protected_option.get_value_string(), installroot_path); + protected_option.set(protected_option.get_priority(), resolved_protected_packages); + } + load_plugins(); p_impl->plugins.init(); diff --git a/libdnf5/conf/config_main.cpp b/libdnf5/conf/config_main.cpp index 4d64bd8625..78a8c0eff4 100644 --- a/libdnf5/conf/config_main.cpp +++ b/libdnf5/conf/config_main.cpp @@ -84,75 +84,6 @@ static int str_to_bytes(const std::string & str) { return static_cast(res); } -static void add_from_file(std::ostream & out, const std::string & file_path) { - utils::fs::File file(file_path, "r"); - - std::string line; - while (file.read_line(line)) { - auto start = line.find_first_not_of(" \t\r"); - if (start == std::string::npos) { - continue; - } - if (line[start] == '#') { - continue; - } - auto end = line.find_last_not_of(" \t\r"); - - out.write(line.c_str() + start, static_cast(end - start + 1)); - out.put(' '); - } -} - -static void add_from_files(std::ostream & out, const std::string & glob_path) { - glob_t glob_buf; - glob(glob_path.c_str(), GLOB_MARK | GLOB_NOSORT, nullptr, &glob_buf); - for (size_t i = 0; i < glob_buf.gl_pathc; ++i) { - auto path = glob_buf.gl_pathv[i]; - if (path[strlen(path) - 1] != '/') { - add_from_file(out, path); - } - } - globfree(&glob_buf); -} - -/// @brief Replaces globs (like /etc/foo.d/\\*.foo) by content of matching files. -/// -/// Ignores comment lines (start with '#') and blank lines in files. -/// Result: -/// Words delimited by spaces. Characters ',' and '\n' are replaced by spaces. -/// Extra spaces are removed. -/// @param strWithGlobs Input string with globs -/// @return Words delimited by space -static std::string resolve_globs(const std::string & str_with_globs) { - std::ostringstream res; - std::string::size_type start{0}; - while (start < str_with_globs.length()) { - auto end = str_with_globs.find_first_of(" ,\n", start); - if (str_with_globs.compare(start, 5, "glob:") == 0) { - start += 5; - if (start >= str_with_globs.length()) { - break; - } - if (end == std::string::npos) { - add_from_files(res, str_with_globs.substr(start)); - break; - } - if ((end - start) != 0) { - add_from_files(res, str_with_globs.substr(start, end - start)); - } - } else { - if (end == std::string::npos) { - res << str_with_globs.substr(start); - break; - } - if ((end - start) != 0) { - res << str_with_globs.substr(start, end - start) << " "; - } - } - start = end + 1; - } - return res.str(); -} class ConfigMain::Impl { friend class ConfigMain; @@ -293,7 +224,7 @@ class ConfigMain::Impl { OptionString proxy_username{nullptr}; OptionString proxy_password{nullptr}; OptionStringSet proxy_auth_method{"any", "any|none|basic|digest|negotiate|ntlm|digest_ie|ntlm_wb", false}; - OptionStringList protected_packages{resolve_globs("dnf5 glob:/etc/dnf/protected.d/*.conf")}; + OptionStringList protected_packages{std::vector{"dnf5", "glob:/etc/dnf/protected.d/*.conf"}}; OptionString username{""}; OptionString password{""}; OptionBool gpgcheck{false}; @@ -534,9 +465,7 @@ ConfigMain::Impl::Impl(Config & owner) : owner(owner) { "protected_packages", protected_packages, [&](Option::Priority priority, const std::string & value) { - if (priority >= protected_packages.get_priority()) { - option_T_list_append(protected_packages, priority, resolve_globs(value)); - } + option_T_list_append(protected_packages, priority, value); }, nullptr, false); diff --git a/libdnf5/conf/config_utils.cpp b/libdnf5/conf/config_utils.cpp new file mode 100644 index 0000000000..68037ab400 --- /dev/null +++ b/libdnf5/conf/config_utils.cpp @@ -0,0 +1,98 @@ +/* +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 "config_utils.hpp" + +#include "libdnf5/utils/fs/file.hpp" + +#include + + +namespace libdnf5 { + + +static void add_from_file(std::ostream & out, const std::string & file_path) { + utils::fs::File file(file_path, "r"); + + std::string line; + while (file.read_line(line)) { + auto start = line.find_first_not_of(" \t\r"); + if (start == std::string::npos) { + continue; + } + if (line[start] == '#') { + continue; + } + auto end = line.find_last_not_of(" \t\r"); + + out.write(line.c_str() + start, static_cast(end - start + 1)); + out.put(' '); + } +} + + +static void add_from_files( + std::ostream & out, const std::string & glob_path, const std::filesystem::path & installroot) { + // Extend path by installroot + const auto full_path = (installroot / std::filesystem::path(glob_path).relative_path()).string(); + glob_t glob_buf; + glob(full_path.c_str(), GLOB_MARK | GLOB_NOSORT, nullptr, &glob_buf); + for (size_t i = 0; i < glob_buf.gl_pathc; ++i) { + auto path = glob_buf.gl_pathv[i]; + if (path[strlen(path) - 1] != '/') { + add_from_file(out, path); + } + } + globfree(&glob_buf); +} + + +std::string resolve_path_globs(const std::string & str_with_globs, const std::filesystem::path & installroot) { + std::ostringstream res; + std::string::size_type start{0}; + while (start < str_with_globs.length()) { + auto end = str_with_globs.find_first_of(" ,\n", start); + if (str_with_globs.compare(start, 5, "glob:") == 0) { + start += 5; + if (start >= str_with_globs.length()) { + break; + } + if (end == std::string::npos) { + add_from_files(res, str_with_globs.substr(start), installroot); + break; + } + if ((end - start) != 0) { + add_from_files(res, str_with_globs.substr(start, end - start), installroot); + } + } else { + if (end == std::string::npos) { + res << str_with_globs.substr(start); + break; + } + if ((end - start) != 0) { + res << str_with_globs.substr(start, end - start) << " "; + } + } + start = end + 1; + } + return res.str(); +} + + +} // namespace libdnf5 diff --git a/libdnf5/conf/config_utils.hpp b/libdnf5/conf/config_utils.hpp index dc610835ce..9f85b426cf 100644 --- a/libdnf5/conf/config_utils.hpp +++ b/libdnf5/conf/config_utils.hpp @@ -22,8 +22,10 @@ along with libdnf. If not, see . #include "libdnf5/conf/option.hpp" + namespace libdnf5 { + template static void option_T_list_append(T & option, Option::Priority priority, const std::string & value) { if (value.empty()) { @@ -47,6 +49,18 @@ static void option_T_list_append(T & option, Option::Priority priority, const st } } + +/// @brief Replaces globs (like /etc/foo.d/\\*.foo) by content of matching files. +/// +/// Ignores comment lines (start with '#') and blank lines in files. +/// Result: +/// Words delimited by spaces. Characters ',' and '\n' are replaced by spaces. +/// Extra spaces are removed. +/// @param strWithGlobs Input string with globs +/// @return Words delimited by space +std::string resolve_path_globs(const std::string & str_with_globs, const std::filesystem::path & installroot); + + } // namespace libdnf5 #endif