// dbshell.cpp /* * Copyright 2010 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 . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #include "mongo/pch.h" #include #include #include #include #include #include "mongo/base/initializer.h" #include "mongo/base/status.h" #include "mongo/client/clientOnly-private.h" #include "mongo/client/dbclientinterface.h" #include "mongo/client/sasl_client_authenticate.h" #include "mongo/db/repl/heartbeat_info.h" #include "mongo/logger/console_appender.h" #include "mongo/logger/logger.h" #include "mongo/logger/message_event_utf8_encoder.h" #include "mongo/scripting/engine.h" #include "mongo/shell/linenoise.h" #include "mongo/shell/shell_options.h" #include "mongo/shell/shell_utils.h" #include "mongo/shell/shell_utils_launcher.h" #include "mongo/util/file.h" #include "mongo/util/net/ssl_options.h" #include "mongo/util/password.h" #include "mongo/util/stacktrace.h" #include "mongo/util/startup_test.h" #include "mongo/util/text.h" #include "mongo/util/version.h" #ifdef _WIN32 #include #include #define isatty _isatty #define fileno _fileno #else #include #endif using namespace std; using namespace mongo; string historyFile; bool gotInterrupted = false; bool inMultiLine = false; static volatile bool atPrompt = false; // can eval before getting to prompt #if !defined(__freebsd__) && !defined(__openbsd__) && !defined(_WIN32) // this is for ctrl-c handling #include jmp_buf jbuf; #endif namespace mongo { Scope * shellMainScope; extern bool dbexitCalled; } void generateCompletions( const string& prefix , vector& all ) { if ( prefix.find( '"' ) != string::npos ) return; try { BSONObj args = BSON( "0" << prefix ); shellMainScope->invokeSafe("function callShellAutocomplete(x) {shellAutocomplete(x)}", &args, NULL); BSONObjBuilder b; shellMainScope->append( b , "" , "__autocomplete__" ); BSONObj res = b.obj(); BSONObj arr = res.firstElement().Obj(); BSONObjIterator i( arr ); while ( i.more() ) { BSONElement e = i.next(); all.push_back( e.String() ); } } catch ( ... ) { } } void completionHook( const char* text , linenoiseCompletions* lc ) { vector all; generateCompletions( text , all ); for ( unsigned i = 0; i < all.size(); ++i ) linenoiseAddCompletion( lc , (char*)all[i].c_str() ); } void shellHistoryInit() { stringstream ss; const char * h = shell_utils::getUserDir(); if ( h ) ss << h << "/"; ss << ".dbshell"; historyFile = ss.str(); linenoiseHistoryLoad( historyFile.c_str() ); linenoiseSetCompletionCallback( completionHook ); } void shellHistoryDone() { linenoiseHistorySave( historyFile.c_str() ); linenoiseHistoryFree(); } void shellHistoryAdd( const char * line ) { if ( line[0] == '\0' ) return; // dont record duplicate lines static string lastLine; if ( lastLine == line ) return; lastLine = line; // We don't want any .auth() or .addUser() shell helpers added, but we want to // be able to add things like `.author`, so be smart about how this is // detected by using regular expresions. static pcrecpp::RE hiddenHelpers( "\\.\\s*(auth|addUser|createUser|updateUser|changeUserPassword)\\s*\\("); // Also don't want the raw user management commands to show in the shell when run directly // via runCommand. static pcrecpp::RE hiddenCommands( "(run|admin)Command\\s*\\(\\s*{\\s*(createUser|updateUser)\\s*:"); if (!hiddenHelpers.PartialMatch(line) && !hiddenCommands.PartialMatch(line)) { linenoiseHistoryAdd( line ); } } #ifdef CTRLC_HANDLE void intr( int sig ) { longjmp( jbuf , 1 ); } #endif void killOps() { if ( mongo::shell_utils::_nokillop ) return; if ( atPrompt ) return; sleepmillis(10); // give current op a chance to finish mongo::shell_utils::connectionRegistry. killOperationsOnAllConnections(!shellGlobalParams.autoKillOp); } void quitNicely( int sig ) { { mongo::mutex::scoped_lock lk(mongo::shell_utils::mongoProgramOutputMutex); mongo::dbexitCalled = true; } if ( sig == SIGINT && inMultiLine ) { gotInterrupted = 1; return; } killOps(); shellHistoryDone(); ::_exit(0); } // the returned string is allocated with strdup() or malloc() and must be freed by calling free() char * shellReadline( const char * prompt , int handlesigint = 0 ) { atPrompt = true; #ifdef CTRLC_HANDLE if ( ! handlesigint ) { char* ret = linenoise( prompt ); atPrompt = false; return ret; } if ( setjmp( jbuf ) ) { gotInterrupted = 1; sigrelse(SIGINT); signal( SIGINT , quitNicely ); return 0; } signal( SIGINT , intr ); #endif char * ret = linenoise( prompt ); if ( ! ret ) { gotInterrupted = true; // got ^C, break out of multiline } signal( SIGINT , quitNicely ); atPrompt = false; return ret; } #ifdef _WIN32 char * strsignal(int sig){ switch (sig){ case SIGINT: return "SIGINT"; case SIGTERM: return "SIGTERM"; case SIGABRT: return "SIGABRT"; case SIGSEGV: return "SIGSEGV"; case SIGFPE: return "SIGFPE"; default: return "unknown"; } } #endif void quitAbruptly( int sig ) { log() << "mongo got signal " << sig << " (" << strsignal( sig ) << "), stack trace: "; mongo::printStackTrace(); mongo::shell_utils::KillMongoProgramInstances(); ::_exit( 14 ); } // this will be called in certain c++ error cases, for example if there are two active // exceptions void myterminate() { mongo::printStackTrace(severe().stream() << "terminate() called in shell, printing stack:\n"); ::_exit( 14 ); } static void ignoreSignal(int ignored) {} void setupSignals() { signal( SIGINT , quitNicely ); signal( SIGTERM , quitNicely ); signal( SIGABRT , quitAbruptly ); signal( SIGSEGV , quitAbruptly ); signal( SIGFPE , quitAbruptly ); #if !defined(_WIN32) // surprisingly these are the only ones that don't work on windows struct sigaction sigactionSignals; sigactionSignals.sa_handler = ignoreSignal; sigemptyset(&sigactionSignals.sa_mask); sigactionSignals.sa_flags = 0; sigaction(SIGPIPE, &sigactionSignals, NULL); // errors are handled in socket code directly signal( SIGBUS , quitAbruptly ); #endif set_terminate( myterminate ); } string fixHost( const std::string& url, const std::string& host, const std::string& port ) { //cout << "fixHost url: " << url << " host: " << host << " port: " << port << endl; if ( host.size() == 0 && port.size() == 0 ) { if ( url.find( "/" ) == string::npos ) { // check for ips if ( url.find( "." ) != string::npos ) return url + "/test"; if ( url.rfind( ":" ) != string::npos && isdigit( url[url.rfind(":")+1] ) ) return url + "/test"; } return url; } if ( url.find( "/" ) != string::npos ) { cerr << "url can't have host or port if you specify them individually" << endl; ::_exit(-1); } string newurl( ( host.size() == 0 ) ? "127.0.0.1" : host ); if ( port.size() > 0 ) newurl += ":" + port; else if ( host.find(':') == string::npos ) { // need to add port with IPv6 addresses newurl += ":27017"; } newurl += "/" + url; return newurl; } static string OpSymbols = "~!%^&*-+=|:,<>/?."; bool isOpSymbol( char c ) { for ( size_t i = 0; i < OpSymbols.size(); i++ ) if ( OpSymbols[i] == c ) return true; return false; } bool isUseCmd( const std::string& code ) { string cmd = code; if ( cmd.find( " " ) > 0 ) cmd = cmd.substr( 0 , cmd.find( " " ) ); return cmd == "use"; } /** * Skip over a quoted string, including quotes escaped with backslash * * @param code String * @param start Starting position within string, always > 0 * @param quote Quote character (single or double quote) * @return Position of ending quote, or code.size() if no quote found */ size_t skipOverString(const std::string& code, size_t start, char quote) { size_t pos = start; while (pos < code.size()) { pos = code.find(quote, pos); if (pos == std::string::npos) { return code.size(); } // We want to break if the quote we found is not escaped, but we need to make sure // that the escaping backslash is not itself escaped. Comparisons of start and pos // are to keep us from reading beyond the beginning of the quoted string. // if (start == pos || code[pos - 1] != '\\' || // previous char was backslash start == pos - 1 || code[pos - 2] == '\\' // char before backslash was not another ) { break; // The quote we found was not preceded by an unescaped backslash; it is real } ++pos; // The quote we found was escaped with backslash, so it doesn't count } return pos; } bool isBalanced( const std::string& code ) { if (isUseCmd( code )) return true; // don't balance "use " in case dbname contains special chars int curlyBrackets = 0; int squareBrackets = 0; int parens = 0; bool danglingOp = false; for ( size_t i=0; i= code.size()) { return true; // Do not let unterminated strings enter multi-line mode } break; case '\\': if ( i + 1 < code.size() && code[i+1] == '/' ) i++; break; case '+': case '-': if ( i + 1 < code.size() && code[i+1] == code[i] ) { i++; continue; // postfix op (++/--) can't be a dangling op } break; } if ( i >= code.size() ) { danglingOp = false; break; } if ( isOpSymbol( code[i] ) ) danglingOp = true; else if ( !std::isspace( static_cast( code[i] ) ) ) danglingOp = false; } return curlyBrackets == 0 && squareBrackets == 0 && parens == 0 && !danglingOp; } struct BalancedTest : public mongo::StartupTest { public: void run() { verify( isBalanced( "x = 5" ) ); verify( isBalanced( "function(){}" ) ); verify( isBalanced( "function(){\n}" ) ); verify( ! isBalanced( "function(){" ) ); verify( isBalanced( "x = \"{\";" ) ); verify( isBalanced( "// {" ) ); verify( ! isBalanced( "// \n {" ) ); verify( ! isBalanced( "\"//\" {" ) ); verify( isBalanced( "{x:/x\\//}" ) ); verify( ! isBalanced( "{ \\/// }" ) ); verify( isBalanced( "x = 5 + y " ) ); verify( ! isBalanced( "x = " ) ); verify( ! isBalanced( "x = // hello" ) ); verify( ! isBalanced( "x = 5 +" ) ); verify( isBalanced( " x ++" ) ); verify( isBalanced( "-- x" ) ); verify( !isBalanced( "a." ) ); verify( !isBalanced( "a. " ) ); verify( isBalanced( "a.b" ) ); // SERVER-5809 and related cases -- verify( isBalanced( "a = {s:\"\\\"\"}" ) ); // a = {s:"\""} verify( isBalanced( "db.test.save({s:\"\\\"\"})" ) ); // db.test.save({s:"\""}) verify( isBalanced( "printjson(\" \\\" \")" ) ); // printjson(" \" ") -- SERVER-8554 verify( isBalanced( "var a = \"\\\\\";" ) ); // var a = "\\"; verify( isBalanced( "var a = (\"\\\\\") //\"" ) ); // var a = ("\\") //" verify( isBalanced( "var a = (\"\\\\\") //\\\"" ) ); // var a = ("\\") //\" verify( isBalanced( "var a = (\"\\\\\") //" ) ); // var a = ("\\") // verify( isBalanced( "var a = (\"\\\\\")" ) ); // var a = ("\\") verify( isBalanced( "var a = (\"\\\\\\\"\")" ) ); // var a = ("\\\"") verify( ! isBalanced( "var a = (\"\\\\\" //\"" ) ); // var a = ("\\" //" verify( ! isBalanced( "var a = (\"\\\\\" //" ) ); // var a = ("\\" // verify( ! isBalanced( "var a = (\"\\\\\"" ) ); // var a = ("\\" } } balanced_test; string finishCode( string code ) { while ( ! isBalanced( code ) ) { inMultiLine = true; code += "\n"; // cancel multiline if two blank lines are entered if ( code.find( "\n\n\n" ) != string::npos ) return ";"; char * line = shellReadline( "... " , 1 ); if ( gotInterrupted ) { if ( line ) free( line ); return ""; } if ( ! line ) return ""; char * linePtr = line; while ( startsWith( linePtr, "... " ) ) linePtr += 4; code += linePtr; free( line ); } return code; } namespace mongo { extern bool isShell; } bool execPrompt( mongo::Scope &scope, const char *promptFunction, string &prompt ) { string execStatement = string( "__prompt__ = " ) + promptFunction + "();"; scope.exec( "delete __prompt__;", "", false, false, false, 0 ); scope.exec( execStatement, "", false, false, false, 0 ); if ( scope.type( "__prompt__" ) == String ) { prompt = scope.getString( "__prompt__" ); return true; } return false; } /** * Edit a variable or input buffer text in an external editor -- EDITOR must be defined * * @param whatToEdit Name of JavaScript variable to be edited, or any text string */ static void edit( const string& whatToEdit ) { // EDITOR may be defined in the JavaScript scope or in the environment string editor; if ( shellMainScope->type( "EDITOR" ) == String ) { editor = shellMainScope->getString( "EDITOR" ); } else { static const char * editorFromEnv = getenv( "EDITOR" ); if ( editorFromEnv ) { editor = editorFromEnv; } } if ( editor.empty() ) { cout << "please define EDITOR as a JavaScript string or as an environment variable" << endl; return; } // "whatToEdit" might look like a variable/property name bool editingVariable = true; for ( const char* p = whatToEdit.c_str(); *p; ++p ) { if ( ! ( isalnum( *p ) || *p == '_' || *p == '.' ) ) { editingVariable = false; break; } } string js; if ( editingVariable ) { // If "whatToEdit" is undeclared or uninitialized, declare int varType = shellMainScope->type( whatToEdit.c_str() ); if ( varType == Undefined ) { shellMainScope->exec( "var " + whatToEdit , "(shell)", false, true, false ); } // Convert "whatToEdit" to JavaScript (JSON) text if ( !shellMainScope->exec( "__jsout__ = tojson(" + whatToEdit + ")", "tojs", false, false, false ) ) return; // Error already printed js = shellMainScope->getString( "__jsout__" ); if ( strstr( js.c_str(), "[native code]" ) ) { cout << "can't edit native functions" << endl; return; } } else { js = whatToEdit; } // Pick a name to use for the temp file string filename; const int maxAttempts = 10; int i; for ( i = 0; i < maxAttempts; ++i ) { StringBuilder sb; #ifdef _WIN32 char tempFolder[MAX_PATH]; GetTempPathA( sizeof tempFolder, tempFolder ); sb << tempFolder << "mongo_edit" << time( 0 ) + i << ".js"; #else sb << "/tmp/mongo_edit" << time( 0 ) + i << ".js"; #endif filename = sb.str(); if (!::mongo::shell_utils::fileExists(filename)) break; } if ( i == maxAttempts ) { cout << "couldn't create unique temp file after " << maxAttempts << " attempts" << endl; return; } // Create the temp file FILE * tempFileStream; tempFileStream = fopen( filename.c_str(), "wt" ); if ( ! tempFileStream ) { cout << "couldn't create temp file (" << filename << "): " << errnoWithDescription() << endl; return; } // Write JSON into the temp file size_t fileSize = js.size(); if ( fwrite( js.data(), sizeof( char ), fileSize, tempFileStream ) != fileSize ) { int systemErrno = errno; cout << "failed to write to temp file: " << errnoWithDescription( systemErrno ) << endl; fclose( tempFileStream ); remove( filename.c_str() ); return; } fclose( tempFileStream ); // Pass file to editor StringBuilder sb; sb << editor << " " << filename; int ret = ::system( sb.str().c_str() ); if ( ret ) { if ( ret == -1 ) { int systemErrno = errno; cout << "failed to launch $EDITOR (" << editor << "): " << errnoWithDescription( systemErrno ) << endl; } else cout << "editor exited with error (" << ret << "), not applying changes" << endl; remove( filename.c_str() ); return; } // The editor gave return code zero, so read the file back in tempFileStream = fopen( filename.c_str(), "rt" ); if ( ! tempFileStream ) { cout << "couldn't open temp file on return from editor: " << errnoWithDescription() << endl; remove( filename.c_str() ); return; } sb.reset(); int bytes; do { char buf[1024]; bytes = fread( buf, sizeof( char ), sizeof buf, tempFileStream ); if ( ferror( tempFileStream ) ) { cout << "failed to read temp file: " << errnoWithDescription() << endl; fclose( tempFileStream ); remove( filename.c_str() ); return; } sb.append( StringData( buf, bytes ) ); } while ( bytes ); // Done with temp file, close and delete it fclose( tempFileStream ); remove( filename.c_str() ); if ( editingVariable ) { // Try to execute assignment to copy edited value back into the variable const string code = whatToEdit + string( " = " ) + sb.str(); if ( !shellMainScope->exec( code, "tojs", false, true, false ) ) { cout << "error executing assignment: " << code << endl; } } else { linenoisePreloadBuffer( sb.str().c_str() ); } } int _main( int argc, char* argv[], char **envp ) { mongo::isShell = true; setupSignals(); mongo::shell_utils::RecordMyLocation( argv[ 0 ] ); shellGlobalParams.url = "test"; mongo::runGlobalInitializersOrDie(argc, argv, envp); // hide password from ps output for ( int i = 0; i < (argc-1); ++i ) { if ( !strcmp(argv[i], "-p") || !strcmp( argv[i], "--password" ) ) { char* arg = argv[i + 1]; while ( *arg ) { *arg++ = 'x'; } } } if (!mongo::serverGlobalParams.quiet) cout << "MongoDB shell version: " << mongo::versionString << endl; mongo::StartupTest::runTests(); logger::globalLogManager()->getNamedDomain("javascriptOutput")->attachAppender( logger::MessageLogDomain::AppenderAutoPtr( new logger::ConsoleAppender( new logger::MessageEventUnadornedEncoder))); if (!shellGlobalParams.nodb) { // connect to db stringstream ss; if (mongo::serverGlobalParams.quiet) ss << "__quiet = true;"; ss << "db = connect( \"" << fixHost(shellGlobalParams.url, shellGlobalParams.dbhost, shellGlobalParams.port) << "\")"; mongo::shell_utils::_dbConnect = ss.str(); if (shellGlobalParams.usingPassword && shellGlobalParams.password.empty()) { shellGlobalParams.password = mongo::askPassword(); } } // Construct the authentication-related code to execute on shell startup. // // This constructs and immediately executes an anonymous function, to avoid // the shell's default behavior of printing statement results to the console. // // It constructs a statement of the following form: // // (function() { // // Set default authentication mechanism and, maybe, authenticate. // }()) stringstream authStringStream; authStringStream << "(function() { " << endl; if (!shellGlobalParams.authenticationMechanism.empty()) { authStringStream << "DB.prototype._defaultAuthenticationMechanism = \"" << escape(shellGlobalParams.authenticationMechanism) << "\";" << endl; } if (!shellGlobalParams.gssapiServiceName.empty()) { authStringStream << "DB.prototype._defaultGssapiServiceName = \"" << escape(shellGlobalParams.gssapiServiceName) << "\";" << endl; } if (!shellGlobalParams.nodb && shellGlobalParams.username.size()) { authStringStream << "var username = \"" << escape(shellGlobalParams.username) << "\";" << endl; if (shellGlobalParams.usingPassword) { authStringStream << "var password = \"" << escape(shellGlobalParams.password) << "\";" << endl; } if (shellGlobalParams.authenticationDatabase.empty()) { authStringStream << "var authDb = db;" << endl; } else { authStringStream << "var authDb = db.getSiblingDB(\"" << escape(shellGlobalParams.authenticationDatabase) << "\");" << endl; } authStringStream << "authDb._authOrThrow({ " << saslCommandUserFieldName << ": username "; if (shellGlobalParams.usingPassword) { authStringStream << ", " << saslCommandPasswordFieldName << ": password "; } if (!shellGlobalParams.gssapiHostName.empty()) { authStringStream << ", " << saslCommandServiceHostnameFieldName << ": \"" << escape(shellGlobalParams.gssapiHostName) << '"' << endl; } authStringStream << "});" << endl; } authStringStream << "}())"; mongo::shell_utils::_dbAuth = authStringStream.str(); mongo::ScriptEngine::setConnectCallback( mongo::shell_utils::onConnect ); mongo::ScriptEngine::setup(); mongo::globalScriptEngine->setScopeInitCallback( mongo::shell_utils::initScope ); auto_ptr< mongo::Scope > scope( mongo::globalScriptEngine->newScope() ); shellMainScope = scope.get(); if( shellGlobalParams.runShell ) cout << "type \"help\" for help" << endl; // Load and execute /etc/mongorc.js before starting shell std::string rcGlobalLocation; #ifndef _WIN32 rcGlobalLocation = "/etc/mongorc.js" ; #else wchar_t programDataPath[MAX_PATH]; if ( S_OK == SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, programDataPath) ) { rcGlobalLocation = str::stream() << toUtf8String(programDataPath) << "\\MongoDB\\mongorc.js"; } #endif if ( !rcGlobalLocation.empty() && ::mongo::shell_utils::fileExists(rcGlobalLocation) ) { if ( ! scope->execFile( rcGlobalLocation , false , true ) ) { cout << "The \"" << rcGlobalLocation << "\" file could not be executed" << endl; } } if ( !shellGlobalParams.script.empty() ) { mongo::shell_utils::MongoProgramScope s; if ( ! scope->exec( shellGlobalParams.script , "(shell eval)" , true , true , false ) ) return -4; } for (size_t i = 0; i < shellGlobalParams.files.size(); ++i) { mongo::shell_utils::MongoProgramScope s; if ( shellGlobalParams.files.size() > 1 ) cout << "loading file: " << shellGlobalParams.files[i] << endl; if ( ! scope->execFile( shellGlobalParams.files[i] , false , true ) ) { cout << "failed to load: " << shellGlobalParams.files[i] << endl; return -3; } } if ( shellGlobalParams.files.size() == 0 && shellGlobalParams.script.empty() ) shellGlobalParams.runShell = true; if ( shellGlobalParams.runShell ) { mongo::shell_utils::MongoProgramScope s; // If they specify norc, assume it's not their first time bool hasMongoRC = shellGlobalParams.norc; string rcLocation; if ( !shellGlobalParams.norc ) { #ifndef _WIN32 if ( getenv( "HOME" ) != NULL ) rcLocation = str::stream() << getenv( "HOME" ) << "/.mongorc.js" ; #else if ( getenv( "HOMEDRIVE" ) != NULL && getenv( "HOMEPATH" ) != NULL ) rcLocation = str::stream() << toUtf8String(_wgetenv(L"HOMEDRIVE")) << toUtf8String(_wgetenv(L"HOMEPATH")) << "\\.mongorc.js"; #endif if ( !rcLocation.empty() && ::mongo::shell_utils::fileExists(rcLocation) ) { hasMongoRC = true; if ( ! scope->execFile( rcLocation , false , true ) ) { cout << "The \".mongorc.js\" file located in your home folder could not be executed" << endl; return -5; } } } if ( !hasMongoRC && isatty(fileno(stdin)) ) { cout << "Welcome to the MongoDB shell.\n" "For interactive help, type \"help\".\n" "For more comprehensive documentation, see\n\thttp://docs.mongodb.org/\n" "Questions? Try the support group\n\thttp://groups.google.com/group/mongodb-user" << endl; File f; f.open(rcLocation.c_str(), false); // Create empty .mongorc.js file } if (!shellGlobalParams.nodb && !mongo::serverGlobalParams.quiet && isatty(fileno(stdin))) { scope->exec( "shellHelper( 'show', 'startupWarnings' )", "(shellwarnings", false, true, false ); } shellHistoryInit(); string prompt; int promptType; //v8::Handle shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject(); while ( 1 ) { inMultiLine = false; gotInterrupted = false; // shellMainScope->localConnect; //DBClientWithCommands *c = getConnection( JSContext *cx, JSObject *obj ); promptType = scope->type( "prompt" ); if ( promptType == String ) { prompt = scope->getString( "prompt" ); } else if ( ( promptType == Code ) && execPrompt( *scope, "prompt", prompt ) ) { } else if ( execPrompt( *scope, "defaultPrompt", prompt ) ) { } else { prompt = "> "; } char * line = shellReadline( prompt.c_str() ); char * linePtr = line; // can't clobber 'line', we need to free() it later if ( linePtr ) { while ( linePtr[0] == ' ' ) ++linePtr; int lineLen = strlen( linePtr ); while ( lineLen > 0 && linePtr[lineLen - 1] == ' ' ) linePtr[--lineLen] = 0; } if ( ! linePtr || ( strlen( linePtr ) == 4 && strstr( linePtr , "exit" ) ) ) { if (!mongo::serverGlobalParams.quiet) cout << "bye" << endl; if ( line ) free( line ); break; } string code = linePtr; if ( code == "exit" || code == "exit;" ) { free( line ); break; } if ( code == "cls" ) { free( line ); linenoiseClearScreen(); continue; } if ( code.size() == 0 ) { free( line ); continue; } if ( startsWith( linePtr, "edit " ) ) { shellHistoryAdd( linePtr ); const char* s = linePtr + 5; // skip "edit " while( *s && isspace( *s ) ) s++; edit( s ); free( line ); continue; } gotInterrupted = false; code = finishCode( code ); if ( gotInterrupted ) { cout << endl; free( line ); continue; } if ( code.size() == 0 ) { free( line ); break; } bool wascmd = false; { string cmd = linePtr; if ( cmd.find( " " ) > 0 ) cmd = cmd.substr( 0 , cmd.find( " " ) ); if ( cmd.find( "\"" ) == string::npos ) { try { scope->exec( (string)"__iscmd__ = shellHelper[\"" + cmd + "\"];" , "(shellhelp1)" , false , true , true ); if ( scope->getBoolean( "__iscmd__" ) ) { scope->exec( (string)"shellHelper( \"" + cmd + "\" , \"" + code.substr( cmd.size() ) + "\");" , "(shellhelp2)" , false , true , false ); wascmd = true; } } catch ( std::exception& e ) { cout << "error2:" << e.what() << endl; wascmd = true; } } } if ( ! wascmd ) { try { if ( scope->exec( code.c_str() , "(shell)" , false , true , false ) ) scope->exec( "shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false ); } catch ( std::exception& e ) { cout << "error:" << e.what() << endl; } } shellHistoryAdd( code.c_str() ); free( line ); } shellHistoryDone(); } { mongo::mutex::scoped_lock lk(mongo::shell_utils::mongoProgramOutputMutex); mongo::dbexitCalled = true; } return 0; } #ifdef _WIN32 int wmain(int argc, wchar_t* argvW[], wchar_t* envpW[]) { static mongo::StaticObserver staticObserver; int returnCode; try { WindowsCommandLine wcl(argc, argvW, envpW); returnCode = _main(argc, wcl.argv(), wcl.envp()); } catch ( mongo::DBException& e ) { cerr << "exception: " << e.what() << endl; returnCode = 1; } ::_exit(returnCode); } #else // #ifdef _WIN32 int main( int argc, char* argv[], char **envp ) { static mongo::StaticObserver staticObserver; int returnCode; try { returnCode = _main( argc , argv, envp ); } catch ( mongo::DBException& e ) { cerr << "exception: " << e.what() << endl; returnCode = 1; } _exit(returnCode); } #endif // #ifdef _WIN32