0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/shell/shell_utils.cpp

960 lines
34 KiB
C++
Raw Normal View History

// 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.
*/
2010-06-06 02:55:11 +02:00
#include "pch.h"
2010-02-03 02:28:31 +01:00
#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"
2010-06-12 23:49:54 +02:00
#include "../util/text.h"
#include "../util/heapcheck.h"
#include "../util/time_support.h"
2011-01-03 23:04:58 +01:00
#include "../util/file.h"
namespace mongo {
2011-01-04 06:40:41 +01:00
DBClientWithCommands *latestConn = 0;
extern bool dbexitCalled;
2011-01-04 06:40:41 +01:00
#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 {
2010-06-12 23:49:54 +02:00
Scope* theScope = 0;
2011-01-04 06:40:41 +01:00
std::string _dbConnect;
std::string _dbAuth;
2011-01-04 06:40:41 +01:00
const char *argv0 = 0;
void RecordMyLocation( const char *_argv0 ) { argv0 = _argv0; }
2011-01-04 06:40:41 +01:00
// helpers
2011-01-04 06:40:41 +01:00
BSONObj makeUndefined() {
BSONObjBuilder b;
b.appendUndefined( "" );
return b.obj();
}
const BSONObj undefined_ = makeUndefined();
2011-01-04 06:40:41 +01:00
BSONObj encapsulate( const BSONObj &obj ) {
return BSON( "" << obj );
}
2011-01-04 06:40:41 +01:00
// real methods
2010-06-16 22:56:05 +02:00
void goingAwaySoon();
2011-05-11 04:14:32 +02:00
BSONObj Quit(const BSONObj& args, void* data) {
// If not arguments are given first element will be EOO, which
// converts to the integer value 0.
2010-06-16 22:56:05 +02:00
goingAwaySoon();
int exit_code = int( args.firstElement().number() );
::exit(exit_code);
return undefined_;
}
2011-05-11 04:14:32 +02:00
BSONObj JSGetMemInfo( const BSONObj& args, void* data ) {
ProcessInfo pi;
uassert( 10258 , "processinfo not supported" , pi.supported() );
2011-01-04 06:40:41 +01:00
BSONObjBuilder e;
e.append( "virtual" , pi.getVirtualMemorySize() );
e.append( "resident" , pi.getResidentSize() );
2011-01-04 06:40:41 +01:00
BSONObjBuilder b;
b.append( "ret" , e.obj() );
2011-01-04 06:40:41 +01:00
return b.obj();
}
#ifndef MONGO_SAFE_SHELL
2011-05-11 04:14:32 +02:00
BSONObj listFiles(const BSONObj& _args, void* data) {
2010-06-07 17:17:08 +02:00
static BSONObj cd = BSON( "0" << "." );
BSONObj args = _args.isEmpty() ? cd : _args;
uassert( 10257 , "need to specify 1 argument to listFiles" , args.nFields() == 1 );
2011-01-04 06:40:41 +01:00
2011-03-17 07:17:31 +01:00
BSONArrayBuilder lst;
2011-01-04 06:40:41 +01:00
string rootname = args.firstElement().valuestrsafe();
path root( rootname );
2010-01-26 21:46:07 +01:00
stringstream ss;
ss << "listFiles: no such directory: " << rootname;
string msg = ss.str();
uassert( 12581, msg.c_str(), boost::filesystem::exists( root ) );
2011-01-04 06:40:41 +01:00
directory_iterator end;
directory_iterator i( root);
2011-01-04 06:40:41 +01:00
while ( i != end ) {
path p = *i;
BSONObjBuilder b;
b << "name" << p.string();
b.appendBool( "isDirectory", is_directory( p ) );
2011-01-04 06:40:41 +01:00
if ( ! is_directory( p ) ) {
try {
b.append( "size" , (double)file_size( p ) );
}
2011-01-04 06:40:41 +01:00
catch ( ... ) {
i++;
continue;
}
}
2009-08-24 17:54:06 +02:00
2011-03-17 07:17:31 +01:00
lst.append( b.obj() );
i++;
}
2011-03-17 07:17:31 +01:00
BSONObjBuilder ret;
ret.appendArray( "", lst.done() );
return ret.obj();
}
2010-02-02 04:07:05 +01:00
2011-05-11 04:14:32 +02:00
BSONObj ls(const BSONObj& args, void* data) {
BSONObj o = listFiles(args, data);
2010-06-07 17:17:08 +02:00
if( !o.isEmpty() ) {
2011-01-04 06:40:41 +01:00
for( BSONObj::iterator i = o.firstElement().Obj().begin(); i.more(); ) {
2010-06-07 17:17:08 +02:00
BSONObj f = i.next().Obj();
cout << f["name"].String();
if( f["isDirectory"].trueValue() ) cout << '/';
cout << '\n';
}
cout.flush();
}
return BSONObj();
}
2011-05-11 04:14:32 +02:00
BSONObj cd(const BSONObj& args, void* data) {
2010-06-12 23:49:54 +02:00
#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();
2011-01-04 06:40:41 +01:00
/* if( chdir(dir.c_str) ) == 0 )
return BSONObj();
*/
2010-06-12 23:49:54 +02:00
if( 1 ) return BSON(""<<"implementation not done for posix");
#endif
return BSON( "" << "change directory failed" );
}
2011-05-11 04:14:32 +02:00
BSONObj pwd(const BSONObj&, void* data) {
2010-06-07 17:17:08 +02:00
boost::filesystem::path p = boost::filesystem::current_path();
return BSON( "" << p.string() );
}
2010-02-02 04:07:05 +01:00
2011-05-11 04:14:32 +02:00
BSONObj hostname(const BSONObj&, void* data) {
2010-06-11 00:02:17 +02:00
return BSON( "" << getHostName() );
}
2011-01-04 06:40:41 +01:00
static BSONElement oneArg(const BSONObj& args) {
uassert( 12597 , "need to specify 1 argument" , args.nFields() == 1 );
2010-06-17 19:46:27 +02:00
return args.firstElement();
}
2010-06-29 00:21:40 +02:00
const int CANT_OPEN_FILE = 13300;
2011-05-11 04:14:32 +02:00
BSONObj cat(const BSONObj& args, void* data) {
2010-06-17 19:46:27 +02:00
BSONElement e = oneArg(args);
stringstream ss;
ifstream f(e.valuestrsafe());
2010-06-29 00:21:40 +02:00
uassert(CANT_OPEN_FILE, "couldn't open file", f.is_open() );
2010-06-17 19:46:27 +02:00
streamsize sz = 0;
while( 1 ) {
char ch = 0;
// slow...maybe change one day
f.get(ch);
if( ch == 0 ) break;
ss << ch;
sz += 1;
2010-06-17 21:02:06 +02:00
uassert(13301, "cat() : file to big to load as a variable", sz < 1024 * 1024 * 16);
2010-06-17 19:46:27 +02:00
}
return BSON( "" << ss.str() );
}
2011-05-11 04:14:32 +02:00
BSONObj md5sumFile(const BSONObj& args, void* data) {
2010-06-29 00:21:40 +02:00
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 ) );
}
2011-05-11 04:14:32 +02:00
BSONObj mkdir(const BSONObj& args, void* data) {
2010-07-02 20:10:13 +02:00
boost::filesystem::create_directories(args.firstElement().String());
return BSON( "" << true );
}
2011-05-11 04:14:32 +02:00
BSONObj removeFile(const BSONObj& args, void* data) {
2010-06-17 19:46:27 +02:00
BSONElement e = oneArg(args);
2010-02-02 04:07:05 +01:00
bool found = false;
2011-01-04 06:40:41 +01:00
path root( e.valuestrsafe() );
2011-01-04 06:40:41 +01:00
if ( boost::filesystem::exists( root ) ) {
2010-02-02 04:07:05 +01:00
found = true;
boost::filesystem::remove_all( root );
}
BSONObjBuilder b;
b.appendBool( "removed" , found );
return b.obj();
}
2011-01-03 23:04:58 +01:00
/**
* @param args - [ name, byte index ]
* In this initial implementation, all bits in the specified byte are flipped.
*/
2011-05-11 04:14:32 +02:00
BSONObj fuzzFile(const BSONObj& args, void* data) {
2011-01-03 23:04:58 +01:00
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() );
2011-01-04 06:40:41 +01:00
2011-01-03 23:04:58 +01:00
char c;
f->read( args.getIntField( "1" ), &c, 1 );
c = ~c;
f->write( args.getIntField( "1" ), &c, 1 );
return undefined_;
2011-01-04 06:40:41 +01:00
// f close is implicit
}
map< int, pair< pid_t, int > > dbs;
map< pid_t, int > shells;
#ifdef _WIN32
map< pid_t, HANDLE > handles;
#endif
2011-01-04 06:40:41 +01:00
2010-05-26 07:10:25 +02:00
mongo::mutex mongoProgramOutputMutex("mongoProgramOutputMutex");
stringstream mongoProgramOutput_;
2011-01-04 06:40:41 +01:00
void goingAwaySoon() {
2010-06-16 22:56:05 +02:00
mongo::mutex::scoped_lock lk( mongoProgramOutputMutex );
mongo::dbexitCalled = true;
2010-06-16 22:56:05 +02:00
}
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 )
2011-07-17 21:15:18 +02:00
buf << " m" << port << "| " << line;
else
buf << "sh" << pid << "| " << line;
cout << buf.str() << endl;
mongoProgramOutput_ << buf.str() << endl;
}
2011-01-04 06:40:41 +01:00
// only returns last 100000 characters
2011-05-11 04:14:32 +02:00
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 );
}
2011-05-11 04:14:32 +02:00
BSONObj ClearRawMongoProgramOutput( const BSONObj &args, void* data ) {
mongo::mutex::scoped_lock lk( mongoProgramOutputMutex );
mongoProgramOutput_.str( "" );
return undefined_;
}
2011-01-04 06:40:41 +01:00
2010-02-13 03:55:37 +01:00
class ProgramRunner {
2010-02-13 01:48:33 +01:00
vector<string> argv_;
int port_;
int pipe_;
pid_t pid_;
public:
pid_t pid() const { return pid_; }
2010-06-22 05:38:26 +02:00
int port() const { return port_; }
2010-06-12 23:49:54 +02:00
2011-01-04 06:40:41 +01:00
boost::filesystem::path find(string prog) {
2010-06-12 23:49:54 +02:00
boost::filesystem::path p = prog;
#ifdef _WIN32
p = change_extension(p, ".exe");
#endif
2011-01-04 06:40:41 +01:00
if( boost::filesystem::exists(p) ) {
2010-06-13 04:45:17 +02:00
#ifndef _WIN32
p = boost::filesystem::initial_path() / p;
#endif
return p;
}
2010-06-12 23:49:54 +02:00
{
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;
}
}
2011-01-04 06:40:41 +01:00
}
catch(...) { }
2010-06-12 23:49:54 +02:00
{
boost::filesystem::path t = boost::filesystem::initial_path() / p;
if( boost::filesystem::exists(t) ) return t;
}
2010-06-14 22:10:23 +02:00
return p; // not found; might find via system path
2011-01-04 06:40:41 +01:00
}
2010-06-12 23:49:54 +02:00
2011-01-04 06:40:41 +01:00
ProgramRunner( const BSONObj &args , bool isMongoProgram=true) {
2010-02-13 01:48:33 +01:00
assert( !args.isEmpty() );
string program( args.firstElement().valuestrsafe() );
assert( !program.empty() );
2010-06-12 23:49:54 +02:00
boost::filesystem::path programPath = find(program);
2011-01-04 06:40:41 +01:00
if (isMongoProgram) {
#if 0
if (program == "mongos") {
argv_.push_back("valgrind");
2010-05-27 03:47:02 +02:00
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
}
2010-02-13 01:48:33 +01:00
argv_.push_back( programPath.native_file_string() );
2011-01-04 06:40:41 +01:00
port_ = -1;
2011-01-04 06:40:41 +01:00
BSONObjIterator j( args );
2010-02-13 01:48:33 +01:00
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();
2011-01-04 06:40:41 +01:00
}
else {
assert( e.type() == mongo::String );
str = e.valuestr();
}
2010-02-13 01:48:33 +01:00
if ( str == "--port" )
port_ = -2;
else if ( port_ == -2 )
2010-02-13 01:48:33 +01:00
port_ = strtol( str.c_str(), 0, 10 );
argv_.push_back(str);
}
2011-01-04 06:40:41 +01:00
2009-05-20 19:39:46 +02:00
if ( program != "mongod" && program != "mongos" && program != "mongobridge" )
port_ = 0;
2010-06-12 23:49:54 +02:00
else {
2010-06-13 04:45:17 +02:00
if ( port_ <= 0 )
cout << "error: a port number is expected when running mongod (etc.) from the shell" << endl;
assert( port_ > 0 );
2010-06-12 23:49:54 +02:00
}
2011-01-04 06:40:41 +01:00
if ( port_ > 0 && dbs.count( port_ ) != 0 ) {
cerr << "count for port: " << port_ << " is not 0 is: " << dbs.count( port_ ) << endl;
2011-01-04 06:40:41 +01:00
assert( dbs.count( port_ ) == 0 );
}
}
2011-01-04 06:40:41 +01:00
void start() {
int pipeEnds[ 2 ];
assert( pipe( pipeEnds ) != -1 );
2011-01-04 06:40:41 +01:00
fflush( 0 );
2010-02-13 01:48:33 +01:00
launch_process(pipeEnds[1]); //sets pid_
2011-01-04 06:40:41 +01:00
2010-06-12 23:49:54 +02:00
{
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 ];
}
2011-01-04 06:40:41 +01:00
// Continue reading output
void operator()() {
2010-06-16 22:56:05 +02:00
try {
2011-01-04 06:40:41 +01:00
// This assumes there aren't any 0's in the mongo program output.
// Hope that's ok.
2011-05-08 23:28:46 +02:00
const unsigned bufSize = 128 * 1024;
2011-01-04 06:40:41 +01:00
char buf[ bufSize ];
char temp[ bufSize ];
char *start = buf;
while( 1 ) {
int lenToRead = ( bufSize - 1 ) - ( start - buf );
2011-05-08 23:28:46 +02:00
if ( lenToRead <= 0 ) {
cout << "error: lenToRead: " << lenToRead << endl;
cout << "first 300: " << string(buf,0,300) << endl;
}
2011-01-04 06:40:41 +01:00
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 );
2011-01-04 06:40:41 +01:00
}
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 );
}
2011-01-04 06:40:41 +01:00
}
catch(...) {
2010-06-16 22:56:05 +02:00
}
}
2011-01-04 06:40:41 +01:00
void launch_process(int child_stdout) {
#ifdef _WIN32
stringstream ss;
2011-01-04 06:40:41 +01:00
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();
2011-01-04 06:40:41 +01:00
boost::scoped_array<TCHAR> args_tchar (new TCHAR[args.size() + 1]);
2010-06-08 02:31:27 +02:00
size_t i;
for(i=0; i < args.size(); i++)
args_tchar[i] = args[i];
2010-06-08 02:31:27 +02:00
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));
2010-06-06 02:55:11 +02:00
bool success = CreateProcess( NULL, args_tchar.get(), NULL, NULL, true, 0, NULL, NULL, &si, &pi) != 0;
2010-06-14 22:10:23 +02:00
{
stringstream ss;
ss << "couldn't start process " << argv_[0];
2011-04-15 19:39:23 +02:00
uassert(14042, ss.str(), success);
2010-06-14 22:10:23 +02:00
}
CloseHandle(pi.hThread);
pid_ = pi.dwProcessId;
handles.insert( make_pair( pid_, pi.hProcess ) );
2011-01-04 06:40:41 +01:00
#else
pid_ = fork();
assert( pid_ != -1 );
2011-01-04 06:40:41 +01:00
if ( pid_ == 0 ) {
2010-02-13 01:48:33 +01:00
// 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
2011-01-04 06:40:41 +01:00
for (unsigned i=0; i < argv_.size(); i++) {
2010-02-13 01:48:33 +01:00
argv[i] = argv_[i].c_str();
}
argv[argv_.size()] = 0;
2011-01-04 06:40:41 +01:00
2010-02-13 01:48:33 +01:00
if ( dup2( child_stdout, STDOUT_FILENO ) == -1 ||
2011-01-04 06:40:41 +01:00
dup2( child_stdout, STDERR_FILENO ) == -1 ) {
2010-04-25 03:42:37 +02:00
cout << "Unable to dup2 child output: " << errnoWithDescription() << endl;
2010-02-13 01:48:33 +01:00
::_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.
2011-01-04 06:40:41 +01:00
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) );
2010-02-13 01:48:33 +01:00
2010-06-14 22:10:23 +02:00
cout << "Unable to start program " << argv[0] << ' ' << errnoWithDescription() << endl;
2010-02-13 01:48:33 +01:00
::_Exit(-1);
}
2010-02-13 01:48:33 +01:00
#endif
}
};
2011-01-04 06:40:41 +01:00
//returns true if process exited
2011-01-04 06:40:41 +01:00
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);
2010-02-13 03:55:37 +01:00
DWORD tmp;
2011-01-04 06:40:41 +01:00
if(GetExitCodeProcess(h, &tmp)) {
2011-02-10 21:33:30 +01:00
if ( tmp == STILL_ACTIVE ) {
return false;
}
CloseHandle(h);
handles.erase(pid);
2010-02-13 03:55:37 +01:00
if (exit_code)
*exit_code = tmp;
return true;
2011-01-04 06:40:41 +01:00
}
else {
return false;
}
#else
2010-02-13 03:55:37 +01:00
int tmp;
bool ret = (pid == waitpid(pid, &tmp, (block ? 0 : WNOHANG)));
if (exit_code)
*exit_code = WEXITSTATUS(tmp);
return ret;
2011-01-04 06:40:41 +01:00
#endif
}
2011-05-11 04:14:32 +02:00
BSONObj WaitProgram( const BSONObj& a, void* data ) {
int pid = oneArg( a ).numberInt();
BSONObj x = BSON( "" << wait_for_pid( pid ) );
shells.erase( pid );
return x;
}
2011-01-05 00:03:40 +01:00
2011-05-11 04:14:32 +02:00
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 );
}
2011-05-11 04:14:32 +02:00
BSONObj StartMongoProgram( const BSONObj &a, void* data ) {
_nokillop = true;
2010-02-13 03:55:37 +01:00
ProgramRunner r( a );
r.start();
boost::thread t( r );
2009-05-20 21:11:45 +02:00
return BSON( string( "" ) << int( r.pid() ) );
}
2011-05-11 04:14:32 +02:00
BSONObj RunMongoProgram( const BSONObj &a, void* data ) {
2010-02-13 03:55:37 +01:00
ProgramRunner r( a );
2009-08-24 17:54:06 +02:00
r.start();
boost::thread t( r );
2010-06-22 05:38:26 +02:00
int exit_code;
wait_for_pid( r.pid(), true, &exit_code );
if ( r.port() > 0 ) {
dbs.erase( r.port() );
2011-01-04 06:40:41 +01:00
}
else {
2010-06-22 05:38:26 +02:00
shells.erase( r.pid() );
}
return BSON( string( "" ) << exit_code );
2009-08-24 17:54:06 +02:00
}
2011-05-11 04:14:32 +02:00
BSONObj RunProgram(const BSONObj &a, void* data) {
2010-02-13 03:55:37 +01:00
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 );
}
2011-05-11 04:14:32 +02:00
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 );
2011-01-04 06:40:41 +01:00
boost::filesystem::create_directory( path );
return undefined_;
}
2011-01-04 06:40:41 +01:00
2010-02-16 19:42:47 +01:00
void copyDir( const path &from, const path &to ) {
directory_iterator end;
directory_iterator i( from );
while( i != end ) {
path p = *i;
2010-02-16 20:51:51 +01:00
if ( p.leaf() != "mongod.lock" ) {
2010-02-16 19:42:47 +01:00
if ( is_directory( p ) ) {
2010-02-16 20:51:51 +01:00
path newDir = to / p.leaf();
2010-02-16 19:42:47 +01:00
boost::filesystem::create_directory( newDir );
copyDir( p, newDir );
2011-01-04 06:40:41 +01:00
}
else {
2010-02-16 20:51:51 +01:00
boost::filesystem::copy_file( p, to / p.leaf() );
2010-02-16 19:42:47 +01:00
}
}
++i;
2011-01-04 06:40:41 +01:00
}
2010-02-16 19:42:47 +01:00
}
2011-01-04 06:40:41 +01:00
2010-02-16 19:42:47 +01:00
// NOTE target dbpath will be cleared first
2011-05-11 04:14:32 +02:00
BSONObj CopyDbpath( const BSONObj &a, void* data ) {
2010-02-16 19:42:47 +01:00
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_;
}
2011-01-04 06:40:41 +01:00
inline void kill_wrapper(pid_t pid, int sig, int port) {
#ifdef _WIN32
2011-01-04 06:40:41 +01:00
if (sig == SIGKILL || port == 0) {
assert( handles.count(pid) );
2010-02-03 04:46:31 +01:00
TerminateProcess(handles[pid], 1); // returns failure for "zombie" processes.
2011-01-04 06:40:41 +01:00
}
else {
DBClientConnection conn;
conn.connect("127.0.0.1:" + BSONObjBuilder::numStr(port));
try {
conn.simpleCommand("admin", NULL, "shutdown");
2011-01-04 06:40:41 +01:00
}
catch (...) {
//Do nothing. This command never returns data to the client and the driver doesn't like that.
}
}
#else
int x = kill( pid, sig );
2011-01-04 06:40:41 +01:00
if ( x ) {
if ( errno == ESRCH ) {
}
else {
cout << "killFailed: " << errnoWithDescription() << endl;
assert( x == 0 );
}
}
#endif
2011-01-04 06:40:41 +01:00
}
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;
2011-01-04 06:40:41 +01:00
}
else {
pid = _pid;
}
2011-01-04 06:40:41 +01:00
kill_wrapper( pid, signal, port );
2011-01-04 06:40:41 +01:00
int i = 0;
2010-10-29 21:04:22 +02:00
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 );
2011-01-04 06:40:41 +01:00
}
if(wait_for_pid(pid, false, &exitCode))
break;
2010-03-27 06:03:45 +01:00
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 );
2011-01-04 06:40:41 +01:00
}
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 ) {
2010-03-27 06:03:45 +01:00
sleepmillis( 4000 ); // allow operating system to reclaim resources
}
2011-01-04 06:40:41 +01:00
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;
}
2011-01-04 06:40:41 +01:00
2010-12-09 21:12:58 +01:00
/** stopMongoProgram(port[, signal]) */
2011-05-11 04:14:32 +02:00
BSONObj StopMongoProgram( const BSONObj &a, void* data ) {
assert( a.nFields() == 1 || a.nFields() == 2 );
2011-07-05 16:24:41 +02:00
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;
2011-07-05 08:29:36 +02:00
return BSON( "" << (double)code );
2011-01-04 06:40:41 +01:00
}
2011-05-11 04:14:32 +02:00
BSONObj StopMongoProgramByPid( const BSONObj &a, void* data ) {
assert( a.nFields() == 1 || a.nFields() == 2 );
2011-07-05 15:12:19 +02:00
uassert( 15852 , "stopMongoByPid needs a number" , a.firstElement().isNumber() );
2011-01-04 06:40:41 +01:00
int pid = int( a.firstElement().number() );
int code = killDb( 0, pid, getSignal( a ) );
cout << "shell: stopped mongo program on pid " << pid << endl;
2011-07-05 08:29:36 +02:00
return BSON( "" << (double)code );
}
2011-01-04 06:40:41 +01:00
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 )
2011-01-04 06:40:41 +01:00
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
2011-01-04 06:40:41 +01:00
MongoProgramScope::~MongoProgramScope() {
DESTRUCTOR_GUARD(
KillMongoProgramInstances();
2011-05-11 04:14:32 +02:00
ClearRawMongoProgramOutput( BSONObj(), 0 );
)
}
unsigned _randomSeed;
2011-01-04 06:40:41 +01:00
2011-05-11 04:14:32 +02:00
BSONObj JSSrand( const BSONObj &a, void* data ) {
uassert( 12518, "srand requires a single numeric argument",
2011-01-04 06:40:41 +01:00
a.nFields() == 1 && a.firstElement().isNumber() );
_randomSeed = (unsigned)a.firstElement().numberLong(); // grab least significant digits
return undefined_;
}
2011-01-04 06:40:41 +01:00
2011-05-11 04:14:32 +02:00
BSONObj JSRand( const BSONObj &a, void* data ) {
uassert( 12519, "rand accepts no arguments", a.nFields() == 0 );
2010-01-05 02:40:47 +01:00
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 ) );
}
2010-02-13 18:37:00 +01:00
2011-05-11 04:14:32 +02:00
BSONObj isWindows(const BSONObj& a, void* data) {
2010-02-13 18:37:00 +01:00
uassert( 13006, "isWindows accepts no arguments", a.nFields() == 0 );
#ifdef _WIN32
return BSON( "" << true );
#else
return BSON( "" << false );
#endif
}
2010-07-23 23:53:49 +02:00
2011-05-11 04:14:32 +02:00
BSONObj getHostName(const BSONObj& a, void* data) {
2010-07-23 23:55:56 +02:00
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';
2010-07-23 23:53:49 +02:00
return BSON("" << buf);
}
2011-01-04 06:40:41 +01:00
void installShellUtils( Scope& scope ) {
2010-06-12 23:49:54 +02:00
theScope = &scope;
2009-05-14 16:58:28 +02:00
scope.injectNative( "quit", Quit );
scope.injectNative( "getMemInfo" , JSGetMemInfo );
scope.injectNative( "_srand" , JSSrand );
scope.injectNative( "_rand" , JSRand );
2010-02-13 18:37:00 +01:00
scope.injectNative( "_isWindows" , isWindows );
#ifndef MONGO_SAFE_SHELL
//can't launch programs
scope.injectNative( "_startMongoProgram", StartMongoProgram );
2010-02-13 03:55:37 +01:00
scope.injectNative( "runProgram", RunProgram );
2010-06-11 06:16:34 +02:00
scope.injectNative( "run", RunProgram );
2009-08-24 17:54:06 +02:00
scope.injectNative( "runMongoProgram", RunMongoProgram );
scope.injectNative( "stopMongod", StopMongoProgram );
2011-01-04 06:40:41 +01:00
scope.injectNative( "stopMongoProgram", StopMongoProgram );
scope.injectNative( "stopMongoProgramByPid", StopMongoProgramByPid );
scope.injectNative( "rawMongoProgramOutput", RawMongoProgramOutput );
scope.injectNative( "clearRawMongoProgramOutput", ClearRawMongoProgramOutput );
scope.injectNative( "waitProgram" , WaitProgram );
scope.injectNative( "waitMongoProgramOnPort" , WaitMongoProgramOnPort );
2010-07-23 23:53:49 +02:00
scope.injectNative( "getHostName" , getHostName );
scope.injectNative( "removeFile" , removeFile );
2011-01-03 23:04:58 +01:00
scope.injectNative( "fuzzFile" , fuzzFile );
scope.injectNative( "listFiles" , listFiles );
2010-06-07 17:17:08 +02:00
scope.injectNative( "ls" , ls );
scope.injectNative( "pwd", pwd );
2010-06-12 23:49:54 +02:00
scope.injectNative( "cd", cd );
2010-06-17 19:46:27 +02:00
scope.injectNative( "cat", cat );
2010-06-11 00:02:17 +02:00
scope.injectNative( "hostname", hostname);
scope.injectNative( "resetDbpath", ResetDbpath );
2010-02-16 19:42:47 +01:00
scope.injectNative( "copyDbpath", CopyDbpath );
2010-06-29 00:21:40 +02:00
scope.injectNative( "md5sumFile", md5sumFile );
2010-07-02 20:10:13 +02:00
scope.injectNative( "mkdir" , mkdir );
#endif
}
void initScope( Scope &scope ) {
scope.externalSetup();
mongo::shellUtils::installShellUtils( scope );
scope.execSetup(JSFiles::servers);
2011-01-04 06:40:41 +01:00
if ( !_dbConnect.empty() ) {
2009-12-30 04:07:44 +01:00
uassert( 12513, "connect failed", scope.exec( _dbConnect , "(connect)" , false , true , false ) );
if ( !_dbAuth.empty() ) {
2010-02-09 02:43:08 +01:00
installGlobalUtils( scope );
2009-12-30 04:07:44 +01:00
uassert( 12514, "login failed", scope.exec( _dbAuth , "(auth)" , true , true , false ) );
}
}
}
2011-01-04 06:40:41 +01:00
2010-08-18 00:09:53 +02:00
// 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 ) ) {
2010-08-18 00:09:53 +02:00
string connstr = dynamic_cast<DBClientBase&>(c).getServerAddress();
mongo::mutex::scoped_lock lk( _allMyUrisMutex );
2010-08-18 00:09:53 +02:00
_allMyUris[connstr].insert(info[ "you" ].str());
}
}
}
}