1 2 /** 3 * Copyright (C) 2018-present MongoDB, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the Server Side Public License, version 1, 7 * as published by MongoDB, Inc. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * Server Side Public License for more details. 13 * 14 * You should have received a copy of the Server Side Public License 15 * along with this program. If not, see 16 * <http://www.mongodb.com/licensing/server-side-public-license>. 17 * 18 * As a special exception, the copyright holders give permission to link the 19 * code of portions of this program with the OpenSSL library under certain 20 * conditions as described in each individual source file and distribute 21 * linked combinations including the program with the OpenSSL library. You 22 * must comply with the Server Side Public License in all respects for 23 * all of the code used other than as permitted herein. If you modify file(s) 24 * with this exception, you may extend this exception to your version of the 25 * file(s), but you are not obligated to do so. If you do not wish to do so, 26 * delete this exception statement from your version. If you delete this 27 * exception statement from all source files in the program, then also delete 28 * it in the license file. 29 */ 30 31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand 32 33 #include "mongo/platform/basic.h" 34 35 #include <string> 36 37 #include "mongo/bson/bsonelement.h" 38 #include "mongo/bson/bsonobj.h" 39 #include "mongo/bson/bsonobjbuilder.h" 40 #include "mongo/bson/util/bson_extract.h" 41 #include "mongo/client/connpool.h" 42 #include "mongo/client/dbclientinterface.h" 43 #include "mongo/db/audit.h" 44 #include "mongo/db/auth/authorization_session.h" 45 #include "mongo/db/commands.h" 46 #include "mongo/rpc/metadata.h" 47 #include "mongo/s/client/shard.h" 48 #include "mongo/s/client/shard_registry.h" 49 #include "mongo/s/grid.h" 50 #include "mongo/util/log.h" 51 #include "mongo/util/mongoutils/str.h" 52 53 namespace mongo { 54 namespace { 55 56 class ClusterKillOpCommand : public BasicCommand { 57 public: ClusterKillOpCommand()58 ClusterKillOpCommand() : BasicCommand("killOp") {} 59 60 supportsWriteConcern(const BSONObj & cmd) const61 virtual bool supportsWriteConcern(const BSONObj& cmd) const override { 62 return false; 63 } 64 slaveOk() const65 bool slaveOk() const final { 66 return true; 67 } 68 adminOnly() const69 bool adminOnly() const final { 70 return true; 71 } 72 checkAuthForCommand(Client * client,const std::string & dbname,const BSONObj & cmdObj)73 Status checkAuthForCommand(Client* client, 74 const std::string& dbname, 75 const BSONObj& cmdObj) final { 76 bool isAuthorized = AuthorizationSession::get(client)->isAuthorizedForActionsOnResource( 77 ResourcePattern::forClusterResource(), ActionType::killop); 78 return isAuthorized ? Status::OK() : Status(ErrorCodes::Unauthorized, "Unauthorized"); 79 } 80 run(OperationContext * opCtx,const std::string & db,const BSONObj & cmdObj,BSONObjBuilder & result)81 bool run(OperationContext* opCtx, 82 const std::string& db, 83 const BSONObj& cmdObj, 84 BSONObjBuilder& result) final { 85 // The format of op is shardid:opid 86 // This is different than the format passed to the mongod killOp command. 87 std::string opToKill; 88 uassertStatusOK(bsonExtractStringField(cmdObj, "op", &opToKill)); 89 90 const auto opSepPos = opToKill.find(':'); 91 92 uassert(28625, 93 str::stream() << "The op argument to killOp must be of the format shardid:opid" 94 << " but found \"" 95 << opToKill 96 << '"', 97 (opToKill.size() >= 3) && // must have at least N:N 98 (opSepPos != std::string::npos) && // must have ':' as separator 99 (opSepPos != 0) && // can't be :NN 100 (opSepPos != (opToKill.size() - 1))); // can't be NN: 101 102 auto shardIdent = opToKill.substr(0, opSepPos); 103 log() << "want to kill op: " << redact(opToKill); 104 105 // Will throw if shard id is not found 106 auto shardStatus = grid.shardRegistry()->getShard(opCtx, shardIdent); 107 if (!shardStatus.isOK()) { 108 return appendCommandStatus(result, shardStatus.getStatus()); 109 } 110 auto shard = shardStatus.getValue(); 111 112 int opId; 113 uassertStatusOK(parseNumberFromStringWithBase(opToKill.substr(opSepPos + 1), 10, &opId)); 114 115 // shardid is actually the opid - keeping for backwards compatibility. 116 result.append("shard", shardIdent); 117 result.append("shardid", opId); 118 119 ScopedDbConnection conn(shard->getConnString()); 120 // intentionally ignore return value - that is how legacy killOp worked. 121 conn->runCommand(OpMsgRequest::fromDBAndBody("admin", BSON("killOp" << 1 << "op" << opId))); 122 conn.done(); 123 124 // The original behavior of killOp on mongos is to always return success, regardless of 125 // whether the shard reported success or not. 126 return true; 127 } 128 129 } clusterKillOpCommand; 130 131 } // namespace 132 } // namespace mongo 133