bool run(OperationContext* txn, const std::string& db, BSONObj& cmdObj, int options, std::string& errmsg, BSONObjBuilder& result) final { const bool includeAll = cmdObj["$all"].trueValue(); const bool ownOpsOnly = cmdObj["$ownOps"].trueValue(); // Filter the output BSONObj filter; { BSONObjBuilder b; BSONObjIterator i(cmdObj); invariant(i.more()); i.next(); // skip {currentOp: 1} which is required to be the first element while (i.more()) { BSONElement e = i.next(); if (str::equals("$all", e.fieldName())) { continue; } else if (str::equals("$ownOps", e.fieldName())) { continue; } b.append(e); } filter = b.obj(); } std::vector<BSONObj> inprogInfos; BSONArrayBuilder inprogBuilder(result.subarrayStart("inprog")); for (ServiceContext::LockedClientsCursor cursor(txn->getClient()->getServiceContext()); Client* client = cursor.next();) { invariant(client); stdx::lock_guard<Client> lk(*client); if (ownOpsOnly && !AuthorizationSession::get(txn->getClient())->isCoauthorizedWithClient(client)) { continue; } const OperationContext* opCtx = client->getOperationContext(); if (!includeAll) { // Skip over inactive connections. if (!opCtx) continue; } BSONObjBuilder infoBuilder; // The client information client->reportState(infoBuilder); const auto& clientMetadata = ClientMetadataIsMasterState::get(client).getClientMetadata(); if (clientMetadata) { auto appName = clientMetadata.get().getApplicationName(); if (!appName.empty()) { infoBuilder.append("appName", appName); } } // Operation context specific information infoBuilder.appendBool("active", static_cast<bool>(opCtx)); if (opCtx) { infoBuilder.append("opid", opCtx->getOpID()); if (opCtx->isKillPending()) { infoBuilder.append("killPending", true); } CurOp::get(opCtx)->reportState(&infoBuilder); // LockState Locker::LockerInfo lockerInfo; opCtx->lockState()->getLockerInfo(&lockerInfo); fillLockerInfo(lockerInfo, infoBuilder); } // If we want to include all results or if the filter is empty, then we can append // straight to the inprogBuilder, but otherwise we should run the filter Matcher // outside this loop so we don't lock the ServiceContext while matching - in some cases // this can cause deadlocks. if (includeAll || filter.isEmpty()) { inprogBuilder.append(infoBuilder.obj()); } else { inprogInfos.emplace_back(infoBuilder.obj()); } } if (!inprogInfos.empty()) { // We use ExtensionsCallbackReal here instead of ExtensionsCallbackNoop in order to // support the use case of having a $where filter with currentOp. However, since we // don't have a collection, we pass in a fake collection name (and this is okay, // because $where parsing only relies on the database part of the namespace). const NamespaceString fakeNS(db, "$dummyNamespaceForCurrop"); const Matcher matcher(filter, ExtensionsCallbackReal(txn, &fakeNS), nullptr); for (const auto& info : inprogInfos) { if (matcher.matches(info)) { inprogBuilder.append(info); } } } inprogBuilder.done(); if (lockedForWriting()) { result.append("fsyncLock", true); result.append("info", "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } return true; }
bool run(OperationContext* txn, const std::string& db, BSONObj& cmdObj, int options, std::string& errmsg, BSONObjBuilder& result) final { const bool includeAll = cmdObj["$all"].trueValue(); const bool ownOpsOnly = cmdObj["$ownOps"].trueValue(); // Filter the output BSONObj filter; { BSONObjBuilder b; BSONObjIterator i(cmdObj); invariant(i.more()); i.next(); // skip {currentOp: 1} which is required to be the first element while (i.more()) { BSONElement e = i.next(); if (str::equals("$all", e.fieldName())) { continue; } else if (str::equals("$ownOps", e.fieldName())) { continue; } b.append(e); } filter = b.obj(); } // We use ExtensionsCallbackReal here instead of ExtensionsCallbackNoop in order to support // the use case of having a $where filter with currentOp. However, since we don't have a // collection, we pass in a fake collection name (and this is okay, because $where parsing // only relies on the database part of the namespace). const NamespaceString fakeNS(db, "$cmd"); const CollatorInterface* collator = nullptr; const Matcher matcher(filter, ExtensionsCallbackReal(txn, &fakeNS), collator); BSONArrayBuilder inprogBuilder(result.subarrayStart("inprog")); for (ServiceContext::LockedClientsCursor cursor(txn->getClient()->getServiceContext()); Client* client = cursor.next();) { invariant(client); stdx::lock_guard<Client> lk(*client); if (ownOpsOnly && !AuthorizationSession::get(txn->getClient())->isCoauthorizedWithClient(client)) { continue; } const OperationContext* opCtx = client->getOperationContext(); if (!includeAll) { // Skip over inactive connections. if (!opCtx) continue; } BSONObjBuilder infoBuilder; // The client information client->reportState(infoBuilder); // Operation context specific information infoBuilder.appendBool("active", static_cast<bool>(opCtx)); if (opCtx) { infoBuilder.append("opid", opCtx->getOpID()); if (opCtx->isKillPending()) { infoBuilder.append("killPending", true); } CurOp::get(opCtx)->reportState(&infoBuilder); // LockState Locker::LockerInfo lockerInfo; opCtx->lockState()->getLockerInfo(&lockerInfo); fillLockerInfo(lockerInfo, infoBuilder); } infoBuilder.done(); const BSONObj info = infoBuilder.obj(); if (includeAll || matcher.matches(info)) { inprogBuilder.append(info); } } inprogBuilder.done(); if (lockedForWriting()) { result.append("fsyncLock", true); result.append("info", "use db.fsyncUnlock() to terminate the fsync write/snapshot lock"); } return true; }