// parameters.cpp
/**
* Copyright (C) 2012 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/platform/basic.h"
#include
#include "mongo/bson/mutable/document.h"
#include "mongo/client/replica_set_monitor.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/config.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/internal_user_auth.h"
#include "mongo/db/commands.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/storage_options.h"
#include "mongo/logger/parse_log_component_settings.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/net/ssl_manager.h"
#include "mongo/util/net/ssl_options.h"
using std::string;
using std::stringstream;
namespace mongo {
namespace {
void appendParameterNames( stringstream& help ) {
help << "supported:\n";
const ServerParameter::Map& m = ServerParameterSet::getGlobal()->getMap();
for ( ServerParameter::Map::const_iterator i = m.begin(); i != m.end(); ++i ) {
help << " " << i->first << "\n";
}
}
}
class CmdGet : public Command {
public:
CmdGet() : Command( "getParameter" ) { }
virtual bool slaveOk() const { return true; }
virtual bool adminOnly() const { return true; }
virtual bool isWriteCommandForConfigServer() const { return false; }
virtual void addRequiredPrivileges(const std::string& dbname,
const BSONObj& cmdObj,
std::vector* out) {
ActionSet actions;
actions.addAction(ActionType::getParameter);
out->push_back(Privilege(ResourcePattern::forClusterResource(), actions));
}
virtual void help( stringstream &help ) const {
help << "get administrative option(s)\nexample:\n";
help << "{ getParameter:1, notablescan:1 }\n";
appendParameterNames( help );
help << "{ getParameter:'*' } to get everything\n";
}
bool run(OperationContext* txn,
const string& dbname,
BSONObj& cmdObj,
int,
string& errmsg,
BSONObjBuilder& result) {
bool all = *cmdObj.firstElement().valuestrsafe() == '*';
int before = result.len();
const ServerParameter::Map& m = ServerParameterSet::getGlobal()->getMap();
for ( ServerParameter::Map::const_iterator i = m.begin(); i != m.end(); ++i ) {
if ( all || cmdObj.hasElement( i->first.c_str() ) ) {
i->second->append(txn, result, i->second->name() );
}
}
if ( before == result.len() ) {
errmsg = "no option found to get";
return false;
}
return true;
}
} cmdGet;
class CmdSet : public Command {
public:
CmdSet() : Command( "setParameter" ) { }
virtual bool slaveOk() const { return true; }
virtual bool adminOnly() const { return true; }
virtual bool isWriteCommandForConfigServer() const { return false; }
virtual void addRequiredPrivileges(const std::string& dbname,
const BSONObj& cmdObj,
std::vector* out) {
ActionSet actions;
actions.addAction(ActionType::setParameter);
out->push_back(Privilege(ResourcePattern::forClusterResource(), actions));
}
virtual void help( stringstream &help ) const {
help << "set administrative option(s)\n";
help << "{ setParameter:1, : }\n";
appendParameterNames( help );
}
bool run(OperationContext* txn,
const string& dbname,
BSONObj& cmdObj,
int,
string& errmsg,
BSONObjBuilder& result) {
int numSet = 0;
bool found = false;
const ServerParameter::Map& parameterMap = ServerParameterSet::getGlobal()->getMap();
// First check that we aren't setting the same parameter twice and that we actually are
// setting parameters that we have registered and can change at runtime
BSONObjIterator parameterCheckIterator(cmdObj);
// We already know that "setParameter" will be the first element in this object, so skip
// past that
parameterCheckIterator.next();
// Set of all the parameters the user is attempting to change
std::map parametersToSet;
// Iterate all parameters the user passed in to do the initial validation checks,
// including verifying that we are not setting the same parameter twice.
while (parameterCheckIterator.more()) {
BSONElement parameter = parameterCheckIterator.next();
std::string parameterName = parameter.fieldName();
ServerParameter::Map::const_iterator foundParameter =
parameterMap.find(parameterName);
// Check to see if this is actually a valid parameter
if (foundParameter == parameterMap.end()) {
errmsg = str::stream() << "attempted to set unrecognized parameter ["
<< parameterName
<< "], use help:true to see options ";
return false;
}
// Make sure we are allowed to change this parameter
if (!foundParameter->second->allowedToChangeAtRuntime()) {
errmsg = str::stream() << "not allowed to change [" << parameterName
<< "] at runtime";
return false;
}
// Make sure we are only setting this parameter once
if (parametersToSet.count(parameterName)) {
errmsg = str::stream() << "attempted to set parameter ["
<< parameterName
<< "] twice in the same setParameter command, "
<< "once to value: ["
<< parametersToSet[parameterName].toString(false)
<< "], and once to value: [" << parameter.toString(false)
<< "]";
return false;
}
parametersToSet[parameterName] = parameter;
}
// Iterate the parameters that we have confirmed we are setting and actually set them.
// Not that if setting any one parameter fails, the command will fail, but the user
// won't see what has been set and what hasn't. See SERVER-8552.
for (std::map::iterator it = parametersToSet.begin();
it != parametersToSet.end(); ++it) {
BSONElement parameter = it->second;
std::string parameterName = it->first;
ServerParameter::Map::const_iterator foundParameter =
parameterMap.find(parameterName);
if (foundParameter == parameterMap.end()) {
errmsg = str::stream() << "Parameter: " << parameterName << " that was "
<< "avaliable during our first lookup in the registered "
<< "parameters map is no longer available.";
return false;
}
if (numSet == 0) {
foundParameter->second->append(txn, result, "was");
}
Status status = foundParameter->second->set(parameter);
if (status.isOK()) {
numSet++;
continue;
}
errmsg = status.reason();
result.append("code", status.code());
return false;
}
if (numSet == 0 && !found) {
errmsg = "no option found to set, use help:true to see options ";
return false;
}
return true;
}
} cmdSet;
namespace {
using logger::globalLogDomain;
using logger::LogComponent;
using logger::LogComponentSetting;
using logger::LogSeverity;
using logger::parseLogComponentSettings;
class LogLevelSetting : public ServerParameter {
public:
LogLevelSetting() : ServerParameter(ServerParameterSet::getGlobal(), "logLevel") {}
virtual void append(OperationContext* txn, BSONObjBuilder& b, const std::string& name) {
b << name << globalLogDomain()->getMinimumLogSeverity().toInt();
}
virtual Status set(const BSONElement& newValueElement) {
int newValue;
if (!newValueElement.coerce(&newValue) || newValue < 0)
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Invalid value for logLevel: " << newValueElement);
LogSeverity newSeverity = (newValue > 0) ? LogSeverity::Debug(newValue) :
LogSeverity::Log();
globalLogDomain()->setMinimumLoggedSeverity(newSeverity);
return Status::OK();
}
virtual Status setFromString(const std::string& str) {
int newValue;
Status status = parseNumberFromString(str, &newValue);
if (!status.isOK())
return status;
if (newValue < 0)
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Invalid value for logLevel: " << newValue);
LogSeverity newSeverity = (newValue > 0) ? LogSeverity::Debug(newValue) :
LogSeverity::Log();
globalLogDomain()->setMinimumLoggedSeverity(newSeverity);
return Status::OK();
}
} logLevelSetting;
/**
* Log component verbosity.
* Log levels of log component hierarchy.
* Negative value for a log component means the default log level will be used.
*/
class LogComponentVerbositySetting : public ServerParameter {
MONGO_DISALLOW_COPYING(LogComponentVerbositySetting);
public:
LogComponentVerbositySetting()
: ServerParameter(ServerParameterSet::getGlobal(), "logComponentVerbosity") {}
virtual void append(OperationContext* txn, BSONObjBuilder& b,
const std::string& name) {
BSONObj currentSettings;
_get(¤tSettings);
b << name << currentSettings;
}
virtual Status set(const BSONElement& newValueElement) {
if (!newValueElement.isABSONObj()) {
return Status(ErrorCodes::TypeMismatch, mongoutils::str::stream() <<
"log component verbosity is not a BSON object: " <<
newValueElement);
}
return _set(newValueElement.Obj());
}
virtual Status setFromString(const std::string& str) {
try {
return _set(mongo::fromjson(str));
}
catch (const DBException& ex) {
return ex.toStatus();
}
}
private:
/**
* Returns current settings as a BSON document.
* The "default" log component is an implementation detail. Don't expose this to users.
*/
void _get(BSONObj* output) const {
static const string defaultLogComponentName =
LogComponent(LogComponent::kDefault).getShortName();
mutablebson::Document doc;
for (int i = 0; i < int(LogComponent::kNumLogComponents); ++i) {
LogComponent component = static_cast(i);
int severity = -1;
if (globalLogDomain()->hasMinimumLogSeverity(component)) {
severity = globalLogDomain()->getMinimumLogSeverity(component).toInt();
}
// Save LogComponent::kDefault LogSeverity at root
if (component == LogComponent::kDefault) {
doc.root().appendInt("verbosity", severity);
continue;
}
mutablebson::Element element = doc.makeElementObject(component.getShortName());
element.appendInt("verbosity", severity);
mutablebson::Element parentElement = _getParentElement(doc, component);
parentElement.pushBack(element);
}
BSONObj result = doc.getObject();
output->swap(result);
invariant(!output->hasField(defaultLogComponentName));
}
/**
* Updates component hierarchy log levels.
*
* BSON Format:
* {
* verbosity: 4, <-- maps to 'default' log component.
* componentA: {
* verbosity: 2, <-- sets componentA's log level to 2.
* componentB: {
* verbosity: 1, <-- sets componentA.componentB's log level to 1.
* }
* componentC: {
* verbosity: -1, <-- clears componentA.componentC's log level so that
* its final loglevel will be inherited from componentA.
* }
* },
* componentD : 3 <-- sets componentD's log level to 3 (alternative to
* subdocument with 'verbosity' field).
* }
*
* For the default component, the log level is read from the top-level
* "verbosity" field.
* For non-default components, we look up the element using the component's
* dotted name. If the "" field is a number, the log
* level will be read from the field's value.
* Otherwise, we assume that the "" field is an
* object with a "verbosity" field that holds the log level for the component.
* The more verbose format with the "verbosity" field is intended to support
* setting of log levels of both parent and child log components in the same
* BSON document.
*
* Ignore elements in BSON object that do not map to a log component's dotted
* name.
*/
Status _set(const BSONObj& bsonSettings) const {
StatusWith< std::vector > parseStatus =
parseLogComponentSettings(bsonSettings);
if (!parseStatus.isOK()) {
return parseStatus.getStatus();
}
std::vector settings = parseStatus.getValue();
std::vector::iterator it = settings.begin();
for (; it < settings.end(); ++it) {
LogComponentSetting newSetting = *it;
// Negative value means to clear log level of component.
if (newSetting.level < 0) {
globalLogDomain()->clearMinimumLoggedSeverity(newSetting.component);
continue;
}
// Convert non-negative value to Log()/Debug(N).
LogSeverity newSeverity = (newSetting.level > 0) ?
LogSeverity::Debug(newSetting.level) : LogSeverity::Log();
globalLogDomain()->setMinimumLoggedSeverity(newSetting.component,
newSeverity);
}
return Status::OK();
}
/**
* Search document for element corresponding to log component's parent.
*/
static mutablebson::Element _getParentElement(mutablebson::Document& doc,
LogComponent component) {
// Hide LogComponent::kDefault
if (component == LogComponent::kDefault) {
return doc.end();
}
LogComponent parentComponent = component.parent();
// Attach LogComponent::kDefault children to root
if (parentComponent == LogComponent::kDefault) {
return doc.root();
}
mutablebson::Element grandParentElement = _getParentElement(doc, parentComponent);
return grandParentElement.findFirstChildNamed(parentComponent.getShortName());
}
} logComponentVerbositySetting;
} // namespace
namespace {
class SSLModeSetting : public ServerParameter {
public:
SSLModeSetting() : ServerParameter(ServerParameterSet::getGlobal(), "sslMode",
false, // allowedToChangeAtStartup
true // allowedToChangeAtRuntime
) {}
std::string sslModeStr() {
switch (sslGlobalParams.sslMode.load()) {
case SSLParams::SSLMode_disabled:
return "disabled";
case SSLParams::SSLMode_allowSSL:
return "allowSSL";
case SSLParams::SSLMode_preferSSL:
return "preferSSL";
case SSLParams::SSLMode_requireSSL:
return "requireSSL";
default:
return "undefined";
}
}
virtual void append(
OperationContext* txn, BSONObjBuilder& b, const std::string& name) {
b << name << sslModeStr();
}
virtual Status set(const BSONElement& newValueElement) {
try {
return setFromString(newValueElement.String());
}
catch (MsgAssertionException msg) {
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Invalid value for sslMode via setParameter command: "
<< newValueElement);
}
}
virtual Status setFromString(const std::string& str) {
#ifndef MONGO_CONFIG_SSL
return Status(ErrorCodes::IllegalOperation, mongoutils::str::stream() <<
"Unable to set sslMode, SSL support is not compiled into server");
#endif
if (str != "disabled" && str != "allowSSL" &&
str != "preferSSL" && str != "requireSSL") {
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Invalid value for sslMode via setParameter command: "
<< str);
}
int oldMode = sslGlobalParams.sslMode.load();
if (str == "preferSSL" && oldMode == SSLParams::SSLMode_allowSSL) {
sslGlobalParams.sslMode.store(SSLParams::SSLMode_preferSSL);
}
else if (str == "requireSSL" && oldMode == SSLParams::SSLMode_preferSSL) {
sslGlobalParams.sslMode.store(SSLParams::SSLMode_requireSSL);
}
else {
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Illegal state transition for sslMode, attempt to change from "
<< sslModeStr() << " to " << str);
}
return Status::OK();
}
} sslModeSetting;
class ClusterAuthModeSetting : public ServerParameter {
public:
ClusterAuthModeSetting() :
ServerParameter(ServerParameterSet::getGlobal(), "clusterAuthMode",
false, // allowedToChangeAtStartup
true // allowedToChangeAtRuntime
) {}
std::string clusterAuthModeStr() {
switch (serverGlobalParams.clusterAuthMode.load()) {
case ServerGlobalParams::ClusterAuthMode_keyFile:
return "keyFile";
case ServerGlobalParams::ClusterAuthMode_sendKeyFile:
return "sendKeyFile";
case ServerGlobalParams::ClusterAuthMode_sendX509:
return "sendX509";
case ServerGlobalParams::ClusterAuthMode_x509:
return "x509";
default:
return "undefined";
}
}
virtual void append(
OperationContext* txn, BSONObjBuilder& b, const std::string& name) {
b << name << clusterAuthModeStr();
}
virtual Status set(const BSONElement& newValueElement) {
try {
return setFromString(newValueElement.String());
}
catch (MsgAssertionException msg) {
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Invalid value for clusterAuthMode via setParameter command: "
<< newValueElement);
}
}
virtual Status setFromString(const std::string& str) {
#ifndef MONGO_CONFIG_SSL
return Status(ErrorCodes::IllegalOperation, mongoutils::str::stream() <<
"Unable to set clusterAuthMode, " <<
"SSL support is not compiled into server");
#endif
if (str != "keyFile" && str != "sendKeyFile" &&
str != "sendX509" && str != "x509") {
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Invalid value for clusterAuthMode via setParameter command: "
<< str);
}
int oldMode = serverGlobalParams.clusterAuthMode.load();
int sslMode = sslGlobalParams.sslMode.load();
if (str == "sendX509" &&
oldMode == ServerGlobalParams::ClusterAuthMode_sendKeyFile) {
if (sslMode == SSLParams::SSLMode_disabled ||
sslMode == SSLParams::SSLMode_allowSSL) {
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Illegal state transition for clusterAuthMode, " <<
"need to enable SSL for outgoing connections");
}
serverGlobalParams.clusterAuthMode.store
(ServerGlobalParams::ClusterAuthMode_sendX509);
#ifdef MONGO_CONFIG_SSL
setInternalUserAuthParams(BSON(saslCommandMechanismFieldName <<
"MONGODB-X509" <<
saslCommandUserDBFieldName << "$external" <<
saslCommandUserFieldName <<
getSSLManager()->getSSLConfiguration()
.clientSubjectName));
#endif
}
else if (str == "x509" &&
oldMode == ServerGlobalParams::ClusterAuthMode_sendX509) {
serverGlobalParams.clusterAuthMode.store
(ServerGlobalParams::ClusterAuthMode_x509);
}
else {
return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
"Illegal state transition for clusterAuthMode, change from "
<< clusterAuthModeStr() << " to " << str);
}
return Status::OK();
}
} clusterAuthModeSetting;
ExportedServerParameter QuietSetting( ServerParameterSet::getGlobal(),
"quiet",
&serverGlobalParams.quiet,
true,
true );
ExportedServerParameter MaxConsecutiveFailedChecksSetting(
ServerParameterSet::getGlobal(),
"replMonitorMaxFailedChecks",
&ReplicaSetMonitor::maxConsecutiveFailedChecks,
false, // allowedToChangeAtStartup
true); // allowedToChangeAtRuntime
ExportedServerParameter TraceExceptionsSetting(ServerParameterSet::getGlobal(),
"traceExceptions",
&DBException::traceExceptions,
false, // allowedToChangeAtStartup
true); // allowedToChangeAtRuntime
}
}