Bitcoin ABC  0.28.12
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 <compat.h>
13 #include <config.h>
14 #include <httprpc.h>
15 #include <init.h>
16 #include <interfaces/chain.h>
17 #include <node/context.h>
18 #include <node/ui_interface.h>
19 #include <noui.h>
20 #include <shutdown.h>
21 #include <util/check.h>
22 #include <util/strencodings.h>
23 #include <util/system.h>
24 #include <util/threadnames.h>
25 #include <util/tokenpipe.h>
26 #include <util/translation.h>
27 
28 #include <any>
29 #include <functional>
30 
31 using node::NodeContext;
32 
33 const std::function<std::string(const char *)> G_TRANSLATION_FUN = nullptr;
34 
35 #if HAVE_DECL_FORK
36 
50 int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd &endpoint) {
51  // communication pipe with child process
52  std::optional<TokenPipe> umbilical = TokenPipe::Make();
53  if (!umbilical) {
54  // pipe or pipe2 failed.
55  return -1;
56  }
57 
58  int pid = fork();
59  if (pid < 0) {
60  // fork failed.
61  return -1;
62  }
63  if (pid != 0) {
64  // Parent process gets read end, closes write end.
65  endpoint = umbilical->TakeReadEnd();
66  umbilical->TakeWriteEnd().Close();
67 
68  int status = endpoint.TokenRead();
69  // Something went wrong while setting up child process.
70  if (status != 0) {
71  endpoint.Close();
72  return -1;
73  }
74 
75  return pid;
76  }
77  // Child process gets write end, closes read end.
78  endpoint = umbilical->TakeWriteEnd();
79  umbilical->TakeReadEnd().Close();
80 
81 #if HAVE_DECL_SETSID
82  if (setsid() < 0) {
83  // setsid failed.
84  exit(1);
85  }
86 #endif
87 
88  if (!nochdir) {
89  if (chdir("/") != 0) {
90  // chdir failed.
91  exit(1);
92  }
93  }
94  if (!noclose) {
95  // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach
96  // from terminal.
97  int fd = open("/dev/null", O_RDWR);
98  if (fd >= 0) {
99  bool err = dup2(fd, STDIN_FILENO) < 0 ||
100  dup2(fd, STDOUT_FILENO) < 0 ||
101  dup2(fd, STDERR_FILENO) < 0;
102  // Don't close if fd<=2 to try to handle the case where the program
103  // was invoked without any file descriptors open.
104  if (fd > 2) {
105  close(fd);
106  }
107  if (err) {
108  // dup2 failed.
109  exit(1);
110  }
111  } else {
112  // open /dev/null failed.
113  exit(1);
114  }
115  }
116  // Success
117  endpoint.TokenWrite(0);
118  return 0;
119 }
120 
121 #endif
122 
124 //
125 // Start
126 //
127 static bool AppInit(int argc, char *argv[]) {
128  // FIXME: Ideally, we'd like to build the config here, but that's currently
129  // not possible as the whole application has too many global state. However,
130  // this is a first step.
131  auto &config = const_cast<Config &>(GetConfig());
132 
133  RPCServer rpcServer;
134 
136  std::any context{&node};
137 
138  HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer, context);
139 
140  bool fRet = false;
141 
143 
144  // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's
145  // main()
147  ArgsManager &args = *Assert(node.args);
148  std::string error;
149  if (!args.ParseParameters(argc, argv, error)) {
150  return InitError(Untranslated(
151  strprintf("Error parsing command line arguments: %s\n", error)));
152  }
153 
154  // Process help and version before taking care about datadir
155  if (HelpRequested(args) || args.IsArgSet("-version")) {
156  std::string strUsage =
157  PACKAGE_NAME " version " + FormatFullVersion() + "\n";
158 
159  if (args.IsArgSet("-version")) {
160  strUsage += FormatParagraph(LicenseInfo()) + "\n";
161  } else {
162  strUsage += "\nUsage: bitcoind [options] "
163  "Start " PACKAGE_NAME "\n";
164  strUsage += "\n" + args.GetHelpMessage();
165  }
166 
167  tfm::format(std::cout, "%s", strUsage);
168  return true;
169  }
170 
171 #if HAVE_DECL_FORK
172  // Communication with parent after daemonizing. This is used for signalling
173  // in the following ways:
174  // - a boolean token is sent when the initialization process (all the Init*
175  // functions) have finished to indicate that the parent process can quit,
176  // and whether it was successful/unsuccessful.
177  // - an unexpected shutdown of the child process creates an unexpected end
178  // of stream at the parent end, which is interpreted as failure to start.
179  TokenPipeEnd daemon_ep;
180 #endif
181  try {
182  if (!CheckDataDirOption()) {
183  return InitError(Untranslated(
184  strprintf("Specified data directory \"%s\" does not exist.\n",
185  args.GetArg("-datadir", ""))));
186  }
187  if (!args.ReadConfigFiles(error, true)) {
188  return InitError(Untranslated(
189  strprintf("Error reading configuration file: %s\n", error)));
190  }
191  // Check for -chain, -testnet or -regtest parameter (Params() calls are
192  // only valid after this clause)
193  try {
194  SelectParams(args.GetChainName());
195  } catch (const std::exception &e) {
196  return InitError(Untranslated(strprintf("%s\n", e.what())));
197  }
198 
199  // Make sure we create the net-specific data directory early on: if it
200  // is new, this has a side effect of also creating
201  // <datadir>/<net>/wallets/.
202  //
203  // TODO: this should be removed once gArgs.GetDataDirNet() no longer
204  // creates the wallets/ subdirectory. See more info at:
205  // https://reviews.bitcoinabc.org/D3312
207 
208  // Error out when loose non-argument tokens are encountered on command
209  // line
210  for (int i = 1; i < argc; i++) {
211  if (!IsSwitchChar(argv[i][0])) {
212  return InitError(Untranslated(
213  strprintf("Command line contains unexpected token '%s', "
214  "see bitcoind -h for a list of options.\n",
215  argv[i])));
216  }
217  }
218 
219  if (!args.InitSettings(error)) {
221  return false;
222  }
223 
224  // -server defaults to true for bitcoind but not for the GUI so do this
225  // here
226  args.SoftSetBoolArg("-server", true);
227  // Set this early so that parameter interactions go to console
228  InitLogging(args);
230  if (!AppInitBasicSetup(args)) {
231  // InitError will have been called with detailed error, which ends
232  // up on console
233  return false;
234  }
235  if (!AppInitParameterInteraction(config, args)) {
236  // InitError will have been called with detailed error, which ends
237  // up on console
238  return false;
239  }
240  if (!AppInitSanityChecks()) {
241  // InitError will have been called with detailed error, which ends
242  // up on console
243  return false;
244  }
245  if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) ||
246  args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
247 #if HAVE_DECL_FORK
248  tfm::format(std::cout, PACKAGE_NAME " starting\n");
249 
250  // Daemonize
251  // don't chdir (1), do close FDs (0)
252  switch (fork_daemon(1, 0, daemon_ep)) {
253  case 0:
254  // Child: continue.
255  // If -daemonwait is not enabled, immediately send a success
256  // token the parent.
257  if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
258  daemon_ep.TokenWrite(1);
259  daemon_ep.Close();
260  }
261  break;
262  case -1:
263  // Error happened.
265  "fork_daemon() failed: %s\n", strerror(errno))));
266  default: {
267  // Parent: wait and exit.
268  int token = daemon_ep.TokenRead();
269  if (token) {
270  // Success
271  exit(EXIT_SUCCESS);
272  } else {
273  // fRet = false or token read error (premature exit).
274  tfm::format(std::cerr, "Error during initialization - "
275  "check debug.log for details\n");
276  exit(EXIT_FAILURE);
277  }
278  }
279  }
280 #else
281  return InitError(Untranslated(
282  "-daemon is not supported on this operating system\n"));
283 #endif // HAVE_DECL_FORK
284  }
285 
286  // Lock data directory after daemonization
287  if (!AppInitLockDataDirectory()) {
288  // If locking the data directory failed, exit immediately
289  return false;
290  }
291  fRet = AppInitInterfaces(node) &&
292  AppInitMain(config, rpcServer, httpRPCRequestProcessor, node);
293  } catch (const std::exception &e) {
294  PrintExceptionContinue(&e, "AppInit()");
295  } catch (...) {
296  PrintExceptionContinue(nullptr, "AppInit()");
297  }
298 
299 #if HAVE_DECL_FORK
300  if (daemon_ep.IsOpen()) {
301  // Signal initialization status to parent, then close pipe.
302  daemon_ep.TokenWrite(fRet);
303  daemon_ep.Close();
304  }
305 #endif
306  if (fRet) {
307  WaitForShutdown();
308  }
309  Interrupt(node);
310  Shutdown(node);
311 
312  return fRet;
313 }
314 
315 int main(int argc, char *argv[]) {
316 #ifdef WIN32
317  util::WinCmdLineArgs winArgs;
318  std::tie(argc, argv) = winArgs.get();
319 #endif
321 
322  // Connect bitcoind signal handlers
323  noui_connect();
324 
325  return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
326 }
int main(int argc, char *argv[])
Definition: bitcoind.cpp:315
static bool AppInit(int argc, char *argv[])
Definition: bitcoind.cpp:127
const std::function< std::string(const char *)> G_TRANSLATION_FUN
Translate string to current locale using Qt.
Definition: bitcoind.cpp:33
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
#define Assert(val)
Identity function.
Definition: check.h:84
bool InitSettings(std::string &error)
Read and update settings file with saved settings.
Definition: system.cpp:494
bool ParseParameters(int argc, const char *const argv[], std::string &error)
Definition: system.cpp:322
std::string GetHelpMessage() const
Get the help string.
Definition: system.cpp:762
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: system.cpp:490
const fs::path & GetDataDirNet() const
Get data directory path with appended network identifier.
Definition: system.h:268
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:603
bool SoftSetBoolArg(const std::string &strArg, bool fValue)
Set a boolean argument if it doesn't already have a value.
Definition: system.cpp:698
bool ReadConfigFiles(std::string &error, bool ignore_invalid_keys=false)
Definition: system.cpp:1021
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: system.cpp:665
std::string GetChainName() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: system.cpp:1123
Definition: config.h:17
Class for registering and managing all RPC calls.
Definition: server.h:39
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()
const Config & GetConfig()
Definition: config.cpp:34
void Interrupt(NodeContext &node)
Interrupt threads.
Definition: init.cpp:192
void InitLogging(const ArgsManager &args)
Initialize global loggers.
Definition: init.cpp:1639
bool AppInitLockDataDirectory()
Lock bitcoin data directory.
Definition: init.cpp:2067
void SetupServerArgs(NodeContext &node)
Register all arguments with the ArgsManager.
Definition: init.cpp:412
void Shutdown(NodeContext &node)
Definition: init.cpp:216
bool AppInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor, NodeContext &node, interfaces::BlockAndHeaderTipInfo *tip_info)
Bitcoin main initialization.
Definition: init.cpp:2089
bool AppInitBasicSetup(const ArgsManager &args)
Initialize bitcoin: Basic context setup.
Definition: init.cpp:1666
bool AppInitSanityChecks()
Initialization sanity checks: ecc init, sanity checks, dir lock.
Definition: init.cpp:2049
bool AppInitInterfaces(NodeContext &node)
Initialize node and wallet interface pointers.
Definition: init.cpp:2079
void InitParameterInteraction(ArgsManager &args)
Parameter interaction: change current parameters depending on various rules.
Definition: init.cpp:1505
std::string LicenseInfo()
Returns licensing information (for -version)
Definition: init.cpp:1424
bool AppInitParameterInteraction(Config &config, const ArgsManager &args)
Initialization: parameter interaction.
Definition: init.cpp:1713
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
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
std::string FormatParagraph(const std::string &in, size_t width, size_t indent)
Format a paragraph of text to a fixed width, adding spaces for indentation to any added line.
NodeContext struct containing references to chain state and connection state.
Definition: context.h:38
bool HelpRequested(const ArgsManager &args)
Definition: system.cpp:841
bool CheckDataDirOption()
Definition: system.cpp:917
ArgsManager gArgs
Definition: system.cpp:80
void SetupEnvironment()
Definition: system.cpp:1398
void PrintExceptionContinue(const std::exception *pex, const char *pszThread)
Definition: system.cpp:886
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
bool IsSwitchChar(char c)
Definition: system.h:108
#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.