diff --git a/dnf5/commands/offline/offline.cpp b/dnf5/commands/offline/offline.cpp
index b8f21f88c0..99fb51f2aa 100644
--- a/dnf5/commands/offline/offline.cpp
+++ b/dnf5/commands/offline/offline.cpp
@@ -19,6 +19,7 @@ along with libdnf. If not, see .
#include "offline.hpp"
+#include "dnf5/offline.hpp"
#include "utils/string.hpp"
#include
@@ -201,6 +202,7 @@ void OfflineCommand::register_subcommands() {
register_subcommand(std::make_unique(get_context()));
register_subcommand(std::make_unique(get_context()));
register_subcommand(std::make_unique(get_context()));
+ register_subcommand(std::make_unique(get_context()));
}
OfflineSubcommand::OfflineSubcommand(Context & context, const std::string & name) : Command(context, name) {}
@@ -298,12 +300,8 @@ void OfflineRebootCommand::run() {
check_state(*state);
if (state->get_data().status != dnf5::offline::STATUS_DOWNLOAD_COMPLETE &&
state->get_data().status != dnf5::offline::STATUS_READY) {
- throw libdnf5::cli::CommandExitError(1, M_("system is not ready for offline transaction"));
+ throw libdnf5::cli::CommandExitError(1, M_("System is not ready for offline transaction."));
}
- if (std::filesystem::is_symlink(get_magic_symlink())) {
- throw libdnf5::cli::CommandExitError(1, M_("offline `{}` is already scheduled"), state->get_data().verb);
- }
-
if (!std::filesystem::is_directory(get_datadir())) {
throw libdnf5::cli::CommandExitError(1, M_("data directory {} does not exist"), get_datadir().string());
}
@@ -321,7 +319,9 @@ void OfflineRebootCommand::run() {
return;
}
- std::filesystem::create_symlink(get_datadir(), get_magic_symlink());
+ if (!std::filesystem::is_symlink(get_magic_symlink())) {
+ std::filesystem::create_symlink(get_datadir(), get_magic_symlink());
+ }
state->get_data().status = dnf5::offline::STATUS_READY;
state->get_data().poweroff_after = poweroff_after->get_value();
@@ -418,6 +418,9 @@ void OfflineExecuteCommand::run() {
throw libdnf5::cli::CommandExitError(1, M_("Use `dnf5 offline reboot` to begin the transaction."));
}
+ state->get_data().status = dnf5::offline::STATUS_TRANSACTION_INCOMPLETE;
+ state->write();
+
const auto & installroot = get_context().base.get_config().get_installroot_option().get_value();
const auto & datadir = installroot / dnf5::offline::DEFAULT_DATADIR.relative_path();
std::filesystem::create_directories(datadir);
@@ -613,4 +616,30 @@ void OfflineLogCommand::run() {
}
}
+void OfflineStatusCommand::run() {
+ const std::string no_transaction_message{"No offline transaction is stored."};
+
+ if (!std::filesystem::exists(state->get_path())) {
+ std::cerr << no_transaction_message << std::endl;
+ return;
+ }
+ check_state(*state);
+
+ const auto & status = state->get_data().status;
+ if (status == offline::STATUS_DOWNLOAD_INCOMPLETE) {
+ std::cout << no_transaction_message << std::endl;
+ } else if (status == offline::STATUS_DOWNLOAD_COMPLETE || status == offline::STATUS_READY) {
+ std::cout << _("An offline transaction was initiated by the following command:") << std::endl
+ << "\t" << state->get_data().cmd_line << std::endl
+ << _("Run `dnf5 offline reboot` to reboot and perform the offline transaction.") << std::endl;
+ } else if (status == offline::STATUS_TRANSACTION_INCOMPLETE) {
+ std::cout << _("An offline transaction was started, but it did not finish. Run `dnf5 offline log` for more "
+ "information. The command that initiated the transaction was:")
+ << std::endl
+ << "\t" << state->get_data().cmd_line << std::endl;
+ } else {
+ std::cout << _("Unknown offline transaction status: ") << "`" << state->get_data().status << "`" << std::endl;
+ }
+}
+
} // namespace dnf5
diff --git a/dnf5/commands/offline/offline.hpp b/dnf5/commands/offline/offline.hpp
index 45ad33bdbe..c04dd224cd 100644
--- a/dnf5/commands/offline/offline.hpp
+++ b/dnf5/commands/offline/offline.hpp
@@ -97,6 +97,12 @@ class OfflineLogCommand : public OfflineSubcommand {
libdnf5::OptionString * number{nullptr};
};
+class OfflineStatusCommand : public OfflineSubcommand {
+public:
+ explicit OfflineStatusCommand(Context & context) : OfflineSubcommand(context, "status") {}
+ void run() override;
+};
+
} // namespace dnf5
#endif // DNF5_COMMANDS_OFFLINE_HPP
diff --git a/dnf5/include/dnf5/offline.cpp b/dnf5/include/dnf5/offline.cpp
index 6526461097..a02d182bb3 100644
--- a/dnf5/include/dnf5/offline.cpp
+++ b/dnf5/include/dnf5/offline.cpp
@@ -17,6 +17,8 @@ You should have received a copy of the GNU Lesser General Public License
along with libdnf. If not, see .
*/
+#include "libdnf5/common/exception.hpp"
+
#include
#include
#include
@@ -28,6 +30,10 @@ OfflineTransactionState::OfflineTransactionState(std::filesystem::path path) : p
}
void OfflineTransactionState::read() {
try {
+ const std::ifstream file{path};
+ if (!file.good()) {
+ throw libdnf5::FileSystemError(errno, path, M_("Error reading offline state file."));
+ }
const auto & value = toml::parse(path);
data = toml::find(value, STATE_HEADER);
if (data.state_version != STATE_VERSION) {
diff --git a/dnf5/include/dnf5/offline.hpp b/dnf5/include/dnf5/offline.hpp
index 2cf3198a48..ee331763a0 100644
--- a/dnf5/include/dnf5/offline.hpp
+++ b/dnf5/include/dnf5/offline.hpp
@@ -44,7 +44,7 @@ const std::string OFFLINE_FINISHED_ID{"8cec00a1566f4d3594f116450395f06c"};
const std::string STATUS_DOWNLOAD_INCOMPLETE{"download-incomplete"};
const std::string STATUS_DOWNLOAD_COMPLETE{"download-complete"};
const std::string STATUS_READY{"ready"};
-const std::string STATUS_UPGRADE_INCOMPLETE{"upgrade-incomplete"};
+const std::string STATUS_TRANSACTION_INCOMPLETE{"transaction-incomplete"};
const int STATE_VERSION = 0;
const std::string STATE_HEADER{"offline-transaction-state"};
@@ -73,6 +73,7 @@ class OfflineTransactionState {
OfflineTransactionState(std::filesystem::path path);
OfflineTransactionStateData & get_data() { return data; };
const std::exception_ptr & get_read_exception() const { return read_exception; };
+ std::filesystem::path get_path() const { return path; };
private:
void read();