4242#include < libdnf5/utils/fs/file.hpp>
4343#include < libdnf5/utils/patterns.hpp>
4444#include < rpm/rpmtypes.h>
45+ #include < signal.h>
4546
4647#include < algorithm>
4748#include < cctype>
@@ -60,6 +61,47 @@ namespace {
6061
6162constexpr const char * STORED_REPO_PREFIX = " @stored_transaction" ;
6263
64+ // Helper function to format active transaction information for error messages
65+ std::string format_active_transaction_info (const libdnf5::base::ActiveTransactionInfo & info) {
66+ std::string result;
67+
68+ if (!info.get_description ().empty ()) {
69+ result += libdnf5::utils::sformat (_ (" Command: {}" ), info.get_description ());
70+ }
71+
72+ if (info.get_pid () > 0 ) {
73+ if (!result.empty ()) {
74+ result += " \n " ;
75+ }
76+ if (kill (info.get_pid (), 0 ) == 0 ) {
77+ result += libdnf5::utils::sformat (_ (" Process ID: {} (still running)" ), info.get_pid ());
78+ } else {
79+ result += libdnf5::utils::sformat (_ (" Process ID: {}" ), info.get_pid ());
80+ }
81+ }
82+
83+ if (info.get_start_time () > 0 ) {
84+ if (!result.empty ()) {
85+ result += " \n " ;
86+ }
87+ result +=
88+ libdnf5::utils::sformat (_ (" Started: {}" ), libdnf5::utils::string::format_epoch (info.get_start_time ()));
89+ }
90+
91+ if (!info.get_comment ().empty ()) {
92+ if (!result.empty ()) {
93+ result += " \n " ;
94+ }
95+ result += libdnf5::utils::sformat (_ (" Comment: {}" ), info.get_comment ());
96+ }
97+
98+ if (!result.empty ()) {
99+ result = _ (" Details about the currently running transaction:\n " ) + result;
100+ }
101+
102+ return result;
103+ }
104+
63105// The `KeyImportRepoCB` class implements callback only for importing repository key.
64106class KeyImportRepoCB : public libdnf5 ::repo::RepoCallbacks2_1 {
65107public:
@@ -379,6 +421,18 @@ void Context::Impl::store_offline(libdnf5::base::Transaction & transaction) {
379421 if (result != libdnf5::base::Transaction::TransactionRunResult::SUCCESS) {
380422 print_error (libdnf5::utils::sformat (
381423 _ (" Transaction failed: {}" ), libdnf5::base::Transaction::transaction_result_to_string (result)));
424+
425+ // For transaction lock errors, provide detailed information about the running transaction
426+ if (result == libdnf5::base::Transaction::TransactionRunResult::ERROR_LOCK) {
427+ auto * active_info = transaction.get_concurrent_transaction ();
428+ if (active_info) {
429+ auto info_str = format_active_transaction_info (*active_info);
430+ if (!info_str.empty ()) {
431+ print_error (info_str);
432+ }
433+ }
434+ }
435+
382436 for (auto const & entry : transaction.get_gpg_signature_problems ()) {
383437 print_error (entry);
384438 }
@@ -532,6 +586,18 @@ void Context::Impl::download_and_run(libdnf5::base::Transaction & transaction) {
532586 if (result != libdnf5::base::Transaction::TransactionRunResult::SUCCESS) {
533587 print_error (libdnf5::utils::sformat (
534588 _ (" Transaction failed: {}" ), libdnf5::base::Transaction::transaction_result_to_string (result)));
589+
590+ // For transaction lock errors, provide detailed information about the running transaction
591+ if (result == libdnf5::base::Transaction::TransactionRunResult::ERROR_LOCK) {
592+ auto * active_info = transaction.get_concurrent_transaction ();
593+ if (active_info) {
594+ auto info_str = format_active_transaction_info (*active_info);
595+ if (!info_str.empty ()) {
596+ print_error (info_str);
597+ }
598+ }
599+ }
600+
535601 for (auto const & entry : transaction.get_gpg_signature_problems ()) {
536602 print_error (entry);
537603 }
0 commit comments