diff options
Diffstat (limited to 'shell/shell_utils.cpp')
| -rw-r--r-- | shell/shell_utils.cpp | 959 |
1 files changed, 0 insertions, 959 deletions
diff --git a/shell/shell_utils.cpp b/shell/shell_utils.cpp deleted file mode 100644 index e09309c640b..00000000000 --- a/shell/shell_utils.cpp +++ /dev/null @@ -1,959 +0,0 @@ -// 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 <boost/thread/xtime.hpp> - -#include <cstring> -#include <cstdio> -#include <cstdlib> -#include <assert.h> -#include <iostream> -#include <map> -#include <sstream> -#include <vector> -#include <fcntl.h> - -#ifdef _WIN32 -# include <io.h> -# define SIGKILL 9 -#else -# include <sys/socket.h> -# include <netinet/in.h> -# include <signal.h> -# include <sys/stat.h> -# include <sys/wait.h> -#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<string> 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<TCHAR> 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<char**>(argv) , const_cast<char**>(env) ); - } -#endif // HEAP_CHECKING - - execvp( argv[ 0 ], const_cast<char**>(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<string> > _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<DBClientBase&>(c).getServerAddress(); - mongo::mutex::scoped_lock lk( _allMyUrisMutex ); - _allMyUris[connstr].insert(info[ "you" ].str()); - } - } - } -} |
