/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 <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) { |
58 | 0 | return -1; // pipe or pipe2 failed. |
59 | 0 | } |
60 | | |
61 | 0 | int pid = fork(); |
62 | 0 | if (pid < 0) { |
63 | 0 | return -1; // fork failed. |
64 | 0 | } |
65 | 0 | if (pid != 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. |
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) { |
84 | 0 | exit(1); // setsid failed. |
85 | 0 | } |
86 | 0 | #endif |
87 | | |
88 | 0 | if (!nochdir) { |
89 | 0 | if (chdir("/") != 0) { |
90 | 0 | exit(1); // chdir failed. |
91 | 0 | } |
92 | 0 | } |
93 | 0 | if (!noclose) { |
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) { |
98 | 0 | bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 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); |
101 | 0 | if (err) { |
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 | 1.19k | { |
116 | 1.19k | ArgsManager& args{*Assert(node.args)}; |
117 | | // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() |
118 | 1.19k | SetupServerArgs(args, node.init->canListenIpc()); |
119 | 1.19k | std::string error; |
120 | 1.19k | if (!args.ParseParameters(argc, argv, error)) { |
121 | 7 | return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error))); |
122 | 7 | } |
123 | | |
124 | 1.18k | if (auto error = common::InitConfig(args)) { |
125 | 27 | return InitError(error->message, error->details); |
126 | 27 | } |
127 | | |
128 | | // Error out when loose non-argument tokens are encountered on command line |
129 | 17.3k | for (int i = 1; i < argc; i++) { |
130 | 16.2k | if (!IsSwitchChar(argv[i][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 | 16.2k | } |
134 | 1.16k | return true; |
135 | 1.16k | } |
136 | | |
137 | | static bool ProcessInitCommands(interfaces::Init& init, ArgsManager& args) |
138 | 1.16k | { |
139 | | // Process help and version before taking care about datadir |
140 | 1.16k | if (HelpRequested(args) || args.GetBoolArg("-version", false)) { |
141 | 8 | std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion(); |
142 | 8 | if (const char* exe_name{init.exeName()}) { |
143 | 8 | strUsage += " "; |
144 | 8 | strUsage += exe_name; |
145 | 8 | } |
146 | 8 | strUsage += "\n"; |
147 | | |
148 | 8 | if (args.GetBoolArg("-version", false)) { |
149 | 7 | strUsage += FormatParagraph(LicenseInfo()); |
150 | 7 | } else { |
151 | 1 | strUsage += "\n" |
152 | 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" |
153 | 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" |
154 | 1 | "There is an optional wallet component which provides transaction services.\n\n" |
155 | 1 | "It can be used in a headless environment or as part of a server setup.\n" |
156 | 1 | "\n" |
157 | 1 | "Usage: bitcoind [options]\n" |
158 | 1 | "\n"; |
159 | 1 | strUsage += args.GetHelpMessage(); |
160 | 1 | } |
161 | | |
162 | 8 | tfm::format(std::cout, "%s", strUsage); |
163 | 8 | return true; |
164 | 8 | } |
165 | | |
166 | 1.15k | return false; |
167 | 1.16k | } |
168 | | |
169 | | static bool AppInit(NodeContext& node) |
170 | 1.15k | { |
171 | 1.15k | bool fRet = false; |
172 | 1.15k | ArgsManager& args = *Assert(node.args); |
173 | | |
174 | 1.15k | #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 | 1.15k | TokenPipeEnd daemon_ep; |
181 | 1.15k | #endif |
182 | 1.15k | std::any context{&node}; |
183 | 1.15k | try |
184 | 1.15k | { |
185 | | // -server defaults to true for bitcoind but not for the GUI so do this here |
186 | 1.15k | args.SoftSetBoolArg("-server", true); |
187 | | // Set this early so that parameter interactions go to console |
188 | 1.15k | InitLogging(args); |
189 | 1.15k | InitParameterInteraction(args); |
190 | 1.15k | if (!AppInitBasicSetup(args, node.exit_status)) { |
191 | | // InitError will have been called with detailed error, which ends up on console |
192 | 0 | return false; |
193 | 0 | } |
194 | 1.15k | if (!AppInitParameterInteraction(args)) { |
195 | | // InitError will have been called with detailed error, which ends up on console |
196 | 20 | return false; |
197 | 20 | } |
198 | | |
199 | 1.13k | node.warnings = std::make_unique<node::Warnings>(); |
200 | | |
201 | 1.13k | node.kernel = std::make_unique<kernel::Context>(); |
202 | 1.13k | node.ecc_context = std::make_unique<ECC_Context>(); |
203 | 1.13k | if (!AppInitSanityChecks(*node.kernel)) |
204 | 2 | { |
205 | | // InitError will have been called with detailed error, which ends up on console |
206 | 2 | return false; |
207 | 2 | } |
208 | | |
209 | 1.13k | if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) { |
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. |
216 | | // If -daemonwait is not enabled, immediately send a success token the parent. |
217 | 0 | if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) { |
218 | 0 | daemon_ep.TokenWrite(1); |
219 | 0 | daemon_ep.Close(); |
220 | 0 | } |
221 | 0 | break; |
222 | 0 | case -1: // Error happened. |
223 | 0 | return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno)))); |
224 | 0 | default: { // Parent: wait and exit. |
225 | 0 | int token = daemon_ep.TokenRead(); |
226 | 0 | if (token) { // Success |
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 | 1.13k | if (!AppInitLockDirectories()) |
240 | 0 | { |
241 | | // If locking a directory failed, exit immediately |
242 | 0 | return false; |
243 | 0 | } |
244 | 1.13k | fRet = AppInitInterfaces(node) && AppInitMain(node); |
245 | 1.13k | } |
246 | 1.15k | catch (const std::exception& e) { |
247 | 7 | PrintExceptionContinue(&e, "AppInit()"); |
248 | 7 | } catch (...) { |
249 | 0 | PrintExceptionContinue(nullptr, "AppInit()"); |
250 | 0 | } |
251 | | |
252 | 0 | #if HAVE_DECL_FORK |
253 | 1.13k | if (daemon_ep.IsOpen()) { |
254 | | // Signal initialization status to parent, then close pipe. |
255 | 0 | daemon_ep.TokenWrite(fRet); |
256 | 0 | daemon_ep.Close(); |
257 | 0 | } |
258 | 1.13k | #endif |
259 | 1.13k | return fRet; |
260 | 1.15k | } |
261 | | |
262 | | /// \anchor main |
263 | | MAIN_FUNCTION |
264 | 1.19k | { |
265 | 1.19k | NodeContext node; |
266 | 1.19k | int exit_status; |
267 | 1.19k | std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status); |
268 | 1.19k | if (!init) { |
269 | 0 | return exit_status; |
270 | 0 | } |
271 | | |
272 | 1.19k | SetupEnvironment(); |
273 | | |
274 | | // Connect bitcoind signal handlers |
275 | 1.19k | noui_connect(); |
276 | | |
277 | 1.19k | util::ThreadSetInternalName("init"); |
278 | | |
279 | | // Interpret command line arguments |
280 | 1.19k | ArgsManager& args = *Assert(node.args); |
281 | 1.19k | if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE; |
282 | | // Process early info return commands such as -help or -version |
283 | 1.16k | if (ProcessInitCommands(*init, args)) return EXIT_SUCCESS; |
284 | | |
285 | | // Start application |
286 | 1.15k | if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) { |
287 | 153 | node.exit_status = EXIT_FAILURE; |
288 | 153 | } |
289 | 1.15k | Interrupt(node); |
290 | 1.15k | Shutdown(node); |
291 | | |
292 | 1.15k | return node.exit_status; |
293 | 1.16k | } |