summaryrefslogtreecommitdiff
path: root/src/mongo/db/db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/db.cpp')
-rw-r--r--src/mongo/db/db.cpp1309
1 files changed, 1309 insertions, 0 deletions
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
new file mode 100644
index 00000000000..af03b447976
--- /dev/null
+++ b/src/mongo/db/db.cpp
@@ -0,0 +1,1309 @@
+// @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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+#include "db.h"
+#include "introspect.h"
+#include "repl.h"
+#include "../util/unittest.h"
+#include "../util/file_allocator.h"
+#include "../util/background.h"
+#include "../util/text.h"
+#include "dbmessage.h"
+#include "instance.h"
+#include "clientcursor.h"
+#include "pdfile.h"
+#include "stats/counters.h"
+#include "repl/rs.h"
+#include "../scripting/engine.h"
+#include "module.h"
+#include "cmdline.h"
+#include "stats/snapshots.h"
+#include "../util/concurrency/task.h"
+#include "../util/version.h"
+#include "../util/ramlog.h"
+#include "../util/net/message_server.h"
+#include "client.h"
+#include "restapi.h"
+#include "dbwebserver.h"
+#include "dur.h"
+#include "concurrency.h"
+#include "../s/d_writeback.h"
+#include "d_globals.h"
+
+#if defined(_WIN32)
+# include "../util/ntservice.h"
+#else
+# include <sys/file.h>
+#endif
+
+namespace mongo {
+
+ namespace dur {
+ extern unsigned long long DataLimitPerJournalFile;
+ }
+
+ /* only off if --nocursors which is for debugging. */
+ extern bool useCursors;
+
+ /* 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 );
+
+ CmdLine cmdLine;
+ static bool scriptingEnabled = true;
+ 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() {
+ assert( 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) {
+ assert( o.valid() );
+ //cout << o << endl;
+ n++;
+ bool testClosingSocketOnError = false;
+ if( testClosingSocketOnError )
+ assert(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);
+ c.getAuthenticationInfo()->isLocalHost = p->remote().isLocalHost();
+ }
+
+ 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;
+ assembleResponse( m, dbresponse, port->remote() );
+
+ if ( dbresponse.response ) {
+ port->reply(m, *dbresponse.response, dbresponse.responseTo);
+ if( dbresponse.exhaust ) {
+ MsgData *header = dbresponse.response->header();
+ QueryResult *qr = (QueryResult *) header;
+ long long cursorid = qr->cursorId;
+ if( cursorid ) {
+ assert( dbresponse.exhaust && *dbresponse.exhaust != 0 );
+ string ns = dbresponse.exhaust; // 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 ) {
+ assert( PDFILE_VERSION == 4 );
+ assert( PDFILE_VERSION_MINOR == 5 );
+
+ list<string> colls = db.getCollectionNames( dbName );
+ for ( list<string>::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;
+
+ //assert(checkNsFilesOnLoad);
+ checkNsFilesOnLoad = false; // we are mainly just checking the header - don't scan the whole .ns file for every db here.
+
+ dblock 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;
+ assert( 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();
+ if ( boost::filesystem::is_directory( *i ) &&
+ fileName.length() && fileName[ 0 ] == '$' )
+ boost::filesystem::remove_all( *i );
+ }
+ }
+
+ void checkIfReplMissingFromCommandLine() {
+ if( !cmdLine.usingReplSets() ) {
+ Client::GodScope gs;
+ DBDirectClient c;
+ unsigned long long x =
+ c.count("local.system.replset");
+ if( x ) {
+ log() << endl;
+ log() << "** warning: mongod started without --replSet yet " << x << " documents are present in local.system.replset" << endl;
+ log() << "** restart with --replSet unless you are doing maintenance and no other clients are connected" << endl;
+ log() << endl;
+ }
+ }
+ }
+
+ void clearTmpCollections() {
+ writelock 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;
+ auto_ptr< DBClientCursor > c = cli.query( "local.system.namespaces", Query( fromjson( "{name:/^local.temp./}" ) ) );
+ 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:
+ string name() const { return "DataFileSync"; }
+ void run() {
+ 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);
+
+ globalFlushCounters.flushed(time_flushing);
+
+ if( logLevel >= 1 || time_flushing >= 10000 ) {
+ log() << "flushing mmaps took " << time_flushing << "ms " << " for " << numFiles << " files" << endl;
+ }
+ }
+ }
+
+ } dataFileSync;
+
+ 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();
+ }
+
+ 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://www.mongodb.org/display/DOCS/Starting+and+Stopping+Mongo" << 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 ) );
+ }
+
+ acquirePathLock(forceRepair);
+ remove_all( dbpath + "/_tmp/" );
+
+ FileAllocator::get()->start();
+
+ MONGO_BOOST_CHECK_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();
+
+ checkIfReplMissingFromCommandLine();
+
+ 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();
+
+#ifndef _WIN32
+ CmdLine::launchOk();
+#endif
+ 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)
+ bool initService() {
+ ServiceController::reportStatus( SERVICE_RUNNING );
+ initAndListen( cmdLine.port );
+ return true;
+ }
+#endif
+
+} // namespace mongo
+
+using namespace mongo;
+
+#include <boost/program_options.hpp>
+#undef assert
+#define assert MONGO_assert
+
+namespace po = boost::program_options;
+
+void show_help_text(po::options_description options) {
+ show_warnings();
+ cout << options << endl;
+};
+
+/* Return error string or "" if no errors. */
+string arg_error_check(int argc, char* argv[]) {
+ return "";
+}
+
+int main(int argc, char* argv[]) {
+ static StaticObserver staticObserver;
+ doPreServerStartupInits();
+ getcurns = ourgetns;
+
+ 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 visible_options("Allowed options");
+ po::options_description hidden_options("Hidden options");
+
+ po::positional_options_description positional_options;
+
+ CmdLine::addGlobalOptions( general_options , hidden_options );
+
+ general_options.add_options()
+ ("auth", "run with security")
+ ("cpu", "periodically show cpu and iowait utilization")
+ ("dbpath", po::value<string>() , "directory for datafiles")
+ ("diaglog", po::value<int>(), "0=off 1=W 2=R 3=both 7=W+some reads")
+ ("directoryperdb", "each database will be stored in a separate directory")
+ ("journal", "enable journaling")
+ ("journalOptions", po::value<int>(), "journal diagnostic options")
+ ("journalCommitInterval", po::value<unsigned>(), "how often to group/batch commit (ms)")
+ ("ipv6", "enable IPv6 support (disabled by default)")
+ ("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<int>()->default_value(16), ".ns file size (in MB) for new databases")
+ ("profile",po::value<int>(), "0=off 1=slow, 2=all")
+ ("quota", "limits each database to a certain number of files (8 default)")
+ ("quotaFiles", po::value<int>(), "number of files allower per db, requires --quota")
+ ("rest","turn on simple rest api")
+ ("repair", "run repair on all dbs")
+ ("repairpath", po::value<string>() , "root directory for repair files - defaults to dbpath" )
+ ("slowms",po::value<int>(&cmdLine.slowMS)->default_value(100), "value of slow for profile and console log" )
+ ("smallfiles", "use a smaller default file size")
+#if defined(__linux__)
+ ("shutdown", "kill a running server (for init scripts)")
+#endif
+ ("syncdelay",po::value<double>(&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<int>(), "size limit (in MB) for op log")
+ ;
+
+ ms_options.add_options()
+ ("master", "master mode")
+ ("slave", "slave mode")
+ ("source", po::value<string>(), "when slave: specify master as <server:port>")
+ ("only", po::value<string>(), "when slave: specify a single database to replicate")
+ ("slavedelay", po::value<int>(), "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<string>(), "arg is <setname>[/<optionalseedhostlist>]")
+ ;
+
+ 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<int>(), "n pretouch threads for applying replicationed operations") // experimental
+ ("command", po::value< vector<string> >(), "command")
+ ("cacheSize", po::value<long>(), "cache size (in MB) for rec store")
+ ("nodur", "disable journaling")
+ // things we don't want people to use
+ ("nocursors", "diagnostic/debugging option that turns off cursors DO NOT USE IN PRODUCTION")
+ ("nohints", "ignore query hints")
+ ("nopreallocj", "don't preallocate journal files")
+ ("dur", "enable journaling") // old name for --journal
+ ("durOptions", po::value<int>(), "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);
+ Module::addOptions( visible_options );
+
+ setupCoreSignals();
+ setupSignals( false );
+
+ dbExecCommand = argv[0];
+
+ srand(curTimeMicros());
+#if( BOOST_VERSION >= 104500 )
+ boost::filesystem::path::default_name_check( boost::filesystem2::no_check );
+#else
+ boost::filesystem::path::default_name_check( boost::filesystem::no_check );
+#endif
+
+ {
+ 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;
+
+ {
+ po::variables_map params;
+
+ string error_message = arg_error_check(argc, argv);
+ if (error_message != "") {
+ cout << error_message << endl << endl;
+ show_help_text(visible_options);
+ return 0;
+ }
+
+ if ( ! CmdLine::store( argc , argv , visible_options , hidden_options , positional_options , params ) )
+ return 0;
+
+ if (params.count("help")) {
+ show_help_text(visible_options);
+ return 0;
+ }
+ if (params.count("version")) {
+ cout << mongodVersion() << endl;
+ printGitVersion();
+ return 0;
+ }
+ if ( params.count( "dbpath" ) ) {
+ dbpath = params["dbpath"].as<string>();
+ 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;
+ }
+ }
+ else {
+ dbpath = "/data/db/";
+ }
+#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<int>() - 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;
+ return EXIT_BADOPTIONS;
+ }
+ journalExplicit = true;
+ cmdLine.dur = true;
+ }
+ if (params.count("durOptions")) {
+ cmdLine.durOptions = params["durOptions"].as<int>();
+ }
+ 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<unsigned>();
+ 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<int>();
+ }
+ if (params.count("repairpath")) {
+ repairpath = params["repairpath"].as<string>();
+ if (!repairpath.size()) {
+ out() << "repairpath is empty" << endl;
+ dbexit( EXIT_BADOPTIONS );
+ }
+ }
+ if (params.count("nocursors")) {
+ useCursors = false;
+ }
+ 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;
+ assert( dur::DataLimitPerJournalFile >= 128 * 1024 * 1024 );
+ dur::DataLimitPerJournalFile = 128 * 1024 * 1024;
+ }
+ if (params.count("diaglog")) {
+ int x = params["diaglog"].as<int>();
+ if ( x < 0 || x > 7 ) {
+ out() << "can't interpret --diaglog setting" << endl;
+ dbexit( EXIT_BADOPTIONS );
+ }
+ _diaglog.setLevel(x);
+ }
+ if (params.count("sysinfo")) {
+ sysRuntimeInfo();
+ return 0;
+ }
+ if (params.count("repair")) {
+ Record::MemoryTrackingEnabled = false;
+ shouldRepairDatabases = 1;
+ forceRepair = 1;
+ }
+ 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<int>();
+ }
+ 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://www.mongodb.org/display/DOCS/Resyncing+a+Very+Stale+Replica+Set+Member" << endl;
+ dbexit( EXIT_BADOPTIONS );
+ }
+ }
+ if (params.count("source")) {
+ /* specifies what the source in local.sources should be */
+ cmdLine.source = params["source"].as<string>().c_str();
+ }
+ if( params.count("pretouch") ) {
+ cmdLine.pretouch = params["pretouch"].as<int>();
+ }
+ 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<string>().c_str();
+ }
+ if (params.count("only")) {
+ cmdLine.only = params["only"].as<string>().c_str();
+ }
+ if( params.count("nssize") ) {
+ int x = params["nssize"].as<int>();
+ if (x <= 0 || x > (0x7fffffff/1024/1024)) {
+ out() << "bad --nssize arg" << endl;
+ dbexit( EXIT_BADOPTIONS );
+ }
+ lenForNewNsFiles = x * 1024 * 1024;
+ assert(lenForNewNsFiles > 0);
+ }
+ if (params.count("oplogSize")) {
+ long long x = params["oplogSize"].as<int>();
+ 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;
+ assert(cmdLine.oplogSize > 0);
+ }
+ if (params.count("cacheSize")) {
+ long x = params["cacheSize"].as<long>();
+ 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;
+ 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<int>();
+ }
+ 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() << "<http://www.mongodb.org/display/DOCS/Replica+Pairs>" << endl;
+ out() << "****" << endl;
+ dbexit( EXIT_BADOPTIONS );
+ }
+
+ // needs to be after things like --configsvr parsing, thus here.
+ if( repairpath.empty() )
+ repairpath = dbpath;
+
+ Module::configAll( params );
+ dataFileSync.go();
+
+ if (params.count("command")) {
+ vector<string> command = params["command"].as< vector<string> >();
+
+ if (command[0].compare("run") == 0) {
+ if (command.size() > 1) {
+ cout << "Too many parameters to 'run' command" << endl;
+ cout << visible_options << endl;
+ return 0;
+ }
+
+ initAndListen(cmdLine.port);
+ return 0;
+ }
+
+ if (command[0].compare("dbpath") == 0) {
+ cout << dbpath << endl;
+ return 0;
+ }
+
+ cout << "Invalid command: " << command[0] << endl;
+ cout << visible_options << endl;
+ return 0;
+ }
+
+ if( cmdLine.pretouch )
+ log() << "--pretouch " << cmdLine.pretouch << endl;
+
+#ifdef __linux__
+ if (params.count("shutdown")){
+ bool failed = false;
+
+ string name = ( boost::filesystem::path( dbpath ) / "mongod.lock" ).native_file_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;
+
+ string exePath = procPath + "/exe";
+ if (boost::filesystem::exists(exePath)){
+ char buf[256];
+ int ret = readlink(exePath.c_str(), buf, sizeof(buf)-1);
+ buf[ret] = '\0'; // readlink doesn't terminate string
+ if (ret == -1) {
+ int e = errno;
+ cerr << "Error resolving " << exePath << ": " << errnoWithDescription(e);
+ failed = true;
+ }
+ else if (!endsWith(buf, "mongod")){
+ cerr << "Process " << pid << " is running " << buf << " not mongod" << endl;
+ ::exit(-1);
+ }
+ }
+ }
+ 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(-1);
+ }
+
+ 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(-1);
+ }
+
+ while (boost::filesystem::exists(procPath)) {
+ sleepsecs(1);
+ }
+
+ ::exit(0);
+ }
+#endif
+
+#if defined(_WIN32)
+ if (serviceParamsCheck( params, dbpath, argc, argv )) {
+ return 0;
+ }
+#endif
+
+
+ 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;
+ }
+
+ }
+
+ UnitTest::runTests();
+ initAndListen(cmdLine.port);
+ dbexit(EXIT_CLEAN);
+ return 0;
+}
+
+namespace mongo {
+
+ string getDbContext();
+
+#undef out
+
+
+#if !defined(_WIN32)
+
+} // namespace mongo
+
+#include <signal.h>
+#include <string.h>
+
+namespace mongo {
+
+ void pipeSigHandler( int signal ) {
+#ifdef psignal
+ psignal( signal, "Signal Received : ");
+#else
+ cout << "got pipe signal:" << signal << endl;
+#endif
+ }
+
+ 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 << 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 x;
+ sigwait( &asyncSignals, &x );
+ log() << "got kill or ctrl c or hup signal " << x << " (" << strsignal( x ) << "), will terminate after current cmd ends" << endl;
+ Client::initThread( "interruptThread" );
+ exitCleanly( EXIT_KILL );
+ }
+
+ // this will be called in certain c++ error cases, for example if there are two active
+ // exceptions
+ void myterminate() {
+ rawOut( "terminate() called, printing stack:" );
+ 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;
+
+ assert( sigaction(SIGSEGV, &addrSignals, 0) == 0 );
+ assert( sigaction(SIGBUS, &addrSignals, 0) == 0 );
+ assert( sigaction(SIGILL, &addrSignals, 0) == 0 );
+ assert( sigaction(SIGFPE, &addrSignals, 0) == 0 );
+
+ assert( signal(SIGABRT, abruptQuit) != SIG_ERR );
+ assert( signal(SIGQUIT, abruptQuit) != SIG_ERR );
+ assert( signal(SIGPIPE, pipeSigHandler) != SIG_ERR );
+
+ setupSIGTRAPforGDB();
+
+ sigemptyset( &asyncSignals );
+
+ if ( inFork )
+ assert( signal( SIGHUP , setupSignals_ignoreHelper ) != SIG_ERR );
+ else
+ sigaddset( &asyncSignals, SIGHUP );
+
+ sigaddset( &asyncSignals, SIGINT );
+ sigaddset( &asyncSignals, SIGTERM );
+ assert( pthread_sigmask( SIG_SETMASK, &asyncSignals, 0 ) == 0 );
+ boost::thread it( interruptThread );
+
+ set_terminate( myterminate );
+ set_new_handler( my_new_handler );
+ }
+
+#else
+ 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:
+ rawOut( "CTRL_LOGOFF_EVENT signal" );
+ consoleTerminate( "CTRL_LOGOFF_EVENT" );
+ return TRUE;
+
+ case CTRL_SHUTDOWN_EVENT:
+ rawOut( "CTRL_SHUTDOWN_EVENT signal" );
+ consoleTerminate( "CTRL_SHUTDOWN_EVENT" );
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ LPTOP_LEVEL_EXCEPTION_FILTER filtLast = 0;
+ ::HANDLE standardOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ LONG WINAPI exceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {
+ {
+ // given the severity of the event we write to console in addition to the --logFile
+ // (rawOut writes to the logfile, if a special one were specified)
+ DWORD written;
+ WriteFile(standardOut, "unhandled windows exception\n", 20, &written, 0);
+ FlushFileBuffers(standardOut);
+ }
+
+ DWORD ec = ExceptionInfo->ExceptionRecord->ExceptionCode;
+ if( ec == EXCEPTION_ACCESS_VIOLATION ) {
+ rawOut("access violation");
+ }
+ else {
+ rawOut("unhandled windows exception");
+ char buf[64];
+ strcpy(buf, "ec=0x");
+ _ui64toa(ec, buf+5, 16);
+ rawOut(buf);
+ }
+ if( filtLast )
+ return filtLast(ExceptionInfo);
+ 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
+
+} // namespace mongo