// utils.cpp /* * Copyright 2010 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pch.h" #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 # include # define SIGKILL 9 #else # include # include # include # include # include #endif #include "utils.h" #include "../client/dbclient.h" #include "../util/md5.hpp" #include "../util/processinfo.h" #include "../util/text.h" #include "../util/heapcheck.h" #include "../util/time_support.h" #include "../util/file.h" namespace mongo { DBClientWithCommands *latestConn = 0; extern bool dbexitCalled; #ifdef _WIN32 inline int close(int fd) { return _close(fd); } inline int read(int fd, void* buf, size_t size) { return _read(fd, buf, size); } inline int pipe(int fds[2]) { return _pipe(fds, 4096, _O_TEXT | _O_NOINHERIT); } #endif namespace JSFiles { extern const JSFile servers; } // these functions have not been audited for thread safety - currently they are called with an exclusive js mutex namespace shellUtils { Scope* theScope = 0; std::string _dbConnect; std::string _dbAuth; const char *argv0 = 0; void RecordMyLocation( const char *_argv0 ) { argv0 = _argv0; } // helpers BSONObj makeUndefined() { BSONObjBuilder b; b.appendUndefined( "" ); return b.obj(); } const BSONObj undefined_ = makeUndefined(); BSONObj encapsulate( const BSONObj &obj ) { return BSON( "" << obj ); } // real methods void goingAwaySoon(); BSONObj Quit(const BSONObj& args, void* data) { // If not arguments are given first element will be EOO, which // converts to the integer value 0. goingAwaySoon(); int exit_code = int( args.firstElement().number() ); ::exit(exit_code); return undefined_; } BSONObj JSGetMemInfo( const BSONObj& args, void* data ) { ProcessInfo pi; uassert( 10258 , "processinfo not supported" , pi.supported() ); BSONObjBuilder e; e.append( "virtual" , pi.getVirtualMemorySize() ); e.append( "resident" , pi.getResidentSize() ); BSONObjBuilder b; b.append( "ret" , e.obj() ); return b.obj(); } #ifndef MONGO_SAFE_SHELL BSONObj listFiles(const BSONObj& _args, void* data) { static BSONObj cd = BSON( "0" << "." ); BSONObj args = _args.isEmpty() ? cd : _args; uassert( 10257 , "need to specify 1 argument to listFiles" , args.nFields() == 1 ); BSONArrayBuilder lst; string rootname = args.firstElement().valuestrsafe(); path root( rootname ); stringstream ss; ss << "listFiles: no such directory: " << rootname; string msg = ss.str(); uassert( 12581, msg.c_str(), boost::filesystem::exists( root ) ); directory_iterator end; directory_iterator i( root); while ( i != end ) { path p = *i; BSONObjBuilder b; b << "name" << p.string(); b.appendBool( "isDirectory", is_directory( p ) ); if ( ! is_directory( p ) ) { try { b.append( "size" , (double)file_size( p ) ); } catch ( ... ) { i++; continue; } } lst.append( b.obj() ); i++; } BSONObjBuilder ret; ret.appendArray( "", lst.done() ); return ret.obj(); } BSONObj ls(const BSONObj& args, void* data) { BSONObj o = listFiles(args, data); if( !o.isEmpty() ) { for( BSONObj::iterator i = o.firstElement().Obj().begin(); i.more(); ) { BSONObj f = i.next().Obj(); cout << f["name"].String(); if( f["isDirectory"].trueValue() ) cout << '/'; cout << '\n'; } cout.flush(); } return BSONObj(); } BSONObj cd(const BSONObj& args, void* data) { #if defined(_WIN32) std::wstring dir = toWideString( args.firstElement().String().c_str() ); if( SetCurrentDirectory(dir.c_str()) ) return BSONObj(); #else string dir = args.firstElement().String(); /* if( chdir(dir.c_str) ) == 0 ) return BSONObj(); */ if( 1 ) return BSON(""<<"implementation not done for posix"); #endif return BSON( "" << "change directory failed" ); } BSONObj pwd(const BSONObj&, void* data) { boost::filesystem::path p = boost::filesystem::current_path(); return BSON( "" << p.string() ); } BSONObj hostname(const BSONObj&, void* data) { return BSON( "" << getHostName() ); } static BSONElement oneArg(const BSONObj& args) { uassert( 12597 , "need to specify 1 argument" , args.nFields() == 1 ); return args.firstElement(); } const int CANT_OPEN_FILE = 13300; BSONObj cat(const BSONObj& args, void* data) { BSONElement e = oneArg(args); stringstream ss; ifstream f(e.valuestrsafe()); uassert(CANT_OPEN_FILE, "couldn't open file", f.is_open() ); streamsize sz = 0; while( 1 ) { char ch = 0; // slow...maybe change one day f.get(ch); if( ch == 0 ) break; ss << ch; sz += 1; uassert(13301, "cat() : file to big to load as a variable", sz < 1024 * 1024 * 16); } return BSON( "" << ss.str() ); } BSONObj md5sumFile(const BSONObj& args, void* data) { BSONElement e = oneArg(args); stringstream ss; FILE* f = fopen(e.valuestrsafe(), "rb"); uassert(CANT_OPEN_FILE, "couldn't open file", f ); md5digest d; md5_state_t st; md5_init(&st); enum {BUFLEN = 4*1024}; char buffer[BUFLEN]; int bytes_read; while( (bytes_read = fread(buffer, 1, BUFLEN, f)) ) { md5_append( &st , (const md5_byte_t*)(buffer) , bytes_read ); } md5_finish(&st, d); return BSON( "" << digestToString( d ) ); } BSONObj mkdir(const BSONObj& args, void* data) { boost::filesystem::create_directories(args.firstElement().String()); return BSON( "" << true ); } BSONObj removeFile(const BSONObj& args, void* data) { BSONElement e = oneArg(args); bool found = false; path root( e.valuestrsafe() ); if ( boost::filesystem::exists( root ) ) { found = true; boost::filesystem::remove_all( root ); } BSONObjBuilder b; b.appendBool( "removed" , found ); return b.obj(); } /** * @param args - [ name, byte index ] * In this initial implementation, all bits in the specified byte are flipped. */ BSONObj fuzzFile(const BSONObj& args, void* data) { uassert( 13619, "fuzzFile takes 2 arguments", args.nFields() == 2 ); shared_ptr< File > f( new File() ); f->open( args.getStringField( "0" ) ); uassert( 13620, "couldn't open file to fuzz", !f->bad() && f->is_open() ); char c; f->read( args.getIntField( "1" ), &c, 1 ); c = ~c; f->write( args.getIntField( "1" ), &c, 1 ); return undefined_; // f close is implicit } map< int, pair< pid_t, int > > dbs; map< pid_t, int > shells; #ifdef _WIN32 map< pid_t, HANDLE > handles; #endif mongo::mutex mongoProgramOutputMutex("mongoProgramOutputMutex"); stringstream mongoProgramOutput_; void goingAwaySoon() { mongo::mutex::scoped_lock lk( mongoProgramOutputMutex ); mongo::dbexitCalled = true; } void writeMongoProgramOutputLine( int port, int pid, const char *line ) { mongo::mutex::scoped_lock lk( mongoProgramOutputMutex ); if( mongo::dbexitCalled ) throw "program is terminating"; stringstream buf; if ( port > 0 ) buf << "m" << port << "| " << line; else buf << "sh" << pid << "| " << line; cout << buf.str() << endl; mongoProgramOutput_ << buf.str() << endl; } // only returns last 100000 characters BSONObj RawMongoProgramOutput( const BSONObj &args, void* data ) { mongo::mutex::scoped_lock lk( mongoProgramOutputMutex ); string out = mongoProgramOutput_.str(); size_t len = out.length(); if ( len > 100000 ) out = out.substr( len - 100000, 100000 ); return BSON( "" << out ); } BSONObj ClearRawMongoProgramOutput( const BSONObj &args, void* data ) { mongo::mutex::scoped_lock lk( mongoProgramOutputMutex ); mongoProgramOutput_.str( "" ); return undefined_; } class ProgramRunner { vector argv_; int port_; int pipe_; pid_t pid_; public: pid_t pid() const { return pid_; } int port() const { return port_; } boost::filesystem::path find(string prog) { boost::filesystem::path p = prog; #ifdef _WIN32 p = change_extension(p, ".exe"); #endif if( boost::filesystem::exists(p) ) { #ifndef _WIN32 p = boost::filesystem::initial_path() / p; #endif return p; } { boost::filesystem::path t = boost::filesystem::current_path() / p; if( boost::filesystem::exists(t) ) return t; } try { if( theScope->type("_path") == String ) { string path = theScope->getString("_path"); if( !path.empty() ) { boost::filesystem::path t = boost::filesystem::path(path) / p; if( boost::filesystem::exists(t) ) return t; } } } catch(...) { } { boost::filesystem::path t = boost::filesystem::initial_path() / p; if( boost::filesystem::exists(t) ) return t; } return p; // not found; might find via system path } ProgramRunner( const BSONObj &args , bool isMongoProgram=true) { assert( !args.isEmpty() ); string program( args.firstElement().valuestrsafe() ); assert( !program.empty() ); boost::filesystem::path programPath = find(program); if (isMongoProgram) { #if 0 if (program == "mongos") { argv_.push_back("valgrind"); argv_.push_back("--log-file=/tmp/mongos-%p.valgrind"); argv_.push_back("--leak-check=yes"); argv_.push_back("--suppressions=valgrind.suppressions"); //argv_.push_back("--error-exitcode=1"); argv_.push_back("--"); } #endif } argv_.push_back( programPath.native_file_string() ); port_ = -1; BSONObjIterator j( args ); j.next(); // skip program name (handled above) while(j.more()) { BSONElement e = j.next(); string str; if ( e.isNumber() ) { stringstream ss; ss << e.number(); str = ss.str(); } else { assert( e.type() == mongo::String ); str = e.valuestr(); } if ( str == "--port" ) port_ = -2; else if ( port_ == -2 ) port_ = strtol( str.c_str(), 0, 10 ); argv_.push_back(str); } if ( program != "mongod" && program != "mongos" && program != "mongobridge" ) port_ = 0; else { if ( port_ <= 0 ) cout << "error: a port number is expected when running mongod (etc.) from the shell" << endl; assert( port_ > 0 ); } if ( port_ > 0 && dbs.count( port_ ) != 0 ) { cerr << "count for port: " << port_ << " is not 0 is: " << dbs.count( port_ ) << endl; assert( dbs.count( port_ ) == 0 ); } } void start() { int pipeEnds[ 2 ]; assert( pipe( pipeEnds ) != -1 ); fflush( 0 ); launch_process(pipeEnds[1]); //sets pid_ { stringstream ss; ss << "shell: started program"; for (unsigned i=0; i < argv_.size(); i++) ss << " " << argv_[i]; ss << '\n'; cout << ss.str(); cout.flush(); } if ( port_ > 0 ) dbs.insert( make_pair( port_, make_pair( pid_, pipeEnds[ 1 ] ) ) ); else shells.insert( make_pair( pid_, pipeEnds[ 1 ] ) ); pipe_ = pipeEnds[ 0 ]; } // Continue reading output void operator()() { try { // This assumes there aren't any 0's in the mongo program output. // Hope that's ok. const unsigned bufSize = 128 * 1024; char buf[ bufSize ]; char temp[ bufSize ]; char *start = buf; while( 1 ) { int lenToRead = ( bufSize - 1 ) - ( start - buf ); if ( lenToRead <= 0 ) { cout << "error: lenToRead: " << lenToRead << endl; cout << "first 300: " << string(buf,0,300) << endl; } assert( lenToRead > 0 ); int ret = read( pipe_, (void *)start, lenToRead ); if( mongo::dbexitCalled ) break; assert( ret != -1 ); start[ ret ] = '\0'; if ( strlen( start ) != unsigned( ret ) ) writeMongoProgramOutputLine( port_, pid_, "WARNING: mongod wrote null bytes to output" ); char *last = buf; for( char *i = strchr( buf, '\n' ); i; last = i + 1, i = strchr( last, '\n' ) ) { *i = '\0'; writeMongoProgramOutputLine( port_, pid_, last ); } if ( ret == 0 ) { if ( *last ) writeMongoProgramOutputLine( port_, pid_, last ); close( pipe_ ); break; } if ( last != buf ) { strcpy( temp, last ); strcpy( buf, temp ); } else { assert( strlen( buf ) < bufSize ); } start = buf + strlen( buf ); } } catch(...) { } } void launch_process(int child_stdout) { #ifdef _WIN32 stringstream ss; for( unsigned i=0; i < argv_.size(); i++ ) { if (i) ss << ' '; if (argv_[i].find(' ') == string::npos) ss << argv_[i]; else ss << '"' << argv_[i] << '"'; } string args = ss.str(); boost::scoped_array args_tchar (new TCHAR[args.size() + 1]); size_t i; for(i=0; i < args.size(); i++) args_tchar[i] = args[i]; args_tchar[i] = 0; HANDLE h = (HANDLE)_get_osfhandle(child_stdout); assert(h != INVALID_HANDLE_VALUE); assert(SetHandleInformation(h, HANDLE_FLAG_INHERIT, 1)); STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.hStdError = h; si.hStdOutput = h; si.dwFlags |= STARTF_USESTDHANDLES; PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi)); bool success = CreateProcess( NULL, args_tchar.get(), NULL, NULL, true, 0, NULL, NULL, &si, &pi) != 0; { stringstream ss; ss << "couldn't start process " << argv_[0]; uassert(14042, ss.str(), success); } CloseHandle(pi.hThread); pid_ = pi.dwProcessId; handles.insert( make_pair( pid_, pi.hProcess ) ); #else pid_ = fork(); assert( pid_ != -1 ); if ( pid_ == 0 ) { // DON'T ASSERT IN THIS BLOCK - very bad things will happen const char** argv = new const char* [argv_.size()+1]; // don't need to free - in child for (unsigned i=0; i < argv_.size(); i++) { argv[i] = argv_[i].c_str(); } argv[argv_.size()] = 0; if ( dup2( child_stdout, STDOUT_FILENO ) == -1 || dup2( child_stdout, STDERR_FILENO ) == -1 ) { cout << "Unable to dup2 child output: " << errnoWithDescription() << endl; ::_Exit(-1); //do not pass go, do not call atexit handlers } const char** env = new const char* [2]; // don't need to free - in child env[0] = NULL; #if defined(HEAP_CHECKING) env[0] = "HEAPCHECK=normal"; env[1] = NULL; // Heap-check for mongos only. 'argv[0]' must be in the path format. if ( argv_[0].find("mongos") != string::npos) { execvpe( argv[ 0 ], const_cast(argv) , const_cast(env) ); } #endif // HEAP_CHECKING execvp( argv[ 0 ], const_cast(argv) ); cout << "Unable to start program " << argv[0] << ' ' << errnoWithDescription() << endl; ::_Exit(-1); } #endif } }; //returns true if process exited bool wait_for_pid(pid_t pid, bool block=true, int* exit_code=NULL) { #ifdef _WIN32 assert(handles.count(pid)); HANDLE h = handles[pid]; if (block) WaitForSingleObject(h, INFINITE); DWORD tmp; if(GetExitCodeProcess(h, &tmp)) { if ( tmp == STILL_ACTIVE ) { return false; } CloseHandle(h); handles.erase(pid); if (exit_code) *exit_code = tmp; return true; } else { return false; } #else int tmp; bool ret = (pid == waitpid(pid, &tmp, (block ? 0 : WNOHANG))); if (exit_code) *exit_code = WEXITSTATUS(tmp); return ret; #endif } BSONObj WaitProgram( const BSONObj& a, void* data ) { int pid = oneArg( a ).numberInt(); BSONObj x = BSON( "" << wait_for_pid( pid ) ); shells.erase( pid ); return x; } BSONObj WaitMongoProgramOnPort( const BSONObj &a, void* data ) { int port = oneArg( a ).numberInt(); uassert( 13621, "no known mongo program on port", dbs.count( port ) != 0 ); log() << "waiting port: " << port << ", pid: " << dbs[ port ].first << endl; bool ret = wait_for_pid( dbs[ port ].first ); if ( ret ) { dbs.erase( port ); } return BSON( "" << ret ); } BSONObj StartMongoProgram( const BSONObj &a, void* data ) { _nokillop = true; ProgramRunner r( a ); r.start(); boost::thread t( r ); return BSON( string( "" ) << int( r.pid() ) ); } BSONObj RunMongoProgram( const BSONObj &a, void* data ) { ProgramRunner r( a ); r.start(); boost::thread t( r ); int exit_code; wait_for_pid( r.pid(), true, &exit_code ); if ( r.port() > 0 ) { dbs.erase( r.port() ); } else { shells.erase( r.pid() ); } return BSON( string( "" ) << exit_code ); } BSONObj RunProgram(const BSONObj &a, void* data) { ProgramRunner r( a, false ); r.start(); boost::thread t( r ); int exit_code; wait_for_pid(r.pid(), true, &exit_code); shells.erase( r.pid() ); return BSON( string( "" ) << exit_code ); } BSONObj ResetDbpath( const BSONObj &a, void* data ) { assert( a.nFields() == 1 ); string path = a.firstElement().valuestrsafe(); assert( !path.empty() ); if ( boost::filesystem::exists( path ) ) boost::filesystem::remove_all( path ); boost::filesystem::create_directory( path ); return undefined_; } void copyDir( const path &from, const path &to ) { directory_iterator end; directory_iterator i( from ); while( i != end ) { path p = *i; if ( p.leaf() != "mongod.lock" ) { if ( is_directory( p ) ) { path newDir = to / p.leaf(); boost::filesystem::create_directory( newDir ); copyDir( p, newDir ); } else { boost::filesystem::copy_file( p, to / p.leaf() ); } } ++i; } } // NOTE target dbpath will be cleared first BSONObj CopyDbpath( const BSONObj &a, void* data ) { assert( a.nFields() == 2 ); BSONObjIterator i( a ); string from = i.next().str(); string to = i.next().str(); assert( !from.empty() ); assert( !to.empty() ); if ( boost::filesystem::exists( to ) ) boost::filesystem::remove_all( to ); boost::filesystem::create_directory( to ); copyDir( from, to ); return undefined_; } inline void kill_wrapper(pid_t pid, int sig, int port) { #ifdef _WIN32 if (sig == SIGKILL || port == 0) { assert( handles.count(pid) ); TerminateProcess(handles[pid], 1); // returns failure for "zombie" processes. } else { DBClientConnection conn; conn.connect("127.0.0.1:" + BSONObjBuilder::numStr(port)); try { conn.simpleCommand("admin", NULL, "shutdown"); } catch (...) { //Do nothing. This command never returns data to the client and the driver doesn't like that. } } #else int x = kill( pid, sig ); if ( x ) { if ( errno == ESRCH ) { } else { cout << "killFailed: " << errnoWithDescription() << endl; assert( x == 0 ); } } #endif } int killDb( int port, pid_t _pid, int signal ) { pid_t pid; int exitCode = 0; if ( port > 0 ) { if( dbs.count( port ) != 1 ) { cout << "No db started on port: " << port << endl; return 0; } pid = dbs[ port ].first; } else { pid = _pid; } kill_wrapper( pid, signal, port ); int i = 0; for( ; i < 130; ++i ) { if ( i == 30 ) { char now[64]; time_t_to_String(time(0), now); now[ 20 ] = 0; cout << now << " process on port " << port << ", with pid " << pid << " not terminated, sending sigkill" << endl; kill_wrapper( pid, SIGKILL, port ); } if(wait_for_pid(pid, false, &exitCode)) break; sleepmillis( 1000 ); } if ( i == 130 ) { char now[64]; time_t_to_String(time(0), now); now[ 20 ] = 0; cout << now << " failed to terminate process on port " << port << ", with pid " << pid << endl; assert( "Failed to terminate process" == 0 ); } if ( port > 0 ) { close( dbs[ port ].second ); dbs.erase( port ); } else { close( shells[ pid ] ); shells.erase( pid ); } // FIXME I think the intention here is to do an extra sleep only when SIGKILL is sent to the child process. // We may want to change the 4 below to 29, since values of i greater than that indicate we sent a SIGKILL. if ( i > 4 || signal == SIGKILL ) { sleepmillis( 4000 ); // allow operating system to reclaim resources } return exitCode; } int getSignal( const BSONObj &a ) { int ret = SIGTERM; if ( a.nFields() == 2 ) { BSONObjIterator i( a ); i.next(); BSONElement e = i.next(); assert( e.isNumber() ); ret = int( e.number() ); } return ret; } /** stopMongoProgram(port[, signal]) */ BSONObj StopMongoProgram( const BSONObj &a, void* data ) { assert( a.nFields() == 1 || a.nFields() == 2 ); uassert( 15853 , "stopMongo needs a number" , a.firstElement().isNumber() ); int port = int( a.firstElement().number() ); int code = killDb( port, 0, getSignal( a ) ); cout << "shell: stopped mongo program on port " << port << endl; return BSON( "" << (double)code ); } BSONObj StopMongoProgramByPid( const BSONObj &a, void* data ) { assert( a.nFields() == 1 || a.nFields() == 2 ); uassert( 15852 , "stopMongoByPid needs a number" , a.firstElement().isNumber() ); int pid = int( a.firstElement().number() ); int code = killDb( 0, pid, getSignal( a ) ); cout << "shell: stopped mongo program on pid " << pid << endl; return BSON( "" << (double)code ); } void KillMongoProgramInstances() { vector< int > ports; for( map< int, pair< pid_t, int > >::iterator i = dbs.begin(); i != dbs.end(); ++i ) ports.push_back( i->first ); for( vector< int >::iterator i = ports.begin(); i != ports.end(); ++i ) killDb( *i, 0, SIGTERM ); vector< pid_t > pids; for( map< pid_t, int >::iterator i = shells.begin(); i != shells.end(); ++i ) pids.push_back( i->first ); for( vector< pid_t >::iterator i = pids.begin(); i != pids.end(); ++i ) killDb( 0, *i, SIGTERM ); } #else // ndef MONGO_SAFE_SHELL void KillMongoProgramInstances() {} #endif MongoProgramScope::~MongoProgramScope() { DESTRUCTOR_GUARD( KillMongoProgramInstances(); ClearRawMongoProgramOutput( BSONObj(), 0 ); ) } unsigned _randomSeed; BSONObj JSSrand( const BSONObj &a, void* data ) { uassert( 12518, "srand requires a single numeric argument", a.nFields() == 1 && a.firstElement().isNumber() ); _randomSeed = (unsigned)a.firstElement().numberLong(); // grab least significant digits return undefined_; } BSONObj JSRand( const BSONObj &a, void* data ) { uassert( 12519, "rand accepts no arguments", a.nFields() == 0 ); unsigned r; #if !defined(_WIN32) r = rand_r( &_randomSeed ); #else r = rand(); // seed not used in this case #endif return BSON( "" << double( r ) / ( double( RAND_MAX ) + 1 ) ); } BSONObj isWindows(const BSONObj& a, void* data) { uassert( 13006, "isWindows accepts no arguments", a.nFields() == 0 ); #ifdef _WIN32 return BSON( "" << true ); #else return BSON( "" << false ); #endif } BSONObj getHostName(const BSONObj& a, void* data) { uassert( 13411, "getHostName accepts no arguments", a.nFields() == 0 ); char buf[260]; // HOST_NAME_MAX is usually 255 assert(gethostname(buf, 260) == 0); buf[259] = '\0'; return BSON("" << buf); } void installShellUtils( Scope& scope ) { theScope = &scope; scope.injectNative( "quit", Quit ); scope.injectNative( "getMemInfo" , JSGetMemInfo ); scope.injectNative( "_srand" , JSSrand ); scope.injectNative( "_rand" , JSRand ); scope.injectNative( "_isWindows" , isWindows ); #ifndef MONGO_SAFE_SHELL //can't launch programs scope.injectNative( "_startMongoProgram", StartMongoProgram ); scope.injectNative( "runProgram", RunProgram ); scope.injectNative( "run", RunProgram ); scope.injectNative( "runMongoProgram", RunMongoProgram ); scope.injectNative( "stopMongod", StopMongoProgram ); scope.injectNative( "stopMongoProgram", StopMongoProgram ); scope.injectNative( "stopMongoProgramByPid", StopMongoProgramByPid ); scope.injectNative( "rawMongoProgramOutput", RawMongoProgramOutput ); scope.injectNative( "clearRawMongoProgramOutput", ClearRawMongoProgramOutput ); scope.injectNative( "waitProgram" , WaitProgram ); scope.injectNative( "waitMongoProgramOnPort" , WaitMongoProgramOnPort ); scope.injectNative( "getHostName" , getHostName ); scope.injectNative( "removeFile" , removeFile ); scope.injectNative( "fuzzFile" , fuzzFile ); scope.injectNative( "listFiles" , listFiles ); scope.injectNative( "ls" , ls ); scope.injectNative( "pwd", pwd ); scope.injectNative( "cd", cd ); scope.injectNative( "cat", cat ); scope.injectNative( "hostname", hostname); scope.injectNative( "resetDbpath", ResetDbpath ); scope.injectNative( "copyDbpath", CopyDbpath ); scope.injectNative( "md5sumFile", md5sumFile ); scope.injectNative( "mkdir" , mkdir ); #endif } void initScope( Scope &scope ) { scope.externalSetup(); mongo::shellUtils::installShellUtils( scope ); scope.execSetup(JSFiles::servers); if ( !_dbConnect.empty() ) { uassert( 12513, "connect failed", scope.exec( _dbConnect , "(connect)" , false , true , false ) ); if ( !_dbAuth.empty() ) { installGlobalUtils( scope ); uassert( 12514, "login failed", scope.exec( _dbAuth , "(auth)" , true , true , false ) ); } } } // connstr, myuris map< string, set > _allMyUris; mongo::mutex _allMyUrisMutex("_allMyUrisMutex"); bool _nokillop = false; void onConnect( DBClientWithCommands &c ) { latestConn = &c; if ( _nokillop ) { return; } BSONObj info; if ( c.runCommand( "admin", BSON( "whatsmyuri" << 1 ), info ) ) { string connstr = dynamic_cast(c).getServerAddress(); mongo::mutex::scoped_lock lk( _allMyUrisMutex ); _allMyUris[connstr].insert(info[ "you" ].str()); } } } }