diff --git a/src/init.cpp b/src/init.cpp index 2992e78ade..f75b4c7627 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -669,9 +669,8 @@ static void BlockNotifyGenesisWait(const CBlockIndex* pBlockIndex) #if HAVE_SYSTEM static void StartupNotify(const ArgsManager& args) { - std::string cmd = args.GetArg("-startupnotify", ""); - if (!cmd.empty()) { - std::thread t(runCommand, cmd); + for (const std::string& command : args.GetArgs("-startupnotify")) { + std::thread t(runCommand, command); t.detach(); // thread runs free } } @@ -1702,14 +1701,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } #if HAVE_SYSTEM - const std::string block_notify = args.GetArg("-blocknotify", ""); - if (!block_notify.empty()) { - uiInterface.NotifyBlockTip_connect([block_notify](SynchronizationState sync_state, const CBlockIndex* pBlockIndex) { + if (args.IsArgSet("-blocknotify")) { + auto blocknotify_commands = args.GetArgs("-blocknotify"); + uiInterface.NotifyBlockTip_connect([blocknotify_commands](SynchronizationState sync_state, const CBlockIndex* pBlockIndex) { if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex) return; - std::string command = block_notify; - ReplaceAll(command, "%s", pBlockIndex->GetBlockHash().GetHex()); - std::thread t(runCommand, command); - t.detach(); // thread runs free + const std::string blockhash_hex = pBlockIndex->GetBlockHash().GetHex(); + for (std::string command : blocknotify_commands) { + ReplaceAll(command, "%s", blockhash_hex); + + std::thread t(runCommand, command); + t.detach(); // thread runs free + } }); } #endif diff --git a/src/node/kernel_notifications.cpp b/src/node/kernel_notifications.cpp index 7224127c72..d619217288 100644 --- a/src/node/kernel_notifications.cpp +++ b/src/node/kernel_notifications.cpp @@ -30,8 +30,7 @@ static void AlertNotify(const std::string& strMessage) { uiInterface.NotifyAlertChanged(); #if HAVE_SYSTEM - std::string strCmd = gArgs.GetArg("-alertnotify", ""); - if (strCmd.empty()) return; + if (!gArgs.IsArgSet("-alertnotify")) return; // Alert text should be plain ascii coming from a trusted source, but to // be safe we first strip anything not in safeChars, then add single quotes around @@ -39,10 +38,13 @@ static void AlertNotify(const std::string& strMessage) std::string singleQuote("'"); std::string safeStatus = SanitizeString(strMessage); safeStatus = singleQuote+safeStatus+singleQuote; - ReplaceAll(strCmd, "%s", safeStatus); - std::thread t(runCommand, strCmd); - t.detach(); // thread runs free + for (std::string command : gArgs.GetArgs("-alertnotify")) { + ReplaceAll(command, "%s", safeStatus); + + std::thread t(runCommand, command); + t.detach(); // thread runs free + } #endif } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 69f42f6223..5b3a774c0b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1203,29 +1203,36 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const #if HAVE_SYSTEM // notify an external script when a wallet transaction comes in or is updated - std::string strCmd = m_notify_tx_changed_script; - - if (!strCmd.empty()) - { - ReplaceAll(strCmd, "%s", hash.GetHex()); - if (auto* conf = wtx.state()) - { - ReplaceAll(strCmd, "%b", conf->confirmed_block_hash.GetHex()); - ReplaceAll(strCmd, "%h", ToString(conf->confirmed_block_height)); - } else { - ReplaceAll(strCmd, "%b", "unconfirmed"); - ReplaceAll(strCmd, "%h", "-1"); - } -#ifndef WIN32 + if (!m_notify_tx_changed_scripts.empty()) { +#ifdef WIN32 // Substituting the wallet name isn't currently supported on windows // because windows shell escaping has not been implemented yet: // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875 // A few ways it could be implemented in the future are described in: // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094 - ReplaceAll(strCmd, "%w", ShellEscape(GetName())); + const std::string walletname_escaped = "wallet_name_substitution_is_not_available_on_Windows"; +#else + const std::string walletname_escaped = ShellEscape(GetName()); #endif - std::thread t(runCommand, strCmd); - t.detach(); // thread runs free + const std::string txid_hex = hash.GetHex(); + std::string blockhash_hex, blockheight_str; + if (auto* conf = wtx.state()) { + blockhash_hex = conf->confirmed_block_hash.GetHex(); + blockheight_str = ToString(conf->confirmed_block_height); + } else { + blockhash_hex = "unconfirmed"; + blockheight_str = "-1"; + } + + for (std::string command : m_notify_tx_changed_scripts) { + ReplaceAll(command, "%s", txid_hex); + ReplaceAll(command, "%b", blockhash_hex); + ReplaceAll(command, "%h", blockheight_str); + ReplaceAll(command, "%w", walletname_escaped); + + std::thread t(runCommand, command); + t.detach(); // thread runs free + } } #endif @@ -2938,7 +2945,7 @@ std::shared_ptr CWallet::Create(WalletContext& context, const std::stri // should be possible to use std::allocate_shared. std::shared_ptr walletInstance(new CWallet(chain, name, std::move(database)), ReleaseWallet); walletInstance->m_keypool_size = std::max(args.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{1}); - walletInstance->m_notify_tx_changed_script = args.GetArg("-walletnotify", ""); + walletInstance->m_notify_tx_changed_scripts = args.GetArgs("-walletnotify"); // Load wallet bool rescan_required = false; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index bdbb8f8664..da5f40e72b 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -738,7 +738,7 @@ public: int64_t m_keypool_size{DEFAULT_KEYPOOL_SIZE}; /** Notify external script when a wallet transaction comes in or is updated (handled by -walletnotify) */ - std::string m_notify_tx_changed_script; + std::vector m_notify_tx_changed_scripts; size_t KeypoolCountExternalKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool TopUpKeyPool(unsigned int kpSize = 0);