Bitcoin ABC 0.30.5
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/system.h>
14#include <compat.h>
15#include <config.h>
16#include <httprpc.h>
17#include <init.h>
18#include <interfaces/chain.h>
19#include <node/context.h>
20#include <node/ui_interface.h>
21#include <noui.h>
22#include <shutdown.h>
23#include <util/check.h>
24#include <util/exception.h>
25#include <util/strencodings.h>
26#include <util/syserror.h>
27#include <util/threadnames.h>
28#include <util/tokenpipe.h>
29#include <util/translation.h>
30
31#include <any>
32#include <functional>
33
35
36const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr;
37
38#if HAVE_DECL_FORK
39
53int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd &endpoint) {
54 // communication pipe with child process
55 std::optional<TokenPipe> umbilical = TokenPipe::Make();
56 if (!umbilical) {
57 // pipe or pipe2 failed.
58 return -1;
59 }
60
61 int pid = fork();
62 if (pid < 0) {
63 // fork failed.
64 return -1;
65 }
66 if (pid != 0) {
67 // Parent process gets read end, closes write end.
68 endpoint = umbilical->TakeReadEnd();
69 umbilical->TakeWriteEnd().Close();
70
71 int status = endpoint.TokenRead();
72 // Something went wrong while setting up child process.
73 if (status != 0) {
74 endpoint.Close();
75 return -1;
76 }
77
78 return pid;
79 }
80 // Child process gets write end, closes read end.
81 endpoint = umbilical->TakeWriteEnd();
82 umbilical->TakeReadEnd().Close();
83
84#if HAVE_DECL_SETSID
85 if (setsid() < 0) {
86 // setsid failed.
87 exit(1);
88 }
89#endif
90
91 if (!nochdir) {
92 if (chdir("/") != 0) {
93 // chdir failed.
94 exit(1);
95 }
96 }
97 if (!noclose) {
98 // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach
99 // from terminal.
100 int fd = open("/dev/null", O_RDWR);
101 if (fd >= 0) {
102 bool err = dup2(fd, STDIN_FILENO) < 0 ||
103 dup2(fd, STDOUT_FILENO) < 0 ||
104 dup2(fd, STDERR_FILENO) < 0;
105 // Don't close if fd<=2 to try to handle the case where the program
106 // was invoked without any file descriptors open.
107 if (fd > 2) {
108 close(fd);
109 }
110 if (err) {
111 // dup2 failed.
112 exit(1);
113 }
114 } else {
115 // open /dev/null failed.
116 exit(1);
117 }
118 }
119 // Success
120 endpoint.TokenWrite(0);
121 return 0;
122}
123
124#endif
125
127//
128// Start
129//
130static bool AppInit(int argc, char *argv[]) {
131 // FIXME: Ideally, we'd like to build the config here, but that's currently
132 // not possible as the whole application has too many global state. However,
133 // this is a first step.
134 auto &config = const_cast<Config &>(GetConfig());
135
136 RPCServer rpcServer;
137
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\n", 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 (!CheckDataDirOption(args)) {
186 return InitError(Untranslated(
187 strprintf("Specified data directory \"%s\" does not exist.\n",
188 args.GetArg("-datadir", ""))));
189 }
190 if (!args.ReadConfigFiles(error, true)) {
191 return InitError(Untranslated(
192 strprintf("Error reading configuration file: %s\n", error)));
193 }
194 // Check for -chain, -testnet or -regtest parameter (Params() calls are
195 // only valid after this clause)
196 try {
198 } catch (const std::exception &e) {
199 return InitError(Untranslated(strprintf("%s\n", e.what())));
200 }
201
202 // Make sure we create the net-specific data directory early on: if it
203 // is new, this has a side effect of also creating
204 // <datadir>/<net>/wallets/.
205 //
206 // TODO: this should be removed once gArgs.GetDataDirNet() no longer
207 // creates the wallets/ subdirectory. See more info at:
208 // https://reviews.bitcoinabc.org/D3312
210
211 // Error out when loose non-argument tokens are encountered on command
212 // line
213 for (int i = 1; i < argc; i++) {
214 if (!IsSwitchChar(argv[i][0])) {
215 return InitError(Untranslated(
216 strprintf("Command line contains unexpected token '%s', "
217 "see bitcoind -h for a list of options.\n",
218 argv[i])));
219 }
220 }
221
222 if (!args.InitSettings(error)) {
224 return false;
225 }
226
227 // -server defaults to true for bitcoind but not for the GUI so do this
228 // here
229 args.SoftSetBoolArg("-server", true);
230 // Set this early so that parameter interactions go to console
231 InitLogging(args);
233 if (!AppInitBasicSetup(args)) {
234 // InitError will have been called with detailed error, which ends
235 // up on console
236 return false;
237 }
238 if (!AppInitParameterInteraction(config, args)) {
239 // InitError will have been called with detailed error, which ends
240 // up on console
241 return false;
242 }
243 if (!AppInitSanityChecks()) {
244 // InitError will have been called with detailed error, which ends
245 // up on console
246 return false;
247 }
248 if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) ||
249 args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
250#if HAVE_DECL_FORK
251 tfm::format(std::cout, PACKAGE_NAME " starting\n");
252
253 // Daemonize
254 // don't chdir (1), do close FDs (0)
255 switch (fork_daemon(1, 0, daemon_ep)) {
256 case 0:
257 // Child: continue.
258 // If -daemonwait is not enabled, immediately send a success
259 // token the parent.
260 if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
261 daemon_ep.TokenWrite(1);
262 daemon_ep.Close();
263 }
264 break;
265 case -1:
266 // Error happened.
268 "fork_daemon() failed: %s\n", SysErrorString(errno))));
269 default: {
270 // Parent: wait and exit.
271 int token = daemon_ep.TokenRead();
272 if (token) {
273 // Success
274 exit(EXIT_SUCCESS);
275 } else {
276 // fRet = false or token read error (premature exit).
277 tfm::format(std::cerr, "Error during initialization - "
278 "check debug.log for details\n");
279 exit(EXIT_FAILURE);
280 }
281 }
282 }
283#else
284 return InitError(Untranslated(
285 "-daemon is not supported on this operating system\n"));
286#endif // HAVE_DECL_FORK
287 }
288
289 // Lock data directory after daemonization
291 // If locking the data directory failed, exit immediately
292 return false;
293 }
294 fRet = AppInitInterfaces(node) &&
295 AppInitMain(config, rpcServer, httpRPCRequestProcessor, node);
296 } catch (const std::exception &e) {
297 PrintExceptionContinue(&e, "AppInit()");
298 } catch (...) {
299 PrintExceptionContinue(nullptr, "AppInit()");
300 }
301
302#if HAVE_DECL_FORK
303 if (daemon_ep.IsOpen()) {
304 // Signal initialization status to parent, then close pipe.
305 daemon_ep.TokenWrite(fRet);
306 daemon_ep.Close();
307 }
308#endif
309 if (fRet) {
311 }
313 Shutdown(node);
314
315 return fRet;
316}
317
318int main(int argc, char *argv[]) {
319#ifdef WIN32
320 common::WinCmdLineArgs winArgs;
321 std::tie(argc, argv) = winArgs.get();
322#endif
324
325 // Connect bitcoind signal handlers
326 noui_connect();
327
328 return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
329}
bool HelpRequested(const ArgsManager &args)
Definition: args.cpp:732
bool CheckDataDirOption(const ArgsManager &args)
Definition: args.cpp:784
ArgsManager gArgs
Definition: args.cpp:38
bool IsSwitchChar(char c)
Definition: args.h:47
int main(int argc, char *argv[])
Definition: bitcoind.cpp:318
static bool AppInit(int argc, char *argv[])
Definition: bitcoind.cpp:130
const std::function< std::string(const char *)> G_TRANSLATION_FUN
Translate string to current locale using Qt.
Definition: bitcoind.cpp:36
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
Definition: chainparams.cpp:51
#define Assert(val)
Identity function.
Definition: check.h:84
bool InitSettings(std::string &error)
Read and update settings file with saved settings.
Definition: args.cpp:385
bool ParseParameters(int argc, const char *const argv[], std::string &error)
Definition: args.cpp:201
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: args.h:215
std::string GetHelpMessage() const
Get the help string.
Definition: args.cpp:653
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: args.cpp:381
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:494
bool SoftSetBoolArg(const std::string &strArg, bool fValue)
Set a boolean argument if it doesn't already have a value.
Definition: args.cpp:589
bool ReadConfigFiles(std::string &error, bool ignore_invalid_keys=false)
Definition: configfile.cpp:132
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:556
std::string GetChainName() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: args.cpp:793
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, const char *pszThread)
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:1655
bool AppInitLockDataDirectory()
Lock bitcoin data directory.
Definition: init.cpp:2052
void SetupServerArgs(NodeContext &node)
Register all arguments with the ArgsManager.
Definition: init.cpp:425
void Shutdown(NodeContext &node)
Definition: init.cpp:225
bool AppInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor, NodeContext &node, interfaces::BlockAndHeaderTipInfo *tip_info)
Bitcoin main initialization.
Definition: init.cpp:2074
bool AppInitBasicSetup(const ArgsManager &args)
Initialize bitcoin: Basic context setup.
Definition: init.cpp:1682
bool AppInitSanityChecks()
Initialization sanity checks: ecc init, sanity checks, dir lock.
Definition: init.cpp:2034
bool AppInitInterfaces(NodeContext &node)
Initialize node and wallet interface pointers.
Definition: init.cpp:2064
void InitParameterInteraction(ArgsManager &args)
Parameter interaction: change current parameters depending on various rules.
Definition: init.cpp:1521
bool AppInitParameterInteraction(Config &config, const ArgsManager &args)
Initialization: parameter interaction.
Definition: init.cpp:1729
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
bool error(const char *fmt, const Args &...args)
Definition: logging.h:226
Definition: init.h:28
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:89
NodeContext struct containing references to chain state and connection state.
Definition: context.h:43
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:14
void SetupEnvironment()
Definition: system.cpp:70
#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.