1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2022 The Limenka 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 <limenka-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 {
53 // communication pipe with child process
54 std::optional<TokenPipe> umbilical = TokenPipe::Make();
55 if (!umbilical) {
56 return -1; // pipe or pipe2 failed.
57 }
58 59 int pid = fork();
60 if (pid < 0) {
61 return -1; // fork failed.
62 }
63 if (pid != 0) {
64 // Parent process gets read end, closes write end.
65 endpoint = umbilical->TakeReadEnd();
66 umbilical->TakeWriteEnd().Close();
67 68 int status = endpoint.TokenRead();
69 if (status != 0) { // Something went wrong while setting up child process.
70 endpoint.Close();
71 return -1;
72 }
73 74 return pid;
75 }
76 // Child process gets write end, closes read end.
77 endpoint = umbilical->TakeWriteEnd();
78 umbilical->TakeReadEnd().Close();
79 80 #if HAVE_DECL_SETSID
81 if (setsid() < 0) {
82 exit(1); // setsid failed.
83 }
84 #endif
85 86 if (!nochdir) {
87 if (chdir("/") != 0) {
88 exit(1); // chdir failed.
89 }
90 }
91 if (!noclose) {
92 // Open /dev/null, and clone it into STDIN, STDOUT and STDERR to detach
93 // from terminal.
94 int fd = open("/dev/null", O_RDWR);
95 if (fd >= 0) {
96 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 if (fd > 2) close(fd);
99 if (err) {
100 exit(1); // dup2 failed.
101 }
102 } else {
103 exit(1); // open /dev/null failed.
104 }
105 }
106 endpoint.TokenWrite(0); // Success
107 return 0;
108 }
109 110 #endif
111 112 static bool ParseArgs(NodeContext& node, int argc, char* argv[])
113 {
114 ArgsManager& args{*Assert(node.args)};
115 // If Qt is used, parameters/limenka.conf are parsed in qt/limenka.cpp's main()
116 SetupServerArgs(args, node.init->canListenIpc());
117 std::string error;
118 if (!args.ParseParameters(argc, argv, error)) {
119 return InitError(Untranslated(strprintf("Error parsing command line arguments: %s", error)));
120 }
121 122 if (auto error = common::InitConfig(args)) {
123 return InitError(error->message, error->details);
124 }
125 126 // Error out when loose non-argument tokens are encountered on command line
127 for (int i = 1; i < argc; i++) {
128 if (!IsSwitchChar(argv[i][0])) {
129 return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see limenkad -h for a list of options.", argv[i])));
130 }
131 }
132 return true;
133 }
134 135 static bool ProcessInitCommands(ArgsManager& args)
136 {
137 // Process help and version before taking care about datadir
138 if (HelpRequested(args) || args.GetBoolArg("-version", false)) {
139 std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion() + "\n";
140 141 if (args.GetBoolArg("-version", false)) {
142 strUsage += FormatParagraph(LicenseInfo());
143 } else {
144 strUsage += "\n"
145 "The " CLIENT_NAME " daemon (limenkad) is a headless program that connects to the Limenka network to validate and relay transactions and blocks, as well as relaying addresses.\n\n"
146 "It provides the backbone of the Limenka network and its RPC, REST and ZMQ services can provide various transaction, block and address-related services.\n\n"
147 "There is an optional wallet component which provides transaction services.\n\n"
148 "It can be used in a headless environment or as part of a server setup.\n"
149 "\n"
150 "Usage: limenkad [options]\n"
151 "\n";
152 strUsage += args.GetHelpMessage();
153 }
154 155 tfm::format(std::cout, "%s", strUsage);
156 return true;
157 }
158 159 return false;
160 }
161 162 static bool AppInit(NodeContext& node)
163 {
164 bool fRet = false;
165 ArgsManager& args = *Assert(node.args);
166 167 #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 TokenPipeEnd daemon_ep;
174 #endif
175 std::any context{&node};
176 try
177 {
178 // -server defaults to true for limenkad but not for the GUI so do this here
179 args.SoftSetBoolArg("-server", true);
180 // Set this early so that parameter interactions go to console
181 InitLogging(args);
182 InitParameterInteraction(args);
183 if (!AppInitBasicSetup(args, node.exit_status)) {
184 // InitError will have been called with detailed error, which ends up on console
185 return false;
186 }
187 if (!AppInitParameterInteraction(args)) {
188 // InitError will have been called with detailed error, which ends up on console
189 return false;
190 }
191 192 node.warnings = std::make_unique<node::Warnings>();
193 194 node.kernel = std::make_unique<kernel::Context>();
195 node.ecc_context = std::make_unique<ECC_Context>();
196 if (!AppInitSanityChecks(*node.kernel))
197 {
198 // InitError will have been called with detailed error, which ends up on console
199 return false;
200 }
201 202 if (args.GetBoolArg("-daemon", DEFAULT_DAEMON) || args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
203 #if HAVE_DECL_FORK
204 tfm::format(std::cout, CLIENT_NAME " starting\n");
205 206 // Daemonize
207 switch (fork_daemon(1, 0, daemon_ep)) { // don't chdir (1), do close FDs (0)
208 case 0: // Child: continue.
209 // If -daemonwait is not enabled, immediately send a success token the parent.
210 if (!args.GetBoolArg("-daemonwait", DEFAULT_DAEMONWAIT)) {
211 daemon_ep.TokenWrite(1);
212 daemon_ep.Close();
213 }
214 break;
215 case -1: // Error happened.
216 return InitError(Untranslated(strprintf("fork_daemon() failed: %s", SysErrorString(errno))));
217 default: { // Parent: wait and exit.
218 int token = daemon_ep.TokenRead();
219 if (token) { // Success
220 exit(EXIT_SUCCESS);
221 } else { // fRet = false or token read error (premature exit).
222 tfm::format(std::cerr, "Error during initialization - check %s for details\n", fs::PathToString(LogInstance().m_file_path.filename()));
223 exit(EXIT_FAILURE);
224 }
225 }
226 }
227 #else
228 return InitError(Untranslated("-daemon is not supported on this operating system"));
229 #endif // HAVE_DECL_FORK
230 }
231 // Lock critical directories after daemonization
232 if (!AppInitLockDirectories())
233 {
234 // If locking a directory failed, exit immediately
235 return false;
236 }
237 fRet = AppInitInterfaces(node) && AppInitMain(node);
238 }
239 catch (const std::exception& e) {
240 PrintExceptionContinue(&e, "AppInit()");
241 } catch (...) {
242 PrintExceptionContinue(nullptr, "AppInit()");
243 }
244 245 #if HAVE_DECL_FORK
246 if (daemon_ep.IsOpen()) {
247 // Signal initialization status to parent, then close pipe.
248 daemon_ep.TokenWrite(fRet);
249 daemon_ep.Close();
250 }
251 #endif
252 return fRet;
253 }
254 255 MAIN_FUNCTION
256 {
257 #ifdef WIN32
258 common::WinCmdLineArgs winArgs;
259 std::tie(argc, argv) = winArgs.get();
260 #endif
261 262 NodeContext node;
263 int exit_status;
264 std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node, argc, argv, exit_status);
265 if (!init) {
266 return exit_status;
267 }
268 269 SetupEnvironment();
270 271 // Connect limenkad signal handlers
272 noui_connect();
273 274 util::ThreadSetInternalName("init");
275 276 // Interpret command line arguments
277 ArgsManager& args = *Assert(node.args);
278 if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
279 // Process early info return commands such as -help or -version
280 if (ProcessInitCommands(args)) return EXIT_SUCCESS;
281 282 // Start application
283 if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
284 node.exit_status = EXIT_FAILURE;
285 }
286 Interrupt(node);
287 Shutdown(node);
288 289 return node.exit_status;
290 }
291