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