-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dnfdaemon: Support to run transactions offline #1543
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
de57714
dnfdaemon: Support for running a transaction offline
m-blaha d21f55e
dnfdaemon-client: --offline option for transactions
m-blaha e7db9ec
dnfdaemon: Add new API for offline transactions
m-blaha 29e5323
dnfdaemon: Strict set_finish_action() value check
m-blaha c84ab42
dnfdaemon: Replace check_pending() with get_status()
m-blaha File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
dnf5daemon-server/dbus/interfaces/org.rpm.dnf.v0.Offline.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
|
||
<!-- | ||
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 <https://www.gnu.org/licenses/>. | ||
--> | ||
|
||
<node> | ||
<!-- org.rpm.dnf.v0.Offline: | ||
@short_description: Interface to offline transactions | ||
--> | ||
<interface name="org.rpm.dnf.v0.Offline"> | ||
<!-- | ||
check_pending: | ||
@pending: boolean, true if there is a pending offline transaction | ||
|
||
Check whether there is an offline transaction configured for the next reboot initiated by dnf5. | ||
--> | ||
<method name="check_pending"> | ||
<arg name="pending" type="b" direction="out" /> | ||
</method> | ||
|
||
|
||
<!-- | ||
cancel: | ||
@success: boolean, returns `false` if there was an error during the transaction cancellation, or if the offline transaction was initiated by another tool than dnf5. Returns `true` if the offline transaction was successfully cancelled or if no offline transaction was configured. | ||
@error_msg: string, contains error encountered while cancelling the transaction | ||
|
||
Cancel the dnf5 offline transaction configured for the next reboot. Offline updates scheduled by another tool are not cancelled. | ||
--> | ||
<method name="cancel"> | ||
<arg name="success" type="b" direction="out" /> | ||
<arg name="error_msg" type="s" direction="out" /> | ||
</method> | ||
|
||
<!-- | ||
clean: | ||
@success: boolean, returns `false` if there was an error during the transaction cleanup. Returns `true` if the offline transaction was successfully cleaned or if no offline transaction was configured. | ||
@error_msg: string, contains error encountered while cleaning the transaction | ||
|
||
Cancel the dnf5 offline transaction configured for the next reboot and remove all stored offline transaction data, including downloaded packages. Offline updates scheduled by another tool are not affected. | ||
--> | ||
<method name="clean"> | ||
<arg name="success" type="b" direction="out" /> | ||
<arg name="error_msg" type="s" direction="out" /> | ||
</method> | ||
|
||
<!-- | ||
set_finish_action: | ||
@action: string, if set to "poweroff", the system will be powered off after applying the offline transaction. Otherwise the system will reboot. | ||
@success: boolean, true if the action was successfully set | ||
@error_msg: string, contains error encountered while setting the action | ||
|
||
Set the action that should be performed after the offline transaction is applied. If the `action` is "poweroff", the system will be powered off, otherwise it will be rebooted (which is default). | ||
The call might fail in case there is no scheduled offline transaction, or the transaction was not scheduled using libdnf5. | ||
--> | ||
<method name="set_finish_action"> | ||
<arg name="action" type="s" direction="in" /> | ||
<arg name="success" type="b" direction="out" /> | ||
<arg name="error_msg" type="s" direction="out" /> | ||
</method> | ||
|
||
</interface> | ||
|
||
</node> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "offline.hpp" | ||
|
||
#include "dbus.hpp" | ||
#include "utils/string.hpp" | ||
|
||
#include <libdnf5/transaction/offline.hpp> | ||
#include <sdbus-c++/sdbus-c++.h> | ||
|
||
#include <exception> | ||
#include <filesystem> | ||
|
||
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<std::string> 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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 DNF5DAEMON_SERVER_SERVICES_OFFLINE_OFFLINE_HPP | ||
#define DNF5DAEMON_SERVER_SERVICES_OFFLINE_OFFLINE_HPP | ||
|
||
#include "session.hpp" | ||
|
||
#include <sdbus-c++/sdbus-c++.h> | ||
|
||
#include <filesystem> | ||
|
||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine to have it like this, even case sensitive, but it might be good to at least test whether the other action is
"reboot"
and claim a warning on the console about "unknown finish action" if it's neither"poweroff"
nor"reboot"
. That way one can check for typos and the older library will not crash when a new value is proposed in the future.This may touch also the documentation in the D-Bus interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can fill the
error_msg
output parameter with such warning. Returning an error reply seems too me.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning an error, aka failing the call, would make the parameter strict. It's fine by me, it only has certain consequences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK then, let's make the value check strict and return an error in case of unsupported value.