/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 | } |