// @file db.cpp : Defines main() for the mongod program.
/**
* 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 .
*/
#include "mongo/pch.h"
#include
#include
#include
#include "mongo/base/initializer.h"
#include "mongo/db/client.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/cmdline.h"
#include "mongo/db/commands/server_status.h"
#include "mongo/db/commands/fail_point_cmd.h"
#include "mongo/db/d_concurrency.h"
#include "mongo/db/d_globals.h"
#include "mongo/db/db.h"
#include "mongo/db/dbmessage.h"
#include "mongo/db/dbwebserver.h"
#include "mongo/db/dur.h"
#include "mongo/db/initialize_server_global_state.h"
#include "mongo/db/instance.h"
#include "mongo/db/introspect.h"
#include "mongo/db/json.h"
#include "mongo/db/kill_current_op.h"
#include "mongo/db/module.h"
#include "mongo/db/pdfile.h"
#include "mongo/db/repl.h"
#include "mongo/db/repl/rs.h"
#include "mongo/db/restapi.h"
#include "mongo/db/stats/counters.h"
#include "mongo/db/stats/snapshots.h"
#include "mongo/db/ttl.h"
#include "mongo/s/d_writeback.h"
#include "mongo/scripting/engine.h"
#include "mongo/util/background.h"
#include "mongo/util/concurrency/task.h"
#include "mongo/util/file_allocator.h"
#include "mongo/util/net/message_server.h"
#include "mongo/util/ntservice.h"
#include "mongo/util/ramlog.h"
#include "mongo/util/stacktrace.h"
#include "mongo/util/startup_test.h"
#include "mongo/util/text.h"
#include "mongo/util/version.h"
#if defined(_WIN32)
# include
#else
# include
#endif
namespace mongo {
namespace dur {
extern unsigned long long DataLimitPerJournalFile;
}
/* only off if --nohints */
extern bool useHints;
extern int diagLogging;
extern unsigned lenForNewNsFiles;
extern int lockFile;
extern bool checkNsFilesOnLoad;
extern string repairpath;
void setupSignals( bool inFork );
void startReplication();
void exitCleanly( ExitCode code );
#ifdef _WIN32
ntservice::NtServiceDefaultStrings defaultServiceStrings = {
L"MongoDB",
L"Mongo DB",
L"Mongo DB Server"
};
#endif
CmdLine cmdLine;
static bool scriptingEnabled = true;
static bool noHttpInterface = false;
bool shouldRepairDatabases = 0;
static bool forceRepair = 0;
Timer startupSrandTimer;
const char *ourgetns() {
Client *c = currentClient.get();
if ( ! c )
return "";
Client::Context* cc = c->getContext();
return cc ? cc->ns() : "";
}
struct MyStartupTests {
MyStartupTests() {
verify( sizeof(OID) == 12 );
}
} mystartupdbcpp;
QueryResult* emptyMoreResult(long long);
/* todo: make this a real test. the stuff in dbtests/ seem to do all dbdirectclient which exhaust doesn't support yet. */
// QueryOption_Exhaust
#define TESTEXHAUST 0
#if( TESTEXHAUST )
void testExhaust() {
sleepsecs(1);
unsigned n = 0;
auto f = [&n](const BSONObj& o) {
verify( o.valid() );
//cout << o << endl;
n++;
bool testClosingSocketOnError = false;
if( testClosingSocketOnError )
verify(false);
};
DBClientConnection db(false);
db.connect("localhost");
const char *ns = "local.foo";
if( db.count(ns) < 10000 )
for( int i = 0; i < 20000; i++ )
db.insert(ns, BSON("aaa" << 3 << "b" << "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
try {
db.query(f, ns, Query() );
}
catch(...) {
cout << "hmmm" << endl;
}
try {
db.query(f, ns, Query() );
}
catch(...) {
cout << "caught" << endl;
}
cout << n << endl;
};
#endif
void sysRuntimeInfo() {
out() << "sysinfo:" << endl;
#if defined(_SC_PAGE_SIZE)
out() << " page size: " << (int) sysconf(_SC_PAGE_SIZE) << endl;
#endif
#if defined(_SC_PHYS_PAGES)
out() << " _SC_PHYS_PAGES: " << sysconf(_SC_PHYS_PAGES) << endl;
#endif
#if defined(_SC_AVPHYS_PAGES)
out() << " _SC_AVPHYS_PAGES: " << sysconf(_SC_AVPHYS_PAGES) << endl;
#endif
}
/* if server is really busy, wait a bit */
void beNice() {
sleepmicros( Client::recommendedYieldMicros() );
}
class MyMessageHandler : public MessageHandler {
public:
virtual void connected( AbstractMessagingPort* p ) {
Client& c = Client::initThread("conn", p);
if( p->remote().isLocalHost() )
c.getAuthenticationInfo()->setIsALocalHostConnectionWithSpecialAuthPowers();
}
virtual void process( Message& m , AbstractMessagingPort* port , LastError * le) {
while ( true ) {
if ( inShutdown() ) {
log() << "got request after shutdown()" << endl;
break;
}
lastError.startRequest( m , le );
DbResponse dbresponse;
try {
assembleResponse( m, dbresponse, port->remote() );
}
catch ( const ClockSkewException & ) {
log() << "ClockSkewException - shutting down" << endl;
exitCleanly( EXIT_CLOCK_SKEW );
}
if ( dbresponse.response ) {
port->reply(m, *dbresponse.response, dbresponse.responseTo);
if( dbresponse.exhaustNS.size() > 0 ) {
MsgData *header = dbresponse.response->header();
QueryResult *qr = (QueryResult *) header;
long long cursorid = qr->cursorId;
if( cursorid ) {
verify( dbresponse.exhaustNS.size() && dbresponse.exhaustNS[0] );
string ns = dbresponse.exhaustNS; // before reset() free's it...
m.reset();
BufBuilder b(512);
b.appendNum((int) 0 /*size set later in appendData()*/);
b.appendNum(header->id);
b.appendNum(header->responseTo);
b.appendNum((int) dbGetMore);
b.appendNum((int) 0);
b.appendStr(ns);
b.appendNum((int) 0); // ntoreturn
b.appendNum(cursorid);
m.appendData(b.buf(), b.len());
b.decouple();
DEV log() << "exhaust=true sending more" << endl;
beNice();
continue; // this goes back to top loop
}
}
}
break;
}
}
virtual void disconnected( AbstractMessagingPort* p ) {
Client * c = currentClient.get();
if( c ) c->shutdown();
globalScriptEngine->threadDone();
}
};
void listen(int port) {
//testTheDb();
MessageServer::Options options;
options.port = port;
options.ipList = cmdLine.bind_ip;
MessageServer * server = createServer( options , new MyMessageHandler() );
server->setAsTimeTracker();
startReplication();
if ( !noHttpInterface )
boost::thread web( boost::bind(&webServerThread, new RestAdminAccess() /* takes ownership */));
#if(TESTEXHAUST)
boost::thread thr(testExhaust);
#endif
server->run();
}
bool doDBUpgrade( const string& dbName , string errmsg , DataFileHeader * h ) {
static DBDirectClient db;
if ( h->version == 4 && h->versionMinor == 4 ) {
verify( PDFILE_VERSION == 4 );
verify( PDFILE_VERSION_MINOR == 5 );
list colls = db.getCollectionNames( dbName );
for ( list::iterator i=colls.begin(); i!=colls.end(); i++) {
string c = *i;
log() << "\t upgrading collection:" << c << endl;
BSONObj out;
bool ok = db.runCommand( dbName , BSON( "reIndex" << c.substr( dbName.size() + 1 ) ) , out );
if ( ! ok ) {
errmsg = "reindex failed";
log() << "\t\t reindex failed: " << out << endl;
return false;
}
}
h->versionMinor = 5;
return true;
}
// do this in the general case
return repairDatabase( dbName.c_str(), errmsg );
}
// ran at startup.
static void repairDatabasesAndCheckVersion() {
// LastError * le = lastError.get( true );
Client::GodScope gs;
LOG(1) << "enter repairDatabases (to check pdfile version #)" << endl;
//verify(checkNsFilesOnLoad);
checkNsFilesOnLoad = false; // we are mainly just checking the header - don't scan the whole .ns file for every db here.
Lock::GlobalWrite lk;
vector< string > dbNames;
getDatabaseNames( dbNames );
for ( vector< string >::iterator i = dbNames.begin(); i != dbNames.end(); ++i ) {
string dbName = *i;
LOG(1) << "\t" << dbName << endl;
Client::Context ctx( dbName );
MongoDataFile *p = cc().database()->getFile( 0 );
DataFileHeader *h = p->getHeader();
if ( !h->isCurrentVersion() || forceRepair ) {
if( h->version <= 0 ) {
uasserted(14026,
str::stream() << "db " << dbName << " appears corrupt pdfile version: " << h->version
<< " info: " << h->versionMinor << ' ' << h->fileLength);
}
log() << "****" << endl;
log() << "****" << endl;
log() << "need to upgrade database " << dbName << " with pdfile version " << h->version << "." << h->versionMinor << ", "
<< "new version: " << PDFILE_VERSION << "." << PDFILE_VERSION_MINOR << endl;
if ( shouldRepairDatabases ) {
// QUESTION: Repair even if file format is higher version than code?
log() << "\t starting upgrade" << endl;
string errmsg;
verify( doDBUpgrade( dbName , errmsg , h ) );
}
else {
log() << "\t Not upgrading, exiting" << endl;
log() << "\t run --upgrade to upgrade dbs, then start again" << endl;
log() << "****" << endl;
dbexit( EXIT_NEED_UPGRADE );
shouldRepairDatabases = 1;
return;
}
}
else {
Database::closeDatabase( dbName.c_str(), dbpath );
}
}
LOG(1) << "done repairDatabases" << endl;
if ( shouldRepairDatabases ) {
log() << "finished checking dbs" << endl;
cc().shutdown();
dbexit( EXIT_CLEAN );
}
checkNsFilesOnLoad = true;
}
void clearTmpFiles() {
boost::filesystem::path path( dbpath );
for ( boost::filesystem::directory_iterator i( path );
i != boost::filesystem::directory_iterator(); ++i ) {
string fileName = boost::filesystem::path(*i).leaf().string();
if ( boost::filesystem::is_directory( *i ) &&
fileName.length() && fileName[ 0 ] == '$' )
boost::filesystem::remove_all( *i );
}
}
/**
* Checks if this server was started without --replset but has a config in local.system.replset
* (meaning that this is probably a replica set member started in stand-alone mode).
*
* @returns the number of documents in local.system.replset or 0 if this was started with
* --replset.
*/
unsigned long long checkIfReplMissingFromCommandLine() {
Lock::GlobalWrite lk; // _openAllFiles is false at this point, so this is helpful for the query below to work as you can't open files when readlocked
if( !cmdLine.usingReplSets() ) {
Client::GodScope gs;
DBDirectClient c;
return c.count("local.system.replset");
}
return 0;
}
void clearTmpCollections() {
Lock::GlobalWrite lk; // _openAllFiles is false at this point, so this is helpful for the query below to work as you can't open files when readlocked
Client::GodScope gs;
vector< string > toDelete;
DBDirectClient cli;
vector< string > dbNames;
getDatabaseNames( dbNames );
for (vector::const_iterator it(dbNames.begin()), end(dbNames.end()); it != end; ++it){
const string coll = *it + ".system.namespaces";
scoped_ptr< DBClientCursor > c (cli.query(coll, Query( fromjson( "{'options.temp': {$in: [true, 1]}}" ) ) ));
while( c->more() ) {
BSONObj o = c->next();
toDelete.push_back( o.getStringField( "name" ) );
}
}
for( vector< string >::iterator i = toDelete.begin(); i != toDelete.end(); ++i ) {
log() << "Dropping old temporary collection: " << *i << endl;
cli.dropCollection( *i );
}
}
/**
* does background async flushes of mmapped files
*/
class DataFileSync : public BackgroundJob , public ServerStatusSection {
public:
DataFileSync()
: ServerStatusSection( "backgroundFlushing" ),
_total_time( 0 ),
_flushes( 0 ),
_last() {
}
virtual bool includeByDefault() const { return true; }
virtual bool adminOnly() const { return false; }
string name() const { return "DataFileSync"; }
void run() {
Client::initThread( name().c_str() );
if( cmdLine.syncdelay == 0 )
log() << "warning: --syncdelay 0 is not recommended and can have strange performance" << endl;
else if( cmdLine.syncdelay == 1 )
log() << "--syncdelay 1" << endl;
else if( cmdLine.syncdelay != 60 )
LOG(1) << "--syncdelay " << cmdLine.syncdelay << endl;
int time_flushing = 0;
while ( ! inShutdown() ) {
_diaglog.flush();
if ( cmdLine.syncdelay == 0 ) {
// in case at some point we add an option to change at runtime
sleepsecs(5);
continue;
}
sleepmillis( (long long) std::max(0.0, (cmdLine.syncdelay * 1000) - time_flushing) );
if ( inShutdown() ) {
// occasional issue trying to flush during shutdown when sleep interrupted
break;
}
Date_t start = jsTime();
int numFiles = MemoryMappedFile::flushAll( true );
time_flushing = (int) (jsTime() - start);
_flushed(time_flushing);
if( logLevel >= 1 || time_flushing >= 10000 ) {
log() << "flushing mmaps took " << time_flushing << "ms " << " for " << numFiles << " files" << endl;
}
}
}
BSONObj generateSection( const BSONElement& configElement, bool userIsAdmin ) const {
BSONObjBuilder b;
b.appendNumber( "flushes" , _flushes );
b.appendNumber( "total_ms" , _total_time );
b.appendNumber( "average_ms" , (_flushes ? (_total_time / double(_flushes)) : 0.0) );
b.appendNumber( "last_ms" , _last_time );
b.append("last_finished", _last);
return b.obj();
}
private:
void _flushed(int ms) {
_flushes++;
_total_time += ms;
_last_time = ms;
_last = jsTime();
}
long long _total_time;
long long _flushes;
int _last_time;
Date_t _last;
} dataFileSync;
namespace {
class MemJournalServerStatusMetric : public ServerStatusMetric {
public:
MemJournalServerStatusMetric() : ServerStatusMetric( ".mem.mapped", false ) {}
virtual void appendAtLeaf( BSONObjBuilder& b ) const {
int m = static_cast(MemoryMappedFile::totalMappedLength() / ( 1024 * 1024 ));
b.appendNumber( "mapped" , m );
if ( cmdLine.dur ) {
m *= 2;
b.appendNumber( "mappedWithJournal" , m );
}
}
} memJournalServerStatusMetric;
}
const char * jsInterruptCallback() {
// should be safe to interrupt in js code, even if we have a write lock
return killCurrentOp.checkForInterruptNoAssert();
}
unsigned jsGetInterruptSpecCallback() {
return cc().curop()->opNum();
}
/// warn if readahead > 256KB (gridfs chunk size)
static void checkReadAhead(const string& dir) {
#ifdef __linux__
const dev_t dev = getPartition(dir);
// This path handles the case where the filesystem uses the whole device (including LVM)
string path = str::stream() <<
"/sys/dev/block/" << major(dev) << ':' << minor(dev) << "/queue/read_ahead_kb";
if (!boost::filesystem::exists(path)){
// This path handles the case where the filesystem is on a partition.
path = str::stream()
<< "/sys/dev/block/" << major(dev) << ':' << minor(dev) // this is a symlink
<< "/.." // parent directory of a partition is for the whole device
<< "/queue/read_ahead_kb";
}
if (boost::filesystem::exists(path)) {
ifstream file (path.c_str());
if (file.is_open()) {
int kb;
file >> kb;
if (kb > 256) {
log() << startupWarningsLog;
log() << "** WARNING: Readahead for " << dir << " is set to " << kb << "KB"
<< startupWarningsLog;
log() << "** We suggest setting it to 256KB (512 sectors) or less"
<< startupWarningsLog;
log() << "** http://www.mongodb.org/display/DOCS/Readahead"
<< startupWarningsLog;
}
}
}
#endif // __linux__
}
void _initAndListen(int listenPort ) {
Client::initThread("initandlisten");
Database::_openAllFiles = false;
Logstream::get().addGlobalTee( new RamLog("global") );
bool is32bit = sizeof(int*) == 4;
{
#if !defined(_WIN32)
pid_t pid = getpid();
#else
DWORD pid=GetCurrentProcessId();
#endif
Nullstream& l = log();
l << "MongoDB starting : pid=" << pid << " port=" << cmdLine.port << " dbpath=" << dbpath;
if( replSettings.master ) l << " master=" << replSettings.master;
if( replSettings.slave ) l << " slave=" << (int) replSettings.slave;
l << ( is32bit ? " 32" : " 64" ) << "-bit host=" << getHostNameCached() << endl;
}
DEV log() << "_DEBUG build (which is slower)" << endl;
show_warnings();
log() << mongodVersion() << endl;
printGitVersion();
printSysInfo();
printCommandLineOpts();
{
stringstream ss;
ss << endl;
ss << "*********************************************************************" << endl;
ss << " ERROR: dbpath (" << dbpath << ") does not exist." << endl;
ss << " Create this directory or give existing directory in --dbpath." << endl;
ss << " See http://dochub.mongodb.org/core/startingandstoppingmongo" << endl;
ss << "*********************************************************************" << endl;
uassert( 10296 , ss.str().c_str(), boost::filesystem::exists( dbpath ) );
}
{
stringstream ss;
ss << "repairpath (" << repairpath << ") does not exist";
uassert( 12590 , ss.str().c_str(), boost::filesystem::exists( repairpath ) );
}
// TODO check non-journal subdirs if using directory-per-db
checkReadAhead(dbpath);
acquirePathLock(forceRepair);
boost::filesystem::remove_all( dbpath + "/_tmp/" );
FileAllocator::get()->start();
MONGO_ASSERT_ON_EXCEPTION_WITH_MSG( clearTmpFiles(), "clear tmp files" );
dur::startup();
if( cmdLine.durOptions & CmdLine::DurRecoverOnly )
return;
// comes after getDur().startup() because this reads from the database
clearTmpCollections();
unsigned long long missingRepl = checkIfReplMissingFromCommandLine();
if (missingRepl) {
log() << startupWarningsLog;
log() << "** WARNING: mongod started without --replSet yet " << missingRepl
<< " documents are present in local.system.replset" << startupWarningsLog;
log() << "** Restart with --replSet unless you are doing maintenance and no"
<< " other clients are connected." << startupWarningsLog;
log() << "** The TTL collection monitor will not start because of this." << startupWarningsLog;
log() << "** For more info see http://www.mongodb.org/display/DOCS/TTL+Monitor" << startupWarningsLog;
log() << startupWarningsLog;
}
Module::initAll();
if ( scriptingEnabled ) {
ScriptEngine::setup();
globalScriptEngine->setCheckInterruptCallback( jsInterruptCallback );
globalScriptEngine->setGetInterruptSpecCallback( jsGetInterruptSpecCallback );
}
repairDatabasesAndCheckVersion();
/* we didn't want to pre-open all files for the repair check above. for regular
operation we do for read/write lock concurrency reasons.
*/
Database::_openAllFiles = true;
if ( shouldRepairDatabases )
return;
/* this is for security on certain platforms (nonce generation) */
srand((unsigned) (curTimeMicros() ^ startupSrandTimer.micros()));
snapshotThread.go();
d.clientCursorMonitor.go();
PeriodicTask::theRunner->go();
if (missingRepl) {
// a warning was logged earlier
}
else {
startTTLBackgroundJob();
}
#ifndef _WIN32
CmdLine::launchOk();
#endif
if( !noauth ) {
// open admin db in case we need to use it later. TODO this is not the right way to
// resolve this.
Client::WriteContext c("admin",dbpath,false);
}
listen(listenPort);
// listen() will return when exit code closes its socket.
exitCleanly(EXIT_NET_ERROR);
}
void testPretouch();
void initAndListen(int listenPort) {
try {
_initAndListen(listenPort);
}
catch ( DBException &e ) {
log() << "exception in initAndListen: " << e.toString() << ", terminating" << endl;
dbexit( EXIT_UNCAUGHT );
}
catch ( std::exception &e ) {
log() << "exception in initAndListen std::exception: " << e.what() << ", terminating" << endl;
dbexit( EXIT_UNCAUGHT );
}
catch ( int& n ) {
log() << "exception in initAndListen int: " << n << ", terminating" << endl;
dbexit( EXIT_UNCAUGHT );
}
catch(...) {
log() << "exception in initAndListen, terminating" << endl;
dbexit( EXIT_UNCAUGHT );
}
}
#if defined(_WIN32)
void initService() {
ntservice::reportStatus( SERVICE_RUNNING );
log() << "Service running" << endl;
initAndListen( cmdLine.port );
}
#endif
} // namespace mongo
using namespace mongo;
#include
namespace po = boost::program_options;
void show_help_text(po::options_description options) {
show_warnings();
cout << options << endl;
};
static int mongoDbMain(int argc, char* argv[], char** envp);
int main(int argc, char* argv[], char** envp) {
int exitCode = mongoDbMain(argc, argv, envp);
::_exit(exitCode);
}
static void buildOptionsDescriptions(po::options_description *pVisible,
po::options_description *pHidden,
po::positional_options_description *pPositional) {
po::options_description& visible_options = *pVisible;
po::options_description& hidden_options = *pHidden;
po::positional_options_description& positional_options = *pPositional;
po::options_description general_options("General options");
#if defined(_WIN32)
po::options_description windows_scm_options("Windows Service Control Manager options");
#endif
po::options_description replication_options("Replication options");
po::options_description ms_options("Master/slave options");
po::options_description rs_options("Replica set options");
po::options_description sharding_options("Sharding options");
po::options_description ssl_options("SSL options");
CmdLine::addGlobalOptions( general_options , hidden_options , ssl_options );
StringBuilder dbpathBuilder;
dbpathBuilder << "directory for datafiles - defaults to " << dbpath;
general_options.add_options()
("auth", "run with security")
("cpu", "periodically show cpu and iowait utilization")
("dbpath", po::value() , dbpathBuilder.str().c_str())
("diaglog", po::value(), "0=off 1=W 2=R 3=both 7=W+some reads")
("directoryperdb", "each database will be stored in a separate directory")
("ipv6", "enable IPv6 support (disabled by default)")
("journal", "enable journaling")
("journalCommitInterval", po::value(), "how often to group/batch commit (ms)")
("journalOptions", po::value(), "journal diagnostic options")
("jsonp","allow JSONP access via http (has security implications)")
("noauth", "run without security")
("nohttpinterface", "disable http interface")
("nojournal", "disable journaling (journaling is on by default for 64 bit)")
("noprealloc", "disable data file preallocation - will often hurt performance")
("noscripting", "disable scripting engine")
("notablescan", "do not allow table scans")
("nssize", po::value()->default_value(16), ".ns file size (in MB) for new databases")
("profile",po::value(), "0=off 1=slow, 2=all")
("quota", "limits each database to a certain number of files (8 default)")
("quotaFiles", po::value(), "number of files allowed per db, requires --quota")
("repair", "run repair on all dbs")
("repairpath", po::value() , "root directory for repair files - defaults to dbpath" )
("rest","turn on simple rest api")
#if defined(__linux__)
("shutdown", "kill a running server (for init scripts)")
#endif
("slowms",po::value(&cmdLine.slowMS)->default_value(100), "value of slow for profile and console log" )
("smallfiles", "use a smaller default file size")
("syncdelay",po::value(&cmdLine.syncdelay)->default_value(60), "seconds between disk syncs (0=never, but not recommended)")
("sysinfo", "print some diagnostic system information")
("upgrade", "upgrade db if needed")
;
#if defined(_WIN32)
CmdLine::addWindowsOptions( windows_scm_options, hidden_options );
#endif
replication_options.add_options()
("oplogSize", po::value(), "size to use (in MB) for replication op log. default is 5% of disk space (i.e. large is good)")
;
ms_options.add_options()
("master", "master mode")
("slave", "slave mode")
("source", po::value(), "when slave: specify master as ")
("only", po::value(), "when slave: specify a single database to replicate")
("slavedelay", po::value(), "specify delay (in seconds) to be used when applying master ops to slave")
("autoresync", "automatically resync if slave data is stale")
;
rs_options.add_options()
("replSet", po::value(), "arg is [/]")
("replIndexPrefetch", po::value(), "specify index prefetching behavior (if secondary) [none|_id_only|all]")
;
sharding_options.add_options()
("configsvr", "declare this is a config db of a cluster; default port 27019; default dir /data/configdb")
("shardsvr", "declare this is a shard db of a cluster; default port 27018")
("noMoveParanoia" , "turn off paranoid saving of data for moveChunk. this is on by default for now, but default will switch" )
;
hidden_options.add_options()
("fastsync", "indicate that this instance is starting from a dbpath snapshot of the repl peer")
("pretouch", po::value(), "n pretouch threads for applying replicationed operations") // experimental
("command", po::value< vector >(), "command")
("cacheSize", po::value(), "cache size (in MB) for rec store")
("nodur", "disable journaling")
// things we don't want people to use
("nohints", "ignore query hints")
("nopreallocj", "don't preallocate journal files")
("dur", "enable journaling") // old name for --journal
("durOptions", po::value(), "durability diagnostic options") // deprecated name
// deprecated pairing command line options
("pairwith", "DEPRECATED")
("arbiter", "DEPRECATED")
("opIdMem", "DEPRECATED")
;
positional_options.add("command", 3);
visible_options.add(general_options);
#if defined(_WIN32)
visible_options.add(windows_scm_options);
#endif
visible_options.add(replication_options);
visible_options.add(ms_options);
visible_options.add(rs_options);
visible_options.add(sharding_options);
#ifdef MONGO_SSL
visible_options.add(ssl_options);
#endif
Module::addOptions( visible_options );
}
static void processCommandLineOptions(const std::vector& argv) {
po::options_description visible_options("Allowed options");
po::options_description hidden_options("Hidden options");
po::positional_options_description positional_options;
buildOptionsDescriptions(&visible_options, &hidden_options, &positional_options);
{
po::variables_map params;
if (!CmdLine::store(argv,
visible_options,
hidden_options,
positional_options,
params)) {
::_exit(EXIT_FAILURE);
}
if (params.count("help")) {
show_help_text(visible_options);
::_exit(EXIT_SUCCESS);
}
if (params.count("version")) {
cout << mongodVersion() << endl;
printGitVersion();
::_exit(EXIT_SUCCESS);
}
if (params.count("sysinfo")) {
sysRuntimeInfo();
::_exit(EXIT_SUCCESS);
}
if ( params.count( "dbpath" ) ) {
dbpath = params["dbpath"].as();
if ( params.count( "fork" ) && dbpath[0] != '/' ) {
// we need to change dbpath if we fork since we change
// cwd to "/"
// fork only exists on *nix
// so '/' is safe
dbpath = cmdLine.cwd + "/" + dbpath;
}
}
#ifdef _WIN32
if (dbpath.size() > 1 && dbpath[dbpath.size()-1] == '/') {
// size() check is for the unlikely possibility of --dbpath "/"
dbpath = dbpath.erase(dbpath.size()-1);
}
#endif
if ( params.count("directoryperdb")) {
directoryperdb = true;
}
if (params.count("cpu")) {
cmdLine.cpu = true;
}
if (params.count("noauth")) {
noauth = true;
}
if (params.count("auth")) {
noauth = false;
}
if (params.count("quota")) {
cmdLine.quota = true;
}
if (params.count("quotaFiles")) {
cmdLine.quota = true;
cmdLine.quotaFiles = params["quotaFiles"].as() - 1;
}
bool journalExplicit = false;
if( params.count("nodur") || params.count( "nojournal" ) ) {
journalExplicit = true;
cmdLine.dur = false;
}
if( params.count("dur") || params.count( "journal" ) ) {
if (journalExplicit) {
log() << "Can't specify both --journal and --nojournal options." << endl;
::_exit(EXIT_BADOPTIONS);
}
journalExplicit = true;
cmdLine.dur = true;
}
if (params.count("durOptions")) {
cmdLine.durOptions = params["durOptions"].as();
}
if( params.count("journalCommitInterval") ) {
// don't check if dur is false here as many will just use the default, and will default to off on win32.
// ie no point making life a little more complex by giving an error on a dev environment.
cmdLine.journalCommitInterval = params["journalCommitInterval"].as();
if( cmdLine.journalCommitInterval <= 1 || cmdLine.journalCommitInterval > 300 ) {
out() << "--journalCommitInterval out of allowed range (0-300ms)" << endl;
dbexit( EXIT_BADOPTIONS );
}
}
if (params.count("journalOptions")) {
cmdLine.durOptions = params["journalOptions"].as();
}
if (params.count("repairpath")) {
repairpath = params["repairpath"].as();
if (!repairpath.size()) {
out() << "repairpath is empty" << endl;
dbexit( EXIT_BADOPTIONS );
}
if (cmdLine.dur && !str::startsWith(repairpath, dbpath)) {
out() << "You must use a --repairpath that is a subdirectory of --dbpath when using journaling" << endl;
dbexit( EXIT_BADOPTIONS );
}
}
if (params.count("nohints")) {
useHints = false;
}
if (params.count("nopreallocj")) {
cmdLine.preallocj = false;
}
if (params.count("nohttpinterface")) {
noHttpInterface = true;
}
if (params.count("rest")) {
cmdLine.rest = true;
}
if (params.count("jsonp")) {
cmdLine.jsonp = true;
}
if (params.count("noscripting")) {
scriptingEnabled = false;
}
if (params.count("noprealloc")) {
cmdLine.prealloc = false;
cout << "note: noprealloc may hurt performance in many applications" << endl;
}
if (params.count("smallfiles")) {
cmdLine.smallfiles = true;
verify( dur::DataLimitPerJournalFile >= 128 * 1024 * 1024 );
dur::DataLimitPerJournalFile = 128 * 1024 * 1024;
}
if (params.count("diaglog")) {
int x = params["diaglog"].as();
if ( x < 0 || x > 7 ) {
out() << "can't interpret --diaglog setting" << endl;
dbexit( EXIT_BADOPTIONS );
}
_diaglog.setLevel(x);
}
if (params.count("repair")) {
if (journalExplicit && cmdLine.dur) {
log() << "Can't specify both --journal and --repair options." << endl;
::_exit(EXIT_BADOPTIONS);
}
Record::MemoryTrackingEnabled = false;
shouldRepairDatabases = 1;
forceRepair = 1;
cmdLine.dur = false;
}
if (params.count("upgrade")) {
Record::MemoryTrackingEnabled = false;
shouldRepairDatabases = 1;
}
if (params.count("notablescan")) {
cmdLine.noTableScan = true;
}
if (params.count("master")) {
replSettings.master = true;
}
if (params.count("slave")) {
replSettings.slave = SimpleSlave;
}
if (params.count("slavedelay")) {
replSettings.slavedelay = params["slavedelay"].as();
}
if (params.count("fastsync")) {
replSettings.fastsync = true;
}
if (params.count("autoresync")) {
replSettings.autoresync = true;
if( params.count("replSet") ) {
out() << "--autoresync is not used with --replSet" << endl;
out() << "see http://dochub.mongodb.org/core/resyncingaverystalereplicasetmember" << endl;
dbexit( EXIT_BADOPTIONS );
}
}
if (params.count("source")) {
/* specifies what the source in local.sources should be */
cmdLine.source = params["source"].as().c_str();
}
if( params.count("pretouch") ) {
cmdLine.pretouch = params["pretouch"].as();
}
if (params.count("replSet")) {
if (params.count("slavedelay")) {
out() << "--slavedelay cannot be used with --replSet" << endl;
dbexit( EXIT_BADOPTIONS );
}
else if (params.count("only")) {
out() << "--only cannot be used with --replSet" << endl;
dbexit( EXIT_BADOPTIONS );
}
/* seed list of hosts for the repl set */
cmdLine._replSet = params["replSet"].as().c_str();
}
if (params.count("replIndexPrefetch")) {
cmdLine.rsIndexPrefetch = params["replIndexPrefetch"].as();
}
if (params.count("only")) {
cmdLine.only = params["only"].as().c_str();
}
if( params.count("nssize") ) {
int x = params["nssize"].as();
if (x <= 0 || x > (0x7fffffff/1024/1024)) {
out() << "bad --nssize arg" << endl;
dbexit( EXIT_BADOPTIONS );
}
lenForNewNsFiles = x * 1024 * 1024;
verify(lenForNewNsFiles > 0);
}
if (params.count("oplogSize")) {
long long x = params["oplogSize"].as();
if (x <= 0) {
out() << "bad --oplogSize arg" << endl;
dbexit( EXIT_BADOPTIONS );
}
// note a small size such as x==1 is ok for an arbiter.
if( x > 1000 && sizeof(void*) == 4 ) {
out() << "--oplogSize of " << x << "MB is too big for 32 bit version. Use 64 bit build instead." << endl;
dbexit( EXIT_BADOPTIONS );
}
cmdLine.oplogSize = x * 1024 * 1024;
verify(cmdLine.oplogSize > 0);
}
if (params.count("cacheSize")) {
long x = params["cacheSize"].as();
if (x <= 0) {
out() << "bad --cacheSize arg" << endl;
dbexit( EXIT_BADOPTIONS );
}
log() << "--cacheSize option not currently supported" << endl;
}
if (params.count("port") == 0 ) {
if( params.count("configsvr") ) {
cmdLine.port = CmdLine::ConfigServerPort;
}
if( params.count("shardsvr") ) {
if( params.count("configsvr") ) {
log() << "can't do --shardsvr and --configsvr at the same time" << endl;
dbexit( EXIT_BADOPTIONS );
}
cmdLine.port = CmdLine::ShardServerPort;
}
}
else {
if ( cmdLine.port <= 0 || cmdLine.port > 65535 ) {
out() << "bad --port number" << endl;
dbexit( EXIT_BADOPTIONS );
}
}
if ( params.count("configsvr" ) ) {
cmdLine.configsvr = true;
cmdLine.smallfiles = true; // config server implies small files
dur::DataLimitPerJournalFile = 128 * 1024 * 1024;
if (cmdLine.usingReplSets() || replSettings.master || replSettings.slave) {
log() << "replication should not be enabled on a config server" << endl;
::_exit(-1);
}
if ( params.count( "nodur" ) == 0 && params.count( "nojournal" ) == 0 )
cmdLine.dur = true;
if ( params.count( "dbpath" ) == 0 )
dbpath = "/data/configdb";
}
if ( params.count( "profile" ) ) {
cmdLine.defaultProfile = params["profile"].as();
}
if (params.count("ipv6")) {
enableIPv6();
}
if (params.count("noMoveParanoia")) {
cmdLine.moveParanoia = false;
}
if (params.count("pairwith") || params.count("arbiter") || params.count("opIdMem")) {
out() << "****" << endl;
out() << "Replica Pairs have been deprecated. Invalid options: --pairwith, --arbiter, and/or --opIdMem" << endl;
out() << "" << endl;
out() << "****" << endl;
dbexit( EXIT_BADOPTIONS );
}
// needs to be after things like --configsvr parsing, thus here.
if( repairpath.empty() )
repairpath = dbpath;
// The "command" option is deprecated. For backward compatibility, still support the "run"
// and "dbppath" command. The "run" command is the same as just running mongod, so just
// falls through.
if (params.count("command")) {
vector command = params["command"].as< vector >();
if (command[0].compare("dbpath") == 0) {
cout << dbpath << endl;
::_exit(EXIT_SUCCESS);
}
if (command[0].compare("run") != 0) {
cout << "Invalid command: " << command[0] << endl;
cout << visible_options << endl;
::_exit(EXIT_FAILURE);
}
if (command.size() > 1) {
cout << "Too many parameters to 'run' command" << endl;
cout << visible_options << endl;
::_exit(EXIT_FAILURE);
}
}
if( cmdLine.pretouch )
log() << "--pretouch " << cmdLine.pretouch << endl;
if (sizeof(void*) == 4 && !journalExplicit){
// trying to make this stand out more like startup warnings
log() << endl;
warning() << "32-bit servers don't have journaling enabled by default. Please use --journal if you want durability." << endl;
log() << endl;
}
if (params.count("enableFaultInjection")) {
enableFailPointCmd();
}
Module::configAll(params);
#ifdef _WIN32
ntservice::configureService(initService,
params,
defaultServiceStrings,
std::vector(),
argv);
#endif // _WIN32
#ifdef __linux__
if (params.count("shutdown")){
bool failed = false;
string name = ( boost::filesystem::path( dbpath ) / "mongod.lock" ).string();
if ( !boost::filesystem::exists( name ) || boost::filesystem::file_size( name ) == 0 )
failed = true;
pid_t pid;
string procPath;
if (!failed){
try {
ifstream f (name.c_str());
f >> pid;
procPath = (str::stream() << "/proc/" << pid);
if (!boost::filesystem::exists(procPath))
failed = true;
}
catch (const std::exception& e){
cerr << "Error reading pid from lock file [" << name << "]: " << e.what() << endl;
failed = true;
}
}
if (failed) {
cerr << "There doesn't seem to be a server running with dbpath: " << dbpath << endl;
::_exit(EXIT_FAILURE);
}
cout << "killing process with pid: " << pid << endl;
int ret = kill(pid, SIGTERM);
if (ret) {
int e = errno;
cerr << "failed to kill process: " << errnoWithDescription(e) << endl;
::_exit(EXIT_FAILURE);
}
while (boost::filesystem::exists(procPath)) {
sleepsecs(1);
}
::_exit(EXIT_SUCCESS);
}
#endif
}
}
static int mongoDbMain(int argc, char* argv[], char **envp) {
static StaticObserver staticObserver;
getcurns = ourgetns;
setupCoreSignals();
setupSignals( false );
dbExecCommand = argv[0];
srand(curTimeMicros());
{
unsigned x = 0x12345678;
unsigned char& b = (unsigned char&) x;
if ( b != 0x78 ) {
out() << "big endian cpus not yet supported" << endl;
return 33;
}
}
if( argc == 1 )
cout << dbExecCommand << " --help for help and startup options" << endl;
processCommandLineOptions(std::vector(argv, argv + argc));
mongo::runGlobalInitializersOrDie(argc, argv, envp);
CmdLine::censor(argc, argv);
if (!initializeServerGlobalState())
::_exit(EXIT_FAILURE);
dataFileSync.go();
#if defined(_WIN32)
if (ntservice::shouldStartService()) {
ntservice::startService();
// exits directly and so never reaches here either.
}
#endif
StartupTest::runTests();
initAndListen(cmdLine.port);
dbexit(EXIT_CLEAN);
return 0;
}
namespace mongo {
string getDbContext();
#undef out
#if !defined(_WIN32)
} // namespace mongo
#include
#include
namespace mongo {
void abruptQuit(int x) {
ostringstream ossSig;
ossSig << "Got signal: " << x << " (" << strsignal( x ) << ")." << endl;
rawOut( ossSig.str() );
/*
ostringstream ossOp;
ossOp << "Last op: " << currentOp.infoNoauth() << endl;
rawOut( ossOp.str() );
*/
ostringstream oss;
oss << "Backtrace:" << endl;
printStackTrace( oss );
rawOut( oss.str() );
// Don't go through normal shutdown procedure. It may make things worse.
::_exit(EXIT_ABRUPT);
}
void abruptQuitWithAddrSignal( int signal, siginfo_t *siginfo, void * ) {
ostringstream oss;
oss << "Invalid";
if ( signal == SIGSEGV || signal == SIGBUS ) {
oss << " access";
} else {
oss << " operation";
}
oss << " at address: " << siginfo->si_addr << " from thread: " << getThreadName() << endl;
rawOut( oss.str() );
abruptQuit( signal );
}
sigset_t asyncSignals;
// The above signals will be processed by this thread only, in order to
// ensure the db and log mutexes aren't held.
void interruptThread() {
int actualSignal;
sigwait( &asyncSignals, &actualSignal );
log() << "got signal " << actualSignal << " (" << strsignal( actualSignal )
<< "), will terminate after current cmd ends" << endl;
Client::initThread( "interruptThread" );
exitCleanly( EXIT_CLEAN );
}
// this will be called in certain c++ error cases, for example if there are two active
// exceptions
void myterminate() {
rawOut( "terminate() called, printing stack (if implemented for platform):" );
printStackTrace();
::abort();
}
// this gets called when new fails to allocate memory
void my_new_handler() {
rawOut( "out of memory, printing stack and exiting:" );
printStackTrace();
::_exit(EXIT_ABRUPT);
}
void setupSignals_ignoreHelper( int signal ) {}
void setupSignals( bool inFork ) {
struct sigaction addrSignals;
memset( &addrSignals, 0, sizeof( struct sigaction ) );
addrSignals.sa_sigaction = abruptQuitWithAddrSignal;
sigemptyset( &addrSignals.sa_mask );
addrSignals.sa_flags = SA_SIGINFO;
verify( sigaction(SIGSEGV, &addrSignals, 0) == 0 );
verify( sigaction(SIGBUS, &addrSignals, 0) == 0 );
verify( sigaction(SIGILL, &addrSignals, 0) == 0 );
verify( sigaction(SIGFPE, &addrSignals, 0) == 0 );
verify( signal(SIGABRT, abruptQuit) != SIG_ERR );
verify( signal(SIGQUIT, abruptQuit) != SIG_ERR );
verify( signal(SIGPIPE, SIG_IGN) != SIG_ERR );
setupSIGTRAPforGDB();
sigemptyset( &asyncSignals );
if ( inFork )
verify( signal( SIGHUP , setupSignals_ignoreHelper ) != SIG_ERR );
else
sigaddset( &asyncSignals, SIGHUP );
sigaddset( &asyncSignals, SIGINT );
sigaddset( &asyncSignals, SIGTERM );
verify( pthread_sigmask( SIG_SETMASK, &asyncSignals, 0 ) == 0 );
boost::thread it( interruptThread );
set_terminate( myterminate );
set_new_handler( my_new_handler );
}
#else // WIN32
void consoleTerminate( const char* controlCodeName ) {
Client::initThread( "consoleTerminate" );
log() << "got " << controlCodeName << ", will terminate after current cmd ends" << endl;
exitCleanly( EXIT_KILL );
}
BOOL CtrlHandler( DWORD fdwCtrlType ) {
switch( fdwCtrlType ) {
case CTRL_C_EVENT:
rawOut( "Ctrl-C signal" );
consoleTerminate( "CTRL_C_EVENT" );
return TRUE ;
case CTRL_CLOSE_EVENT:
rawOut( "CTRL_CLOSE_EVENT signal" );
consoleTerminate( "CTRL_CLOSE_EVENT" );
return TRUE ;
case CTRL_BREAK_EVENT:
rawOut( "CTRL_BREAK_EVENT signal" );
consoleTerminate( "CTRL_BREAK_EVENT" );
return TRUE;
case CTRL_LOGOFF_EVENT:
// only sent to services, and only in pre-Vista Windows; FALSE means ignore
return FALSE;
case CTRL_SHUTDOWN_EVENT:
rawOut( "CTRL_SHUTDOWN_EVENT signal" );
consoleTerminate( "CTRL_SHUTDOWN_EVENT" );
return TRUE;
default:
return FALSE;
}
}
LPTOP_LEVEL_EXCEPTION_FILTER filtLast = 0;
/* create a process dump.
To use, load up windbg. Set your symbol and source path.
Open the crash dump file. To see the crashing context, use .ecxr
*/
void doMinidump(struct _EXCEPTION_POINTERS* exceptionInfo) {
LPCWSTR dumpFilename = L"mongo.dmp";
HANDLE hFile = CreateFileW(dumpFilename,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if ( INVALID_HANDLE_VALUE == hFile ) {
DWORD lasterr = GetLastError();
log() << "failed to open minidump file " << toUtf8String(dumpFilename) << " : "
<< errnoWithDescription( lasterr ) << endl;
return;
}
MINIDUMP_EXCEPTION_INFORMATION aMiniDumpInfo;
aMiniDumpInfo.ThreadId = GetCurrentThreadId();
aMiniDumpInfo.ExceptionPointers = exceptionInfo;
aMiniDumpInfo.ClientPointers = TRUE;
log() << "writing minidump diagnostic file " << toUtf8String(dumpFilename) << endl;
BOOL bstatus = MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
&aMiniDumpInfo,
NULL,
NULL);
if ( FALSE == bstatus ) {
DWORD lasterr = GetLastError();
log() << "failed to create minidump : "
<< errnoWithDescription( lasterr ) << endl;
}
CloseHandle(hFile);
}
LONG WINAPI exceptionFilter( struct _EXCEPTION_POINTERS *excPointers ) {
char exceptionString[128];
sprintf_s( exceptionString, sizeof( exceptionString ),
( excPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ) ?
"(access violation)" : "0x%08X", excPointers->ExceptionRecord->ExceptionCode );
char addressString[32];
sprintf_s( addressString, sizeof( addressString ), "0x%p",
excPointers->ExceptionRecord->ExceptionAddress );
log() << "*** unhandled exception " << exceptionString <<
" at " << addressString << ", terminating" << endl;
if ( excPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ) {
ULONG acType = excPointers->ExceptionRecord->ExceptionInformation[0];
const char* acTypeString;
switch ( acType ) {
case 0:
acTypeString = "read from";
break;
case 1:
acTypeString = "write to";
break;
case 8:
acTypeString = "DEP violation at";
break;
default:
acTypeString = "unknown violation at";
break;
}
sprintf_s( addressString, sizeof( addressString ), " 0x%p",
excPointers->ExceptionRecord->ExceptionInformation[1] );
log() << "*** access violation was a " << acTypeString << addressString << endl;
}
log() << "*** stack trace for unhandled exception:" << endl;
printWindowsStackTrace( *excPointers->ContextRecord );
doMinidump(excPointers);
// Don't go through normal shutdown procedure. It may make things worse.
log() << "*** immediate exit due to unhandled exception" << endl;
::_exit(EXIT_ABRUPT);
// We won't reach here
return EXCEPTION_EXECUTE_HANDLER;
}
// called by mongoAbort()
extern void (*reportEventToSystem)(const char *msg);
void reportEventToSystemImpl(const char *msg) {
static ::HANDLE hEventLog = RegisterEventSource( NULL, TEXT("mongod") );
if( hEventLog ) {
std::wstring s = toNativeString(msg);
LPCTSTR txt = s.c_str();
BOOL ok = ReportEvent(
hEventLog, EVENTLOG_ERROR_TYPE,
0, 0, NULL,
1,
0,
&txt,
0);
wassert(ok);
}
}
void myPurecallHandler() {
printStackTrace();
mongoAbort("pure virtual");
}
void setupSignals( bool inFork ) {
reportEventToSystem = reportEventToSystemImpl;
filtLast = SetUnhandledExceptionFilter(exceptionFilter);
massert(10297 , "Couldn't register Windows Ctrl-C handler", SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE));
_set_purecall_handler( myPurecallHandler );
}
#endif // if !defined(_WIN32)
} // namespace mongo