/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/pch.h"
#include "mongo/db/initialize_server_global_state.h"
#include
#include
#ifndef _WIN32
#include
#include
#include
#endif
#include "mongo/base/init.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/security_key.h"
#include "mongo/logger/logger.h"
#include "mongo/logger/console_appender.h"
#include "mongo/logger/message_event.h"
#include "mongo/logger/message_event_utf8_encoder.h"
#include "mongo/logger/ramlog.h"
#include "mongo/logger/rotatable_file_appender.h"
#include "mongo/logger/rotatable_file_manager.h"
#include "mongo/logger/rotatable_file_writer.h"
#include "mongo/logger/syslog_appender.h"
#include "mongo/platform/process_id.h"
#include "mongo/util/log.h"
#include "mongo/util/net/listen.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/processinfo.h"
namespace fs = boost::filesystem;
namespace mongo {
#ifndef _WIN32
// support for exit value propagation with fork
void launchSignal( int sig ) {
if ( sig == SIGUSR2 ) {
ProcessId cur = ProcessId::getCurrent();
if (cur == serverGlobalParams.parentProc || cur == serverGlobalParams.leaderProc) {
// signal indicates successful start allowing us to exit
_exit(0);
}
}
}
static void setupLaunchSignals() {
verify( signal(SIGUSR2 , launchSignal ) != SIG_ERR );
}
void signalForkSuccess() {
if (serverGlobalParams.doFork) {
// killing leader will propagate to parent
verify(kill(serverGlobalParams.leaderProc.toNative(), SIGUSR2) == 0);
}
}
#endif
static bool forkServer() {
#ifndef _WIN32
if (serverGlobalParams.doFork) {
fassert(16447, !serverGlobalParams.logpath.empty() ||
serverGlobalParams.logWithSyslog);
cout.flush();
cerr.flush();
serverGlobalParams.parentProc = ProcessId::getCurrent();
// facilitate clean exit when child starts successfully
setupLaunchSignals();
cout << "about to fork child process, waiting until server is ready for connections."
<< endl;
pid_t child1 = fork();
if (child1 == -1) {
cout << "ERROR: stage 1 fork() failed: " << errnoWithDescription();
_exit(EXIT_ABRUPT);
}
else if (child1) {
// this is run in the original parent process
int pstat;
waitpid(child1, &pstat, 0);
if (WIFEXITED(pstat)) {
if (WEXITSTATUS(pstat)) {
cout << "ERROR: child process failed, exited with error number "
<< WEXITSTATUS(pstat) << endl;
}
else {
cout << "child process started successfully, parent exiting" << endl;
}
_exit(WEXITSTATUS(pstat));
}
_exit(50);
}
if ( chdir("/") < 0 ) {
cout << "Cant chdir() while forking server process: " << strerror(errno) << endl;
::_exit(-1);
}
setsid();
serverGlobalParams.leaderProc = ProcessId::getCurrent();
pid_t child2 = fork();
if (child2 == -1) {
cout << "ERROR: stage 2 fork() failed: " << errnoWithDescription();
_exit(EXIT_ABRUPT);
}
else if (child2) {
// this is run in the middle process
int pstat;
cout << "forked process: " << child2 << endl;
waitpid(child2, &pstat, 0);
if ( WIFEXITED(pstat) ) {
_exit( WEXITSTATUS(pstat) );
}
_exit(51);
}
// this is run in the final child process (the server)
FILE* f = freopen("/dev/null", "w", stdout);
if ( f == NULL ) {
cout << "Cant reassign stdout while forking server process: " << strerror(errno) << endl;
return false;
}
f = freopen("/dev/null", "w", stderr);
if ( f == NULL ) {
cout << "Cant reassign stderr while forking server process: " << strerror(errno) << endl;
return false;
}
f = freopen("/dev/null", "r", stdin);
if ( f == NULL ) {
cout << "Cant reassign stdin while forking server process: " << strerror(errno) << endl;
return false;
}
}
#endif // !defined(_WIN32)
return true;
}
void forkServerOrDie() {
if (!forkServer())
_exit(EXIT_FAILURE);
}
MONGO_INITIALIZER_GENERAL(ServerLogRedirection,
("GlobalLogManager", "EndStartupOptionHandling", "ForkServer"),
("default"))(
InitializerContext*) {
using logger::LogManager;
using logger::MessageEventEphemeral;
using logger::MessageEventDetailsEncoder;
using logger::MessageEventWithContextEncoder;
using logger::MessageLogDomain;
using logger::RotatableFileAppender;
using logger::StatusWithRotatableFileWriter;
if (serverGlobalParams.logWithSyslog) {
#ifdef _WIN32
return Status(ErrorCodes::InternalError,
"Syslog requested in Windows build; command line processor logic error");
#else
using logger::SyslogAppender;
StringBuilder sb;
sb << serverGlobalParams.binaryName << "." << serverGlobalParams.port;
openlog(strdup(sb.str().c_str()),
LOG_PID | LOG_CONS,
serverGlobalParams.syslogFacility);
LogManager* manager = logger::globalLogManager();
manager->getGlobalDomain()->clearAppenders();
manager->getGlobalDomain()->attachAppender(
MessageLogDomain::AppenderAutoPtr(
new SyslogAppender(
new logger::MessageEventWithContextEncoder)));
manager->getNamedDomain("javascriptOutput")->attachAppender(
MessageLogDomain::AppenderAutoPtr(
new SyslogAppender(
new logger::MessageEventWithContextEncoder)));
#endif // defined(_WIN32)
}
else if (!serverGlobalParams.logpath.empty()) {
fassert(16448, !serverGlobalParams.logWithSyslog);
std::string absoluteLogpath = boost::filesystem::absolute(
serverGlobalParams.logpath, serverGlobalParams.cwd).string();
bool exists;
try{
exists = boost::filesystem::exists(absoluteLogpath);
} catch(boost::filesystem::filesystem_error& e) {
return Status(ErrorCodes::FileNotOpen, mongoutils::str::stream() <<
"Failed probe for \"" << absoluteLogpath << "\": " <<
e.code().message());
}
if (exists) {
if (boost::filesystem::is_directory(absoluteLogpath)) {
return Status(ErrorCodes::FileNotOpen, mongoutils::str::stream() <<
"logpath \"" << absoluteLogpath <<
"\" should name a file, not a directory.");
}
if (!serverGlobalParams.logAppend &&
boost::filesystem::is_regular(absoluteLogpath)) {
std::string renameTarget = absoluteLogpath + "." + terseCurrentTime(false);
if (0 == rename(absoluteLogpath.c_str(), renameTarget.c_str())) {
log() << "log file \"" << absoluteLogpath
<< "\" exists; moved to \"" << renameTarget << "\".";
}
else {
return Status(ErrorCodes::FileRenameFailed, mongoutils::str::stream() <<
"Could not rename preexisting log file \"" <<
absoluteLogpath << "\" to \"" << renameTarget <<
"\"; run with --logappend or manually remove file: " <<
errnoWithDescription());
}
}
}
StatusWithRotatableFileWriter writer =
logger::globalRotatableFileManager()->openFile(absoluteLogpath,
serverGlobalParams.logAppend);
if (!writer.isOK()) {
return writer.getStatus();
}
LogManager* manager = logger::globalLogManager();
manager->getGlobalDomain()->clearAppenders();
manager->getGlobalDomain()->attachAppender(
MessageLogDomain::AppenderAutoPtr(
new RotatableFileAppender(
new MessageEventDetailsEncoder, writer.getValue())));
manager->getNamedDomain("javascriptOutput")->attachAppender(
MessageLogDomain::AppenderAutoPtr(
new RotatableFileAppender(
new MessageEventDetailsEncoder, writer.getValue())));
if (serverGlobalParams.logAppend && exists) {
log() << "***** SERVER RESTARTED *****" << endl;
Status status =
logger::RotatableFileWriter::Use(writer.getValue()).status();
if (!status.isOK())
return status;
}
}
else {
logger::globalLogManager()->getNamedDomain("javascriptOutput")->attachAppender(
MessageLogDomain::AppenderAutoPtr(
new logger::ConsoleAppender(
new MessageEventDetailsEncoder)));
}
logger::globalLogDomain()->attachAppender(
logger::MessageLogDomain::AppenderAutoPtr(
new RamLogAppender(RamLog::get("global"))));
return Status::OK();
}
bool initializeServerGlobalState() {
Listener::globalTicketHolder.resize(serverGlobalParams.maxConns);
#ifndef _WIN32
if (!fs::is_directory(serverGlobalParams.socket)) {
cout << serverGlobalParams.socket << " must be a directory" << endl;
return false;
}
#endif
if (!serverGlobalParams.pidFile.empty()) {
if (!writePidFile(serverGlobalParams.pidFile)) {
// error message logged in writePidFile
return false;
}
}
if (!serverGlobalParams.keyFile.empty() && serverGlobalParams.clusterAuthMode != "x509") {
if (!setUpSecurityKey(serverGlobalParams.keyFile)) {
// error message printed in setUpPrivateKey
return false;
}
}
// Auto-enable auth except if clusterAuthMode is not set.
// clusterAuthMode is automatically set if a --keyFile parameter is provided.
if (!serverGlobalParams.clusterAuthMode.empty()) {
getGlobalAuthorizationManager()->setAuthEnabled(true);
}
#ifdef MONGO_SSL
if (serverGlobalParams.clusterAuthMode == "x509" ||
serverGlobalParams.clusterAuthMode == "sendX509") {
setInternalUserAuthParams(BSON(saslCommandMechanismFieldName << "MONGODB-X509" <<
saslCommandUserSourceFieldName << "$external" <<
saslCommandUserFieldName <<
getSSLManager()->getClientSubjectName()));
}
#endif
return true;
}
static void ignoreSignal( int sig ) {}
void setupCoreSignals() {
#if !defined(_WIN32)
verify( signal(SIGHUP , ignoreSignal ) != SIG_ERR );
verify( signal(SIGUSR2, ignoreSignal ) != SIG_ERR );
#endif
}
} // namespace mongo