// server.cpp
/**
* 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/platform/basic.h"
#include "mongo/s/server.h"
#include
#include "mongo/base/init.h"
#include "mongo/base/initializer.h"
#include "mongo/base/status.h"
#include "mongo/client/connpool.h"
#include "mongo/client/replica_set_monitor.h"
#include "mongo/db/audit.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/authz_manager_external_state_s.h"
#include "mongo/db/auth/user_cache_invalidator_job.h"
#include "mongo/db/client_basic.h"
#include "mongo/db/dbwebserver.h"
#include "mongo/db/global_environment_experiment.h"
#include "mongo/db/global_environment_noop.h"
#include "mongo/db/initialize_server_global_state.h"
#include "mongo/db/instance.h"
#include "mongo/db/lasterror.h"
#include "mongo/db/log_process_details.h"
#include "mongo/db/operation_context_noop.h"
#include "mongo/platform/process_id.h"
#include "mongo/s/balance.h"
#include "mongo/s/chunk.h"
#include "mongo/s/client_info.h"
#include "mongo/s/config.h"
#include "mongo/s/config_server_checker_service.h"
#include "mongo/s/config_upgrade.h"
#include "mongo/s/cursors.h"
#include "mongo/s/grid.h"
#include "mongo/s/mongos_options.h"
#include "mongo/s/request.h"
#include "mongo/s/version_mongos.h"
#include "mongo/scripting/engine.h"
#include "mongo/util/admin_access.h"
#include "mongo/util/cmdline_utils/censor_cmdline.h"
#include "mongo/util/concurrency/task.h"
#include "mongo/util/concurrency/thread_name.h"
#include "mongo/util/exception_filter_win32.h"
#include "mongo/util/exit.h"
#include "mongo/util/gcov.h"
#include "mongo/util/log.h"
#include "mongo/util/net/message.h"
#include "mongo/util/net/message_server.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/ntservice.h"
#include "mongo/util/options_parser/startup_options.h"
#include "mongo/util/processinfo.h"
#include "mongo/util/ramlog.h"
#include "mongo/util/scopeguard.h"
#include "mongo/util/signal_handlers.h"
#include "mongo/util/stacktrace.h"
#include "mongo/util/stringutils.h"
#include "mongo/util/text.h"
#include "mongo/util/version.h"
namespace mongo {
MONGO_LOG_DEFAULT_COMPONENT_FILE(::mongo::logger::LogComponent::kSharding);
#if defined(_WIN32)
ntservice::NtServiceDefaultStrings defaultServiceStrings = {
L"MongoS",
L"MongoDB Router",
L"MongoDB Sharding Router"
};
static void initService();
#endif
Database *database = 0;
string mongosCommand;
bool dbexitCalled = false;
bool inShutdown() {
return dbexitCalled;
}
bool haveLocalShardingInfo( const string& ns ) {
verify( 0 );
return false;
}
static BSONObj buildErrReply( const DBException& ex ) {
BSONObjBuilder errB;
errB.append( "$err", ex.what() );
errB.append( "code", ex.getCode() );
if ( !ex._shard.empty() ) {
errB.append( "shard", ex._shard );
}
return errB.obj();
}
class ShardedMessageHandler : public MessageHandler {
public:
virtual ~ShardedMessageHandler() {}
virtual void connected( AbstractMessagingPort* p ) {
ClientInfo::create(p);
}
virtual void process( Message& m , AbstractMessagingPort* p , LastError * le) {
verify( p );
Request r( m , p );
verify( le );
lastError.startRequest( m , le );
try {
r.init();
r.process();
}
catch ( const AssertionException& ex ) {
LOG( ex.isUserAssertion() ? 1 : 0 ) << "Assertion failed"
<< " while processing " << opToString( m.operation() ) << " op"
<< " for " << r.getns() << causedBy( ex ) << endl;
if ( r.expectResponse() ) {
m.header()->id = r.id();
replyToQuery( ResultFlag_ErrSet, p , m , buildErrReply( ex ) );
}
// We *always* populate the last error for now
le->raiseError( ex.getCode() , ex.what() );
}
catch ( const DBException& ex ) {
log() << "Exception thrown"
<< " while processing " << opToString( m.operation() ) << " op"
<< " for " << r.getns() << causedBy( ex ) << endl;
if ( r.expectResponse() ) {
m.header()->id = r.id();
replyToQuery( ResultFlag_ErrSet, p , m , buildErrReply( ex ) );
}
// We *always* populate the last error for now
le->raiseError( ex.getCode() , ex.what() );
}
// Release connections back to pool, if any still cached
ShardConnection::releaseMyConnections();
}
virtual void disconnected( AbstractMessagingPort* p ) {
// all things are thread local
}
};
void init() {
serverID.init();
}
void start( const MessageServer::Options& opts ) {
balancer.go();
cursorCache.startTimeoutThread();
UserCacheInvalidator cacheInvalidatorThread(getGlobalAuthorizationManager());
cacheInvalidatorThread.go();
PeriodicTask::startRunningPeriodicTasks();
ShardedMessageHandler handler;
MessageServer * server = createServer( opts , &handler );
server->setAsTimeTracker();
server->setupSockets();
server->run();
}
DBClientBase *createDirectClient() {
uassert( 10197 , "createDirectClient not implemented for sharding yet" , 0 );
return 0;
}
} // namespace mongo
using namespace mongo;
static bool runMongosServer( bool doUpgrade ) {
setThreadName( "mongosMain" );
printShardingVersionInfo( false );
// set some global state
// Add sharding hooks to both connection pools - ShardingConnectionHook includes auth hooks
pool.addHook( new ShardingConnectionHook( false ) );
shardConnectionPool.addHook( new ShardingConnectionHook( true ) );
// Mongos shouldn't lazily kill cursors, otherwise we can end up with extras from migration
DBClientConnection::setLazyKillCursor( false );
ReplicaSetMonitor::setConfigChangeHook(
stdx::bind(&ConfigServer::replicaSetChange, &configServer, stdx::placeholders::_1 , stdx::placeholders::_2));
// Mongos connection pools already takes care of authenticating new connections so the
// replica set connection shouldn't need to.
DBClientReplicaSet::setAuthPooledSecondaryConn(false);
if (getHostName().empty()) {
dbexit(EXIT_BADOPTIONS);
}
if (!configServer.init(mongosGlobalParams.configdbs)) {
log() << "couldn't resolve config db address" << endl;
return false;
}
if ( ! configServer.ok( true ) ) {
log() << "configServer connection startup check failed" << endl;
return false;
}
startConfigServerChecker();
VersionType initVersionInfo;
VersionType versionInfo;
string errMsg;
string configServerURL = configServer.getPrimary().getConnString();
ConnectionString configServerConnString = ConnectionString::parse(configServerURL, errMsg);
if (!configServerConnString.isValid()) {
error() << "Invalid connection string for config servers: " << configServerURL << endl;
return false;
}
bool upgraded = checkAndUpgradeConfigVersion(configServerConnString,
doUpgrade,
&initVersionInfo,
&versionInfo,
&errMsg);
if (!upgraded) {
error() << "error upgrading config database to v" << CURRENT_CONFIG_VERSION
<< causedBy(errMsg) << endl;
return false;
}
if ( doUpgrade ) {
log() << "Config database is at version v" << CURRENT_CONFIG_VERSION;
return true;
}
configServer.reloadSettings();
init();
#if !defined(_WIN32)
mongo::signalForkSuccess();
#endif
if (serverGlobalParams.isHttpInterfaceEnabled)
boost::thread web( stdx::bind(&webServerThread,
new NoAdminAccess())); // takes ownership
OperationContextNoop txn;
Status status = getGlobalAuthorizationManager()->initialize(&txn);
if (!status.isOK()) {
log() << "Initializing authorization data failed: " << status;
return false;
}
MessageServer::Options opts;
opts.port = serverGlobalParams.port;
opts.ipList = serverGlobalParams.bind_ip;
start(opts);
// listen() will return when exit code closes its socket.
dbexit( EXIT_NET_ERROR );
return true;
}
MONGO_INITIALIZER_GENERAL(ForkServer,
("EndStartupOptionHandling"),
("default"))(InitializerContext* context) {
mongo::forkServerOrDie();
return Status::OK();
}
/*
* This function should contain the startup "actions" that we take based on the startup config. It
* is intended to separate the actions from "storage" and "validation" of our startup configuration.
*/
static void startupConfigActions(const std::vector& argv) {
#if defined(_WIN32)
vector disallowedOptions;
disallowedOptions.push_back( "upgrade" );
ntservice::configureService(initService,
moe::startupOptionsParsed,
defaultServiceStrings,
disallowedOptions,
argv);
#endif
}
static int _main() {
if (!initializeServerGlobalState())
return EXIT_FAILURE;
startSignalProcessingThread();
// we either have a setting where all processes are in localhost or none are
for (std::vector::const_iterator it = mongosGlobalParams.configdbs.begin();
it != mongosGlobalParams.configdbs.end(); ++it) {
try {
HostAndPort configAddr( *it ); // will throw if address format is invalid
if (it == mongosGlobalParams.configdbs.begin()) {
grid.setAllowLocalHost( configAddr.isLocalHost() );
}
if ( configAddr.isLocalHost() != grid.allowLocalHost() ) {
log() << "cannot mix localhost and ip addresses in configdbs" << endl;
return 10;
}
}
catch ( DBException& e) {
log() << "configdb: " << e.what() << endl;
return 9;
}
}
#if defined(_WIN32)
if (ntservice::shouldStartService()) {
ntservice::startService();
// if we reach here, then we are not running as a service. service installation
// exits directly and so never reaches here either.
}
#endif
return !runMongosServer(mongosGlobalParams.upgrade);
}
#if defined(_WIN32)
namespace mongo {
static void initService() {
ntservice::reportStatus( SERVICE_RUNNING );
log() << "Service running" << endl;
runMongosServer( false );
}
} // namespace mongo
#endif
MONGO_INITIALIZER_GENERAL(CreateAuthorizationManager,
("SetupInternalSecurityUser"),
MONGO_NO_DEPENDENTS)
(InitializerContext* context) {
AuthorizationManager* authzManager =
new AuthorizationManager(new AuthzManagerExternalStateMongos());
setGlobalAuthorizationManager(authzManager);
return Status::OK();
}
MONGO_INITIALIZER(SetGlobalEnvironment)(InitializerContext* context) {
setGlobalEnvironment(new GlobalEnvironmentNoop());
return Status::OK();
}
#ifdef MONGO_SSL
MONGO_INITIALIZER_GENERAL(setSSLManagerType,
MONGO_NO_PREREQUISITES,
("SSLManager"))(InitializerContext* context) {
isSSLServer = true;
return Status::OK();
}
#endif
int mongoSMain(int argc, char* argv[], char** envp) {
static StaticObserver staticObserver;
if (argc < 1)
return EXIT_FAILURE;
setupSignalHandlers(false);
mongosCommand = argv[0];
Status status = mongo::runGlobalInitializers(argc, argv, envp);
if (!status.isOK()) {
severe() << "Failed global initialization: " << status;
::_exit(EXIT_FAILURE);
}
startupConfigActions(std::vector(argv, argv + argc));
cmdline_utils::censorArgvArray(argc, argv);
try {
int exitCode = _main();
return exitCode;
}
catch(SocketException& e) {
cout << "uncaught SocketException in mongos main:" << endl;
cout << e.toString() << endl;
}
catch(DBException& e) {
cout << "uncaught DBException in mongos main:" << endl;
cout << e.toString() << endl;
}
catch(std::exception& e) {
cout << "uncaught std::exception in mongos main:" << endl;
cout << e.what() << endl;
}
catch(...) {
cout << "uncaught unknown exception in mongos main" << endl;
}
return 20;
}
#if defined(_WIN32)
// In Windows, wmain() is an alternate entry point for main(), and receives the same parameters
// as main() but encoded in Windows Unicode (UTF-16); "wide" 16-bit wchar_t characters. The
// WindowsCommandLine object converts these wide character strings to a UTF-8 coded equivalent
// and makes them available through the argv() and envp() members. This enables mongoSMain()
// to process UTF-8 encoded arguments and environment variables without regard to platform.
int wmain(int argc, wchar_t* argvW[], wchar_t* envpW[]) {
WindowsCommandLine wcl(argc, argvW, envpW);
int exitCode = mongoSMain(argc, wcl.argv(), wcl.envp());
::_exit(exitCode);
}
#else
int main(int argc, char* argv[], char** envp) {
int exitCode = mongoSMain(argc, argv, envp);
::_exit(exitCode);
}
#endif
#undef exit
void mongo::exitCleanly( ExitCode code ) {
// TODO: do we need to add anything?
mongo::dbexit( code );
}
void mongo::dbexit( ExitCode rc, const char *why ) {
dbexitCalled = true;
audit::logShutdown(ClientBasic::getCurrent());
#if defined(_WIN32)
if ( rc == EXIT_WINDOWS_SERVICE_STOP ) {
log() << "dbexit: exiting because Windows service was stopped" << endl;
return;
}
#endif
log() << "dbexit: " << why
<< " rc:" << rc
<< " " << ( why ? why : "" )
<< endl;
flushForGcov();
::_exit(rc);
}