Branch data Line data Source code
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 : : #if defined(HAVE_CONFIG_H)
7 : : #include <config/bitcoin-config.h>
8 : : #endif
9 : :
10 : : #include <chainparams.h>
11 : : #include <clientversion.h>
12 : : #include <common/args.h>
13 : : #include <common/init.h>
14 : : #include <common/system.h>
15 : : #include <common/url.h>
16 : : #include <compat/compat.h>
17 : 0 : #include <init.h>
18 : 0 : #include <interfaces/chain.h>
19 : : #include <interfaces/init.h>
20 : : #include <node/context.h>
21 : : #include <node/interface_ui.h>
22 : : #include <noui.h>
23 : : #include <shutdown.h>
24 : : #include <util/check.h>
25 : : #include <util/exception.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 : 0 : const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
39 : : UrlDecodeFn* const URL_DECODE = urlDecode;
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 : 0 : int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
54 : : {
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 : : }
60 : :
61 : 0 : int pid = fork();
62 : 0 : if (pid < 0) {
63 : 0 : return -1; // fork failed.
64 : : }
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 : : }
78 : : // Child process gets write end, closes read end.
79 : 0 : endpoint = umbilical->TakeWriteEnd();
80 : 0 : umbilical->TakeReadEnd().Close();
81 : :
82 : : #if HAVE_DECL_SETSID
83 : 0 : if (setsid() < 0) {
84 : 0 : exit(1); // setsid failed.
85 : : }
86 : : #endif
87 : :
88 : 0 : if (!nochdir) {
89 : 0 : if (chdir("/") != 0) {
90 : 0 : exit(1); // chdir failed.
91 : : }
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 : : }
104 : 0 : } else {
105 : 0 : exit(1); // open /dev/null failed.
106 : : }
107 : 0 : }
108 : 0 : endpoint.TokenWrite(0); // Success
109 : 0 : return 0;
110 : 0 : }
111 : :
112 : : #endif
113 : :
114 : 0 : static bool ParseArgs(ArgsManager& args, int argc, char* argv[])
115 : : {
116 : : // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
117 : 0 : SetupServerArgs(args);
118 : 0 : std::string error;
119 : 0 : if (!args.ParseParameters(argc, argv, error)) {
120 : 0 : return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
121 : : }
122 : :
123 : 0 : if (auto error = common::InitConfig(args)) {
124 : 0 : return InitError(error->message, error->details);
125 : : }
126 : :
127 : : // Error out when loose non-argument tokens are encountered on command line
128 : 0 : for (int i = 1; i < argc; i++) {
129 : 0 : 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 : : }
132 : 0 : }
133 : 0 : return true;
134 : 0 : }
135 : :
136 : 0 : static bool ProcessInitCommands(ArgsManager& args)
137 : : {
138 : : // Process help and version before taking care about datadir
139 : 0 : if (HelpRequested(args) || args.IsArgSet("-version")) {
140 : 0 : std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
141 : :
142 : 0 : if (args.IsArgSet("-version")) {
143 : 0 : strUsage += FormatParagraph(LicenseInfo());
144 : 0 : } else {
145 : 0 : strUsage += "\nUsage: bitcoind [options] Start " PACKAGE_NAME "\n"
146 : : "\n";
147 : 0 : strUsage += args.GetHelpMessage();
148 : : }
149 : :
150 : 0 : tfm::format(std::cout, "%s", strUsage);
151 : 0 : return true;
152 : 0 : }
153 : :
154 : 0 : return false;
155 : 0 : }
156 : :
157 : 0 : static bool AppInit(NodeContext& node)
158 : : {
159 : 0 : bool fRet = false;
160 : 0 : ArgsManager& args = *Assert(node.args);
161 : :
162 : : #if HAVE_DECL_FORK
163 : : // Communication with parent after daemonizing. This is used for signalling in the following ways:
164 : : // - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate
165 : : // that the parent process can quit, and whether it was successful/unsuccessful.
166 : : // - an unexpected shutdown of the child process creates an unexpected end of stream at the parent
167 : : // end, which is interpreted as failure to start.
168 : 0 : TokenPipeEnd daemon_ep;
169 : : #endif
170 : 0 : std::any context{&node};
171 : : try
172 : : {
173 : : // -server defaults to true for bitcoind but not for the GUI so do this here
174 : 0 : args.SoftSetBoolArg("-server", true);
175 : : // Set this early so that parameter interactions go to console
176 : 0 : InitLogging(args);
177 : 0 : InitParameterInteraction(args);
178 : 0 : if (!AppInitBasicSetup(args, node.exit_status)) {
179 : : // InitError will have been called with detailed error, which ends up on console
180 : 0 : return false;
181 : : }
182 : 0 : if (!AppInitParameterInteraction(args)) {
183 : : // InitError will have been called with detailed error, which ends up on console
184 : 0 : return false;
185 : : }
186 : :
187 : 0 : node.kernel = std::make_unique<kernel::Context>();
188 : 0 : if (!AppInitSanityChecks(*node.kernel))
189 : : {
190 : : // InitError will have been called with detailed error, which ends up on console
191 : 0 : return false;
192 : : }
193 : :
194 : 0 : if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
195 : : #if HAVE_DECL_FORK
196 : 0 : tfm::format(std::cout, PACKAGE_NAME " starting\n");
197 : :
198 : : // Daemonize
199 : 0 : switch (fork_daemon(1, 0, daemon_ep)) { // don't chdir (1), do close FDs (0)
200 : : case 0: // Child: continue.
201 : : // If -daemonwait is not enabled, immediately send a success token the parent.
202 : 0 : if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
203 : 0 : daemon_ep.TokenWrite(1);
204 : 0 : daemon_ep.Close();
205 : 0 : }
206 : 0 : break;
207 : : case -1: // Error happened.
208 : 0 : return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno))));
209 : : default: { // Parent: wait and exit.
210 : 0 : int token = daemon_ep.TokenRead();
211 : 0 : if (token) { // Success
212 : 0 : exit(EXIT_SUCCESS);
213 : : } else { // fRet = false or token read error (premature exit).
214 : 0 : tfm::format(std::cerr, "Error during initialization - check debug.log for details\n");
215 : 0 : exit(EXIT_FAILURE);
216 : : }
217 : : }
218 : : }
219 : : #else
220 : : return InitError(Untranslated("-daemon is not supported on this operating system"));
221 : : #endif // HAVE_DECL_FORK
222 : 0 : }
223 : : // Lock data directory after daemonization
224 : 0 : if (!AppInitLockDataDirectory())
225 : : {
226 : : // If locking the data directory failed, exit immediately
227 : 0 : return false;
228 : : }
229 : 0 : fRet = AppInitInterfaces(node) && AppInitMain(node);
230 : 0 : }
231 : : catch (const std::exception& e) {
232 : 0 : PrintExceptionContinue(&e, "AppInit()");
233 : 0 : } catch (...) {
234 : 0 : PrintExceptionContinue(nullptr, "AppInit()");
235 : 0 : }
236 : :
237 : : #if HAVE_DECL_FORK
238 : 0 : if (daemon_ep.IsOpen()) {
239 : : // Signal initialization status to parent, then close pipe.
240 : 0 : daemon_ep.TokenWrite(fRet);
241 : 0 : daemon_ep.Close();
242 : 0 : }
243 : : #endif
244 : 0 : return fRet;
245 : 0 : }
246 : :
247 : 0 : MAIN_FUNCTION
248 : : {
249 : : #ifdef WIN32
250 : : common::WinCmdLineArgs winArgs;
251 : : std::tie(argc, argv) = winArgs.get();
252 : : #endif
253 : :
254 : 0 : NodeContext node;
255 : : int exit_status;
256 : 0 : std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
257 : 0 : if (!init) {
258 : 0 : return exit_status;
259 : : }
260 : :
261 : 0 : SetupEnvironment();
262 : :
263 : : // Connect bitcoind signal handlers
264 : 0 : noui_connect();
265 : :
266 : 0 : util::ThreadSetInternalName("init");
267 : :
268 : : // Interpret command line arguments
269 : 0 : ArgsManager& args = *Assert(node.args);
270 : 0 : if (!ParseArgs(args, argc, argv)) return EXIT_FAILURE;
271 : : // Process early info return commands such as -help or -version
272 : 0 : if (ProcessInitCommands(args)) return EXIT_SUCCESS;
273 : :
274 : : // Start application
275 : 0 : if (AppInit(node)) {
276 : 0 : WaitForShutdown();
277 : 0 : } else {
278 : 0 : node.exit_status = EXIT_FAILURE;
279 : : }
280 : 0 : Interrupt(node);
281 : 0 : Shutdown(node);
282 : :
283 : 0 : return node.exit_status;
284 : 0 : }
|