2009-01-26 22:19:15 -05:00
|
|
|
// dbshell.cpp
|
2010-02-09 16:48:21 -05:00
|
|
|
/*
|
|
|
|
|
* 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-05 20:55:11 -04:00
|
|
|
#include "pch.h"
|
2009-05-13 17:00:02 -04:00
|
|
|
#include <stdio.h>
|
2011-04-06 16:46:45 -04:00
|
|
|
#include <string.h>
|
2009-01-26 22:19:15 -05:00
|
|
|
|
2011-03-18 02:41:55 -04:00
|
|
|
#include "../third_party/linenoise/linenoise.h"
|
2009-05-04 17:37:02 -04:00
|
|
|
#include "../scripting/engine.h"
|
2009-05-13 17:00:02 -04:00
|
|
|
#include "../client/dbclient.h"
|
2009-08-25 13:54:01 -04:00
|
|
|
#include "../util/unittest.h"
|
2010-01-13 21:08:23 -05:00
|
|
|
#include "../db/cmdline.h"
|
2009-05-13 23:15:59 -04:00
|
|
|
#include "utils.h"
|
2010-05-04 10:47:20 -04:00
|
|
|
#include "../util/password.h"
|
2010-05-28 12:08:36 -04:00
|
|
|
#include "../util/version.h"
|
2010-06-14 16:22:11 -04:00
|
|
|
#include "../util/goodies.h"
|
2011-09-08 18:49:58 -04:00
|
|
|
#include "../util/file.h"
|
2010-11-23 04:16:04 -05:00
|
|
|
#include "../db/repl/rs_member.h"
|
2009-01-26 22:19:15 -05:00
|
|
|
|
2009-10-18 08:35:03 +08:00
|
|
|
using namespace std;
|
|
|
|
|
using namespace boost::filesystem;
|
2010-08-17 18:00:43 -04:00
|
|
|
using namespace mongo;
|
2010-06-07 11:06:02 -04:00
|
|
|
|
2009-07-28 11:19:13 -04:00
|
|
|
string historyFile;
|
2009-08-25 17:00:01 -04:00
|
|
|
bool gotInterrupted = 0;
|
|
|
|
|
bool inMultiLine = 0;
|
2010-07-21 18:10:37 -04:00
|
|
|
static volatile bool atPrompt = false; // can eval before getting to prompt
|
2010-07-21 18:45:40 -04:00
|
|
|
bool autoKillOp = false;
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
#if !defined(__freebsd__) && !defined(__openbsd__) && !defined(_WIN32)
|
2011-03-18 15:19:43 -04:00
|
|
|
// this is for ctrl-c handling
|
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
jmp_buf jbuf;
|
2009-11-01 20:18:37 -05:00
|
|
|
#endif
|
|
|
|
|
|
2010-11-11 01:47:15 -05:00
|
|
|
namespace mongo {
|
|
|
|
|
|
|
|
|
|
Scope * shellMainScope;
|
|
|
|
|
|
|
|
|
|
extern bool dbexitCalled;
|
|
|
|
|
}
|
2010-06-07 11:06:02 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
void generateCompletions( const string& prefix , vector<string>& all ) {
|
2010-06-07 11:06:02 -04:00
|
|
|
if ( prefix.find( '"' ) != string::npos )
|
|
|
|
|
return;
|
2010-08-09 20:19:00 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
BSONObj args = BSON( "0" << prefix );
|
|
|
|
|
shellMainScope->invokeSafe( "function(x) {shellAutocomplete(x)}", &args, 0, 1000 );
|
2010-06-07 11:06:02 -04:00
|
|
|
BSONObjBuilder b;
|
|
|
|
|
shellMainScope->append( b , "" , "__autocomplete__" );
|
|
|
|
|
BSONObj res = b.obj();
|
|
|
|
|
BSONObj arr = res.firstElement().Obj();
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
BSONObjIterator i( arr );
|
2011-01-04 00:40:41 -05:00
|
|
|
while ( i.more() ) {
|
2010-06-07 11:06:02 -04:00
|
|
|
BSONElement e = i.next();
|
|
|
|
|
all.push_back( e.String() );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
void completionHook( const char* text , linenoiseCompletions* lc ) {
|
2010-06-07 11:06:02 -04:00
|
|
|
vector<string> all;
|
2011-03-18 02:41:55 -04:00
|
|
|
generateCompletions( text , all );
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
for ( unsigned i = 0; i < all.size(); ++i )
|
2011-03-18 02:41:55 -04:00
|
|
|
linenoiseAddCompletion( lc , (char*)all[i].c_str() );
|
2011-09-08 18:49:58 -04:00
|
|
|
}
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
void shellHistoryInit() {
|
2009-07-28 11:19:13 -04:00
|
|
|
stringstream ss;
|
2011-10-11 18:44:29 -04:00
|
|
|
const char * h = shellUtils::getUserDir();
|
2009-07-28 11:19:13 -04:00
|
|
|
if ( h )
|
|
|
|
|
ss << h << "/";
|
|
|
|
|
ss << ".dbshell";
|
|
|
|
|
historyFile = ss.str();
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
linenoiseHistoryLoad( historyFile.c_str() );
|
2011-03-18 02:41:55 -04:00
|
|
|
linenoiseSetCompletionCallback( completionHook );
|
2009-02-10 13:45:30 -05:00
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
void shellHistoryDone() {
|
2011-11-16 13:07:37 -05:00
|
|
|
linenoiseHistorySave( historyFile.c_str() );
|
|
|
|
|
linenoiseHistoryFree();
|
2009-02-10 13:45:30 -05:00
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
void shellHistoryAdd( const char * line ) {
|
2010-06-29 18:00:07 -04:00
|
|
|
if ( line[0] == '\0' )
|
2009-02-10 13:45:30 -05:00
|
|
|
return;
|
2010-06-29 17:58:44 -04:00
|
|
|
|
|
|
|
|
// dont record duplicate lines
|
|
|
|
|
static string lastLine;
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( lastLine == line )
|
2010-06-29 17:58:44 -04:00
|
|
|
return;
|
|
|
|
|
lastLine = line;
|
2010-06-29 18:00:07 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( strstr( line, ".auth") == NULL )
|
2011-03-18 02:41:55 -04:00
|
|
|
linenoiseHistoryAdd( line );
|
2009-02-10 13:45:30 -05:00
|
|
|
}
|
|
|
|
|
|
2009-11-01 20:18:37 -05:00
|
|
|
#ifdef CTRLC_HANDLE
|
2011-11-27 09:51:40 -05:00
|
|
|
void intr( int sig ) {
|
2009-08-25 17:00:01 -04:00
|
|
|
longjmp( jbuf , 1 );
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
#endif
|
2009-08-25 16:11:51 -04:00
|
|
|
|
2010-02-17 17:06:54 -08:00
|
|
|
void killOps() {
|
2010-03-07 14:05:12 -05:00
|
|
|
if ( mongo::shellUtils::_nokillop || mongo::shellUtils::_allMyUris.size() == 0 )
|
2010-02-22 12:45:58 -08:00
|
|
|
return;
|
2010-07-21 18:01:22 -04:00
|
|
|
|
2010-07-21 18:10:37 -04:00
|
|
|
if ( atPrompt )
|
|
|
|
|
return;
|
2010-07-21 18:01:22 -04:00
|
|
|
|
2010-08-18 16:03:27 -04:00
|
|
|
sleepmillis(10); // give current op a chance to finish
|
2010-07-21 18:10:37 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
for( map< string, set<string> >::const_iterator i = shellUtils::_allMyUris.begin(); i != shellUtils::_allMyUris.end(); ++i ) {
|
2010-08-17 18:09:53 -04:00
|
|
|
string errmsg;
|
2011-11-27 09:51:40 -05:00
|
|
|
ConnectionString cs = ConnectionString::parse( i->first, errmsg );
|
2010-08-17 18:09:53 -04:00
|
|
|
if (!cs.isValid()) continue;
|
2011-11-27 09:51:40 -05:00
|
|
|
boost::scoped_ptr<DBClientWithCommands> conn( cs.connect( errmsg ) );
|
2010-08-17 18:09:53 -04:00
|
|
|
if (!conn) continue;
|
|
|
|
|
|
|
|
|
|
const set<string>& uris = i->second;
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
BSONObj inprog = conn->findOne( "admin.$cmd.sys.inprog", Query() )["inprog"].embeddedObject().getOwned();
|
|
|
|
|
BSONForEach( op, inprog ) {
|
|
|
|
|
if ( uris.count( op["client"].String() ) ) {
|
2010-08-18 16:03:27 -04:00
|
|
|
ONCE if ( !autoKillOp ) {
|
|
|
|
|
cout << endl << "do you want to kill the current op(s) on the server? (y/n): ";
|
|
|
|
|
cout.flush();
|
|
|
|
|
|
|
|
|
|
char yn;
|
|
|
|
|
cin >> yn;
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( yn != 'y' && yn != 'Y' )
|
2010-08-18 16:03:27 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
conn->findOne( "admin.$cmd.sys.killop", QUERY( "op"<< op["opid"] ) );
|
2010-08-17 18:09:53 -04:00
|
|
|
}
|
|
|
|
|
}
|
2010-03-17 23:50:57 -07:00
|
|
|
}
|
2010-02-17 17:06:54 -08:00
|
|
|
}
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
void quitNicely( int sig ) {
|
2010-11-11 01:47:15 -05:00
|
|
|
mongo::dbexitCalled = true;
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( sig == SIGINT && inMultiLine ) {
|
2009-08-25 17:00:01 -04:00
|
|
|
gotInterrupted = 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-04-06 16:30:19 -04:00
|
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
2009-05-29 15:56:45 -04:00
|
|
|
if ( sig == SIGPIPE )
|
|
|
|
|
mongo::rawOut( "mongo got signal SIGPIPE\n" );
|
2011-04-06 16:30:19 -04:00
|
|
|
#endif
|
|
|
|
|
|
2010-02-17 17:06:54 -08:00
|
|
|
killOps();
|
2009-02-10 13:45:30 -05:00
|
|
|
shellHistoryDone();
|
2009-01-26 22:19:15 -05:00
|
|
|
exit(0);
|
2010-06-15 10:31:01 -04:00
|
|
|
}
|
2009-01-26 22:19:15 -05:00
|
|
|
|
2011-11-16 13:07:37 -05:00
|
|
|
// the returned string is allocated with strdup() or malloc() and must be freed by calling free()
|
2011-01-04 00:40:41 -05:00
|
|
|
char * shellReadline( const char * prompt , int handlesigint = 0 ) {
|
2010-07-21 18:01:22 -04:00
|
|
|
atPrompt = true;
|
2010-06-07 11:06:02 -04:00
|
|
|
|
2009-11-01 20:18:37 -05:00
|
|
|
#ifdef CTRLC_HANDLE
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( ! handlesigint ) {
|
2011-03-18 02:41:55 -04:00
|
|
|
char* ret = linenoise( prompt );
|
2010-07-21 18:01:22 -04:00
|
|
|
atPrompt = false;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( setjmp( jbuf ) ) {
|
2009-08-25 17:00:01 -04:00
|
|
|
gotInterrupted = 1;
|
2009-09-18 13:15:16 -04:00
|
|
|
sigrelse(SIGINT);
|
2009-08-25 17:00:01 -04:00
|
|
|
signal( SIGINT , quitNicely );
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
signal( SIGINT , intr );
|
2009-11-01 20:18:37 -05:00
|
|
|
#endif
|
|
|
|
|
|
2011-03-18 02:41:55 -04:00
|
|
|
char * ret = linenoise( prompt );
|
2010-07-29 16:55:15 -04:00
|
|
|
signal( SIGINT , quitNicely );
|
2010-07-21 18:01:22 -04:00
|
|
|
atPrompt = false;
|
2009-08-25 17:00:01 -04:00
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 16:46:45 -04:00
|
|
|
#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
|
2009-08-25 17:00:01 -04:00
|
|
|
|
2009-05-29 15:56:45 -04:00
|
|
|
void quitAbruptly( int sig ) {
|
|
|
|
|
ostringstream ossSig;
|
|
|
|
|
ossSig << "mongo got signal " << sig << " (" << strsignal( sig ) << "), stack trace: " << endl;
|
|
|
|
|
mongo::rawOut( ossSig.str() );
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-05-29 15:56:45 -04:00
|
|
|
ostringstream ossBt;
|
|
|
|
|
mongo::printStackTrace( ossBt );
|
|
|
|
|
mongo::rawOut( ossBt.str() );
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-05-15 11:07:43 -04:00
|
|
|
mongo::shellUtils::KillMongoProgramInstances();
|
2011-11-27 09:51:40 -05:00
|
|
|
exit( 14 );
|
2009-01-30 12:37:46 -05:00
|
|
|
}
|
|
|
|
|
|
2010-09-07 13:26:30 -07:00
|
|
|
// this will be called in certain c++ error cases, for example if there are two active
|
|
|
|
|
// exceptions
|
|
|
|
|
void myterminate() {
|
|
|
|
|
mongo::rawOut( "terminate() called in shell, printing stack:" );
|
|
|
|
|
mongo::printStackTrace();
|
2011-11-27 09:51:40 -05:00
|
|
|
exit( 14 );
|
2010-09-07 13:26:30 -07:00
|
|
|
}
|
|
|
|
|
|
2009-01-30 12:37:46 -05:00
|
|
|
void setupSignals() {
|
|
|
|
|
signal( SIGINT , quitNicely );
|
2009-02-02 09:53:14 -05:00
|
|
|
signal( SIGTERM , quitNicely );
|
2009-01-30 12:37:46 -05:00
|
|
|
signal( SIGABRT , quitAbruptly );
|
2009-02-02 09:53:14 -05:00
|
|
|
signal( SIGSEGV , quitAbruptly );
|
|
|
|
|
signal( SIGFPE , quitAbruptly );
|
2011-04-06 16:46:45 -04:00
|
|
|
|
|
|
|
|
#if !defined(_WIN32) // surprisingly these are the only ones that don't work on windows
|
|
|
|
|
signal( SIGPIPE , quitNicely ); // Maybe just log and continue?
|
|
|
|
|
signal( SIGBUS , quitAbruptly );
|
|
|
|
|
#endif
|
|
|
|
|
|
2010-09-07 13:26:30 -07:00
|
|
|
set_terminate( myterminate );
|
2009-01-30 12:37:46 -05:00
|
|
|
}
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
string fixHost( string url , string host , string port ) {
|
2009-09-22 16:29:15 -04:00
|
|
|
//cout << "fixHost url: " << url << " host: " << host << " port: " << port << endl;
|
2009-09-25 15:51:11 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( host.size() == 0 && port.size() == 0 ) {
|
|
|
|
|
if ( url.find( "/" ) == string::npos ) {
|
2009-09-22 16:29:15 -04:00
|
|
|
// check for ips
|
|
|
|
|
if ( url.find( "." ) != string::npos )
|
|
|
|
|
return url + "/test";
|
2009-09-25 15:51:11 -04:00
|
|
|
|
2010-04-07 15:45:41 -04:00
|
|
|
if ( url.rfind( ":" ) != string::npos &&
|
2011-01-04 00:40:41 -05:00
|
|
|
isdigit( url[url.rfind(":")+1] ) )
|
2009-09-22 16:29:15 -04:00
|
|
|
return url + "/test";
|
|
|
|
|
}
|
2009-01-29 07:46:43 -05:00
|
|
|
return url;
|
2009-01-30 13:00:47 -05:00
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( url.find( "/" ) != string::npos ) {
|
2009-01-29 07:46:43 -05:00
|
|
|
cerr << "url can't have host or port if you specify them individually" << endl;
|
|
|
|
|
exit(-1);
|
|
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-01-29 07:46:43 -05:00
|
|
|
if ( host.size() == 0 )
|
|
|
|
|
host = "127.0.0.1";
|
|
|
|
|
|
|
|
|
|
string newurl = host;
|
|
|
|
|
if ( port.size() > 0 )
|
|
|
|
|
newurl += ":" + port;
|
2011-11-27 09:51:40 -05:00
|
|
|
else if ( host.find(':') == string::npos ) {
|
2010-04-07 15:45:41 -04:00
|
|
|
// need to add port with IPv6 addresses
|
|
|
|
|
newurl += ":27017";
|
|
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-01-29 07:46:43 -05:00
|
|
|
newurl += "/" + url;
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-01-29 07:46:43 -05:00
|
|
|
return newurl;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-04 16:12:56 -04:00
|
|
|
static string OpSymbols = "~!%^&*-+=|:,<>/?.";
|
2010-12-02 18:44:07 -05:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
bool isOpSymbol( char c ) {
|
2010-12-02 18:44:07 -05:00
|
|
|
for ( size_t i = 0; i < OpSymbols.size(); i++ )
|
|
|
|
|
if ( OpSymbols[i] == c ) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-03 20:17:34 -04:00
|
|
|
bool isUseCmd( string code ) {
|
|
|
|
|
string cmd = code;
|
|
|
|
|
if ( cmd.find( " " ) > 0 )
|
|
|
|
|
cmd = cmd.substr( 0 , cmd.find( " " ) );
|
|
|
|
|
return cmd == "use";
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
bool isBalanced( string code ) {
|
2011-11-03 20:17:34 -04:00
|
|
|
if (isUseCmd( code ))
|
|
|
|
|
return true; // don't balance "use <dbname>" in case dbname contains special chars
|
2009-08-25 13:54:01 -04:00
|
|
|
int brackets = 0;
|
|
|
|
|
int parens = 0;
|
2010-12-02 18:44:07 -05:00
|
|
|
bool danglingOp = false;
|
2009-09-18 13:15:16 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
for ( size_t i=0; i<code.size(); i++ ) {
|
|
|
|
|
switch( code[i] ) {
|
2009-09-01 13:50:08 -04:00
|
|
|
case '/':
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( i + 1 < code.size() && code[i+1] == '/' ) {
|
|
|
|
|
while ( i <code.size() && code[i] != '\n' )
|
2009-09-01 13:50:08 -04:00
|
|
|
i++;
|
|
|
|
|
}
|
2010-12-06 09:32:17 -05:00
|
|
|
continue;
|
2009-08-25 13:54:01 -04:00
|
|
|
case '{': brackets++; break;
|
2009-12-08 15:14:00 -05:00
|
|
|
case '}': if ( brackets <= 0 ) return true; brackets--; break;
|
2009-08-25 13:54:01 -04:00
|
|
|
case '(': parens++; break;
|
2009-12-08 15:14:00 -05:00
|
|
|
case ')': if ( parens <= 0 ) return true; parens--; break;
|
2009-08-25 13:54:01 -04:00
|
|
|
case '"':
|
2009-08-25 16:11:51 -04:00
|
|
|
i++;
|
2009-09-18 13:15:16 -04:00
|
|
|
while ( i < code.size() && code[i] != '"' ) i++;
|
2009-08-25 13:54:01 -04:00
|
|
|
break;
|
|
|
|
|
case '\'':
|
2009-08-25 16:11:51 -04:00
|
|
|
i++;
|
2009-09-18 13:15:16 -04:00
|
|
|
while ( i < code.size() && code[i] != '\'' ) i++;
|
2009-08-25 13:54:01 -04:00
|
|
|
break;
|
2010-09-08 15:40:21 -04:00
|
|
|
case '\\':
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( i + 1 < code.size() && code[i+1] == '/' ) i++;
|
2010-12-02 18:44:07 -05:00
|
|
|
break;
|
|
|
|
|
case '+':
|
|
|
|
|
case '-':
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( i + 1 < code.size() && code[i+1] == code[i] ) {
|
2010-12-02 18:44:07 -05:00
|
|
|
i++;
|
|
|
|
|
continue; // postfix op (++/--) can't be a dangling op
|
|
|
|
|
}
|
|
|
|
|
break;
|
2009-08-25 13:54:01 -04:00
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( i >= code.size() ) {
|
2011-10-12 15:50:31 -04:00
|
|
|
danglingOp = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( isOpSymbol( code[i] ) ) danglingOp = true;
|
|
|
|
|
else if ( !std::isspace( code[i] ) ) danglingOp = false;
|
2009-08-25 13:54:01 -04:00
|
|
|
}
|
2009-09-18 13:15:16 -04:00
|
|
|
|
2010-12-02 18:44:07 -05:00
|
|
|
return brackets == 0 && parens == 0 && !danglingOp;
|
2009-08-25 13:54:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using mongo::asserted;
|
|
|
|
|
|
|
|
|
|
struct BalancedTest : public mongo::UnitTest {
|
|
|
|
|
public:
|
2011-01-04 00:40:41 -05:00
|
|
|
void run() {
|
2009-08-25 13:54:01 -04:00
|
|
|
assert( isBalanced( "x = 5" ) );
|
|
|
|
|
assert( isBalanced( "function(){}" ) );
|
|
|
|
|
assert( isBalanced( "function(){\n}" ) );
|
|
|
|
|
assert( ! isBalanced( "function(){" ) );
|
2009-08-25 16:11:51 -04:00
|
|
|
assert( isBalanced( "x = \"{\";" ) );
|
2009-09-01 13:50:08 -04:00
|
|
|
assert( isBalanced( "// {" ) );
|
|
|
|
|
assert( ! isBalanced( "// \n {" ) );
|
2009-09-01 13:50:54 -04:00
|
|
|
assert( ! isBalanced( "\"//\" {" ) );
|
2010-09-08 15:40:21 -04:00
|
|
|
assert( isBalanced( "{x:/x\\//}" ) );
|
|
|
|
|
assert( ! isBalanced( "{ \\/// }" ) );
|
2010-12-02 18:44:07 -05:00
|
|
|
assert( isBalanced( "x = 5 + y ") );
|
|
|
|
|
assert( ! isBalanced( "x = ") );
|
2010-12-06 09:32:17 -05:00
|
|
|
assert( ! isBalanced( "x = // hello") );
|
2010-12-02 18:44:07 -05:00
|
|
|
assert( ! isBalanced( "x = 5 +") );
|
|
|
|
|
assert( isBalanced( " x ++") );
|
|
|
|
|
assert( isBalanced( "-- x") );
|
2011-04-04 16:12:56 -04:00
|
|
|
assert( !isBalanced( "a.") );
|
|
|
|
|
assert( !isBalanced( "a. ") );
|
|
|
|
|
assert( isBalanced( "a.b") );
|
2009-08-25 13:54:01 -04:00
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
} balanced_test;
|
2009-08-25 13:54:01 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
string finishCode( string code ) {
|
|
|
|
|
while ( ! isBalanced( code ) ) {
|
2009-08-25 17:00:01 -04:00
|
|
|
inMultiLine = 1;
|
2009-08-25 13:54:01 -04:00
|
|
|
code += "\n";
|
2011-09-25 18:34:52 -07:00
|
|
|
// cancel multiline if two blank lines are entered
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( code.find( "\n\n\n ") != string::npos )
|
2011-09-25 18:34:52 -07:00
|
|
|
return ";";
|
2011-11-27 09:51:40 -05:00
|
|
|
char * line = shellReadline( "... " , 1 );
|
2011-11-16 13:07:37 -05:00
|
|
|
if ( gotInterrupted ) {
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( line )
|
|
|
|
|
free( line );
|
2009-08-25 17:00:01 -04:00
|
|
|
return "";
|
2011-11-16 13:07:37 -05:00
|
|
|
}
|
2009-08-25 13:54:01 -04:00
|
|
|
if ( ! line )
|
|
|
|
|
return "";
|
2010-08-24 13:26:59 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
while ( startsWith( line, "... " ) )
|
2010-08-24 13:26:59 -04:00
|
|
|
line += 4;
|
|
|
|
|
|
2009-09-18 13:15:16 -04:00
|
|
|
code += line;
|
2011-11-27 09:51:40 -05:00
|
|
|
free( line );
|
2009-08-25 13:54:01 -04:00
|
|
|
}
|
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-07 10:43:39 -04:00
|
|
|
#include <boost/program_options.hpp>
|
|
|
|
|
namespace po = boost::program_options;
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
void show_help_text( const char* name, po::options_description options ) {
|
2009-09-22 10:07:53 -04:00
|
|
|
cout << "MongoDB shell version: " << mongo::versionString << endl;
|
2009-08-07 10:43:39 -04:00
|
|
|
cout << "usage: " << name << " [options] [db address] [file names (ending in .js)]" << endl
|
|
|
|
|
<< "db address can be:" << endl
|
|
|
|
|
<< " foo foo database on local machine" << endl
|
|
|
|
|
<< " 192.169.0.5/foo foo database on 192.168.0.5 machine" << endl
|
|
|
|
|
<< " 192.169.0.5:9999/foo foo database on 192.168.0.5 machine on port 9999" << endl
|
|
|
|
|
<< options << endl
|
|
|
|
|
<< "file names: a list of files to run. files have to end in .js and will exit after "
|
|
|
|
|
<< "unless --shell is specified" << endl;
|
|
|
|
|
};
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
bool fileExists( string file ) {
|
2009-09-19 22:06:36 -04:00
|
|
|
try {
|
2011-11-27 09:51:40 -05:00
|
|
|
path p( file );
|
2009-09-19 22:06:36 -04:00
|
|
|
return boost::filesystem::exists( file );
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
catch ( ... ) {
|
2009-09-19 22:06:36 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-07 21:50:36 -04:00
|
|
|
namespace mongo {
|
|
|
|
|
extern bool isShell;
|
2010-11-23 04:16:04 -05:00
|
|
|
extern DBClientWithCommands *latestConn;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
string sayReplSetMemberState() {
|
2010-11-23 04:16:04 -05:00
|
|
|
try {
|
2011-01-04 00:40:41 -05:00
|
|
|
if( latestConn ) {
|
2010-11-23 04:16:04 -05:00
|
|
|
BSONObj info;
|
2011-11-27 09:51:40 -05:00
|
|
|
if( latestConn->runCommand( "admin", BSON( "replSetGetStatus" << 1 << "forShell" << 1 ) , info ) ) {
|
2010-11-23 04:16:04 -05:00
|
|
|
stringstream ss;
|
|
|
|
|
ss << info["set"].String() << ':';
|
2011-10-25 16:00:04 -04:00
|
|
|
|
2010-11-23 08:18:37 -05:00
|
|
|
int s = info["myState"].Int();
|
2011-11-27 09:51:40 -05:00
|
|
|
MemberState ms( s );
|
2011-10-25 16:00:04 -04:00
|
|
|
ss << ms.toString();
|
|
|
|
|
|
|
|
|
|
return ss.str();
|
2010-11-23 04:16:04 -05:00
|
|
|
}
|
2011-09-20 16:05:45 -04:00
|
|
|
else {
|
2011-11-27 09:51:40 -05:00
|
|
|
string s = info.getStringField( "info" );
|
2011-09-20 16:05:45 -04:00
|
|
|
if( s.size() < 20 )
|
|
|
|
|
return s; // "mongos", "configsvr"
|
2011-04-14 16:47:06 -04:00
|
|
|
}
|
2010-11-23 04:16:04 -05:00
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
}
|
2011-02-01 11:38:33 -05:00
|
|
|
catch( std::exception& e ) {
|
2011-11-27 09:51:40 -05:00
|
|
|
log( 1 ) << "error in sayReplSetMemberState:" << e.what() << endl;
|
2011-02-01 11:38:33 -05:00
|
|
|
}
|
2010-12-03 15:55:40 -05:00
|
|
|
return "";
|
2010-06-07 21:50:36 -04:00
|
|
|
}
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
/**
|
|
|
|
|
* Edit a variable in an external editor -- EDITOR must be defined
|
|
|
|
|
*
|
|
|
|
|
* @param var Name of JavaScript variable to be edited
|
|
|
|
|
*/
|
|
|
|
|
static void edit( const string& var ) {
|
|
|
|
|
|
|
|
|
|
// EDITOR must be defined in the environment
|
|
|
|
|
static const char * editor = getenv( "EDITOR" );
|
|
|
|
|
if ( !editor ) {
|
|
|
|
|
cout << "please define the EDITOR environment variable" << endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "var" must look like a variable/property name
|
|
|
|
|
for ( const char* p=var.c_str(); *p; ++p ) {
|
|
|
|
|
if ( ! ( isalnum( *p ) || *p == '_' || *p == '.' ) ) {
|
|
|
|
|
cout << "can only edit variable or property" << endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert "var" to JavaScript (JSON) text
|
|
|
|
|
if ( !shellMainScope->exec( "__jsout__ = tojson(" + var + ")", "tojs", false, false, false ) )
|
|
|
|
|
return; // Error already printed
|
|
|
|
|
|
|
|
|
|
const string js = shellMainScope->getString( "__jsout__" );
|
|
|
|
|
|
|
|
|
|
if ( strstr( js.c_str(), "[native code]" ) ) {
|
|
|
|
|
cout << "can't edit native functions" << endl;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 ( ! 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();
|
|
|
|
|
sb << var << " = ";
|
|
|
|
|
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() );
|
|
|
|
|
|
|
|
|
|
// Try to execute assignment to copy edited value back into the variable
|
|
|
|
|
const string code = sb.str();
|
|
|
|
|
if ( !shellMainScope->exec( code, "tojs", false, false, false ) )
|
|
|
|
|
return; // Error already printed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int _main( int argc, char* argv[] ) {
|
2010-06-07 21:50:36 -04:00
|
|
|
mongo::isShell = true;
|
2009-01-30 12:37:46 -05:00
|
|
|
setupSignals();
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-05-14 11:42:52 -04:00
|
|
|
mongo::shellUtils::RecordMyLocation( argv[ 0 ] );
|
2009-01-26 22:19:15 -05:00
|
|
|
|
2009-01-29 07:46:43 -05:00
|
|
|
string url = "test";
|
|
|
|
|
string dbhost;
|
|
|
|
|
string port;
|
2009-08-07 10:43:39 -04:00
|
|
|
vector<string> files;
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-01-26 22:19:15 -05:00
|
|
|
string username;
|
|
|
|
|
string password;
|
|
|
|
|
|
|
|
|
|
bool runShell = false;
|
2009-01-28 10:25:16 -05:00
|
|
|
bool nodb = false;
|
2011-06-17 16:29:58 -04:00
|
|
|
bool norc = false;
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2009-05-15 14:55:18 -04:00
|
|
|
string script;
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
po::options_description shell_options( "options" );
|
|
|
|
|
po::options_description hidden_options( "Hidden options" );
|
|
|
|
|
po::options_description cmdline_options( "Command line options" );
|
2009-08-07 10:43:39 -04:00
|
|
|
po::positional_options_description positional_options;
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2009-08-07 10:43:39 -04:00
|
|
|
shell_options.add_options()
|
2011-11-27 09:51:40 -05:00
|
|
|
( "shell", "run the shell after executing files" )
|
|
|
|
|
( "nodb", "don't connect to mongod on startup - no 'db address' arg expected" )
|
|
|
|
|
( "norc", "will not run the \".mongorc.js\" file on start up" )
|
|
|
|
|
( "quiet", "be less chatty" )
|
|
|
|
|
( "port", po::value<string>( &port ), "port to connect to" )
|
|
|
|
|
( "host", po::value<string>( &dbhost ), "server to connect to" )
|
|
|
|
|
( "eval", po::value<string>( &script ), "evaluate javascript" )
|
|
|
|
|
( "username,u", po::value<string>(&username), "username for authentication" )
|
|
|
|
|
( "password,p", new mongo::PasswordValue( &password ), "password for authentication" )
|
|
|
|
|
( "help,h", "show this usage information" )
|
|
|
|
|
( "version", "show version information" )
|
|
|
|
|
( "verbose", "increase verbosity" )
|
|
|
|
|
( "ipv6", "enable IPv6 support (disabled by default)" )
|
2011-07-26 17:23:42 -04:00
|
|
|
#ifdef MONGO_SSL
|
2011-11-27 09:51:40 -05:00
|
|
|
( "ssl", "use all for connections" )
|
2011-07-26 17:23:42 -04:00
|
|
|
#endif
|
2011-01-04 00:40:41 -05:00
|
|
|
;
|
2009-08-07 10:43:39 -04:00
|
|
|
|
2009-08-07 16:02:56 -04:00
|
|
|
hidden_options.add_options()
|
2011-11-27 09:51:40 -05:00
|
|
|
( "dbaddress", po::value<string>(), "dbaddress" )
|
|
|
|
|
( "files", po::value< vector<string> >(), "files" )
|
|
|
|
|
( "nokillop", "nokillop" ) // for testing, kill op will also be disabled automatically if the tests starts a mongo program
|
|
|
|
|
( "autokillop", "autokillop" ) // for testing, will kill op without prompting
|
2011-01-04 00:40:41 -05:00
|
|
|
;
|
2009-08-07 16:02:56 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
positional_options.add( "dbaddress", 1 );
|
|
|
|
|
positional_options.add( "files", -1 );
|
2009-08-07 10:43:39 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
cmdline_options.add( shell_options ).add( hidden_options );
|
2009-08-07 16:02:56 -04:00
|
|
|
|
2009-09-18 13:15:16 -04:00
|
|
|
po::variables_map params;
|
|
|
|
|
|
|
|
|
|
/* using the same style as db.cpp uses because eventually we're going
|
|
|
|
|
* to merge some of this stuff. */
|
|
|
|
|
int command_line_style = (((po::command_line_style::unix_style ^
|
|
|
|
|
po::command_line_style::allow_guessing) |
|
|
|
|
|
po::command_line_style::allow_long_disguise) ^
|
|
|
|
|
po::command_line_style::allow_sticky);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
po::store(po::command_line_parser(argc, argv).options(cmdline_options).
|
|
|
|
|
positional(positional_options).
|
|
|
|
|
style(command_line_style).run(), params);
|
2011-11-27 09:51:40 -05:00
|
|
|
po::notify( params );
|
2011-01-04 00:40:41 -05:00
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
catch ( po::error &e ) {
|
2009-09-18 13:15:16 -04:00
|
|
|
cout << "ERROR: " << e.what() << endl << endl;
|
2011-11-27 09:51:40 -05:00
|
|
|
show_help_text( argv[0], shell_options );
|
2009-09-18 13:15:16 -04:00
|
|
|
return mongo::EXIT_BADOPTIONS;
|
|
|
|
|
}
|
2009-08-07 10:43:39 -04:00
|
|
|
|
2010-05-24 16:55:26 -04:00
|
|
|
// hide password from ps output
|
2011-11-27 09:51:40 -05:00
|
|
|
for ( int i = 0; i < (argc-1); ++i ) {
|
|
|
|
|
if ( !strcmp(argv[i], "-p") || !strcmp( argv[i], "--password" ) ) {
|
|
|
|
|
char* arg = argv[i + 1];
|
|
|
|
|
while ( *arg ) {
|
2010-05-24 16:55:26 -04:00
|
|
|
*arg++ = 'x';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "shell" ) ) {
|
2009-09-18 13:15:16 -04:00
|
|
|
runShell = true;
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "nodb" ) ) {
|
2009-09-18 13:15:16 -04:00
|
|
|
nodb = true;
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "norc" ) ) {
|
2011-06-17 16:29:58 -04:00
|
|
|
norc = true;
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "help" ) ) {
|
|
|
|
|
show_help_text( argv[0], shell_options );
|
2009-09-18 13:15:16 -04:00
|
|
|
return mongo::EXIT_CLEAN;
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "files" ) ) {
|
2009-09-18 13:15:16 -04:00
|
|
|
files = params["files"].as< vector<string> >();
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "version" ) ) {
|
2009-09-22 10:07:53 -04:00
|
|
|
cout << "MongoDB shell version: " << mongo::versionString << endl;
|
|
|
|
|
return mongo::EXIT_CLEAN;
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "quiet" ) ) {
|
2010-01-13 21:08:23 -05:00
|
|
|
mongo::cmdLine.quiet = true;
|
|
|
|
|
}
|
2011-07-26 17:23:42 -04:00
|
|
|
#ifdef MONGO_SSL
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "ssl" ) ) {
|
2011-07-26 17:23:42 -04:00
|
|
|
mongo::cmdLine.sslOnNormalPorts = true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "nokillop" ) ) {
|
2010-02-22 12:45:58 -08:00
|
|
|
mongo::shellUtils::_nokillop = true;
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "autokillop" ) ) {
|
2010-07-21 18:45:40 -04:00
|
|
|
autoKillOp = true;
|
|
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2009-09-18 13:15:16 -04:00
|
|
|
/* This is a bit confusing, here are the rules:
|
|
|
|
|
*
|
|
|
|
|
* if nodb is set then all positional parameters are files
|
|
|
|
|
* otherwise the first positional parameter might be a dbaddress, but
|
|
|
|
|
* only if one of these conditions is met:
|
|
|
|
|
* - it contains no '.' after the last appearance of '\' or '/'
|
|
|
|
|
* - it doesn't end in '.js' and it doesn't specify a path to an existing file */
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "dbaddress" ) ) {
|
2009-09-18 13:15:16 -04:00
|
|
|
string dbaddress = params["dbaddress"].as<string>();
|
|
|
|
|
if (nodb) {
|
2011-11-27 09:51:40 -05:00
|
|
|
files.insert( files.begin(), dbaddress );
|
2011-01-04 00:40:41 -05:00
|
|
|
}
|
|
|
|
|
else {
|
2011-11-27 09:51:40 -05:00
|
|
|
string basename = dbaddress.substr( dbaddress.find_last_of( "/\\" ) + 1 );
|
|
|
|
|
if (basename.find_first_of( '.' ) == string::npos ||
|
|
|
|
|
( basename.find( ".js", basename.size() - 3 ) == string::npos && !fileExists( dbaddress ) ) ) {
|
2009-09-18 13:15:16 -04:00
|
|
|
url = dbaddress;
|
2011-01-04 00:40:41 -05:00
|
|
|
}
|
|
|
|
|
else {
|
2011-11-27 09:51:40 -05:00
|
|
|
files.insert( files.begin(), dbaddress );
|
2009-08-07 10:43:39 -04:00
|
|
|
}
|
2009-01-30 13:00:47 -05:00
|
|
|
}
|
2009-01-26 22:19:15 -05:00
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "ipv6" ) ) {
|
2010-04-08 15:14:38 -04:00
|
|
|
mongo::enableIPv6();
|
|
|
|
|
}
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "verbose" ) ) {
|
2011-02-01 11:38:33 -05:00
|
|
|
logLevel = 1;
|
|
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2011-10-28 11:39:07 -04:00
|
|
|
if ( url == "*" ) {
|
|
|
|
|
cout << "ERROR: " << "\"*\" is an invalid db address" << endl << endl;
|
2011-11-27 09:51:40 -05:00
|
|
|
show_help_text( argv[0], shell_options );
|
2011-10-28 11:39:07 -04:00
|
|
|
return mongo::EXIT_BADOPTIONS;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( ! mongo::cmdLine.quiet )
|
2010-01-13 21:08:23 -05:00
|
|
|
cout << "MongoDB shell version: " << mongo::versionString << endl;
|
2009-06-24 14:53:58 -04:00
|
|
|
|
2009-08-25 13:54:01 -04:00
|
|
|
mongo::UnitTest::runTests();
|
|
|
|
|
|
2009-05-13 17:00:02 -04:00
|
|
|
if ( !nodb ) { // connect to db
|
2010-06-07 11:17:08 -04:00
|
|
|
//if ( ! mongo::cmdLine.quiet ) cout << "url: " << url << endl;
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2010-01-13 21:08:23 -05:00
|
|
|
stringstream ss;
|
|
|
|
|
if ( mongo::cmdLine.quiet )
|
|
|
|
|
ss << "__quiet = true;";
|
|
|
|
|
ss << "db = connect( \"" << fixHost( url , dbhost , port ) << "\")";
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2010-01-13 21:08:23 -05:00
|
|
|
mongo::shellUtils::_dbConnect = ss.str();
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( params.count( "password" ) && password.empty() )
|
2010-05-04 10:47:20 -04:00
|
|
|
password = mongo::askPassword();
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( username.size() && password.size() ) {
|
2009-01-26 22:19:15 -05:00
|
|
|
stringstream ss;
|
|
|
|
|
ss << "if ( ! db.auth( \"" << username << "\" , \"" << password << "\" ) ){ throw 'login failed'; }";
|
2009-12-29 19:01:10 -08:00
|
|
|
mongo::shellUtils::_dbAuth = ss.str();
|
2009-01-26 22:19:15 -05:00
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
}
|
|
|
|
|
|
2010-02-22 12:08:54 -08:00
|
|
|
mongo::ScriptEngine::setConnectCallback( mongo::shellUtils::onConnect );
|
2009-12-29 19:01:10 -08:00
|
|
|
mongo::ScriptEngine::setup();
|
|
|
|
|
mongo::globalScriptEngine->setScopeInitCallback( mongo::shellUtils::initScope );
|
2011-01-04 00:40:41 -05:00
|
|
|
auto_ptr< mongo::Scope > scope( mongo::globalScriptEngine->newScope() );
|
2010-06-07 11:06:02 -04:00
|
|
|
shellMainScope = scope.get();
|
2010-06-17 13:10:16 -04:00
|
|
|
|
|
|
|
|
if( runShell )
|
|
|
|
|
cout << "type \"help\" for help" << endl;
|
2011-01-04 00:40:41 -05:00
|
|
|
|
2009-05-15 14:55:18 -04:00
|
|
|
if ( !script.empty() ) {
|
|
|
|
|
mongo::shellUtils::MongoProgramScope s;
|
2009-08-28 13:26:23 -04:00
|
|
|
if ( ! scope->exec( script , "(shell eval)" , true , true , false ) )
|
2009-05-15 14:55:18 -04:00
|
|
|
return -4;
|
|
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
for (size_t i = 0; i < files.size(); ++i) {
|
2009-05-14 13:19:45 -04:00
|
|
|
mongo::shellUtils::MongoProgramScope s;
|
|
|
|
|
|
2009-08-14 11:06:10 -04:00
|
|
|
if ( files.size() > 1 )
|
|
|
|
|
cout << "loading file: " << files[i] << endl;
|
2009-09-18 13:15:16 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( ! scope->execFile( files[i] , false , true , false ) ) {
|
2009-08-14 11:06:10 -04:00
|
|
|
cout << "failed to load: " << files[i] << endl;
|
2009-05-13 17:00:02 -04:00
|
|
|
return -3;
|
2009-01-26 22:19:15 -05:00
|
|
|
}
|
|
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( files.size() == 0 && script.empty() )
|
2009-01-26 22:19:15 -05:00
|
|
|
runShell = true;
|
|
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( runShell ) {
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-05-14 11:42:52 -04:00
|
|
|
mongo::shellUtils::MongoProgramScope s;
|
2009-02-10 13:45:30 -05:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( !norc ) {
|
2011-06-17 16:29:58 -04:00
|
|
|
string rcLocation;
|
|
|
|
|
#ifndef _WIN32
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( getenv( "HOME" ) != NULL )
|
|
|
|
|
rcLocation = str::stream() << getenv( "HOME" ) << "/.mongorc.js" ;
|
2011-06-17 16:29:58 -04:00
|
|
|
#else
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( getenv( "HOMEDRIVE" ) != NULL && getenv( "HOMEPATH" ) != NULL )
|
|
|
|
|
rcLocation = str::stream() << getenv( "HOMEDRIVE" ) << getenv( "HOMEPATH" ) << "\\.mongorc.js";
|
2011-06-17 16:29:58 -04:00
|
|
|
#endif
|
|
|
|
|
if ( !rcLocation.empty() && fileExists(rcLocation) ) {
|
|
|
|
|
if ( ! scope->execFile( rcLocation , false , true , false , 0 ) ) {
|
|
|
|
|
cout << "The \".mongorc.js\" file located in your home folder could not be executed" << endl;
|
|
|
|
|
return -5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-13 12:01:37 -05:00
|
|
|
shellHistoryInit();
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-06-15 11:35:46 -04:00
|
|
|
string prompt;
|
2011-06-15 16:32:58 -04:00
|
|
|
int promptType;
|
2011-06-15 11:35:46 -04:00
|
|
|
|
2009-05-13 17:00:02 -04:00
|
|
|
//v8::Handle<v8::Object> shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject();
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
while ( 1 ) {
|
2009-08-25 17:00:01 -04:00
|
|
|
inMultiLine = 0;
|
|
|
|
|
gotInterrupted = 0;
|
2010-11-23 04:16:04 -05:00
|
|
|
// shellMainScope->localConnect;
|
|
|
|
|
//DBClientWithCommands *c = getConnection( JSContext *cx, JSObject *obj );
|
|
|
|
|
|
2011-10-13 07:54:51 -04:00
|
|
|
bool haveStringPrompt = false;
|
2011-11-27 09:51:40 -05:00
|
|
|
promptType = scope->type( "prompt" );
|
2011-10-13 07:54:51 -04:00
|
|
|
if( promptType == String ) {
|
2011-11-27 09:51:40 -05:00
|
|
|
prompt = scope->getString( "prompt" );
|
2011-10-13 07:54:51 -04:00
|
|
|
haveStringPrompt = true;
|
|
|
|
|
}
|
|
|
|
|
else if( promptType == Code ) {
|
2011-11-27 09:51:40 -05:00
|
|
|
scope->exec( "delete __prompt__;", "", false, false, false, 0 );
|
|
|
|
|
scope->exec( "__prompt__ = prompt();", "", false, false, false, 0 );
|
|
|
|
|
if( scope->type( "__prompt__" ) == String ) {
|
|
|
|
|
prompt = scope->getString( "__prompt__" );
|
2011-10-13 07:54:51 -04:00
|
|
|
haveStringPrompt = true;
|
|
|
|
|
}
|
2011-06-15 14:51:52 -04:00
|
|
|
}
|
2011-10-13 07:54:51 -04:00
|
|
|
if( !haveStringPrompt )
|
2011-11-27 09:51:40 -05:00
|
|
|
prompt = sayReplSetMemberState() + "> ";
|
2011-06-15 16:32:58 -04:00
|
|
|
|
2010-12-03 15:55:40 -05:00
|
|
|
char * line = shellReadline( prompt.c_str() );
|
2009-09-18 13:15:16 -04:00
|
|
|
|
2011-11-16 13:07:37 -05:00
|
|
|
char * linePtr = line; // can't clobber 'line', we need to free() it later
|
|
|
|
|
if ( linePtr ) {
|
|
|
|
|
while ( linePtr[0] == ' ' )
|
2011-11-27 09:51:40 -05:00
|
|
|
++linePtr;
|
2010-08-24 13:26:59 -04:00
|
|
|
}
|
2009-09-18 13:15:16 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( ! linePtr || ( strlen( linePtr ) == 4 && strstr( linePtr , "exit" ) ) ) {
|
2011-11-28 00:16:36 -05:00
|
|
|
if ( ! mongo::cmdLine.quiet )
|
|
|
|
|
cout << "bye" << endl;
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( line )
|
|
|
|
|
free( line );
|
2009-01-26 22:19:15 -05:00
|
|
|
break;
|
|
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-11-16 13:07:37 -05:00
|
|
|
string code = linePtr;
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( code == "exit" || code == "exit;" ) {
|
2011-11-27 09:51:40 -05:00
|
|
|
free( line );
|
2009-03-27 16:11:57 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2011-09-08 18:49:58 -04:00
|
|
|
|
2011-11-16 13:07:37 -05:00
|
|
|
if ( code.size() == 0 ) {
|
2011-11-27 09:51:40 -05:00
|
|
|
free( line );
|
2009-08-20 13:23:21 -04:00
|
|
|
continue;
|
2011-11-16 13:07:37 -05:00
|
|
|
}
|
2009-09-18 13:15:16 -04:00
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
if ( startsWith( linePtr, "edit " ) ) {
|
2011-11-16 13:07:37 -05:00
|
|
|
shellHistoryAdd( linePtr );
|
2011-09-08 18:49:58 -04:00
|
|
|
|
2011-11-16 13:07:37 -05:00
|
|
|
const char* s = linePtr + 5; // skip "edit "
|
2011-11-27 09:51:40 -05:00
|
|
|
while( *s && isspace( *s ) )
|
2011-09-08 18:49:58 -04:00
|
|
|
s++;
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
edit( s );
|
|
|
|
|
free( line );
|
2011-09-08 18:49:58 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-25 17:00:01 -04:00
|
|
|
code = finishCode( code );
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( gotInterrupted ) {
|
2009-08-25 16:11:51 -04:00
|
|
|
cout << endl;
|
2011-11-27 09:51:40 -05:00
|
|
|
free( line );
|
2009-08-25 16:11:51 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
2009-08-25 13:54:01 -04:00
|
|
|
|
2011-11-16 13:07:37 -05:00
|
|
|
if ( code.size() == 0 ) {
|
2011-11-27 09:51:40 -05:00
|
|
|
free( line );
|
2009-08-25 13:54:01 -04:00
|
|
|
break;
|
2011-11-16 13:07:37 -05:00
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-06-11 20:15:31 -04:00
|
|
|
bool wascmd = false;
|
2009-01-26 22:19:15 -05:00
|
|
|
{
|
2011-11-16 13:07:37 -05:00
|
|
|
string cmd = linePtr;
|
2009-01-26 22:19:15 -05:00
|
|
|
if ( cmd.find( " " ) > 0 )
|
|
|
|
|
cmd = cmd.substr( 0 , cmd.find( " " ) );
|
2009-09-25 15:51:11 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( cmd.find( "\"" ) == string::npos ) {
|
2010-02-10 21:57:14 -05:00
|
|
|
try {
|
|
|
|
|
scope->exec( (string)"__iscmd__ = shellHelper[\"" + cmd + "\"];" , "(shellhelp1)" , false , true , true );
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( scope->getBoolean( "__iscmd__" ) ) {
|
2010-02-10 21:57:14 -05:00
|
|
|
scope->exec( (string)"shellHelper( \"" + cmd + "\" , \"" + code.substr( cmd.size() ) + "\");" , "(shellhelp2)" , false , true , false );
|
|
|
|
|
wascmd = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
catch ( std::exception& e ) {
|
|
|
|
|
cout << "error2:" << e.what() << endl;
|
2009-06-22 09:31:28 -04:00
|
|
|
wascmd = true;
|
|
|
|
|
}
|
2009-01-26 22:19:15 -05:00
|
|
|
}
|
|
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-01-04 00:40:41 -05:00
|
|
|
if ( ! wascmd ) {
|
2009-09-22 10:22:24 -04:00
|
|
|
try {
|
2010-03-30 13:48:47 -04:00
|
|
|
if ( scope->exec( code.c_str() , "(shell)" , false , true , false ) )
|
|
|
|
|
scope->exec( "shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false );
|
2009-09-22 10:22:24 -04:00
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
catch ( std::exception& e ) {
|
2009-09-22 10:22:24 -04:00
|
|
|
cout << "error:" << e.what() << endl;
|
|
|
|
|
}
|
2009-06-11 20:27:07 -04:00
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2011-11-18 13:26:05 -05:00
|
|
|
shellHistoryAdd( code.c_str() );
|
2011-11-27 09:51:40 -05:00
|
|
|
free( line );
|
2009-01-26 22:19:15 -05:00
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2009-03-27 16:11:57 -04:00
|
|
|
shellHistoryDone();
|
2009-01-26 22:19:15 -05:00
|
|
|
}
|
2009-08-06 14:15:18 -04:00
|
|
|
|
2010-11-11 01:47:15 -05:00
|
|
|
mongo::dbexitCalled = true;
|
2009-01-26 22:19:15 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-27 09:51:40 -05:00
|
|
|
int main( int argc, char* argv[] ) {
|
2010-03-15 09:42:01 -07:00
|
|
|
static mongo::StaticObserver staticObserver;
|
2009-08-10 10:25:43 -04:00
|
|
|
try {
|
|
|
|
|
return _main( argc , argv );
|
|
|
|
|
}
|
2011-01-04 00:40:41 -05:00
|
|
|
catch ( mongo::DBException& e ) {
|
2009-08-10 10:25:43 -04:00
|
|
|
cerr << "exception: " << e.what() << endl;
|
2009-09-25 15:51:11 -04:00
|
|
|
return -1;
|
2009-08-10 10:25:43 -04:00
|
|
|
}
|
|
|
|
|
}
|