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();