Bitcoin ABC 0.32.4
P2P Digital Currency
bitcoind.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2019 The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#if defined(HAVE_CONFIG_H)
7#include <config/bitcoin-config.h>
8#endif
9
10#include <chainparams.h>
11#include <clientversion.h>
12#include <common/args.h>
13#include <common/init.h>
14#include <common/system.h>
15#include <compat.h>
16#include <config.h>
17#include <httprpc.h>
18#include <init.h>
19#include <interfaces/chain.h>
20#include <node/context.h>
21#include <node/ui_interface.h>
22#include <noui.h>
23#include <shutdown.h>
24#include <util/check.h>
25#include <util/exception.h>
26#include <util/strencodings.h>
27#include <util/syserror.h>
28#include <util/threadnames.h>
29#include <util/tokenpipe.h>
30#include <util/translation.h>
31
32#include <any>
33#include <functional>
34
36
37const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr;
38
39#if HAVE_DECL_FORK
40
54int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd &endpoint) {
55 // communication pipe with child process
56 std::optional<TokenPipe> umbilical = TokenPipe::Make();
57 if (!umbilical) {
58 // pipe or pipe2 failed.
59 return -1;
60 }
61
62 int pid = fork();
63 if (pid < 0) {
64 // fork failed.
65 return -1;
66 }
67 if (pid != 0) {
68 // Parent process gets read end, closes write end.
69 endpoint = umbilical->TakeReadEnd();
70 umbilical->TakeWriteEnd().Close();
71
72 int status = endpoint.TokenRead();
73 // Something went wrong while setting up child process.
74 if (status != 0) {
75 endpoint.Close();
76 return -1;
77 }
78
79 return pid;
80 }
81 // Child process gets write end, closes read end.
82 endpoint = umbilical->TakeWriteEnd();
83 umbilical->TakeReadEnd().Close();
84
85#if HAVE_DECL_SETSID
86 if (setsid() < 0) {
87 // setsid failed.
88 exit(1);
89 }
90#endif
91
92 if (!nochdir) {
93 if (chdir("/") != 0) {
94 // chdir failed.
95 exit(1);
96 }
97 }
98 if (!noclose) {
99 // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach
100 // from terminal.
101 int fd = open("/dev/null", O_RDWR);
102 if (fd >= 0) {
103 bool err = dup2(fd, STDIN_FILENO) < 0 ||
104 dup2(fd, STDOUT_FILENO) < 0 ||
105 dup2(fd, STDERR_FILENO) < 0;
106 // Don't close if fd<=2 to try to handle the case where the program
107 // was invoked without any file descriptors open.
108 if (fd > 2) {
109 close(fd);
110 }
111 if (err) {
112 // dup2 failed.
113 exit(1);
114 }
115 } else {
116 // open /dev/null failed.
117 exit(1);
118 }
119 }
120 // Success
121 endpoint.TokenWrite(0);
122 return 0;
123}
124
125#endif
126
128//
129// Start
130//
131static bool AppInit(NodeContext &node, int argc, char *argv[]) {
132 // FIXME: Ideally, we'd like to build the config here, but that's currently
133 // not possible as the whole application has too many global state. However,
134 // this is a first step.
135 auto &config = const_cast<Config &>(GetConfig());
136
137 RPCServer rpcServer;
138
139 std::any context{&node};
140
141 HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer, context);
142
143 bool fRet = false;
144
146
147 // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's
148 // main()
150 ArgsManager &args = *Assert(node.args);
151 std::string error;
152 if (!args.ParseParameters(argc, argv, error)) {
153 return InitError(Untranslated(
154 strprintf("Error parsing command line arguments: %s", error)));
155 }
156
157 // Process help and version before taking care about datadir
158 if (HelpRequested(args) || args.IsArgSet("-version")) {
159 std::string strUsage =
160 PACKAGE_NAME " version " + FormatFullVersion() + "\n";
161
162 if (args.IsArgSet("-version")) {
163 strUsage += FormatParagraph(LicenseInfo()) + "\n";
164 } else {
165 strUsage += "\nUsage: bitcoind [options] "
166 "Start " PACKAGE_NAME "\n";
167 strUsage += "\n" + args.GetHelpMessage();
168 }
169
170 tfm::format(std::cout, "%s", strUsage);
171 return true;
172 }
173
174#if HAVE_DECL_FORK
175 // Communication with parent after daemonizing. This is used for signalling
176 // in the following ways:
177 // - a boolean token is sent when the initialization process (all the Init*
178 // functions) have finished to indicate that the parent process can quit,
179 // and whether it was successful/unsuccessful.
180 // - an unexpected shutdown of the child process creates an unexpected end
181 // of stream at the parent end, which is interpreted as failure to start.
182 TokenPipeEnd daemon_ep;
183#endif
184 try {
185 if (auto err = common::InitConfig(args)) {
186 return InitError(err->message, err->details);
187 }
188
189 // Make sure we create the net-specific data directory early on: if it
190 // is new, this has a side effect of also creating
191 // <datadir>/<net>/wallets/.
192 //
193 // TODO: this should be removed once gArgs.GetDataDirNet() no longer
194 // creates the wallets/ subdirectory. See more info at:
195 // https://reviews.bitcoinabc.org/D3312
197
198 // Error out when loose non-argument tokens are encountered on command
199 // line
200 for (int i = 1; i < argc; i++) {
201 if (!IsSwitchChar(argv[i][0])) {
202 return InitError(Untranslated(
203 strprintf("Command line contains unexpected token '%s', "
204 "see bitcoind -h for a list of options.",
205 argv[i])));
206 }
207 }
208
209 // -server defaults to true for bitcoind but not for the GUI so do this
210 // here
211 args.SoftSetBoolArg("-server", true);
212 // Set this early so that parameter interactions go to console
213 InitLogging(args);
215 if (!AppInitBasicSetup(args, node.exit_status)) {
216 // InitError will have been called with detailed error, which ends
217 // up on console
218 return false;
219 }
220 if (!AppInitParameterInteraction(config, args)) {
221 // InitError will have been called with detailed error, which ends
222 // up on console
223 return false;
224 }
225
226 node.kernel = std::make_unique<kernel::Context>();
227 if (!AppInitSanityChecks(*node.kernel)) {
228 // InitError will have been called with detailed error, which ends
229 // up on console
230 return false;
231 }
232
233 if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) ||
234 args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
235#if HAVE_DECL_FORK
236 tfm::format(std::cout, PACKAGE_NAME " starting\n");
237
238 // Daemonize
239 // don't chdir (1), do close FDs (0)
240 switch (fork_daemon(1, 0, daemon_ep)) {
241 case 0:
242 // Child: continue.
243 // If -daemonwait is not enabled, immediately send a success
244 // token the parent.
245 if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
246 daemon_ep.TokenWrite(1);
247 daemon_ep.Close();
248 }
249 break;
250 case -1:
251 // Error happened.
253 "fork_daemon() failed: %s", SysErrorString(errno))));
254 default: {
255 // Parent: wait and exit.
256 int token = daemon_ep.TokenRead();
257 if (token) {
258 // Success
259 exit(EXIT_SUCCESS);
260 } else {
261 // fRet = false or token read error (premature exit).
262 tfm::format(std::cerr, "Error during initialization - "
263 "check debug.log for details\n");
264 exit(EXIT_FAILURE);
265 }
266 }
267 }
268#else
269 return InitError(Untranslated(
270 "-daemon is not supported on this operating system"));
271#endif // HAVE_DECL_FORK
272 }
273
274 // Lock data directory after daemonization
276 // If locking the data directory failed, exit immediately
277 return false;
278 }
279 fRet = AppInitInterfaces(node) &&
280 AppInitMain(config, rpcServer, httpRPCRequestProcessor, node);
281 } catch (const std::exception &e) {
282 PrintExceptionContinue(&e, "AppInit()");
283 } catch (...) {
284 PrintExceptionContinue(nullptr, "AppInit()");
285 }
286
287#if HAVE_DECL_FORK
288 if (daemon_ep.IsOpen()) {
289 // Signal initialization status to parent, then close pipe.
290 daemon_ep.TokenWrite(fRet);
291 daemon_ep.Close();
292 }
293#endif
294 if (fRet) {
296 } else {
297 node.exit_status = EXIT_FAILURE;
298 }
300 Shutdown(node);
301
302 return fRet;
303}
304
305int main(int argc, char *argv[]) {
306#ifdef WIN32
307 common::WinCmdLineArgs winArgs;
308 std::tie(argc, argv) = winArgs.get();
309#endif
311
313
314 // Connect bitcoind signal handlers
315 noui_connect();
316
317 return (AppInit(node, argc, argv) ? node.exit_status.load() : EXIT_FAILURE);
318}
bool HelpRequested(const ArgsManager &args)
Definition: args.cpp:701
ArgsManager gArgs
Definition: args.cpp:40
bool IsSwitchChar(char c)
Definition: args.h:50
int main(int argc, char *argv[])
Definition: bitcoind.cpp:305
static bool AppInit(NodeContext &node, int argc, char *argv[])
Definition: bitcoind.cpp:131
const std::function< std::string(const char *)> G_TRANSLATION_FUN
Translate string to current locale using Qt.
Definition: bitcoind.cpp:37
#define Assert(val)
Identity function.
Definition: check.h:84
bool ParseParameters(int argc, const char *const argv[], std::string &error)
Definition: args.cpp:211
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:239
std::string GetHelpMessage() const
Get the help string.
Definition: args.cpp:622
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: args.cpp:372
bool SoftSetBoolArg(const std::string &strArg, bool fValue)
Set a boolean argument if it doesn't already have a value.
Definition: args.cpp:558
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:525
Definition: config.h:19
Class for registering and managing all RPC calls.
Definition: server.h:40
One end of a token pipe.
Definition: tokenpipe.h:14
bool IsOpen()
Return whether endpoint is open.
Definition: tokenpipe.h:54
int TokenWrite(uint8_t token)
Write token to endpoint.
Definition: tokenpipe.cpp:32
void Close()
Explicit close function.
Definition: tokenpipe.cpp:68
int TokenRead()
Read token from endpoint.
Definition: tokenpipe.cpp:49
static std::optional< TokenPipe > Make()
Create a new pipe.
Definition: tokenpipe.cpp:75
std::string FormatFullVersion()
std::string LicenseInfo()
Returns licensing information (for -version)
const Config & GetConfig()
Definition: config.cpp:40
void PrintExceptionContinue(const std::exception *pex, std::string_view thread_name)
Definition: exception.cpp:38
void Interrupt(NodeContext &node)
Interrupt threads.
Definition: init.cpp:201
void InitLogging(const ArgsManager &args)
Initialize global loggers.
Definition: init.cpp:1761
bool AppInitLockDataDirectory()
Lock bitcoin data directory.
Definition: init.cpp:2149
void SetupServerArgs(NodeContext &node)
Register all arguments with the ArgsManager.
Definition: init.cpp:425
void Shutdown(NodeContext &node)
Definition: init.cpp:225
bool AppInitBasicSetup(const ArgsManager &args, std::atomic< int > &exit_status)
Initialize bitcoin: Basic context setup.
Definition: init.cpp:1788
bool AppInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor, NodeContext &node, interfaces::BlockAndHeaderTipInfo *tip_info)
Bitcoin main initialization.
Definition: init.cpp:2171
bool AppInitInterfaces(NodeContext &node)
Initialize node and wallet interface pointers.
Definition: init.cpp:2161
void InitParameterInteraction(ArgsManager &args)
Parameter interaction: change current parameters depending on various rules.
Definition: init.cpp:1627
bool AppInitParameterInteraction(Config &config, const ArgsManager &args)
Initialization: parameter interaction.
Definition: init.cpp:1830
bool AppInitSanityChecks(const kernel::Context &kernel)
Initialization sanity checks.
Definition: init.cpp:2133
static constexpr bool DEFAULT_DAEMON
Default value for -daemon option.
Definition: init.h:16
static constexpr bool DEFAULT_DAEMONWAIT
Default value for -daemonwait option.
Definition: init.h:18
std::optional< ConfigError > InitConfig(ArgsManager &args, SettingsAbortFn settings_abort_fn)
Read config files, and create datadir and settings.json if they don't exist.
Definition: init.cpp:19
Definition: init.h:31
void format(std::ostream &out, const char *fmt, const Args &...args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1112
void ThreadSetInternalName(std::string &&)
Set the internal (in-memory) name of the current thread only.
Definition: threadnames.cpp:53
void noui_connect()
Connect all bitcoind signal handlers.
Definition: noui.cpp:59
void WaitForShutdown()
Wait for StartShutdown to be called in any thread.
Definition: shutdown.cpp:33
NodeContext struct containing references to chain state and connection state.
Definition: context.h:48
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:14
void SetupEnvironment()
Definition: system.cpp:71
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
bool InitError(const bilingual_str &str)
Show error message.
std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
Format a paragraph of text to a fixed width, adding spaces for indentation to any added line.