diff --git a/dnf5/commands/offline/offline.cpp b/dnf5/commands/offline/offline.cpp index 52f47e8cc..b38d720c2 100644 --- a/dnf5/commands/offline/offline.cpp +++ b/dnf5/commands/offline/offline.cpp @@ -206,7 +206,8 @@ void OfflineSubcommand::configure() { const std::filesystem::path installroot = ctx.get_base().get_config().get_installroot_option().get_value(); datadir = installroot / libdnf5::offline::DEFAULT_DATADIR.relative_path(); std::filesystem::create_directories(datadir); - state = std::make_optional(datadir / "offline-transaction-state.toml"); + state = std::make_optional( + datadir / libdnf5::offline::TRANSACTION_STATE_FILENAME); } void check_state(const libdnf5::offline::OfflineTransactionState & state) { diff --git a/dnf5daemon-server/dbus.hpp b/dnf5daemon-server/dbus.hpp index 3c8981423..61482b90e 100644 --- a/dnf5daemon-server/dbus.hpp +++ b/dnf5daemon-server/dbus.hpp @@ -61,6 +61,7 @@ const char * const INTERFACE_RPM = "org.rpm.dnf.v0.rpm.Rpm"; const char * const INTERFACE_GOAL = "org.rpm.dnf.v0.Goal"; const char * const INTERFACE_GROUP = "org.rpm.dnf.v0.comps.Group"; const char * const INTERFACE_ADVISORY = "org.rpm.dnf.v0.Advisory"; +const char * const INTERFACE_OFFLINE = "org.rpm.dnf.v0.Offline"; const char * const INTERFACE_SESSION_MANAGER = "org.rpm.dnf.v0.SessionManager"; // signals diff --git a/dnf5daemon-server/dbus/interfaces/org.rpm.dnf.v0.Offline.xml b/dnf5daemon-server/dbus/interfaces/org.rpm.dnf.v0.Offline.xml new file mode 100644 index 000000000..6e060aa48 --- /dev/null +++ b/dnf5daemon-server/dbus/interfaces/org.rpm.dnf.v0.Offline.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dnf5daemon-server/services/goal/goal.cpp b/dnf5daemon-server/services/goal/goal.cpp index dc6927cc2..58797100c 100644 --- a/dnf5daemon-server/services/goal/goal.cpp +++ b/dnf5daemon-server/services/goal/goal.cpp @@ -28,7 +28,6 @@ along with libdnf. If not, see . #include "utils.hpp" #include -#include #include #include #include diff --git a/dnf5daemon-server/services/offline/offline.cpp b/dnf5daemon-server/services/offline/offline.cpp new file mode 100644 index 000000000..2735a92cf --- /dev/null +++ b/dnf5daemon-server/services/offline/offline.cpp @@ -0,0 +1,190 @@ +/* +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 . +*/ + +#include "offline.hpp" + +#include "dbus.hpp" +#include "utils/string.hpp" + +#include +#include + +#include +#include + +const char * const ERR_ANOTHER_TOOL = "Offline transaction was initiated by another tool."; + +std::filesystem::path Offline::get_datadir() { + auto base = session.get_base(); + const auto & installroot = base->get_config().get_installroot_option().get_value(); + return installroot / libdnf5::offline::DEFAULT_DATADIR.relative_path(); +} + +Offline::Scheduled Offline::offline_transaction_scheduled() { + std::error_code ec; + // magic symlink exists + if (std::filesystem::exists(libdnf5::offline::MAGIC_SYMLINK, ec)) { + // and points to dnf5 location + if (std::filesystem::equivalent(libdnf5::offline::MAGIC_SYMLINK, get_datadir())) { + return Scheduled::SCHEDULED; + } else { + return Scheduled::ANOTHER_TOOL; + } + } + return Scheduled::NOT_SCHEDULED; +} + +void Offline::dbus_register() { + auto dbus_object = session.get_dbus_object(); + dbus_object->registerMethod( + dnfdaemon::INTERFACE_OFFLINE, + "cancel", + {}, + {}, + "bs", + {"success", "error_msg"}, + [this](sdbus::MethodCall call) -> void { + session.get_threads_manager().handle_method(*this, &Offline::cancel, call, session.session_locale); + }); + dbus_object->registerMethod( + dnfdaemon::INTERFACE_OFFLINE, + "check_pending", + {}, + {}, + "b", + {"pending"}, + [this](sdbus::MethodCall call) -> void { + session.get_threads_manager().handle_method(*this, &Offline::check_pending, call, session.session_locale); + }); + dbus_object->registerMethod( + dnfdaemon::INTERFACE_OFFLINE, + "clean", + {}, + {}, + "bs", + {"success", "error_msg"}, + [this](sdbus::MethodCall call) -> void { + session.get_threads_manager().handle_method(*this, &Offline::clean, call, session.session_locale); + }); + dbus_object->registerMethod( + dnfdaemon::INTERFACE_OFFLINE, + "set_finish_action", + "s", + {"action"}, + "bs", + {"success", "error_msg"}, + [this](sdbus::MethodCall call) -> void { + session.get_threads_manager().handle_method( + *this, &Offline::set_finish_action, call, session.session_locale); + }); +} + +sdbus::MethodReply Offline::check_pending(sdbus::MethodCall & call) { + auto reply = call.createReply(); + reply << (offline_transaction_scheduled() == Scheduled::SCHEDULED); + return reply; +} + +sdbus::MethodReply Offline::cancel(sdbus::MethodCall & call) { + if (!session.check_authorization(dnfdaemon::POLKIT_EXECUTE_RPM_TRANSACTION, call.getSender())) { + throw std::runtime_error("Not authorized"); + } + bool success = true; + std::string error_msg; + switch (offline_transaction_scheduled()) { + case Scheduled::SCHEDULED: { + std::error_code ec; + if (!std::filesystem::remove(libdnf5::offline::MAGIC_SYMLINK, ec) && ec) { + success = false; + error_msg = ec.message(); + } + } break; + case Scheduled::ANOTHER_TOOL: + success = false; + error_msg = ERR_ANOTHER_TOOL; + break; + case Scheduled::NOT_SCHEDULED: + break; + } + auto reply = call.createReply(); + reply << success; + reply << error_msg; + return reply; +} + +sdbus::MethodReply Offline::clean(sdbus::MethodCall & call) { + if (!session.check_authorization(dnfdaemon::POLKIT_EXECUTE_RPM_TRANSACTION, call.getSender())) { + throw std::runtime_error("Not authorized"); + } + std::vector error_msgs; + bool success = true; + if (offline_transaction_scheduled() == Scheduled::SCHEDULED) { + // remove the magic symlink if it was created by dnf5 + std::error_code ec; + if (!std::filesystem::remove(libdnf5::offline::MAGIC_SYMLINK, ec) && ec) { + success = false; + error_msgs.push_back(ec.message()); + } + } + // clean dnf5 offline transaction files + for (const auto & entry : std::filesystem::directory_iterator(get_datadir())) { + std::error_code ec; + std::filesystem::remove_all(entry.path(), ec); + if (ec) { + success = false; + error_msgs.push_back(ec.message()); + } + } + auto reply = call.createReply(); + reply << success; + reply << libdnf5::utils::string::join(error_msgs, ", "); + return reply; +} + +sdbus::MethodReply Offline::set_finish_action(sdbus::MethodCall & call) { + if (!session.check_authorization(dnfdaemon::POLKIT_EXECUTE_RPM_TRANSACTION, call.getSender())) { + throw std::runtime_error("Not authorized"); + } + bool success{false}; + std::string error_msg{}; + // try load the offline transaction state + const std::filesystem::path state_path{ + libdnf5::offline::MAGIC_SYMLINK / libdnf5::offline::TRANSACTION_STATE_FILENAME}; + libdnf5::offline::OfflineTransactionState state{state_path}; + const auto & read_exception = state.get_read_exception(); + if (read_exception == nullptr) { + // set the poweroff_after item accordingly + std::string finish_action; + call >> finish_action; + state.get_data().set_poweroff_after(finish_action == "poweroff"); + // write the new state + state.write(); + success = true; + } else { + try { + std::rethrow_exception(read_exception); + } catch (const std::exception & ex) { + error_msg = ex.what(); + } + } + auto reply = call.createReply(); + reply << success; + reply << error_msg; + return reply; +} diff --git a/dnf5daemon-server/services/offline/offline.hpp b/dnf5daemon-server/services/offline/offline.hpp new file mode 100644 index 000000000..e9f444932 --- /dev/null +++ b/dnf5daemon-server/services/offline/offline.hpp @@ -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 . +*/ + +#ifndef DNF5DAEMON_SERVER_SERVICES_OFFLINE_OFFLINE_HPP +#define DNF5DAEMON_SERVER_SERVICES_OFFLINE_OFFLINE_HPP + +#include "session.hpp" + +#include + +#include + +class Offline : public IDbusSessionService { +public: + using IDbusSessionService::IDbusSessionService; + ~Offline() = default; + void dbus_register(); + void dbus_deregister(); + +private: + sdbus::MethodReply cancel(sdbus::MethodCall & call); + sdbus::MethodReply check_pending(sdbus::MethodCall & call); + sdbus::MethodReply clean(sdbus::MethodCall & call); + sdbus::MethodReply set_finish_action(sdbus::MethodCall & call); + + enum class Scheduled { NOT_SCHEDULED, ANOTHER_TOOL, SCHEDULED }; + Scheduled offline_transaction_scheduled(); + std::filesystem::path get_datadir(); +}; + +#endif diff --git a/dnf5daemon-server/session.cpp b/dnf5daemon-server/session.cpp index ea0c93ff0..1869dd492 100644 --- a/dnf5daemon-server/session.cpp +++ b/dnf5daemon-server/session.cpp @@ -25,6 +25,7 @@ along with libdnf. If not, see . #include "services/base/base.hpp" #include "services/comps/group.hpp" #include "services/goal/goal.hpp" +#include "services/offline/offline.hpp" #include "services/repo/repo.hpp" #include "services/rpm/rpm.hpp" #include "utils.hpp" @@ -135,6 +136,7 @@ Session::Session( services.emplace_back(std::make_unique(*this)); services.emplace_back(std::make_unique(*this)); services.emplace_back(std::make_unique(*this)); + services.emplace_back(std::make_unique(*this)); services.emplace_back(std::make_unique(*this)); services.emplace_back(std::make_unique(*this));