Coverage Report

Created: 2025-06-10 13:21

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