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