// 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/json.h"
#include "mongo/bson/mutable/document.h"
#include "mongo/client/replica_set_monitor.h"
#include "mongo/config.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/internal_user_auth.h"
#include "mongo/db/command_generic_argument.h"
#include "mongo/db/commands.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/storage/storage_options.h"
#include "mongo/logger/logger.h"
#include "mongo/logger/parse_log_component_settings.h"
#include "mongo/util/mongoutils/str.h"
using std::string;
using std::stringstream;
namespace mongo {
namespace {
void appendParameterNames(std::string* help) {
*help += "supported:\n";
for (const auto& kv : ServerParameterSet::getGlobal()->getMap()) {
*help += " ";
*help += kv.first;
*help += '\n';
}
}
} // namespace
class CmdGet : public ErrmsgCommandDeprecated {
public:
CmdGet() : ErrmsgCommandDeprecated("getParameter") {}
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
return AllowedOnSecondary::kAlways;
}
virtual bool adminOnly() const {
return true;
}
virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
return false;
}
virtual void addRequiredPrivileges(const std::string& dbname,
const BSONObj& cmdObj,
std::vector* out) const {
ActionSet actions;
actions.addAction(ActionType::getParameter);
out->push_back(Privilege(ResourcePattern::forClusterResource(), actions));
}
std::string help() const override {
std::string h =
"get administrative option(s)\nexample:\n"
"{ getParameter:1, notablescan:1 }\n";
appendParameterNames(&h);
h += "{ getParameter:'*' } to get everything\n";
return h;
}
bool errmsgRun(OperationContext* opCtx,
const string& dbname,
const BSONObj& cmdObj,
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(opCtx, result, i->second->name());
}
}
if (before == result.len()) {
errmsg = "no option found to get";
return false;
}
return true;
}
} cmdGet;
class CmdSet : public ErrmsgCommandDeprecated {
public:
CmdSet() : ErrmsgCommandDeprecated("setParameter") {}
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
return AllowedOnSecondary::kAlways;
}
virtual bool adminOnly() const {
return true;
}
virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
return false;
}
virtual void addRequiredPrivileges(const std::string& dbname,
const BSONObj& cmdObj,
std::vector* out) const {
ActionSet actions;
actions.addAction(ActionType::setParameter);
out->push_back(Privilege(ResourcePattern::forClusterResource(), actions));
}
std::string help() const override {
std::string h =
"set administrative option(s)\n"
"{ setParameter:1, : }\n";
appendParameterNames(&h);
return h;
}
bool errmsgRun(OperationContext* opCtx,
const string& dbname,
const BSONObj& cmdObj,
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();
if (isGenericArgument(parameterName))
continue;
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(opCtx, result, "was");
}
uassertStatusOK(foundParameter->second->set(parameter));
numSet++;
}
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* opCtx, 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* opCtx, 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).transitional_ignore();
continue;
}
mutablebson::Element element = doc.makeElementObject(component.getShortName());
element.appendInt("verbosity", severity).transitional_ignore();
mutablebson::Element parentElement = _getParentElement(doc, component);
parentElement.pushBack(element).transitional_ignore();
}
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> 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;
ExportedServerParameter QuietSetting(
ServerParameterSet::getGlobal(), "quiet", &serverGlobalParams.quiet);
ExportedServerParameter TraceExceptionsSetting(
ServerParameterSet::getGlobal(), "traceExceptions", &DBException::traceExceptions);
class AutomationServiceDescriptor final : public ServerParameter {
public:
static constexpr auto kName = "automationServiceDescriptor"_sd;
static constexpr auto kMaxSize = 64U;
AutomationServiceDescriptor()
: ServerParameter(ServerParameterSet::getGlobal(), kName.toString(), true, true) {}
virtual void append(OperationContext* opCtx,
BSONObjBuilder& builder,
const std::string& name) override {
const stdx::lock_guard lock(_mutex);
if (!_value.empty())
builder << name << _value;
}
virtual Status set(const BSONElement& newValueElement) override {
if (newValueElement.type() != mongo::String)
return {ErrorCodes::TypeMismatch,
mongoutils::str::stream() << "Value for parameter " << kName
<< " must be of type 'string'"};
return setFromString(newValueElement.String());
}
virtual Status setFromString(const std::string& str) override {
if (str.size() > kMaxSize)
return {ErrorCodes::Overflow,
mongoutils::str::stream() << "Value for parameter " << kName
<< " must be no more than "
<< kMaxSize
<< " bytes"};
{
const stdx::lock_guard lock(_mutex);
_value = str;
}
return Status::OK();
}
private:
stdx::mutex _mutex;
std::string _value;
} automationServiceDescriptor;
constexpr decltype(AutomationServiceDescriptor::kName) AutomationServiceDescriptor::kName;
constexpr decltype(AutomationServiceDescriptor::kMaxSize) AutomationServiceDescriptor::kMaxSize;
} // namespace
} // namespace mongo