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