diff options
Diffstat (limited to 'src/mongo/db/cmdline.cpp')
-rw-r--r-- | src/mongo/db/cmdline.cpp | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/src/mongo/db/cmdline.cpp b/src/mongo/db/cmdline.cpp new file mode 100644 index 00000000000..a9b0d7097ca --- /dev/null +++ b/src/mongo/db/cmdline.cpp @@ -0,0 +1,519 @@ +// cmdline.cpp + +/** +* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. +*/ + +#include "pch.h" +#include "cmdline.h" +#include "commands.h" +#include "../util/password.h" +#include "../util/processinfo.h" +#include "../util/net/listen.h" +#include "security_common.h" +#ifdef _WIN32 +#include <direct.h> +#else +#include <sys/types.h> +#include <sys/wait.h> +#endif +#include "globals.h" + +#define MAX_LINE_LENGTH 256 + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +namespace mongo { + + void setupSignals( bool inFork ); + string getHostNameCached(); + static BSONArray argvArray; + static BSONObj parsedOpts; + + void CmdLine::addGlobalOptions( boost::program_options::options_description& general , + boost::program_options::options_description& hidden ) { + /* support for -vv -vvvv etc. */ + for (string s = "vv"; s.length() <= 12; s.append("v")) { + hidden.add_options()(s.c_str(), "verbose"); + } + + general.add_options() + ("help,h", "show this usage information") + ("version", "show version information") + ("config,f", po::value<string>(), "configuration file specifying additional options") + ("verbose,v", "be more verbose (include multiple times for more verbosity e.g. -vvvvv)") + ("quiet", "quieter output") + ("port", po::value<int>(&cmdLine.port), "specify port number") + ("bind_ip", po::value<string>(&cmdLine.bind_ip), "comma separated list of ip addresses to listen on - all local ips by default") + ("maxConns",po::value<int>(), "max number of simultaneous connections") + ("objcheck", "inspect client data for validity on receipt") + ("logpath", po::value<string>() , "log file to send write to instead of stdout - has to be a file, not directory" ) + ("logappend" , "append to logpath instead of over-writing" ) + ("pidfilepath", po::value<string>(), "full path to pidfile (if not set, no pidfile is created)") + ("keyFile", po::value<string>(), "private key for cluster authentication (only for replica sets)") +#ifndef _WIN32 + ("nounixsocket", "disable listening on unix sockets") + ("unixSocketPrefix", po::value<string>(), "alternative directory for UNIX domain sockets (defaults to /tmp)") + ("fork" , "fork server process" ) + ("syslog" , "log to system's syslog facility instead of file or stdout" ) +#endif + ; + + hidden.add_options() + ("cloud", po::value<string>(), "custom dynamic host naming") +#ifdef MONGO_SSL + ("sslOnNormalPorts" , "use ssl on configured ports" ) + ("sslPEMKeyFile" , po::value<string>(&cmdLine.sslPEMKeyFile), "PEM file for ssl" ) + ("sslPEMKeyPassword" , new PasswordValue(&cmdLine.sslPEMKeyPassword) , "PEM file password" ) +#endif + ; + + } + + +#if defined(_WIN32) + void CmdLine::addWindowsOptions( boost::program_options::options_description& windows , + boost::program_options::options_description& hidden ) { + windows.add_options() + ("install", "install mongodb service") + ("remove", "remove mongodb service") + ("reinstall", "reinstall mongodb service (equivilant of mongod --remove followed by mongod --install)") + ("serviceName", po::value<string>(), "windows service name") + ("serviceDisplayName", po::value<string>(), "windows service display name") + ("serviceDescription", po::value<string>(), "windows service description") + ("serviceUser", po::value<string>(), "user name service executes as") + ("servicePassword", po::value<string>(), "password used to authenticate serviceUser") + ; + hidden.add_options()("service", "start mongodb service"); + } +#endif + + void CmdLine::parseConfigFile( istream &f, stringstream &ss ) { + string s; + char line[MAX_LINE_LENGTH]; + + while ( f ) { + f.getline(line, MAX_LINE_LENGTH); + s = line; + std::remove(s.begin(), s.end(), ' '); + std::remove(s.begin(), s.end(), '\t'); + boost::to_upper(s); + + if ( s.find( "FASTSYNC" ) != string::npos ) + cout << "warning \"fastsync\" should not be put in your configuration file" << endl; + + if ( s.c_str()[0] == '#' ) { + // skipping commented line + } else if ( s.find( "=FALSE" ) == string::npos ) { + ss << line << endl; + } else { + cout << "warning: remove or comment out this line by starting it with \'#\', skipping now : " << line << endl; + } + } + return; + } + +#ifndef _WIN32 + // support for exit value propagation with fork + void launchSignal( int sig ) { + if ( sig == SIGUSR2 ) { + pid_t cur = getpid(); + + if ( cur == cmdLine.parentProc || cur == cmdLine.leaderProc ) { + // signal indicates successful start allowing us to exit + _exit(0); + } + } + } + + void setupLaunchSignals() { + assert( signal(SIGUSR2 , launchSignal ) != SIG_ERR ); + } + + + void CmdLine::launchOk() { + if ( cmdLine.doFork ) { + // killing leader will propagate to parent + assert( kill( cmdLine.leaderProc, SIGUSR2 ) == 0 ); + } + } +#endif + + bool CmdLine::store( int argc , char ** argv , + boost::program_options::options_description& visible, + boost::program_options::options_description& hidden, + boost::program_options::positional_options_description& positional, + boost::program_options::variables_map ¶ms ) { + + + { + // setup binary name + cmdLine.binaryName = argv[0]; + size_t i = cmdLine.binaryName.rfind( '/' ); + if ( i != string::npos ) + cmdLine.binaryName = cmdLine.binaryName.substr( i + 1 ); + + // setup cwd + char buffer[1024]; +#ifdef _WIN32 + assert( _getcwd( buffer , 1000 ) ); +#else + assert( getcwd( buffer , 1000 ) ); +#endif + cmdLine.cwd = buffer; + } + + + /* don't allow guessing - creates ambiguities when some options are + * prefixes of others. allow long disguises and don't allow guessing + * to get away with our vvvvvvv trick. */ + int 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::options_description all; + all.add( visible ); + all.add( hidden ); + + po::store( po::command_line_parser(argc, argv) + .options( all ) + .positional( positional ) + .style( style ) + .run(), + params ); + + if ( params.count("config") ) { + ifstream f( params["config"].as<string>().c_str() ); + if ( ! f.is_open() ) { + cout << "ERROR: could not read from config file" << endl << endl; + cout << visible << endl; + return false; + } + + stringstream ss; + CmdLine::parseConfigFile( f, ss ); + po::store( po::parse_config_file( ss , all ) , params ); + f.close(); + } + + po::notify(params); + } + catch (po::error &e) { + cout << "error command line: " << e.what() << endl; + cout << "use --help for help" << endl; + //cout << visible << endl; + return false; + } + + if (params.count("verbose")) { + logLevel = 1; + } + + for (string s = "vv"; s.length() <= 12; s.append("v")) { + if (params.count(s)) { + logLevel = s.length(); + } + } + + if (params.count("quiet")) { + cmdLine.quiet = true; + } + + if ( params.count( "maxConns" ) ) { + int newSize = params["maxConns"].as<int>(); + if ( newSize < 5 ) { + out() << "maxConns has to be at least 5" << endl; + dbexit( EXIT_BADOPTIONS ); + } + else if ( newSize >= 10000000 ) { + out() << "maxConns can't be greater than 10000000" << endl; + dbexit( EXIT_BADOPTIONS ); + } + connTicketHolder.resize( newSize ); + } + + if (params.count("objcheck")) { + cmdLine.objcheck = true; + } + + string logpath; + +#ifndef _WIN32 + if (params.count("unixSocketPrefix")) { + cmdLine.socket = params["unixSocketPrefix"].as<string>(); + if (!fs::is_directory(cmdLine.socket)) { + cout << cmdLine.socket << " must be a directory" << endl; + ::exit(-1); + } + } + + if (params.count("nounixsocket")) { + cmdLine.noUnixSocket = true; + } + + if (params.count("fork")) { + cmdLine.doFork = true; + if ( ! params.count( "logpath" ) && ! params.count( "syslog" ) ) { + cout << "--fork has to be used with --logpath or --syslog" << endl; + ::exit(-1); + } + + if ( params.count( "logpath" ) ) { + // test logpath + logpath = params["logpath"].as<string>(); + assert( logpath.size() ); + if ( logpath[0] != '/' ) { + logpath = cmdLine.cwd + "/" + logpath; + } + FILE * test = fopen( logpath.c_str() , "a" ); + if ( ! test ) { + cout << "can't open [" << logpath << "] for log file: " << errnoWithDescription() << endl; + ::exit(-1); + } + fclose( test ); + } + + cout.flush(); + cerr.flush(); + + cmdLine.parentProc = getpid(); + + // facilitate clean exit when child starts successfully + setupLaunchSignals(); + + pid_t c = fork(); + if ( c ) { + int pstat; + waitpid(c, &pstat, 0); + + if ( WIFEXITED(pstat) ) { + if ( ! WEXITSTATUS(pstat) ) { + cout << "child process started successfully, parent exiting" << endl; + } + + _exit( WEXITSTATUS(pstat) ); + } + + _exit(50); + } + + if ( chdir("/") < 0 ) { + cout << "Cant chdir() while forking server process: " << strerror(errno) << endl; + ::exit(-1); + } + setsid(); + + cmdLine.leaderProc = getpid(); + + pid_t c2 = fork(); + if ( c2 ) { + int pstat; + cout << "forked process: " << c2 << endl; + waitpid(c2, &pstat, 0); + + if ( WIFEXITED(pstat) ) { + _exit( WEXITSTATUS(pstat) ); + } + + _exit(51); + } + + // stdout handled in initLogging + //fclose(stdout); + //freopen("/dev/null", "w", stdout); + + fclose(stderr); + fclose(stdin); + + FILE* f = freopen("/dev/null", "w", stderr); + if ( f == NULL ) { + cout << "Cant reassign stderr while forking server process: " << strerror(errno) << endl; + ::exit(-1); + } + + f = freopen("/dev/null", "r", stdin); + if ( f == NULL ) { + cout << "Cant reassign stdin while forking server process: " << strerror(errno) << endl; + ::exit(-1); + } + + setupCoreSignals(); + setupSignals( true ); + } + + if (params.count("syslog")) { + StringBuilder sb(128); + sb << cmdLine.binaryName << "." << cmdLine.port; + Logstream::useSyslog( sb.str().c_str() ); + } +#endif + if (params.count("logpath")) { + if ( params.count("syslog") ) { + cout << "Cant use both a logpath and syslog " << endl; + ::exit(-1); + } + + if ( logpath.size() == 0 ) + logpath = params["logpath"].as<string>(); + uassert( 10033 , "logpath has to be non-zero" , logpath.size() ); + initLogging( logpath , params.count( "logappend" ) ); + } + + if ( params.count("pidfilepath")) { + writePidFile( params["pidfilepath"].as<string>() ); + } + + if (params.count("keyFile")) { + const string f = params["keyFile"].as<string>(); + + if (!setUpSecurityKey(f)) { + // error message printed in setUpPrivateKey + dbexit(EXIT_BADOPTIONS); + } + + cmdLine.keyFile = true; + noauth = false; + } + else { + cmdLine.keyFile = false; + } + +#ifdef MONGO_SSL + if (params.count("sslOnNormalPorts") ) { + cmdLine.sslOnNormalPorts = true; + + if ( cmdLine.sslPEMKeyPassword.size() == 0 ) { + log() << "need sslPEMKeyPassword" << endl; + dbexit(EXIT_BADOPTIONS); + } + + if ( cmdLine.sslPEMKeyFile.size() == 0 ) { + log() << "need sslPEMKeyFile" << endl; + dbexit(EXIT_BADOPTIONS); + } + + cmdLine.sslServerManager = new SSLManager( false ); + cmdLine.sslServerManager->setupPEM( cmdLine.sslPEMKeyFile , cmdLine.sslPEMKeyPassword ); + } + + if ( cmdLine.sslPEMKeyFile.size() || cmdLine.sslPEMKeyPassword.size() ) { + log() << "need to enable sslOnNormalPorts" << endl; + dbexit(EXIT_BADOPTIONS); + } +#endif + + { + BSONObjBuilder b; + for (po::variables_map::const_iterator it(params.begin()), end(params.end()); it != end; it++){ + if (!it->second.defaulted()){ + const string& key = it->first; + const po::variable_value& value = it->second; + const type_info& type = value.value().type(); + + if (type == typeid(string)){ + if (value.as<string>().empty()) + b.appendBool(key, true); // boost po uses empty string for flags like --quiet + else + b.append(key, value.as<string>()); + } + else if (type == typeid(int)) + b.append(key, value.as<int>()); + else if (type == typeid(double)) + b.append(key, value.as<double>()); + else if (type == typeid(bool)) + b.appendBool(key, value.as<bool>()); + else if (type == typeid(long)) + b.appendNumber(key, (long long)value.as<long>()); + else if (type == typeid(unsigned)) + b.appendNumber(key, (long long)value.as<unsigned>()); + else if (type == typeid(unsigned long long)) + b.appendNumber(key, (long long)value.as<unsigned long long>()); + else if (type == typeid(vector<string>)) + b.append(key, value.as<vector<string> >()); + else + b.append(key, "UNKNOWN TYPE: " + demangleName(type)); + } + } + parsedOpts = b.obj(); + } + + { + BSONArrayBuilder b; + for (int i=0; i < argc; i++) + b << argv[i]; + argvArray = b.arr(); + } + + return true; + } + + void printCommandLineOpts() { + log() << "options: " << parsedOpts << endl; + } + + void ignoreSignal( int sig ) {} + + void setupCoreSignals() { +#if !defined(_WIN32) + assert( signal(SIGUSR1 , rotateLogs ) != SIG_ERR ); + assert( signal(SIGHUP , ignoreSignal ) != SIG_ERR ); +#endif + } + + class CmdGetCmdLineOpts : Command { + public: + CmdGetCmdLineOpts(): Command("getCmdLineOpts") {} + void help(stringstream& h) const { h << "get argv"; } + virtual LockType locktype() const { return NONE; } + virtual bool adminOnly() const { return true; } + virtual bool slaveOk() const { return true; } + + virtual bool run(const string&, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { + result.append("argv", argvArray); + result.append("parsed", parsedOpts); + return true; + } + + } cmdGetCmdLineOpts; + + string prettyHostName() { + StringBuilder s(128); + s << getHostNameCached(); + if( cmdLine.port != CmdLine::DefaultDBPort ) + s << ':' << mongo::cmdLine.port; + return s.str(); + } + + casi< map<string,ParameterValidator*> * > pv_all (NULL); + + ParameterValidator::ParameterValidator( const string& name ) : _name( name ) { + if ( ! pv_all) + pv_all.ref() = new map<string,ParameterValidator*>(); + (*pv_all.ref())[_name] = this; + } + + ParameterValidator * ParameterValidator::get( const string& name ) { + map<string,ParameterValidator*>::const_iterator i = pv_all.get()->find( name ); + if ( i == pv_all.get()->end() ) + return NULL; + return i->second; + } + +} |