#include <bitcoin-build-config.h>
#include <chainparams.h>
#include <clientversion.h>
#include <common/args.h>
#include <common/init.h>
#include <common/system.h>
#include <compat/compat.h>
#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/init.h>
#include <kernel/context.h>
#include <node/context.h>
#include <node/interface_ui.h>
#include <node/warnings.h>
#include <noui.h>
#include <util/check.h>
#include <util/exception.h>
#include <util/signalinterrupt.h>
#include <util/strencodings.h>
#include <util/syserror.h>
#include <util/threadnames.h>
#include <util/tokenpipe.h>
#include <util/translation.h>
#include <any>
#include <functional>
#include <optional>
using node::NodeContext;
const TranslateFn G_TRANSLATION_FUN{nullptr};
#if HAVE_DECL_FORK
int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
{
std::optional<TokenPipe> umbilical = TokenPipe::Make();
if (!umbilical) {
return -1; }
int pid = fork();
if (pid < 0) {
return -1; }
if (pid != 0) {
endpoint = umbilical->TakeReadEnd();
umbilical->TakeWriteEnd().Close();
int status = endpoint.TokenRead();
if (status != 0) { endpoint.Close();
return -1;
}
return pid;
}
endpoint = umbilical->TakeWriteEnd();
umbilical->TakeReadEnd().Close();
#if HAVE_DECL_SETSID
if (setsid() < 0) {
exit(1); }
#endif
if (!nochdir) {
if (chdir("/") != 0) {
exit(1); }
}
if (!noclose) {
int fd = open("/dev/null", O_RDWR);
if (fd >= 0) {
bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0;
if (fd > 2) close(fd);
if (err) {
exit(1); }
} else {
exit(1); }
}
endpoint.TokenWrite(0); return 0;
}
#endif
static bool ParseArgs(NodeContext& node, int argc, char* argv[])
{
ArgsManager& args{*Assert(node.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)));
}
if (auto error = common::InitConfig(args)) {
return InitError(error->message, error->details);
}
for (int i = 1; i < argc; i++) {
if (!IsSwitchChar(argv[i][0])) {
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
}
}
return true;
}
static bool ProcessInitCommands(interfaces::Init& init, ArgsManager& args)
{
if (HelpRequested(args) || args.GetBoolArg("-version", false)) {
std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion();
if (const char* exe_name{init.exeName()}) {
strUsage += " ";
strUsage += exe_name;
}
strUsage += "\n";
if (args.GetBoolArg("-version", false)) {
strUsage += FormatParagraph(LicenseInfo());
} else {
strUsage += "\n"
"The " CLIENT_NAME " daemon (bitcoind) is a headless program that connects to the Bitcoin network to validate and relay transactions and blocks, as well as relaying addresses.\n\n"
"It provides the backbone of the Bitcoin network and its RPC, REST and ZMQ services can provide various transaction, block and address-related services.\n\n"
"There is an optional wallet component which provides transaction services.\n\n"
"It can be used in a headless environment or as part of a server setup.\n"
"\n"
"Usage: bitcoind [options]\n"
"\n";
strUsage += args.GetHelpMessage();
}
tfm::format(std::cout, "%s", strUsage);
return true;
}
return false;
}
static bool AppInit(NodeContext& node)
{
bool fRet = false;
ArgsManager& args = *Assert(node.args);
#if HAVE_DECL_FORK
TokenPipeEnd daemon_ep;
#endif
std::any context{&node};
try
{
args.SoftSetBoolArg("-server", true);
InitLogging(args);
InitParameterInteraction(args);
if (!AppInitBasicSetup(args, node.exit_status)) {
return false;
}
if (!AppInitParameterInteraction(args)) {
return false;
}
node.warnings = std::make_unique<node::Warnings>();
node.kernel = std::make_unique<kernel::Context>();
node.ecc_context = std::make_unique<ECC_Context>();
if (!AppInitSanityChecks(*node.kernel))
{
return false;
}
if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
#if HAVE_DECL_FORK
tfm::format(std::cout, CLIENT_NAME " starting\n");
switch (fork_daemon(1, 0, daemon_ep)) { case 0: if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
daemon_ep.TokenWrite(1);
daemon_ep.Close();
}
break;
case -1: return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno))));
default: { int token = daemon_ep.TokenRead();
if (token) { exit(EXIT_SUCCESS);
} else { tfm::format(std::cerr, "Error during initialization - check debug.log for details\n");
exit(EXIT_FAILURE);
}
}
}
#else
return InitError(Untranslated("-daemon is not supported on this operating system"));
#endif }
if (!AppInitLockDirectories())
{
return false;
}
fRet = AppInitInterfaces(node) && AppInitMain(node);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()");
} catch (...) {
PrintExceptionContinue(nullptr, "AppInit()");
}
#if HAVE_DECL_FORK
if (daemon_ep.IsOpen()) {
daemon_ep.TokenWrite(fRet);
daemon_ep.Close();
}
#endif
return fRet;
}
MAIN_FUNCTION
{
NodeContext node;
int exit_status;
std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
if (!init) {
return exit_status;
}
SetupEnvironment();
noui_connect();
util::ThreadSetInternalName("init");
ArgsManager& args = *Assert(node.args);
if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
if (ProcessInitCommands(*init, args)) return EXIT_SUCCESS;
if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
node.exit_status = EXIT_FAILURE;
}
Interrupt(node);
Shutdown(node);
return node.exit_status;
}