#include "mongo/base/init.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_manager_global.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/auth/user.h"
#include "mongo/db/background.h"
#include "mongo/db/commands.h"
#include "mongo/db/db.h"
#include "mongo/db/service_context.h"
#include "mongo/db/instance.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/stats/snapshots.h"
#include "mongo/rpc/command_reply.h"
#include "mongo/rpc/command_reply_builder.h"
#include "mongo/rpc/command_request.h"
#include "mongo/rpc/command_request_builder.h"
#include "mongo/rpc/metadata.h"
#include "mongo/util/admin_access.h"
#include "mongo/util/md5.hpp"
#include "mongo/util/mongoutils/html.h"
#include "mongo/util/ramlog.h"
#include "mongo/util/version.h"
namespace mongo {
using std::map;
using std::stringstream;
using std::vector;
using namespace html;
namespace {
void doUnlockedStuff(stringstream& ss) {
// This is in the header already ss << "port: " << port << '\n'
ss << "";
ss << mongodVersion() << '\n';
ss << "git hash: " << gitVersion() << '\n';
ss << openSSLVersion("OpenSSL version: ", "\n");
ss << "uptime: " << time(0) - serverGlobalParams.started << " seconds\n";
ss << "
";
}
bool prisort(const Prioritizable* a, const Prioritizable* b) {
return a->priority() < b->priority();
}
struct Timing {
Timing() {
start = timeLocked = 0;
}
unsigned long long start, timeLocked;
};
class LogPlugin : public WebStatusPlugin {
public:
LogPlugin() : WebStatusPlugin("Log", 100), _log(0) {
_log = RamLog::get("global");
}
virtual void init() {}
virtual void run(OperationContext* txn, stringstream& ss) {
_log->toHTML(ss);
}
RamLog* _log;
};
class FavIconHandler : public DbWebHandler {
public:
FavIconHandler() : DbWebHandler("favicon.ico", 0, false) {}
virtual void handle(OperationContext* txn,
const char* rq,
const std::string& url,
BSONObj params,
string& responseMsg,
int& responseCode,
vector& headers,
const SockAddr& from) {
responseCode = 404;
headers.push_back("Content-Type: text/plain;charset=utf-8");
responseMsg = "no favicon\n";
}
} faviconHandler;
class StatusHandler : public DbWebHandler {
public:
StatusHandler() : DbWebHandler("_status", 1, false) {}
virtual void handle(OperationContext* txn,
const char* rq,
const std::string& url,
BSONObj params,
string& responseMsg,
int& responseCode,
vector& headers,
const SockAddr& from) {
headers.push_back("Content-Type: application/json;charset=utf-8");
responseCode = 200;
static vector commands;
if (commands.size() == 0) {
commands.push_back("serverStatus");
commands.push_back("buildinfo");
}
BSONObjBuilder buf(1024);
for (unsigned i = 0; i < commands.size(); i++) {
string cmd = commands[i];
Command* c = Command::findCommand(cmd);
verify(c);
BSONObj co;
{
BSONObjBuilder b;
b.append(cmd, 1);
if (cmd == "serverStatus" && params["repl"].type()) {
b.append("repl", atoi(params["repl"].valuestr()));
}
co = b.obj();
}
string errmsg;
BSONObjBuilder sub;
if (!c->run(txn, "admin.$cmd", co, 0, errmsg, sub))
buf.append(cmd, errmsg);
else
buf.append(cmd, sub.obj());
}
responseMsg = buf.obj().jsonString();
}
} statusHandler;
class CommandListHandler : public DbWebHandler {
public:
CommandListHandler() : DbWebHandler("_commands", 1, true) {}
virtual void handle(OperationContext* txn,
const char* rq,
const std::string& url,
BSONObj params,
string& responseMsg,
int& responseCode,
vector& headers,
const SockAddr& from) {
headers.push_back("Content-Type: text/html;charset=utf-8");
responseCode = 200;
stringstream ss;
ss << start("Commands List");
ss << p(a("/", "back", "Home"));
ss << p(
"MongoDB List of "
"Commands"
"\n");
const Command::CommandMap* m = Command::commandsByBestName();
ss << "S:slave-ok R:read-lock W:write-lock A:admin-only
\n";
ss << table();
ss << "Command | Attributes | Help |
\n";
for (Command::CommandMap::const_iterator i = m->begin(); i != m->end(); ++i) {
i->second->htmlHelp(ss);
}
ss << _table() << _end();
responseMsg = ss.str();
}
} commandListHandler;
class CommandsHandler : public DbWebHandler {
public:
CommandsHandler() : DbWebHandler("DUMMY COMMANDS", 2, true) {}
bool _cmd(const string& url, string& cmd, bool& text, bo params) const {
cmd = str::after(url, '/');
text = params["text"].boolean();
return true;
}
Command* _cmd(const string& cmd) const {
const Command::CommandMap* m = Command::webCommands();
if (!m)
return 0;
Command::CommandMap::const_iterator i = m->find(cmd);
if (i == m->end())
return 0;
return i->second;
}
virtual bool handles(const string& url) const {
string cmd;
bool text;
if (!_cmd(url, cmd, text, bo()))
return false;
return _cmd(cmd) != 0;
}
virtual void handle(OperationContext* txn,
const char* rq,
const std::string& url,
BSONObj params,
string& responseMsg,
int& responseCode,
vector& headers,
const SockAddr& from) {
string cmd;
bool text = false;
verify(_cmd(url, cmd, text, params));
Command* c = _cmd(cmd);
verify(c);
BSONObj cmdObj = BSON(cmd << 1);
rpc::CommandRequestBuilder requestBuilder{};
requestBuilder.setDatabase("admin")
.setCommandName(cmd)
.setMetadata(rpc::makeEmptyMetadata())
.setCommandArgs(cmdObj);
auto cmdRequestMsg = requestBuilder.done();
rpc::CommandRequest cmdRequest{&cmdRequestMsg};
rpc::CommandReplyBuilder cmdReplyBuilder{};
Command::execCommand(txn, c, cmdRequest, &cmdReplyBuilder);
auto cmdReplyMsg = cmdReplyBuilder.done();
rpc::CommandReply cmdReply{&cmdReplyMsg};
responseCode = 200;
string j = cmdReply.getCommandReply().jsonString(Strict, text);
responseMsg = j;
if (text) {
headers.push_back("Content-Type: text/plain;charset=utf-8");
responseMsg += '\n';
} else {
headers.push_back("Content-Type: application/json;charset=utf-8");
}
}
} commandsHandler;
MONGO_INITIALIZER(WebStatusLogPlugin)(InitializerContext*) {
if (serverGlobalParams.isHttpInterfaceEnabled) {
new LogPlugin;
}
return Status::OK();
}
} // namespace
DbWebServer::DbWebServer(const string& ip, int port, AdminAccess* webUsers)
: MiniWebServer("admin web console", ip, port), _webUsers(webUsers) {
WebStatusPlugin::initAll();
}
void DbWebServer::doRequest(const char* rq,
string url,
string& responseMsg,
int& responseCode,
vector& headers,
const SockAddr& from) {
Client* client = &cc();
auto txn = client->makeOperationContext();
if (url.size() > 1) {
if (!_allowed(txn.get(), rq, headers, from)) {
responseCode = 401;
headers.push_back("Content-Type: text/plain;charset=utf-8");
responseMsg = "not allowed\n";
return;
}
{
BSONObj params;
const size_t pos = url.find("?");
if (pos != string::npos) {
MiniWebServer::parseParams(params, url.substr(pos + 1));
url = url.substr(0, pos);
}
DbWebHandler* handler = DbWebHandler::findHandler(url);
if (handler) {
if (handler->requiresREST(url) && !serverGlobalParams.rest) {
_rejectREST(responseMsg, responseCode, headers);
} else {
const string callback = params.getStringField("jsonp");
uassert(13453,
"server not started with --jsonp",
callback.empty() || serverGlobalParams.jsonp);
handler->handle(
txn.get(), rq, url, params, responseMsg, responseCode, headers, from);
if (responseCode == 200 && !callback.empty()) {
responseMsg = callback + '(' + responseMsg + ')';
}
}
return;
}
}
if (!serverGlobalParams.rest) {
_rejectREST(responseMsg, responseCode, headers);
return;
}
responseCode = 404;
headers.push_back("Content-Type: text/html;charset=utf-8");
responseMsg = "unknown url\n";
return;
}
// generate home page
if (!_allowed(txn.get(), rq, headers, from)) {
responseCode = 401;
headers.push_back("Content-Type: text/plain;charset=utf-8");
responseMsg = "not allowed\n";
return;
}
responseCode = 200;
stringstream ss;
string dbname;
{
stringstream z;
z << serverGlobalParams.binaryName << ' ' << prettyHostName();
dbname = z.str();
}
ss << start(dbname) << h2(dbname);
ss << "List all commands | \n";
ss << "Replica set status
\n";
{
const Command::CommandMap* m = Command::webCommands();
if (m) {
ss << a("",
"These read-only context-less commands can be executed from the web "
"interface. Results are json format, unless ?text=1 is appended in which "
"case the result is output as text for easier human viewing",
"Commands") << ": ";
for (Command::CommandMap::const_iterator i = m->begin(); i != m->end(); ++i) {
stringstream h;
i->second->help(h);
const string help = h.str();
ss << "first << "?text=1\"";
if (help != "no help defined") {
ss << " title=\"" << help << '"';
}
ss << ">" << i->first << " ";
}
ss << '\n';
}
}
ss << '\n';
doUnlockedStuff(ss);
WebStatusPlugin::runAll(txn.get(), ss);
ss << "