Skip to content

Commit

Permalink
Initial implementation of needs-restarting
Browse files Browse the repository at this point in the history
  • Loading branch information
evan-goode committed Oct 18, 2023
1 parent 07b2a7f commit 56ee44e
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 18 deletions.
1 change: 1 addition & 0 deletions dnf5-plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ include_directories("${PROJECT_SOURCE_DIR}/dnf5/include/")
add_subdirectory("builddep_plugin")
add_subdirectory("changelog_plugin")
add_subdirectory("copr_plugin")
add_subdirectory("needs_restarting_plugin")
add_subdirectory("repoclosure_plugin")
12 changes: 12 additions & 0 deletions dnf5-plugins/needs_restarting_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# set gettext domain for translations
add_definitions(-DGETTEXT_DOMAIN=\"dnf5_cmd_needs_restarting\")

add_library(needs_restarting_cmd_plugin MODULE needs_restarting.cpp needs_restarting_cmd_plugin.cpp)

# disable the 'lib' prefix in order to create needs_restarting_cmd_plugin.so
set_target_properties(needs_restarting_cmd_plugin PROPERTIES PREFIX "")

target_link_libraries(needs_restarting_cmd_plugin PRIVATE libdnf5 libdnf5-cli)
target_link_libraries(needs_restarting_cmd_plugin PRIVATE dnf5)

install(TARGETS needs_restarting_cmd_plugin LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/dnf5/plugins/)
112 changes: 112 additions & 0 deletions dnf5-plugins/needs_restarting_plugin/needs_restarting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
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 General Public License as published by
the Free Software Foundation, either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with libdnf. If not, see <https://www.gnu.org/licenses/>.
*/

#include "needs_restarting.hpp"

#include <libdnf5-cli/argument_parser.hpp>
#include <libdnf5-cli/output/changelogs.hpp>
#include <libdnf5/conf/const.hpp>
#include <libdnf5/conf/option_string.hpp>
#include <libdnf5/rpm/package.hpp>
#include <libdnf5/rpm/package_query.hpp>
#include <libdnf5/utils/bgettext/bgettext-mark-domain.h>

#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>

namespace dnf5 {

using namespace libdnf5::cli;

void NeedsRestartingCommand::set_parent_command() {
auto * arg_parser_parent_cmd = get_session().get_argument_parser().get_root_command();
auto * arg_parser_this_cmd = get_argument_parser_command();
arg_parser_parent_cmd->register_command(arg_parser_this_cmd);
}

void NeedsRestartingCommand::set_argument_parser() {
auto & cmd = *get_argument_parser_command();
cmd.set_description("Determine whether system or systemd services need restarting");
}

void NeedsRestartingCommand::configure() {
auto & context = get_context();
context.set_load_system_repo(true);

context.set_load_available_repos(Context::LoadAvailableRepos::ENABLED);

context.base.get_config().get_optional_metadata_types_option().add_item(
libdnf5::Option::Priority::RUNTIME, libdnf5::METADATA_TYPE_UPDATEINFO);
}

time_t get_boot_time() {
time_t proc_1_boot_time = 0;
struct stat proc_1_stat = {};
if (stat("/proc/1", &proc_1_stat) == 0) {
proc_1_boot_time = proc_1_stat.st_mtime;
}

time_t uptime_boot_time = 0;
std::ifstream uptime_stream{"/proc/uptime"};
if (uptime_stream.is_open()) {
double uptime = 0;
uptime_stream >> uptime;
if (uptime > 0) {
uptime_boot_time = std::time(nullptr) - static_cast<time_t>(uptime);
}
}

return std::max(proc_1_boot_time, uptime_boot_time);
}

void NeedsRestartingCommand::run() {
auto & ctx = get_context();

const auto boot_time = get_boot_time();

libdnf5::rpm::PackageQuery base_query{ctx.base};

libdnf5::rpm::PackageQuery installed{base_query};
installed.filter_installed();

libdnf5::rpm::PackageQuery reboot_suggested{installed};
reboot_suggested.filter_reboot_suggested();

std::vector<libdnf5::rpm::Package> need_reboot = {};
for (const auto & pkg : reboot_suggested) {
if (pkg.get_install_time() > static_cast<unsigned long long>(boot_time)) {
need_reboot.push_back(pkg);
}
}

if (need_reboot.empty()) {
std::cout << "No core libraries or services have been updated since boot-up." << std::endl
<< "Reboot should not be necessary." << std::endl;
} else {
std::cout << "Core libraries or services have been updated since boot-up:" << std::endl;
for (const auto & pkg : need_reboot) {
std::cout << "\t" << pkg.get_name() << std::endl;
}
throw libdnf5::cli::SilentCommandExitError(1);
}
}

} // namespace dnf5
47 changes: 47 additions & 0 deletions dnf5-plugins/needs_restarting_plugin/needs_restarting.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
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 General Public License as published by
the Free Software Foundation, either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with libdnf. If not, see <https://www.gnu.org/licenses/>.
*/

#ifndef DNF5_COMMANDS_NEEDS_RESTARTING_HPP
#define DNF5_COMMANDS_NEEDS_RESTARTING_HPP

#include <dnf5/context.hpp>
#include <libdnf5/conf/option_bool.hpp>
#include <libdnf5/conf/option_number.hpp>
#include <sys/stat.h>

#include <fstream>
#include <memory>
#include <vector>

namespace dnf5 {

class NeedsRestartingCommand : public Command {
public:
explicit NeedsRestartingCommand(Context & context) : Command(context, "needs-restarting") {}
void set_parent_command() override;
void set_argument_parser() override;
void configure() override;
void run() override;

private:
};

} // namespace dnf5

#endif // DNF5_COMMANDS_CHANGELOG_HPP
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "needs_restarting.hpp"

#include <dnf5/iplugin.hpp>

#include <iostream>

using namespace dnf5;

namespace {

constexpr const char * PLUGIN_NAME{"needs_restarting"};
constexpr PluginVersion PLUGIN_VERSION{.major = 1, .minor = 0, .micro = 0};

constexpr const char * attrs[]{"author.name", "author.email", "description", nullptr};
constexpr const char * attrs_value[]{"Evan Goode", "[email protected]", "needs_restarting command."};

class NeedsRestartingCmdPlugin : public IPlugin {
public:
using IPlugin::IPlugin;

PluginAPIVersion get_api_version() const noexcept override { return PLUGIN_API_VERSION; }

const char * get_name() const noexcept override { return PLUGIN_NAME; }

PluginVersion get_version() const noexcept override { return PLUGIN_VERSION; }

const char * const * get_attributes() const noexcept override { return attrs; }

const char * get_attribute(const char * attribute) const noexcept override {
for (size_t i = 0; attrs[i]; ++i) {
if (std::strcmp(attribute, attrs[i]) == 0) {
return attrs_value[i];
}
}
return nullptr;
}

std::vector<std::unique_ptr<Command>> create_commands() override;

void finish() noexcept override {}
};


std::vector<std::unique_ptr<Command>> NeedsRestartingCmdPlugin::create_commands() {
std::vector<std::unique_ptr<Command>> commands;
commands.push_back(std::make_unique<NeedsRestartingCommand>(get_context()));
return commands;
}


} // namespace


PluginAPIVersion dnf5_plugin_get_api_version(void) {
return PLUGIN_API_VERSION;
}

const char * dnf5_plugin_get_name(void) {
return PLUGIN_NAME;
}

PluginVersion dnf5_plugin_get_version(void) {
return PLUGIN_VERSION;
}

IPlugin * dnf5_plugin_new_instance([[maybe_unused]] ApplicationVersion application_version, Context & context) try {
return new NeedsRestartingCmdPlugin(context);
} catch (...) {
return nullptr;
}

void dnf5_plugin_delete_instance(IPlugin * plugin_object) {
delete plugin_object;
}
41 changes: 23 additions & 18 deletions include/libdnf5/rpm/nevra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,65 +213,70 @@ int evrcmp(const L & lhs, const R & rhs) {
return rpmvercmp(lhs.get_release().c_str(), rhs.get_release().c_str());
}


/// Compare two objects by their Name, Epoch:Version-Release and Arch.
/// @return `true` if `lhs` < `rhs`. Return `false` otherwise.
template <typename T>
bool cmp_nevra(const T & lhs, const T & rhs) {
template <typename L, typename R>
bool cmp_nevra(const L & lhs, const R & rhs) {
// compare by name
int r = lhs.get_name().compare(rhs.get_name());
if (r < 0) {
return true;
} else if (r > 0) {
}
if (r > 0) {
return false;
}

// names are equal, compare by evr
r = evrcmp(lhs, rhs);
if (r < 0) {
return true;
} else if (r > 0) {
}
if (r > 0) {
return false;
}

// names and evrs are equal, compare by arch
r = lhs.get_arch().compare(rhs.get_arch());
if (r < 0) {
return true;
}
return false;
return r < 0;
};

template <typename T>
bool cmp_nevra(const T & lhs, const T & rhs) {
return cmp_nevra<T, T>(lhs, rhs);
}

/// Compare two objects by their Name, Arch and Epoch:Version-Release.
/// @return `true` if `lhs` < `rhs`. Return `false` otherwise.
template <typename T>
bool cmp_naevr(const T & lhs, const T & rhs) {
template <typename L, typename R>
bool cmp_naevr(const L & lhs, const R & rhs) {
// compare by name
int r = lhs.get_name().compare(rhs.get_name());
if (r < 0) {
return true;
} else if (r > 0) {
}
if (r > 0) {
return false;
}

// names are equal, compare by arch
r = lhs.get_arch().compare(rhs.get_arch());
if (r < 0) {
return true;
} else if (r > 0) {
}
if (r > 0) {
return false;
}

// names and arches are equal, compare by evr
r = evrcmp(lhs, rhs);
if (r < 0) {
return true;
}

return false;
return r < 0;
};

template <typename T>
bool cmp_naevr(const T & lhs, const T rhs) {
return cmp_naevr<T, T>(lhs, rhs);
}

} // namespace libdnf5::rpm

#endif // LIBDNF5_RPM_NEVRA_HPP
4 changes: 4 additions & 0 deletions include/libdnf5/rpm/package_query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
#include "libdnf5/common/sack/exclude_flags.hpp"
#include "libdnf5/common/sack/query_cmp.hpp"

#include <ranges>
#include <string>
#include <vector>

Expand Down Expand Up @@ -680,6 +681,9 @@ class PackageQuery : public PackageSet {
/// in such cycles that are not required by any other installed package are also leaf.
void filter_leaves();

/// TODO reboot suggested
void filter_reboot_suggested();

/// Filter the leaf packages and return them grouped by their dependencies.
///
/// Leaf packages are installed packages that are not required as a dependency of another installed package.
Expand Down
Loading

0 comments on commit 56ee44e

Please sign in to comment.