Files
mongo/shell/dbshell.cpp

364 lines
11 KiB
C++
Raw Normal View History

2009-01-26 22:19:15 -05:00
// dbshell.cpp
#include <stdio.h>
2009-01-26 22:19:15 -05:00
2009-02-10 13:45:30 -05:00
#ifdef USE_READLINE
2009-01-26 22:19:15 -05:00
#include <readline/readline.h>
#include <readline/history.h>
2009-02-10 13:45:30 -05:00
#endif
2009-01-26 22:19:15 -05:00
#include "../scripting/engine.h"
#include "../client/dbclient.h"
#include "utils.h"
2009-01-26 22:19:15 -05:00
extern const char * jsconcatcode;
2009-01-26 22:19:15 -05:00
string historyFile;
2009-01-26 22:19:15 -05:00
2009-02-10 13:45:30 -05:00
void shellHistoryInit(){
#ifdef USE_READLINE
2009-08-06 14:15:18 -04:00
stringstream ss;
char * h = getenv( "HOME" );
if ( h )
ss << h << "/";
ss << ".dbshell";
historyFile = ss.str();
2009-02-10 13:45:30 -05:00
using_history();
read_history( historyFile.c_str() );
2009-02-11 09:15:06 -05:00
#else
cout << "type \"exit\" to exit" << endl;
2009-02-10 13:45:30 -05:00
#endif
}
void shellHistoryDone(){
#ifdef USE_READLINE
write_history( historyFile.c_str() );
2009-02-10 13:45:30 -05:00
#endif
}
void shellHistoryAdd( const char * line ){
if ( strlen(line) == 0 )
return;
#ifdef USE_READLINE
add_history( line );
#endif
}
char * shellReadline( const char * prompt ){
#ifdef USE_READLINE
return readline( "> " );
#else
printf( "> " );
char * buf = new char[1024];
2009-02-11 21:15:54 -05:00
char * l = fgets( buf , 1024 , stdin );
2009-02-11 09:15:06 -05:00
int len = strlen( buf );
buf[len-1] = 0;
2009-02-11 21:15:54 -05:00
return l;
2009-02-10 13:45:30 -05:00
#endif
}
2009-03-24 17:35:50 -04:00
#if !defined(_WIN32)
#include <string.h>
2009-02-10 13:45:30 -05:00
void quitNicely( int sig ){
if ( sig == SIGPIPE )
mongo::rawOut( "mongo got signal SIGPIPE\n" );
2009-02-10 13:45:30 -05:00
shellHistoryDone();
2009-01-26 22:19:15 -05:00
exit(0);
}
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
ostringstream ossBt;
mongo::printStackTrace( ossBt );
mongo::rawOut( ossBt.str() );
2009-08-06 14:15:18 -04:00
mongo::shellUtils::KillMongoProgramInstances();
2009-08-06 14:15:18 -04:00
exit(14);
}
void setupSignals() {
signal( SIGINT , quitNicely );
2009-02-02 09:53:14 -05:00
signal( SIGTERM , quitNicely );
signal( SIGPIPE , quitNicely ); // Maybe just log and continue?
signal( SIGABRT , quitAbruptly );
2009-02-02 09:53:14 -05:00
signal( SIGSEGV , quitAbruptly );
signal( SIGBUS , quitAbruptly );
signal( SIGFPE , quitAbruptly );
}
2009-03-24 17:35:50 -04:00
#else
inline void setupSignals() {}
#endif
2009-01-29 07:46:43 -05:00
string fixHost( string url , string host , string port ){
2009-01-30 13:00:47 -05:00
if ( host.size() == 0 && port.size() == 0 ){
if ( url.find( "/" ) == string::npos && url.find( "." ) != string::npos )
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
2009-01-29 07:46:43 -05:00
if ( url.find( "/" ) != string::npos ){
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;
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;
}
2009-08-07 10:43:39 -04:00
#include <boost/program_options.hpp>
namespace po = boost::program_options;
void show_help_text(const char* name, po::options_description options) {
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;
};
2009-08-10 10:25:43 -04:00
int _main(int argc, char* argv[]) {
setupSignals();
2009-08-06 14:15:18 -04:00
mongo::shellUtils::RecordMyLocation( argv[ 0 ] );
2009-01-26 22:19:15 -05:00
mongo::ScriptEngine::setup();
auto_ptr< mongo::Scope > scope( mongo::globalScriptEngine->createScope() );
2009-08-06 14:15:18 -04: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;
2009-08-06 14:15:18 -04:00
string script;
2009-08-06 14:15:18 -04:00
2009-08-07 10:43:39 -04:00
po::options_description shell_options("options");
2009-08-07 16:02:56 -04:00
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;
shell_options.add_options()
("shell", "run the shell after executing files")
("nodb", "don't connect to mongod on startup - no 'db address' arg expected")
("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", po::value<string>(&password), "password for authentication")
("help,h", "show this usage information")
;
2009-08-07 16:02:56 -04:00
hidden_options.add_options()
("dbaddress", po::value<string>(), "dbaddress")
("files", po::value< vector<string> >(), "files")
;
2009-08-07 10:43:39 -04:00
positional_options.add("dbaddress", 1);
positional_options.add("files", -1);
2009-08-07 16:02:56 -04:00
cmdline_options.add(shell_options).add(hidden_options);
2009-08-07 10:43:39 -04:00
if (argc >= 2) {
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 {
2009-08-07 16:02:56 -04:00
po::store(po::command_line_parser(argc, argv).options(cmdline_options).
2009-08-07 10:43:39 -04:00
positional(positional_options).
style(command_line_style).run(), params);
po::notify(params);
} catch (po::error &e) {
cout << "ERROR: " << e.what() << endl << endl;
show_help_text(argv[0], shell_options);
2009-08-07 16:02:56 -04:00
return mongo::EXIT_BADOPTIONS;
2009-08-07 10:43:39 -04:00
}
2009-08-06 14:15:18 -04:00
2009-08-07 10:43:39 -04:00
if (params.count("shell")) {
2009-01-26 22:19:15 -05:00
runShell = true;
2009-08-06 14:15:18 -04:00
}
2009-08-07 10:43:39 -04:00
if (params.count("nodb")) {
2009-01-28 10:25:16 -05:00
nodb = true;
2009-08-06 14:15:18 -04:00
}
2009-08-07 10:43:39 -04:00
if (params.count("help")) {
show_help_text(argv[0], shell_options);
2009-08-07 16:02:56 -04:00
return mongo::EXIT_CLEAN;
2009-08-06 14:15:18 -04:00
}
2009-01-26 22:19:15 -05:00
if (params.count("files")) {
files = params["files"].as< vector<string> >();
}
2009-08-07 10:43:39 -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 */
if (params.count("dbaddress")) {
2009-08-07 16:02:56 -04:00
string dbaddress = params["dbaddress"].as<string>();
2009-08-07 10:43:39 -04:00
if (nodb) {
files.insert(files.begin(), dbaddress);
} else {
string basename = dbaddress.substr(dbaddress.find_last_of("/\\") + 1);
path p(dbaddress);
2009-08-07 16:02:56 -04:00
if (basename.find_first_of('.') == string::npos ||
(basename.find(".js", basename.size() - 3) == string::npos && !boost::filesystem::exists(p))) {
2009-08-07 10:43:39 -04:00
url = dbaddress;
} else {
files.insert(files.begin(), dbaddress);
}
}
2009-01-30 13:00:47 -05:00
}
2009-01-26 22:19:15 -05:00
}
2009-08-06 14:15:18 -04:00
2009-05-15 16:32:31 -04:00
scope->externalSetup();
mongo::shellUtils::installShellUtils( *scope );
2009-01-26 22:19:15 -05:00
2009-06-24 14:53:58 -04:00
cout << "MongoDB shell version: " << mongo::versionString << endl;
if ( !nodb ) { // connect to db
2009-01-30 13:00:47 -05:00
cout << "url: " << url << endl;
2009-01-29 07:46:43 -05:00
string setup = (string)"db = connect( \"" + fixHost( url , dbhost , port ) + "\")";
if ( ! scope->exec( setup , "(connect)" , false , true , false ) )
2009-01-26 22:19:15 -05:00
return -1;
2009-08-06 14:15:18 -04:00
2009-01-26 22:19:15 -05:00
if ( username.size() && password.size() ){
stringstream ss;
ss << "if ( ! db.auth( \"" << username << "\" , \"" << password << "\" ) ){ throw 'login failed'; }";
if ( ! scope->exec( ss.str() , "(auth)" , true , true , false ) ){
2009-01-26 22:19:15 -05:00
cout << "login failed" << endl;
return -1;
}
}
2009-08-06 14:15:18 -04:00
}
if ( !script.empty() ) {
script = "function() { " + script + " }";
mongo::shellUtils::MongoProgramScope s;
if ( scope->invoke( script.c_str(), mongo::BSONObj() ) )
return -4;
}
2009-08-06 14:15:18 -04:00
2009-08-07 10:43:39 -04:00
for (size_t i = 0; i < files.size(); i++) {
mongo::shellUtils::MongoProgramScope s;
2009-08-14 11:06:10 -04:00
if ( files.size() > 1 )
cout << "loading file: " << files[i] << endl;
2009-08-07 10:43:39 -04:00
if ( ! scope->execFile( files[i] , false , true , false ) ){
2009-08-14 11:06:10 -04:00
cout << "failed to load: " << files[i] << endl;
return -3;
2009-01-26 22:19:15 -05:00
}
}
2009-08-06 14:15:18 -04:00
2009-08-07 10:43:39 -04:00
if ( files.size() == 0 && script.empty() ) {
2009-01-26 22:19:15 -05:00
runShell = true;
2009-08-07 10:43:39 -04:00
}
2009-01-26 22:19:15 -05:00
if ( runShell ){
2009-08-06 14:15:18 -04:00
mongo::shellUtils::MongoProgramScope s;
2009-02-10 13:45:30 -05:00
shellHistoryInit();
2009-08-06 14:15:18 -04:00
2009-01-26 22:19:15 -05:00
cout << "type \"help\" for help" << endl;
2009-08-06 14:15:18 -04:00
//v8::Handle<v8::Object> shellHelper = baseContext_->Global()->Get( v8::String::New( "shellHelper" ) )->ToObject();
2009-08-06 14:15:18 -04:00
2009-01-26 22:19:15 -05:00
while ( 1 ){
2009-08-06 14:15:18 -04:00
2009-02-10 13:45:30 -05:00
char * line = shellReadline( "> " );
2009-07-10 14:17:19 -04:00
if ( line )
while ( line[0] == ' ' )
line++;
2009-08-20 13:23:21 -04:00
2009-01-26 22:19:15 -05:00
if ( ! line || ( strlen(line) == 4 && strstr( line , "exit" ) ) ){
cout << "bye" << endl;
break;
}
2009-08-06 14:15:18 -04:00
2009-01-26 22:19:15 -05:00
string code = line;
2009-02-11 09:15:06 -05:00
if ( code == "exit" ){
2009-03-27 16:11:57 -04:00
break;
}
2009-08-20 13:23:21 -04:00
if ( code.size() == 0 )
continue;
2009-08-06 14:15:18 -04:00
bool wascmd = false;
2009-01-26 22:19:15 -05:00
{
string cmd = line;
if ( cmd.find( " " ) > 0 )
cmd = cmd.substr( 0 , cmd.find( " " ) );
2009-08-06 14:15:18 -04:00
2009-06-22 09:31:28 -04:00
if ( cmd.find( "\"" ) == string::npos ){
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;
}
2009-01-26 22:19:15 -05:00
}
2009-08-06 14:15:18 -04:00
2009-01-26 22:19:15 -05:00
}
2009-08-06 14:15:18 -04:00
2009-06-11 20:27:07 -04:00
if ( ! wascmd ){
scope->exec( code.c_str() , "(shell)" , false , true , false );
scope->exec( "shellPrintHelper( __lastres__ );" , "(shell2)" , true , true , false );
}
2009-08-06 14:15:18 -04:00
2009-03-27 16:11:57 -04:00
shellHistoryAdd( 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
2009-01-26 22:19:15 -05:00
return 0;
}
2009-08-10 10:25:43 -04:00
int main(int argc, char* argv[]) {
try {
return _main( argc , argv );
}
catch ( mongo::DBException& e ){
cerr << "exception: " << e.what() << endl;
}
}
2009-01-26 22:19:15 -05:00
namespace mongo {
DBClientBase * createDirectClient(){
uassert( "no createDirectClient in shell" , 0 );
return 0;
}
}