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