Coverage Report

Created: 2026-04-29 19:21

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