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