bitcoind.cpp raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-present 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  #include <bitcoin-build-config.h> // IWYU pragma: keep
   7  
   8  #include <chainparams.h>
   9  #include <clientversion.h>
  10  #include <common/args.h>
  11  #include <common/init.h>
  12  #include <common/license_info.h>
  13  #include <common/system.h>
  14  #include <compat/compat.h>
  15  #include <init.h>
  16  #include <interfaces/chain.h>
  17  #include <interfaces/init.h>
  18  #include <kernel/context.h>
  19  #include <logging.h>
  20  #include <node/context.h>
  21  #include <node/interface_ui.h>
  22  #include <node/warnings.h>
  23  #include <noui.h>
  24  #include <util/check.h>
  25  #include <util/exception.h>
  26  #include <util/signalinterrupt.h>
  27  #include <util/strencodings.h>
  28  #include <util/syserror.h>
  29  #include <util/threadnames.h>
  30  #include <util/tokenpipe.h>
  31  #include <util/translation.h>
  32  
  33  #include <any>
  34  #include <functional>
  35  #include <optional>
  36  
  37  using node::NodeContext;
  38  
  39  const TranslateFn G_TRANSLATION_FUN{nullptr};
  40  
  41  #if HAVE_DECL_FORK
  42  
  43  /** Custom implementation of daemon(). This implements the same order of operations as glibc.
  44   * Opens a pipe to the child process to be able to wait for an event to occur.
  45   *
  46   * @returns 0 if successful, and in child process.
  47   *          >0 if successful, and in parent process.
  48   *          -1 in case of error (in parent process).
  49   *
  50   *          In case of success, endpoint will be one end of a pipe from the child to parent process,
  51   *          which can be used with TokenWrite (in the child) or TokenRead (in the parent).
  52   */
  53  int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
  54  {
  55      // communication pipe with child process
  56      std::optional<TokenPipe> umbilical = TokenPipe::Make();
  57      if (!umbilical) {
  58          return -1; // pipe or pipe2 failed.
  59      }
  60  
  61      int pid = fork();
  62      if (pid < 0) {
  63          return -1; // fork failed.
  64      }
  65      if (pid != 0) {
  66          // Parent process gets read end, closes write end.
  67          endpoint = umbilical->TakeReadEnd();
  68          umbilical->TakeWriteEnd().Close();
  69  
  70          int status = endpoint.TokenRead();
  71          if (status != 0) { // Something went wrong while setting up child process.
  72              endpoint.Close();
  73              return -1;
  74          }
  75  
  76          return pid;
  77      }
  78      // Child process gets write end, closes read end.
  79      endpoint = umbilical->TakeWriteEnd();
  80      umbilical->TakeReadEnd().Close();
  81  
  82  #if HAVE_DECL_SETSID
  83      if (setsid() < 0) {
  84          exit(1); // setsid failed.
  85      }
  86  #endif
  87  
  88      if (!nochdir) {
  89          if (chdir("/") != 0) {
  90              exit(1); // chdir failed.
  91          }
  92      }
  93      if (!noclose) {
  94          // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach
  95          // from terminal.
  96          int fd = open("/dev/null", O_RDWR);
  97          if (fd >= 0) {
  98              bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0;
  99              // Don't close if fd<=2 to try to handle the case where the program was invoked without any file descriptors open.
 100              if (fd > 2) close(fd);
 101              if (err) {
 102                  exit(1); // dup2 failed.
 103              }
 104          } else {
 105              exit(1); // open /dev/null failed.
 106          }
 107      }
 108      endpoint.TokenWrite(0); // Success
 109      return 0;
 110  }
 111  
 112  #endif
 113  
 114  static bool ParseArgs(NodeContext& node, int argc, char* argv[])
 115  {
 116      ArgsManager& args{*Assert(node.args)};
 117      // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
 118      SetupServerArgs(args, node.init->canListenIpc());
 119      std::string error;
 120      if (!args.ParseParameters(argc, argv, error)) {
 121          return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
 122      }
 123  
 124      if (auto error = common::InitConfig(args)) {
 125          return InitError(error->message, error->details);
 126      }
 127  
 128      // Error out when loose non-argument tokens are encountered on command line
 129      for (int i = 1; i < argc; i++) {
 130          if (!IsSwitchChar(argv[i][0])) {
 131              return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i])));
 132          }
 133      }
 134      return true;
 135  }
 136  
 137  static bool ProcessInitCommands(interfaces::Init& init, ArgsManager& args)
 138  {
 139      // Process help and version before taking care about datadir
 140      if (HelpRequested(args) || args.GetBoolArg("-version", false)) {
 141          std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion();
 142          if (const char* exe_name{init.exeName()}) {
 143              strUsage += " ";
 144              strUsage += exe_name;
 145          }
 146          strUsage += "\n";
 147  
 148          if (args.GetBoolArg("-version", false)) {
 149              strUsage += FormatParagraph(LicenseInfo());
 150          } else {
 151              strUsage += "\n"
 152                  "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"
 153                  "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"
 154                  "There is an optional wallet component which provides transaction services.\n\n"
 155                  "It can be used in a headless environment or as part of a server setup.\n"
 156                  "\n"
 157                  "Usage: bitcoind [options]\n"
 158                  "\n";
 159              strUsage += args.GetHelpMessage();
 160          }
 161  
 162          tfm::format(std::cout, "%s", strUsage);
 163          return true;
 164      }
 165  
 166      return false;
 167  }
 168  
 169  static bool AppInit(NodeContext& node)
 170  {
 171      bool fRet = false;
 172      ArgsManager& args = *Assert(node.args);
 173  
 174  #if HAVE_DECL_FORK
 175      // Communication with parent after daemonizing. This is used for signalling in the following ways:
 176      // - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate
 177      // that the parent process can quit, and whether it was successful/unsuccessful.
 178      // - an unexpected shutdown of the child process creates an unexpected end of stream at the parent
 179      // end, which is interpreted as failure to start.
 180      TokenPipeEnd daemon_ep;
 181  #endif
 182      std::any context{&node};
 183      try
 184      {
 185          // -server defaults to true for bitcoind but not for the GUI so do this here
 186          args.SoftSetBoolArg("-server", true);
 187          // Set this early so that parameter interactions go to console
 188          InitLogging(args);
 189          InitParameterInteraction(args);
 190          if (!AppInitBasicSetup(args, node.exit_status)) {
 191              // InitError will have been called with detailed error, which ends up on console
 192              return false;
 193          }
 194          if (!AppInitParameterInteraction(args)) {
 195              // InitError will have been called with detailed error, which ends up on console
 196              return false;
 197          }
 198  
 199          node.warnings = std::make_unique<node::Warnings>();
 200  
 201          node.kernel = std::make_unique<kernel::Context>();
 202          node.ecc_context = std::make_unique<ECC_Context>();
 203          if (!AppInitSanityChecks(*node.kernel))
 204          {
 205              // InitError will have been called with detailed error, which ends up on console
 206              return false;
 207          }
 208  
 209          if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
 210  #if HAVE_DECL_FORK
 211              tfm::format(std::cout, CLIENT_NAME " starting\n");
 212  
 213              // Daemonize
 214              switch (fork_daemon(1, 0, daemon_ep)) { // don't chdir (1), do close FDs (0)
 215              case 0: // Child: continue.
 216                  // If -daemonwait is not enabled, immediately send a success token the parent.
 217                  if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
 218                      daemon_ep.TokenWrite(1);
 219                      daemon_ep.Close();
 220                  }
 221                  break;
 222              case -1: // Error happened.
 223                  return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno))));
 224              default: { // Parent: wait and exit.
 225                  int token = daemon_ep.TokenRead();
 226                  if (token) { // Success
 227                      exit(EXIT_SUCCESS);
 228                  } else { // fRet = false or token read error (premature exit).
 229                      tfm::format(std::cerr, "Error during initialization - check %s for details\n", fs::PathToString(LogInstance().m_file_path.filename()));
 230                      exit(EXIT_FAILURE);
 231                  }
 232              }
 233              }
 234  #else
 235              return InitError(Untranslated("-daemon is not supported on this operating system"));
 236  #endif // HAVE_DECL_FORK
 237          }
 238          // Lock critical directories after daemonization
 239          if (!AppInitLockDirectories())
 240          {
 241              // If locking a directory failed, exit immediately
 242              return false;
 243          }
 244          fRet = AppInitInterfaces(node) && AppInitMain(node);
 245      }
 246      catch (const std::exception& e) {
 247          PrintExceptionContinue(&e, "AppInit()");
 248      } catch (...) {
 249          PrintExceptionContinue(nullptr, "AppInit()");
 250      }
 251  
 252  #if HAVE_DECL_FORK
 253      if (daemon_ep.IsOpen()) {
 254          // Signal initialization status to parent, then close pipe.
 255          daemon_ep.TokenWrite(fRet);
 256          daemon_ep.Close();
 257      }
 258  #endif
 259      return fRet;
 260  }
 261  
 262  /// \anchor main
 263  MAIN_FUNCTION
 264  {
 265      NodeContext node;
 266      int exit_status;
 267      std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
 268      if (!init) {
 269          return exit_status;
 270      }
 271  
 272      SetupEnvironment();
 273  
 274      // Connect bitcoind signal handlers
 275      noui_connect();
 276  
 277      util::ThreadSetInternalName("init");
 278  
 279      // Interpret command line arguments
 280      ArgsManager& args = *Assert(node.args);
 281      if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
 282      // Process early info return commands such as -help or -version
 283      if (ProcessInitCommands(*init, args)) return EXIT_SUCCESS;
 284  
 285      // Start application
 286      if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
 287          node.exit_status = EXIT_FAILURE;
 288      }
 289      Interrupt(node);
 290      Shutdown(node);
 291  
 292      return node.exit_status;
 293  }
 294