From 30073e6b3a24cbe417c45cd5df6a3a2de0251e9d Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 23 Aug 2018 13:42:31 -0400 Subject: [PATCH] multiprocess: Add -ipcbind option to bitcoin-node Add `-ipcbind` option to `bitcoin-node` to listen on an IPC socket and accept connections from other processes. In the future, there will be an `-ipcconnect` option added to `bitcoin-wallet` and `bitcoin-node` to allow wallet and gui processes to connect to the node and access it. Example usage: src/bitcoin-node -regtest -debug -ipcbind=unix src/bitcoin-wallet -regtest -ipcconnect=unix info src/bitcoin-gui -regtest -ipcconnect=unix src/bitcoin-mine -regtest -ipcconnect=unix --- src/bitcoind.cpp | 7 ++++--- src/common/args.cpp | 3 +++ src/common/args.h | 1 + src/init.cpp | 17 ++++++++++++++++- src/init.h | 2 +- src/init/bitcoin-gui.cpp | 5 +++++ src/init/bitcoin-node.cpp | 1 + src/interfaces/init.h | 1 + src/qt/bitcoin.cpp | 2 +- src/test/fuzz/system.cpp | 2 +- 10 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index a09bb5c9dab07..1e27924dfdbc6 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -109,10 +109,11 @@ int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint) #endif -static bool ParseArgs(ArgsManager& args, int argc, char* argv[]) +static bool ParseArgs(NodeContext& node, int argc, char* argv[]) { + ArgsManager& args{*Assert(node.args)}; // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() - SetupServerArgs(args); + SetupServerArgs(args, node.init->canListenIpc()); std::string error; if (!args.ParseParameters(argc, argv, error)) { return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error))); @@ -268,7 +269,7 @@ MAIN_FUNCTION // Interpret command line arguments ArgsManager& args = *Assert(node.args); - if (!ParseArgs(args, argc, argv)) return EXIT_FAILURE; + if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE; // Process early info return commands such as -help or -version if (ProcessInitCommands(args)) return EXIT_SUCCESS; diff --git a/src/common/args.cpp b/src/common/args.cpp index 9b3ad382963ca..f59d2b8f0fcd1 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -635,6 +635,9 @@ std::string ArgsManager::GetHelpMessage() const case OptionsCategory::RPC: usage += HelpMessageGroup("RPC server options:"); break; + case OptionsCategory::IPC: + usage += HelpMessageGroup("IPC interprocess connection options:"); + break; case OptionsCategory::WALLET: usage += HelpMessageGroup("Wallet options:"); break; diff --git a/src/common/args.h b/src/common/args.h index 3f4cbbfab46ef..8d9daf5f65d86 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -64,6 +64,7 @@ enum class OptionsCategory { COMMANDS, REGISTER_COMMANDS, CLI_COMMANDS, + IPC, HIDDEN // Always the last option to avoid printing these in the help }; diff --git a/src/init.cpp b/src/init.cpp index e60feecf108be..3123266e4b61a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -441,7 +442,7 @@ static void OnRPCStopped() LogDebug(BCLog::RPC, "RPC stopped.\n"); } -void SetupServerArgs(ArgsManager& argsman) +void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) { SetupHelpOptions(argsman); argsman.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now @@ -676,6 +677,9 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcworkqueue=", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + if (can_listen_ipc) { + argsman.AddArg("-ipcbind=
", "Bind to Unix socket address and listen for incoming connections. Valid address values are \"unix\" to listen on the default path, /node.sock, or \"unix:/custom/path\" to specify a custom path. Can be specified multiple times to listen on multiple paths. Default behavior is not to listen on any path. If relative paths are specified, they are interpreted relative to the network data directory. If paths include any parent directory components and the parent directories do not exist, they will be created.", ArgsManager::ALLOW_ANY, OptionsCategory::IPC); + } #if HAVE_DECL_FORK argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -1200,6 +1204,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) g_wallet_init_interface.Construct(node); uiInterface.InitWallet(); + if (interfaces::Ipc* ipc = node.init->ipc()) { + for (std::string address : gArgs.GetArgs("-ipcbind")) { + try { + ipc->listenAddress(address); + } catch (const std::exception& e) { + return InitError(strprintf(Untranslated("Unable to bind to IPC address '%s'. %s"), address, e.what())); + } + LogPrintf("Listening for IPC requests on address %s\n", address); + } + } + /* Register RPC commands regardless of -server setting so they will be * available in the GUI RPC console even if external calls are disabled. */ diff --git a/src/init.h b/src/init.h index 40a5da3c0b561..6d8a35d80ea0c 100644 --- a/src/init.h +++ b/src/init.h @@ -74,7 +74,7 @@ bool AppInitMain(node::NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip /** * Register all arguments with the ArgsManager */ -void SetupServerArgs(ArgsManager& argsman); +void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc=false); /** Validates requirements to run the indexes and spawns each index initial sync thread */ bool StartIndexBackgroundSync(node::NodeContext& node); diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp index aceff1e40f0bc..eae30bc995ae6 100644 --- a/src/init/bitcoin-gui.cpp +++ b/src/init/bitcoin-gui.cpp @@ -34,6 +34,11 @@ class BitcoinGuiInit : public interfaces::Init } std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } interfaces::Ipc* ipc() override { return m_ipc.get(); } + // bitcoin-gui accepts -ipcbind option even though it does not use it + // directly. It just returns true here to accept the option because + // bitcoin-node accepts the option, and bitcoin-gui accepts all bitcoin-node + // options and will start the node with those options. + bool canListenIpc() override { return true; } node::NodeContext m_node; std::unique_ptr m_ipc; }; diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp index 00a3822791174..3f8c50b8d66c9 100644 --- a/src/init/bitcoin-node.cpp +++ b/src/init/bitcoin-node.cpp @@ -37,6 +37,7 @@ class BitcoinNodeInit : public interfaces::Init } std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } interfaces::Ipc* ipc() override { return m_ipc.get(); } + bool canListenIpc() override { return true; } node::NodeContext& m_node; std::unique_ptr m_ipc; }; diff --git a/src/interfaces/init.h b/src/interfaces/init.h index 094ead399dc67..b496ada05f4ab 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -37,6 +37,7 @@ class Init virtual std::unique_ptr makeWalletLoader(Chain& chain) { return nullptr; } virtual std::unique_ptr makeEcho() { return nullptr; } virtual Ipc* ipc() { return nullptr; } + virtual bool canListenIpc() { return false; } }; //! Return implementation of Init interface for the node process. If the argv diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 7a0979056eeeb..ec415f0bac913 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -525,7 +525,7 @@ int GuiMain(int argc, char* argv[]) /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these // Command-line options take precedence: - SetupServerArgs(gArgs); + SetupServerArgs(gArgs, init->canListenIpc()); SetupUIArgs(gArgs); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 3a404c0b48fb1..2ab5b7ed39aef 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -59,7 +59,7 @@ FUZZ_TARGET(system, .init = initialize_system) args_manager.SoftSetBoolArg(str_arg, f_value); }, [&] { - const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::CLI_COMMANDS, OptionsCategory::HIDDEN}); + const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::CLI_COMMANDS, OptionsCategory::IPC, OptionsCategory::HIDDEN}); // Avoid hitting: // common/args.cpp:563: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16));