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
This commit is contained in:
Russell Yanofsky 2018-08-23 13:42:31 -04:00
parent 73fe7d7230
commit 30073e6b3a
10 changed files with 34 additions and 7 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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
};

View file

@ -29,6 +29,7 @@
#include <init/common.h>
#include <interfaces/chain.h>
#include <interfaces/init.h>
#include <interfaces/ipc.h>
#include <interfaces/mining.h>
#include <interfaces/node.h>
#include <kernel/context.h>
@ -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=<n>", 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=<address>", "Bind to Unix socket address and listen for incoming connections. Valid address values are \"unix\" to listen on the default path, <datadir>/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.
*/

View file

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

View file

@ -34,6 +34,11 @@ public:
}
std::unique_ptr<interfaces::Echo> 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<interfaces::Ipc> m_ipc;
};

View file

@ -37,6 +37,7 @@ public:
}
std::unique_ptr<interfaces::Echo> 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<interfaces::Ipc> m_ipc;
};

View file

@ -37,6 +37,7 @@ public:
virtual std::unique_ptr<WalletLoader> makeWalletLoader(Chain& chain) { return nullptr; }
virtual std::unique_ptr<Echo> 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

View file

@ -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)) {

View file

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