16
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#include "../util/password.h"
23
#include "../util/processinfo.h"
24
#include "../util/net/listen.h"
25
#include "../bson/util/builder.h"
26
#include "security_common.h"
19
#include "mongo/pch.h"
21
#include "mongo/db/cmdline.h"
23
#include "mongo/base/status.h"
24
#include "mongo/bson/util/builder.h"
25
#include "mongo/db/server_parameters.h"
26
#include "mongo/util/map_util.h"
27
27
#include "mongo/util/mongoutils/str.h"
28
#include "mongo/util/net/listen.h"
29
#include "mongo/util/password.h"
29
32
#include <direct.h>
31
#include <sys/types.h>
36
35
#define MAX_LINE_LENGTH 256
39
#include <boost/filesystem/operations.hpp>
41
39
namespace po = boost::program_options;
42
namespace fs = boost::filesystem;
46
void setupSignals( bool inFork );
47
string getHostNameCached();
48
static BSONArray argvArray;
49
static BSONObj parsedOpts;
43
static bool _isPasswordArgument(char const* argumentName);
44
static bool _isPasswordSwitch(char const* switchName);
51
BSONArray CmdLine::getArgvArray() {
55
BSONObj CmdLine::getParsedOpts() {
51
59
void CmdLine::addGlobalOptions( boost::program_options::options_description& general ,
52
60
boost::program_options::options_description& hidden ,
239
if (params.count("verbose")) {
243
for (string s = "vv"; s.length() <= 12; s.append("v")) {
244
if (params.count(s)) {
245
logLevel = s.length();
249
if (params.count("quiet")) {
250
cmdLine.quiet = true;
253
if (params.count("traceExceptions")) {
254
DBException::traceExceptions = true;
257
if ( params.count( "maxConns" ) ) {
258
int newSize = params["maxConns"].as<int>();
260
out() << "maxConns has to be at least 5" << endl;
261
::_exit( EXIT_BADOPTIONS );
263
else if ( newSize >= 10000000 ) {
264
out() << "maxConns can't be greater than 10000000" << endl;
265
::_exit( EXIT_BADOPTIONS );
267
connTicketHolder.resize( newSize );
270
if (params.count("objcheck")) {
271
cmdLine.objcheck = true;
274
if (params.count("bind_ip")) {
275
// passing in wildcard is the same as default behavior; remove and warn
276
if ( cmdLine.bind_ip == "0.0.0.0" ) {
277
cout << "warning: bind_ip of 0.0.0.0 is unnecessary; listens on all ips by default" << endl;
278
cmdLine.bind_ip = "";
285
if (params.count("unixSocketPrefix")) {
286
cmdLine.socket = params["unixSocketPrefix"].as<string>();
287
if (!fs::is_directory(cmdLine.socket)) {
288
cout << cmdLine.socket << " must be a directory" << endl;
293
if (params.count("nounixsocket")) {
294
cmdLine.noUnixSocket = true;
297
if (params.count("fork") && !params.count("shutdown")) {
298
cmdLine.doFork = true;
299
if ( ! params.count( "logpath" ) && ! params.count( "syslog" ) ) {
300
cout << "--fork has to be used with --logpath or --syslog" << endl;
301
::_exit(EXIT_BADOPTIONS);
304
if ( params.count( "logpath" ) ) {
306
logpath = params["logpath"].as<string>();
307
verify( logpath.size() );
308
if ( logpath[0] != '/' ) {
309
logpath = cmdLine.cwd + "/" + logpath;
311
bool exists = boost::filesystem::exists( logpath );
312
FILE * test = fopen( logpath.c_str() , "a" );
314
cout << "can't open [" << logpath << "] for log file: " << errnoWithDescription() << endl;
318
// if we created a file, unlink it (to avoid confusing log rotation code)
320
unlink( logpath.c_str() );
327
cmdLine.parentProc = getpid();
329
// facilitate clean exit when child starts successfully
330
setupLaunchSignals();
335
waitpid(c, &pstat, 0);
337
if ( WIFEXITED(pstat) ) {
338
if ( ! WEXITSTATUS(pstat) ) {
339
cout << "child process started successfully, parent exiting" << endl;
342
_exit( WEXITSTATUS(pstat) );
348
if ( chdir("/") < 0 ) {
349
cout << "Cant chdir() while forking server process: " << strerror(errno) << endl;
354
cmdLine.leaderProc = getpid();
359
cout << "forked process: " << c2 << endl;
360
waitpid(c2, &pstat, 0);
362
if ( WIFEXITED(pstat) ) {
363
_exit( WEXITSTATUS(pstat) );
369
// stdout handled in initLogging
371
//freopen("/dev/null", "w", stdout);
376
FILE* f = freopen("/dev/null", "w", stderr);
378
cout << "Cant reassign stderr while forking server process: " << strerror(errno) << endl;
382
f = freopen("/dev/null", "r", stdin);
384
cout << "Cant reassign stdin while forking server process: " << strerror(errno) << endl;
388
setupSignals( true );
390
if (params.count("syslog")) {
392
sb << cmdLine.binaryName << "." << cmdLine.port;
393
Logstream::useSyslog( sb.str().c_str() );
396
if (params.count("logpath") && !params.count("shutdown")) {
397
if ( params.count("syslog") ) {
398
cout << "Cant use both a logpath and syslog " << endl;
399
::_exit(EXIT_BADOPTIONS);
402
if ( logpath.size() == 0 )
403
logpath = params["logpath"].as<string>();
404
uassert( 10033 , "logpath has to be non-zero" , logpath.size() );
405
initLogging( logpath , params.count( "logappend" ) );
408
if ( params.count("pidfilepath")) {
409
writePidFile( params["pidfilepath"].as<string>() );
412
if (params.count("keyFile")) {
413
const string f = params["keyFile"].as<string>();
415
if (!setUpSecurityKey(f)) {
416
// error message printed in setUpPrivateKey
417
::_exit(EXIT_BADOPTIONS);
420
cmdLine.keyFile = true;
424
cmdLine.keyFile = false;
428
if (params.count("sslOnNormalPorts") ) {
429
cmdLine.sslOnNormalPorts = true;
431
if ( cmdLine.sslPEMKeyPassword.size() == 0 ) {
432
log() << "need sslPEMKeyPassword" << endl;
433
::_exit(EXIT_BADOPTIONS);
436
if ( cmdLine.sslPEMKeyFile.size() == 0 ) {
437
log() << "need sslPEMKeyFile" << endl;
438
::_exit(EXIT_BADOPTIONS);
441
cmdLine.sslServerManager = new SSLManager( false );
442
if ( ! cmdLine.sslServerManager->setupPEM( cmdLine.sslPEMKeyFile , cmdLine.sslPEMKeyPassword ) ) {
443
::_exit(EXIT_BADOPTIONS);
446
else if ( cmdLine.sslPEMKeyFile.size() || cmdLine.sslPEMKeyPassword.size() ) {
447
log() << "need to enable sslOnNormalPorts" << endl;
448
::_exit(EXIT_BADOPTIONS);
241
std::vector<std::string> censoredArgv = argv;
242
censor(&censoredArgv);
243
for (size_t i=0; i < censoredArgv.size(); i++) {
244
b << censoredArgv[i];
453
250
BSONObjBuilder b;
454
251
for (po::variables_map::const_iterator it(params.begin()), end(params.end()); it != end; it++){
490
287
parsedOpts = b.obj();
495
for (int i=0; i < argc; i++) {
497
if ( mongoutils::str::equals(argv[i], "--sslPEMKeyPassword")
498
|| mongoutils::str::equals(argv[i], "-sslPEMKeyPassword")
499
|| mongoutils::str::equals(argv[i], "--servicePassword")
500
|| mongoutils::str::equals(argv[i], "-servicePassword")) {
504
// hide password from ps output
290
if (params.count("verbose")) {
294
for (string s = "vv"; s.length() <= 12; s.append("v")) {
295
if (params.count(s)) {
296
logLevel = s.length();
300
if (params.count("quiet")) {
301
cmdLine.quiet = true;
304
if (params.count("traceExceptions")) {
305
DBException::traceExceptions = true;
308
if (params.count("maxConns")) {
309
cmdLine.maxConns = params["maxConns"].as<int>();
311
if ( cmdLine.maxConns < 5 ) {
312
out() << "maxConns has to be at least 5" << endl;
315
else if ( cmdLine.maxConns > MAX_MAX_CONN ) {
316
out() << "maxConns can't be greater than " << MAX_MAX_CONN << endl;
321
if (params.count("objcheck")) {
322
cmdLine.objcheck = true;
324
if (params.count("noobjcheck")) {
325
if (params.count("objcheck")) {
326
out() << "can't have both --objcheck and --noobjcheck" << endl;
329
cmdLine.objcheck = false;
332
if (params.count("bind_ip")) {
333
// passing in wildcard is the same as default behavior; remove and warn
334
if ( cmdLine.bind_ip == "0.0.0.0" ) {
335
cout << "warning: bind_ip of 0.0.0.0 is unnecessary; listens on all ips by default" << endl;
336
cmdLine.bind_ip = "";
341
if (params.count("unixSocketPrefix")) {
342
cmdLine.socket = params["unixSocketPrefix"].as<string>();
345
if (params.count("nounixsocket")) {
346
cmdLine.noUnixSocket = true;
349
if (params.count("fork") && !params.count("shutdown")) {
350
cmdLine.doFork = true;
354
if (params.count("logpath")) {
355
cmdLine.logpath = params["logpath"].as<string>();
356
if (cmdLine.logpath.empty()) {
357
cout << "logpath cannot be empty if supplied" << endl;
362
cmdLine.logWithSyslog = params.count("syslog");
363
cmdLine.logAppend = params.count("logappend");
364
if (!cmdLine.logpath.empty() && cmdLine.logWithSyslog) {
365
cout << "Cant use both a logpath and syslog " << endl;
369
if (cmdLine.doFork && cmdLine.logpath.empty() && !cmdLine.logWithSyslog) {
370
cout << "--fork has to be used with --logpath or --syslog" << endl;
374
if (params.count("keyFile")) {
375
cmdLine.keyFile = params["keyFile"].as<string>();
378
if ( params.count("pidfilepath")) {
379
cmdLine.pidFile = params["pidfilepath"].as<string>();
382
if (params.count("setParameter")) {
383
std::vector<std::string> parameters =
384
params["setParameter"].as<std::vector<std::string> >();
385
for (size_t i = 0, length = parameters.size(); i < length; ++i) {
388
if (!mongoutils::str::splitOn(parameters[i], '=', name, value)) {
389
cout << "Illegal option assignment: \"" << parameters[i] << "\"" << endl;
392
ServerParameter* parameter = mapFindWithDefault(
393
ServerParameterSet::getGlobal()->getMap(),
395
static_cast<ServerParameter*>(NULL));
396
if (NULL == parameter) {
397
cout << "Illegal --setParameter parameter: \"" << name << "\"" << endl;
400
if (!parameter->allowedToChangeAtStartup()) {
401
cout << "Cannot use --setParameter to set \"" << name << "\" at startup" <<
405
Status status = parameter->setFromString(value);
406
if (!status.isOK()) {
407
cout << "Bad value for parameter \"" << name << "\": " << status.reason()
415
if (params.count("sslWeakCertificateValidation")) {
416
cmdLine.sslWeakCertificateValidation = true;
418
if (params.count("sslOnNormalPorts")) {
419
cmdLine.sslOnNormalPorts = true;
420
if ( cmdLine.sslPEMKeyFile.size() == 0 ) {
421
log() << "need sslPEMKeyFile with sslOnNormalPorts" << endl;
424
if (cmdLine.sslWeakCertificateValidation &&
425
cmdLine.sslCAFile.empty()) {
426
log() << "need sslCAFile with sslWeakCertificateValidation" << endl;
429
if (!cmdLine.sslCRLFile.empty() &&
430
cmdLine.sslCAFile.empty()) {
431
log() << "need sslCAFile with sslCRLFile" << endl;
434
if (params.count("sslFIPSMode")) {
435
cmdLine.sslFIPSMode = true;
438
else if (cmdLine.sslPEMKeyFile.size() ||
439
cmdLine.sslPEMKeyPassword.size() ||
440
cmdLine.sslCAFile.size() ||
441
cmdLine.sslCRLFile.size() ||
442
cmdLine.sslWeakCertificateValidation ||
443
cmdLine.sslFIPSMode) {
444
log() << "need to enable sslOnNormalPorts" << endl;
452
static bool _isPasswordArgument(const char* argumentName) {
453
static const char* const passwordArguments[] = {
456
NULL // Last entry sentinel.
458
for (const char* const* current = passwordArguments; *current; ++current) {
459
if (mongoutils::str::equals(argumentName, *current))
465
static bool _isPasswordSwitch(const char* switchName) {
466
if (switchName[0] != '-')
469
if (switchName[1] == '-')
473
return _isPasswordArgument(switchName);
476
static void _redact(char* arg) {
481
void CmdLine::censor(std::vector<std::string>* args) {
482
for (size_t i = 0; i < args->size(); ++i) {
483
std::string& arg = args->at(i);
484
const std::string::iterator endSwitch = std::find(arg.begin(), arg.end(), '=');
485
std::string switchName(arg.begin(), endSwitch);
486
if (_isPasswordSwitch(switchName.c_str())) {
487
if (endSwitch == arg.end()) {
488
if (i + 1 < args->size()) {
489
args->at(i + 1) = "<password>";
493
arg = switchName + "=<password>";
499
void CmdLine::censor(int argc, char** argv) {
500
// Algorithm: For each arg in argv:
501
// Look for an equal sign in arg; if there is one, temporarily nul it out.
502
// check to see if arg is a password switch. If so, overwrite the value
503
// component with xs.
504
// restore the nul'd out equal sign, if any.
505
for (int i = 0; i < argc; ++i) {
507
char* const arg = argv[i];
508
char* const firstEqSign = strchr(arg, '=');
509
if (NULL != firstEqSign) {
513
if (_isPasswordSwitch(arg)) {
514
if (NULL == firstEqSign) {
516
_redact(argv[i + 1]);
520
_redact(firstEqSign + 1);
524
if (NULL != firstEqSign) {
517
530
void printCommandLineOpts() {
518
531
log() << "options: " << parsedOpts << endl;
521
void ignoreSignal( int sig ) {}
523
static void rotateLogsOrDie(int sig) {
524
fassert(16176, rotateLogs());
527
void setupCoreSignals() {
529
verify( signal(SIGUSR1 , rotateLogsOrDie ) != SIG_ERR );
530
verify( signal(SIGHUP , ignoreSignal ) != SIG_ERR );
534
class CmdGetCmdLineOpts : Command {
536
CmdGetCmdLineOpts(): Command("getCmdLineOpts") {}
537
void help(stringstream& h) const { h << "get argv"; }
538
virtual LockType locktype() const { return NONE; }
539
virtual bool adminOnly() const { return true; }
540
virtual bool slaveOk() const { return true; }
542
virtual bool run(const string&, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
543
result.append("argv", argvArray);
544
result.append("parsed", parsedOpts);
550
string prettyHostName() {
552
s << getHostNameCached();
553
if( cmdLine.port != CmdLine::DefaultDBPort )
554
s << ':' << mongo::cmdLine.port;
558
casi< map<string,ParameterValidator*> * > pv_all (NULL);
560
ParameterValidator::ParameterValidator( const string& name ) : _name( name ) {
562
pv_all.ref() = new map<string,ParameterValidator*>();
563
(*pv_all.ref())[_name] = this;
566
ParameterValidator * ParameterValidator::get( const string& name ) {
567
map<string,ParameterValidator*>::const_iterator i = pv_all.get()->find( name );
568
if ( i == pv_all.get()->end() )