1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl
32
33 #include "mongo/platform/basic.h"
34
35 #include "mongo/db/initialize_server_global_state.h"
36
37 #include <boost/filesystem/operations.hpp>
38 #include <iostream>
39 #include <memory>
40 #include <signal.h>
41
42 #ifndef _WIN32
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <syslog.h>
47 #endif
48
49 #include "mongo/base/init.h"
50 #include "mongo/client/sasl_client_authenticate.h"
51 #include "mongo/config.h"
52 #include "mongo/db/auth/authorization_manager.h"
53 #include "mongo/db/auth/authorization_manager_global.h"
54 #include "mongo/db/auth/internal_user_auth.h"
55 #include "mongo/db/auth/security_key.h"
56 #include "mongo/db/server_options.h"
57 #include "mongo/db/server_parameters.h"
58 #include "mongo/logger/console_appender.h"
59 #include "mongo/logger/logger.h"
60 #include "mongo/logger/message_event.h"
61 #include "mongo/logger/message_event_utf8_encoder.h"
62 #include "mongo/logger/ramlog.h"
63 #include "mongo/logger/rotatable_file_appender.h"
64 #include "mongo/logger/rotatable_file_manager.h"
65 #include "mongo/logger/rotatable_file_writer.h"
66 #include "mongo/logger/syslog_appender.h"
67 #include "mongo/platform/process_id.h"
68 #include "mongo/util/log.h"
69 #include "mongo/util/mongoutils/str.h"
70 #include "mongo/util/net/listen.h"
71 #include "mongo/util/net/ssl_manager.h"
72 #include "mongo/util/processinfo.h"
73 #include "mongo/util/quick_exit.h"
74 #include "mongo/util/signal_handlers_synchronous.h"
75
76 #if defined(__APPLE__)
77 #include <TargetConditionals.h>
78 #endif
79
80 namespace fs = boost::filesystem;
81
82 namespace mongo {
83
84 using std::cerr;
85 using std::cout;
86 using std::endl;
87
88 #ifndef _WIN32
89 // support for exit value propagation with fork
launchSignal(int sig)90 void launchSignal(int sig) {
91 if (sig == SIGUSR2) {
92 ProcessId cur = ProcessId::getCurrent();
93
94 if (cur == serverGlobalParams.parentProc || cur == serverGlobalParams.leaderProc) {
95 // signal indicates successful start allowing us to exit
96 quickExit(0);
97 }
98 }
99 }
100
signalForkSuccess()101 void signalForkSuccess() {
102 if (serverGlobalParams.doFork) {
103 // killing leader will propagate to parent
104 verify(kill(serverGlobalParams.leaderProc.toNative(), SIGUSR2) == 0);
105 }
106 }
107 #endif
108
109
forkServer()110 static bool forkServer() {
111 #if !defined(_WIN32) && !(defined(__APPLE__) && TARGET_OS_TV)
112 if (serverGlobalParams.doFork) {
113 fassert(16447, !serverGlobalParams.logpath.empty() || serverGlobalParams.logWithSyslog);
114
115 cout.flush();
116 cerr.flush();
117
118 serverGlobalParams.parentProc = ProcessId::getCurrent();
119
120 // clear signal mask so that SIGUSR2 will always be caught and we can clean up the original
121 // parent process
122 clearSignalMask();
123
124 // facilitate clean exit when child starts successfully
125 verify(signal(SIGUSR2, launchSignal) != SIG_ERR);
126
127 cout << "about to fork child process, waiting until server is ready for connections."
128 << endl;
129
130 pid_t child1 = fork();
131 if (child1 == -1) {
132 cout << "ERROR: stage 1 fork() failed: " << errnoWithDescription();
133 quickExit(EXIT_ABRUPT);
134 } else if (child1) {
135 // this is run in the original parent process
136 int pstat;
137 waitpid(child1, &pstat, 0);
138
139 if (WIFEXITED(pstat)) {
140 if (WEXITSTATUS(pstat)) {
141 cout << "ERROR: child process failed, exited with error number "
142 << WEXITSTATUS(pstat) << endl
143 << "To see additional information in this output, start without "
144 << "the \"--fork\" option." << endl;
145 } else {
146 cout << "child process started successfully, parent exiting" << endl;
147 }
148
149 quickExit(WEXITSTATUS(pstat));
150 }
151
152 quickExit(50);
153 }
154
155 if (chdir("/") < 0) {
156 cout << "Cant chdir() while forking server process: " << strerror(errno) << endl;
157 quickExit(-1);
158 }
159 setsid();
160
161 serverGlobalParams.leaderProc = ProcessId::getCurrent();
162
163 pid_t child2 = fork();
164 if (child2 == -1) {
165 cout << "ERROR: stage 2 fork() failed: " << errnoWithDescription();
166 quickExit(EXIT_ABRUPT);
167 } else if (child2) {
168 // this is run in the middle process
169 int pstat;
170 cout << "forked process: " << child2 << endl;
171 waitpid(child2, &pstat, 0);
172
173 if (WIFEXITED(pstat)) {
174 quickExit(WEXITSTATUS(pstat));
175 }
176
177 quickExit(51);
178 }
179
180 // this is run in the final child process (the server)
181
182 FILE* f = freopen("/dev/null", "w", stdout);
183 if (f == NULL) {
184 cout << "Cant reassign stdout while forking server process: " << strerror(errno)
185 << endl;
186 return false;
187 }
188
189 f = freopen("/dev/null", "w", stderr);
190 if (f == NULL) {
191 cout << "Cant reassign stderr while forking server process: " << strerror(errno)
192 << endl;
193 return false;
194 }
195
196 f = freopen("/dev/null", "r", stdin);
197 if (f == NULL) {
198 cout << "Cant reassign stdin while forking server process: " << strerror(errno) << endl;
199 return false;
200 }
201 }
202 #endif // !defined(_WIN32)
203 return true;
204 }
205
forkServerOrDie()206 void forkServerOrDie() {
207 if (!forkServer())
208 quickExit(EXIT_FAILURE);
209 }
210
211 MONGO_INITIALIZER_GENERAL(
212 ServerLogRedirection,
213 ("GlobalLogManager", "EndStartupOptionHandling", "ForkServer", "MungeUmask"),
214 ("default"))
215 (InitializerContext*) {
216 using logger::LogManager;
217 using logger::MessageEventEphemeral;
218 using logger::MessageEventDetailsEncoder;
219 using logger::MessageEventWithContextEncoder;
220 using logger::MessageLogDomain;
221 using logger::RotatableFileAppender;
222 using logger::StatusWithRotatableFileWriter;
223
224 if (serverGlobalParams.logWithSyslog) {
225 #ifdef _WIN32
226 return Status(ErrorCodes::InternalError,
227 "Syslog requested in Windows build; command line processor logic error");
228 #else
229 using logger::SyslogAppender;
230
231 StringBuilder sb;
232 sb << serverGlobalParams.binaryName << "." << serverGlobalParams.port;
233 openlog(strdup(sb.str().c_str()), LOG_PID | LOG_CONS, serverGlobalParams.syslogFacility);
234 LogManager* manager = logger::globalLogManager();
235 manager->getGlobalDomain()->clearAppenders();
236 manager->getGlobalDomain()->attachAppender(MessageLogDomain::AppenderAutoPtr(
237 new SyslogAppender<MessageEventEphemeral>(new logger::MessageEventDetailsEncoder)));
238 manager->getNamedDomain("javascriptOutput")
239 ->attachAppender(MessageLogDomain::AppenderAutoPtr(
240 new SyslogAppender<MessageEventEphemeral>(new logger::MessageEventDetailsEncoder)));
241 #endif // defined(_WIN32)
242 } else if (!serverGlobalParams.logpath.empty()) {
243 fassert(16448, !serverGlobalParams.logWithSyslog);
244 std::string absoluteLogpath =
245 boost::filesystem::absolute(serverGlobalParams.logpath, serverGlobalParams.cwd)
246 .string();
247
248 bool exists;
249
250 try {
251 exists = boost::filesystem::exists(absoluteLogpath);
252 } catch (boost::filesystem::filesystem_error& e) {
253 return Status(ErrorCodes::FileNotOpen,
254 mongoutils::str::stream() << "Failed probe for \"" << absoluteLogpath
255 << "\": "
256 << e.code().message());
257 }
258
259 if (exists) {
260 if (boost::filesystem::is_directory(absoluteLogpath)) {
261 return Status(
262 ErrorCodes::FileNotOpen,
263 mongoutils::str::stream() << "logpath \"" << absoluteLogpath
264 << "\" should name a file, not a directory.");
265 }
266
267 if (!serverGlobalParams.logAppend && boost::filesystem::is_regular(absoluteLogpath)) {
268 std::string renameTarget = absoluteLogpath + "." + terseCurrentTime(false);
269 boost::system::error_code ec;
270 boost::filesystem::rename(absoluteLogpath, renameTarget, ec);
271 if (!ec) {
272 log() << "log file \"" << absoluteLogpath << "\" exists; moved to \""
273 << renameTarget << "\".";
274 } else {
275 return Status(ErrorCodes::FileRenameFailed,
276 mongoutils::str::stream()
277 << "Could not rename preexisting log file \""
278 << absoluteLogpath
279 << "\" to \""
280 << renameTarget
281 << "\"; run with --logappend or manually remove file: "
282 << ec.message());
283 }
284 }
285 }
286
287 StatusWithRotatableFileWriter writer = logger::globalRotatableFileManager()->openFile(
288 absoluteLogpath, serverGlobalParams.logAppend);
289 if (!writer.isOK()) {
290 return writer.getStatus();
291 }
292
293 LogManager* manager = logger::globalLogManager();
294 manager->getGlobalDomain()->clearAppenders();
295 manager->getGlobalDomain()->attachAppender(
296 MessageLogDomain::AppenderAutoPtr(new RotatableFileAppender<MessageEventEphemeral>(
297 new MessageEventDetailsEncoder, writer.getValue())));
298 manager->getNamedDomain("javascriptOutput")
299 ->attachAppender(
300 MessageLogDomain::AppenderAutoPtr(new RotatableFileAppender<MessageEventEphemeral>(
301 new MessageEventDetailsEncoder, writer.getValue())));
302
303 if (serverGlobalParams.logAppend && exists) {
304 log() << "***** SERVER RESTARTED *****";
305 Status status = logger::RotatableFileWriter::Use(writer.getValue()).status();
306 if (!status.isOK())
307 return status;
308 }
309 } else {
310 logger::globalLogManager()
311 ->getNamedDomain("javascriptOutput")
312 ->attachAppender(MessageLogDomain::AppenderAutoPtr(
313 new logger::ConsoleAppender<MessageEventEphemeral>(
314 new MessageEventDetailsEncoder)));
315 }
316
317 logger::globalLogDomain()->attachAppender(
318 logger::MessageLogDomain::AppenderAutoPtr(new RamLogAppender(RamLog::get("global"))));
319 logger::LogstreamBuilder::setNewlineEscape();
320
321 return Status::OK();
322 }
323
324 /**
325 * atexit handler to terminate the process before static destructors run.
326 *
327 * Mongo server processes cannot safely call ::exit() or std::exit(), but
328 * some third-party libraries may call one of those functions. In that
329 * case, to avoid static-destructor problems in the server, this exits the
330 * process immediately with code EXIT_FAILURE.
331 *
332 * TODO: Remove once exit() executes safely in mongo server processes.
333 */
shortCircuitExit()334 static void shortCircuitExit() {
335 quickExit(EXIT_FAILURE);
336 }
337
MONGO_INITIALIZER(RegisterShortCircuitExitHandler)338 MONGO_INITIALIZER(RegisterShortCircuitExitHandler)(InitializerContext*) {
339 if (std::atexit(&shortCircuitExit) != 0)
340 return Status(ErrorCodes::InternalError, "Failed setting short-circuit exit handler.");
341 return Status::OK();
342 }
343
344 // On non-windows platforms, drop rwx for group and other unless the
345 // user has opted into using the system umask. To do so, we first read
346 // out the current umask (by temporarily setting it to
347 // no-permissions), and then or the returned umask with the
348 // restrictions we want to apply and set it back. The overall effect
349 // is to set the bits for 'other' and 'group', but leave umask bits
350 // bits for 'user' unaltered.
351 namespace {
352 #ifndef _WIN32
353 MONGO_EXPORT_STARTUP_SERVER_PARAMETER(honorSystemUmask, bool, false);
354 #endif
355
356 MONGO_INITIALIZER_WITH_PREREQUISITES(MungeUmask, ("EndStartupOptionHandling"))
357 (InitializerContext*) {
358 #ifndef _WIN32
359 if (!honorSystemUmask) {
360 umask(umask(S_IRWXU | S_IRWXG | S_IRWXO) | S_IRWXG | S_IRWXO);
361 }
362 #endif
363
364 return Status::OK();
365 }
366 } // namespace
367
initializeServerGlobalState()368 bool initializeServerGlobalState() {
369 Listener::globalTicketHolder.resize(serverGlobalParams.maxConns).transitional_ignore();
370
371 #ifndef _WIN32
372 if (!fs::is_directory(serverGlobalParams.socket)) {
373 cout << serverGlobalParams.socket << " must be a directory" << endl;
374 return false;
375 }
376 #endif
377
378 if (!serverGlobalParams.pidFile.empty()) {
379 if (!writePidFile(serverGlobalParams.pidFile)) {
380 // error message logged in writePidFile
381 return false;
382 }
383 }
384
385 int clusterAuthMode = serverGlobalParams.clusterAuthMode.load();
386 if (!serverGlobalParams.keyFile.empty() &&
387 clusterAuthMode != ServerGlobalParams::ClusterAuthMode_x509) {
388 if (!setUpSecurityKey(serverGlobalParams.keyFile)) {
389 // error message printed in setUpPrivateKey
390 return false;
391 }
392 }
393
394 // Auto-enable auth unless we are in mixed auth/no-auth or clusterAuthMode was not provided.
395 // clusterAuthMode defaults to "keyFile" if a --keyFile parameter is provided.
396 if (clusterAuthMode != ServerGlobalParams::ClusterAuthMode_undefined &&
397 !serverGlobalParams.transitionToAuth) {
398 getGlobalAuthorizationManager()->setAuthEnabled(true);
399 }
400
401 #ifdef MONGO_CONFIG_SSL
402
403 if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509 ||
404 clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509) {
405 setInternalUserAuthParams(
406 BSON(saslCommandMechanismFieldName
407 << "MONGODB-X509"
408 << saslCommandUserDBFieldName
409 << "$external"
410 << saslCommandUserFieldName
411 << getSSLManager()->getSSLConfiguration().clientSubjectName.toString()));
412 }
413 #endif
414 return true;
415 }
416
417 } // namespace mongo
418