summaryrefslogtreecommitdiff
path: root/src/mongo/db/cmdline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/cmdline.cpp')
-rw-r--r--src/mongo/db/cmdline.cpp519
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 &params ) {
+
+
+ {
+ // 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;
+ }
+
+}