Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2021 The Bitcoin Core developers
3 : // Copyright (c) 2014-2025 The Dash Core developers
4 : // Distributed under the MIT software license, see the accompanying
5 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 :
7 : #if defined(HAVE_CONFIG_H)
8 : #include <config/bitcoin-config.h>
9 : #endif
10 :
11 : #include <chainparams.h>
12 : #include <clientversion.h>
13 : #include <common/url.h>
14 : #include <compat/compat.h>
15 : #include <init.h>
16 : #include <interfaces/chain.h>
17 : #include <interfaces/init.h>
18 : #include <node/context.h>
19 : #include <node/interface_ui.h>
20 : #include <noui.h>
21 : #include <shutdown.h>
22 : #include <util/check.h>
23 : #include <util/syserror.h>
24 : #include <util/system.h>
25 : #include <util/strencodings.h>
26 : #include <util/threadnames.h>
27 : #include <util/tokenpipe.h>
28 : #include <util/translation.h>
29 : #include <stacktraces.h>
30 :
31 : #include <cstdio>
32 : #include <functional>
33 :
34 : using node::NodeContext;
35 :
36 0 : const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
37 : UrlDecodeFn* const URL_DECODE = urlDecode;
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 0 : int fork_daemon(bool nochdir, bool noclose, TokenPipeEnd& endpoint)
52 : {
53 : // communication pipe with child process
54 0 : std::optional<TokenPipe> umbilical = TokenPipe::Make();
55 0 : if (!umbilical) {
56 0 : return -1; // pipe or pipe2 failed.
57 : }
58 :
59 0 : int pid = fork();
60 0 : if (pid < 0) {
61 0 : return -1; // fork failed.
62 : }
63 0 : if (pid != 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.
70 0 : endpoint.Close();
71 0 : return -1;
72 : }
73 :
74 0 : return pid;
75 : }
76 : // Child process gets write end, closes read end.
77 0 : endpoint = umbilical->TakeWriteEnd();
78 0 : umbilical->TakeReadEnd().Close();
79 :
80 : #if HAVE_DECL_SETSID
81 0 : if (setsid() < 0) {
82 0 : exit(1); // setsid failed.
83 : }
84 : #endif
85 :
86 0 : if (!nochdir) {
87 0 : if (chdir("/") != 0) {
88 0 : exit(1); // chdir failed.
89 : }
90 0 : }
91 0 : if (!noclose) {
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) {
96 0 : bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 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);
99 0 : if (err) {
100 0 : exit(1); // dup2 failed.
101 : }
102 0 : } else {
103 0 : exit(1); // open /dev/null failed.
104 : }
105 0 : }
106 0 : endpoint.TokenWrite(0); // Success
107 0 : return 0;
108 0 : }
109 :
110 : #endif
111 :
112 0 : static bool AppInit(NodeContext& node, int argc, char* argv[])
113 : {
114 0 : bool fRet = false;
115 :
116 0 : util::ThreadSetInternalName("init");
117 :
118 : // If Qt is used, parameters/dash.conf are parsed in qt/bitcoin.cpp's main()
119 0 : ArgsManager& args = *Assert(node.args);
120 0 : SetupServerArgs(args);
121 0 : std::string error;
122 0 : if (!args.ParseParameters(argc, argv, error)) {
123 0 : return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error)));
124 : }
125 :
126 0 : if (args.IsArgSet("-printcrashinfo")) {
127 0 : std::cout << GetCrashInfoStrFromSerializedStr(args.GetArg("-printcrashinfo", "")) << std::endl;
128 0 : return true;
129 : }
130 :
131 : // Process help and version before taking care about datadir
132 0 : if (HelpRequested(args) || args.IsArgSet("-version")) {
133 0 : std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
134 :
135 0 : if (args.IsArgSet("-version")) {
136 0 : strUsage += FormatParagraph(LicenseInfo());
137 0 : } else {
138 0 : strUsage += "\nUsage: dashd [options] Start " PACKAGE_NAME "\n"
139 : "\n";
140 0 : strUsage += args.GetHelpMessage();
141 : }
142 :
143 0 : tfm::format(std::cout, "%s", strUsage);
144 0 : return true;
145 0 : }
146 :
147 0 : CoreContext context{node};
148 : #if HAVE_DECL_FORK
149 : // Communication with parent after daemonizing. This is used for signalling in the following ways:
150 : // - a boolean token is sent when the initialization process (all the Init* functions) have finished to indicate
151 : // that the parent process can quit, and whether it was successful/unsuccessful.
152 : // - an unexpected shutdown of the child process creates an unexpected end of stream at the parent
153 : // end, which is interpreted as failure to start.
154 0 : TokenPipeEnd daemon_ep;
155 : #endif
156 : try
157 : {
158 0 : if (!CheckDataDirOption()) {
159 0 : return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""))));
160 : }
161 0 : if (!args.ReadConfigFiles(error, true)) {
162 0 : return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error)));
163 : }
164 : // Check for chain settings (Params() calls are only valid after this clause)
165 : try {
166 0 : SelectParams(args.GetChainName());
167 0 : } catch (const std::exception& e) {
168 0 : return InitError(Untranslated(strprintf("%s\n", e.what())));
169 0 : }
170 :
171 : // Error out when loose non-argument tokens are encountered on command line
172 0 : for (int i = 1; i < argc; i++) {
173 0 : if (!IsSwitchChar(argv[i][0])) {
174 0 : return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see dashd -h for a list of options.\n", argv[i])));
175 : }
176 0 : }
177 :
178 0 : if (!gArgs.InitSettings(error)) {
179 0 : InitError(Untranslated(error));
180 0 : return false;
181 : }
182 :
183 : // -server defaults to true for dashd but not for the GUI so do this here
184 0 : args.SoftSetBoolArg("-server", true);
185 : // Set this early so that parameter interactions go to console
186 0 : InitLogging(args);
187 0 : InitParameterInteraction(args);
188 0 : if (!AppInitBasicSetup(args)) {
189 : // InitError will have been called with detailed error, which ends up on console
190 0 : return false;
191 : }
192 0 : if (!AppInitParameterInteraction(args)) {
193 : // InitError will have been called with detailed error, which ends up on console
194 0 : return false;
195 : }
196 0 : if (!AppInitSanityChecks())
197 : {
198 : // InitError will have been called with detailed error, which ends up on console
199 0 : return false;
200 : }
201 0 : if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
202 : #if HAVE_DECL_FORK
203 0 : tfm::format(std::cout, PACKAGE_NAME " starting\n");
204 :
205 : // Daemonize
206 0 : switch (fork_daemon(1, 0, daemon_ep)) { // don't chdir (1), do close FDs (0)
207 : case 0: // Child: continue.
208 : // If -daemonwait is not enabled, immediately send a success token the parent.
209 0 : if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
210 0 : daemon_ep.TokenWrite(1);
211 0 : daemon_ep.Close();
212 0 : }
213 0 : break;
214 : case -1: // Error happened.
215 0 : return InitError(Untranslated(strprintf("fork_daemon() failed: %s\n", SysErrorString(errno))));
216 : default: { // Parent: wait and exit.
217 0 : int token = daemon_ep.TokenRead();
218 0 : if (token) { // Success
219 0 : exit(EXIT_SUCCESS);
220 : } else { // fRet = false or token read error (premature exit).
221 0 : tfm::format(std::cerr, "Error during initialization - check debug.log for details\n");
222 0 : exit(EXIT_FAILURE);
223 : }
224 : }
225 : }
226 : #else
227 : return InitError(Untranslated("-daemon is not supported on this operating system\n"));
228 : #endif // HAVE_DECL_FORK
229 0 : }
230 : // Lock data directory after daemonization
231 0 : if (!AppInitLockDataDirectory())
232 : {
233 : // If locking the data directory failed, exit immediately
234 0 : return false;
235 : }
236 0 : fRet = AppInitInterfaces(node) && AppInitMain(node);
237 0 : } catch (...) {
238 0 : PrintExceptionContinue(std::current_exception(), "AppInit()");
239 0 : }
240 :
241 : #if HAVE_DECL_FORK
242 0 : if (daemon_ep.IsOpen()) {
243 : // Signal initialization status to parent, then close pipe.
244 0 : daemon_ep.TokenWrite(fRet);
245 0 : daemon_ep.Close();
246 0 : }
247 : #endif
248 0 : if (fRet) {
249 0 : WaitForShutdown();
250 0 : }
251 0 : Interrupt(node);
252 0 : Shutdown(node);
253 :
254 0 : return fRet;
255 0 : }
256 :
257 0 : MAIN_FUNCTION
258 : {
259 0 : RegisterPrettyTerminateHander();
260 0 : RegisterPrettySignalHandlers();
261 :
262 : #ifdef WIN32
263 : util::WinCmdLineArgs winArgs;
264 : std::tie(argc, argv) = winArgs.get();
265 : #endif
266 :
267 0 : NodeContext node;
268 : int exit_status;
269 0 : std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
270 0 : if (!init) {
271 0 : return exit_status;
272 : }
273 :
274 0 : SetupEnvironment();
275 :
276 : // Connect dashd signal handlers
277 0 : noui_connect();
278 :
279 0 : return (AppInit(node, argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
280 0 : }
|