/** * Copyright (C) 2009 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 #include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/curop.h" #include "mongo/db/dbwebserver.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/operation_context.h" #include "mongo/db/service_context.h" #include "mongo/db/stats/fill_locker_info.h" #include "mongo/util/mongoutils/html.h" #include "mongo/util/stringutils.h" namespace mongo { using boost::scoped_ptr; using std::string; namespace { class ClientListPlugin : public WebStatusPlugin { public: ClientListPlugin() : WebStatusPlugin( "clients" , 20 ) {} virtual void init() {} virtual void run(OperationContext* txn, std::stringstream& ss ) { using namespace html; ss << "\n"; ss << "" << th( a("", "Connections to the database, both internal and external.", "Client") ) << th( a("http://dochub.mongodb.org/core/viewingandterminatingcurrentoperation", "", "OpId") ) << "" << "" << "" << "" << th( a("http://dochub.mongodb.org/core/whatisanamespace", "", "Namespace") ) << "" << "" << "" << "" << "\n"; _processAllClients(txn->getClient()->getServiceContext(), ss); ss << "
LockingWaitingSecsRunningOpQueryclientmsgprogress
\n"; } private: static void _processAllClients(ServiceContext* service, std::stringstream& ss) { using namespace html; for (ServiceContext::LockedClientsCursor cursor(service); Client* client = cursor.next();) { invariant(client); // Make the client stable boost::unique_lock clientLock(*client); const OperationContext* txn = client->getOperationContext(); if (!txn) continue; CurOp* curOp = CurOp::get(txn); if (!curOp) continue; ss << "" << client->desc() << ""; tablecell(ss, curOp->opNum()); tablecell(ss, curOp->active()); // LockState { Locker::LockerInfo lockerInfo; txn->lockState()->getLockerInfo(&lockerInfo); BSONObjBuilder lockerInfoBuilder; fillLockerInfo(lockerInfo, lockerInfoBuilder); tablecell(ss, lockerInfoBuilder.obj()); } if (curOp->active()) { tablecell(ss, curOp->elapsedSeconds()); } else { tablecell(ss, ""); } tablecell(ss, curOp->getOp()); tablecell(ss, html::escape(curOp->getNS())); if (curOp->haveQuery()) { tablecell(ss, html::escape(curOp->query().toString())); } else { tablecell(ss, ""); } tablecell(ss, client->clientAddress(true /*includePort*/)); tablecell(ss, curOp->getMessage()); tablecell(ss, curOp->getProgressMeter().toString()); ss << "\n"; } } } clientListPlugin; class CurrentOpContexts : public Command { public: CurrentOpContexts() : Command( "currentOpCtx" ) { } virtual bool isWriteCommandForConfigServer() const { return false; } virtual bool slaveOk() const { return true; } virtual Status checkAuthForCommand(ClientBasic* client, const std::string& dbname, const BSONObj& cmdObj) { if ( AuthorizationSession::get(client) ->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), ActionType::inprog) ) { return Status::OK(); } return Status(ErrorCodes::Unauthorized, "unauthorized"); } bool run( OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result) { scoped_ptr filter; if ( cmdObj["filter"].isABSONObj() ) { StatusWithMatchExpression res = MatchExpressionParser::parse( cmdObj["filter"].Obj() ); if ( !res.isOK() ) { return appendCommandStatus( result, res.getStatus() ); } filter.reset( res.getValue() ); } result.appendArray( "operations", _processAllClients(txn->getClient()->getServiceContext(), filter.get())); return true; } private: static BSONArray _processAllClients(ServiceContext* service, MatchExpression* matcher) { BSONArrayBuilder array; for (ServiceContext::LockedClientsCursor cursor(service); Client* client = cursor.next();) { invariant(client); BSONObjBuilder b; // Make the client stable boost::unique_lock clientLock(*client); client->reportState(b); const OperationContext* txn = client->getOperationContext(); if (txn) { // CurOp if (CurOp::get(txn)) { CurOp::get(txn)->reportState(&b); } // LockState if (txn->lockState()) { StringBuilder ss; ss << txn->lockState(); b.append("lockStatePointer", ss.str()); Locker::LockerInfo lockerInfo; txn->lockState()->getLockerInfo(&lockerInfo); BSONObjBuilder lockerInfoBuilder; fillLockerInfo(lockerInfo, lockerInfoBuilder); b.append("lockState", lockerInfoBuilder.obj()); } // RecoveryUnit if (txn->recoveryUnit()) { txn->recoveryUnit()->reportState(&b); } } const BSONObj obj = b.obj(); if (!matcher || matcher->matchesBSON(obj)) { array.append(obj); } } return array.arr(); } } currentOpContexts; } // namespace } // namespace mongo