diff options
-rw-r--r-- | src/mongo/SConscript | 6 | ||||
-rw-r--r-- | src/mongo/db/cloner.cpp | 318 | ||||
-rw-r--r-- | src/mongo/db/commands/clone.cpp | 137 | ||||
-rw-r--r-- | src/mongo/db/commands/clone_collection.cpp | 147 | ||||
-rw-r--r-- | src/mongo/db/commands/copydb.cpp | 198 | ||||
-rw-r--r-- | src/mongo/db/commands/copydb_getnonce.cpp | 133 | ||||
-rw-r--r-- | src/mongo/db/commands/copydb_getnonce.h | 37 | ||||
-rw-r--r-- | src/mongo/db/curop.h | 2 |
8 files changed, 656 insertions, 322 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index aaefb398725..66d0d77762a 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -643,6 +643,10 @@ serverOnlyFiles = [ "db/curop.cpp", # most commands are only for mongod "db/stats/top.cpp", "db/commands/apply_ops.cpp", + "db/commands/clone_collection.cpp", + "db/commands/clone.cpp", + "db/commands/copydb.cpp", + "db/commands/copydb_getnonce.cpp", "db/commands/compact.cpp", "db/commands/auth_schema_upgrade_d.cpp", "db/commands/create_indexes.cpp", @@ -659,14 +663,12 @@ serverOnlyFiles = [ "db/curop.cpp", "db/commands/find_and_modify.cpp", "db/commands/group.cpp", "db/commands/index_filter_commands.cpp", - "db/commands/index_stats.cpp", "db/commands/mr.cpp", "db/commands/oplog_note.cpp", "db/commands/pipeline_command.cpp", "db/commands/parallel_collection_scan.cpp", "db/commands/plan_cache_commands.cpp", "db/commands/rename_collection.cpp", - "db/commands/storage_details.cpp", "db/commands/test_commands.cpp", "db/commands/validate.cpp", "db/pipeline/pipeline_d.cpp", diff --git a/src/mongo/db/cloner.cpp b/src/mongo/db/cloner.cpp index 8c0d075cc7d..99a7d31a766 100644 --- a/src/mongo/db/cloner.cpp +++ b/src/mongo/db/cloner.cpp @@ -545,322 +545,4 @@ namespace mongo { errCode); } - /* Usage: - mydb.$cmd.findOne( { clone: "fromhost" } ); - Note: doesn't work with authentication enabled, except as internal operation or for - old-style users for backwards compatibility. - */ - class CmdClone : public Command { - public: - virtual bool slaveOk() const { - return false; - } - virtual bool isWriteCommandForConfigServer() const { return true; } - virtual void help( stringstream &help ) const { - help << "clone this database from an instance of the db on another host\n"; - help << "{ clone : \"host13\" }"; - } - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - ActionSet actions; - actions.addAction(ActionType::insert); - actions.addAction(ActionType::createIndex); - if (!client->getAuthorizationSession()->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(dbname), actions)) { - return Status(ErrorCodes::Unauthorized, "Unauthorized"); - } - return Status::OK(); - } - CmdClone() : Command("clone") { } - virtual bool run(OperationContext* txn, const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { - string from = cmdObj.getStringField("clone"); - if ( from.empty() ) - return false; - - CloneOptions opts; - opts.fromDB = dbname; - opts.logForRepl = ! fromRepl; - - // See if there's any collections we should ignore - if( cmdObj["collsToIgnore"].type() == Array ){ - BSONObjIterator it( cmdObj["collsToIgnore"].Obj() ); - - while( it.more() ){ - BSONElement e = it.next(); - if( e.type() == String ){ - opts.collsToIgnore.insert( e.String() ); - } - } - } - - set<string> clonedColls; - - Lock::DBWrite dbXLock(dbname); - Client::Context context( dbname ); - - Cloner cloner; - bool rval = cloner.go(txn, context, from, opts, &clonedColls, errmsg); - - BSONArrayBuilder barr; - barr.append( clonedColls ); - - result.append( "clonedColls", barr.arr() ); - - return rval; - - } - } cmdClone; - - class CmdCloneCollection : public Command { - public: - virtual bool slaveOk() const { - return false; - } - virtual bool isWriteCommandForConfigServer() const { return false; } - CmdCloneCollection() : Command("cloneCollection") { } - - virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { - return parseNsFullyQualified(dbname, cmdObj); - } - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - std::string ns = parseNs(dbname, cmdObj); - - ActionSet actions; - actions.addAction(ActionType::insert); - actions.addAction(ActionType::createIndex); // SERVER-11418 - - if (!client->getAuthorizationSession()->isAuthorizedForActionsOnResource( - ResourcePattern::forExactNamespace(NamespaceString(ns)), actions)) { - return Status(ErrorCodes::Unauthorized, "Unauthorized"); - } - return Status::OK(); - } - virtual void help( stringstream &help ) const { - help << "{ cloneCollection: <collection>, from: <host> [,query: <query_filter>] [,copyIndexes:<bool>] }" - "\nCopies a collection from one server to another. Do not use on a single server as the destination " - "is placed at the same db.collection (namespace) as the source.\n" - ; - } - virtual bool run(OperationContext* txn, const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { - string fromhost = cmdObj.getStringField("from"); - if ( fromhost.empty() ) { - errmsg = "missing 'from' parameter"; - return false; - } - { - HostAndPort h(fromhost); - if( h.isSelf() ) { - errmsg = "can't cloneCollection from self"; - return false; - } - } - string collection = parseNs(dbname, cmdObj); - if ( collection.empty() ) { - errmsg = "bad 'cloneCollection' value"; - return false; - } - BSONObj query = cmdObj.getObjectField("query"); - if ( query.isEmpty() ) - query = BSONObj(); - - BSONElement copyIndexesSpec = cmdObj.getField("copyindexes"); - bool copyIndexes = copyIndexesSpec.isBoolean() ? copyIndexesSpec.boolean() : true; - - log() << "cloneCollection. db:" << dbname << " collection:" << collection << " from: " << fromhost - << " query: " << query << " " << ( copyIndexes ? "" : ", not copying indexes" ) << endl; - - Cloner cloner; - auto_ptr<DBClientConnection> myconn; - myconn.reset( new DBClientConnection() ); - if ( ! myconn->connect( fromhost , errmsg ) ) - return false; - - cloner.setConnection( myconn.release() ); - - return cloner.copyCollection(txn, collection, query, errmsg, true, false, copyIndexes); - } - } cmdCloneCollection; - - - // SERVER-4328 todo review for concurrency - thread_specific_ptr< DBClientBase > authConn_; - /* Usage: - * admindb.$cmd.findOne( { copydbgetnonce: 1, fromhost: <connection string> } ); - * - * Run against the mongod that is the intended target for the "copydb" command. Used to get a - * nonce from the source of a "copydb" operation for authentication purposes. See the - * description of the "copydb" command below. - */ - class CmdCopyDbGetNonce : public Command { - public: - CmdCopyDbGetNonce() : Command("copydbgetnonce") { } - virtual bool adminOnly() const { - return true; - } - virtual bool slaveOk() const { - return false; - } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) {} // No auth required - virtual void help( stringstream &help ) const { - help << "get a nonce for subsequent copy db request from secure server\n"; - help << "usage: {copydbgetnonce: 1, fromhost: <hostname>}"; - } - virtual bool run(OperationContext* txn, const string& , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { - string fromhost = cmdObj.getStringField("fromhost"); - if ( fromhost.empty() ) { - /* copy from self */ - stringstream ss; - ss << "localhost:" << serverGlobalParams.port; - fromhost = ss.str(); - } - BSONObj ret; - ConnectionString cs = ConnectionString::parse(fromhost, errmsg); - if (!cs.isValid()) { - return false; - } - authConn_.reset(cs.connect(errmsg)); - if (!authConn_.get()) { - return false; - } - if( !authConn_->runCommand( "admin", BSON( "getnonce" << 1 ), ret ) ) { - errmsg = "couldn't get nonce " + ret.toString(); - return false; - } - result.appendElements( ret ); - return true; - } - } cmdCopyDBGetNonce; - - /* Usage: - * admindb.$cmd.findOne( { copydb: 1, fromhost: <connection string>, fromdb: <db>, - * todb: <db>[, username: <username>, nonce: <nonce>, key: <key>] } ); - * - * The "copydb" command is used to copy a database. Note that this is a very broad definition. - * This means that the "copydb" command can be used in the following ways: - * - * 1. To copy a database within a single node - * 2. To copy a database within a sharded cluster, possibly to another shard - * 3. To copy a database from one cluster to another - * - * Note that in all cases both the target and source database must be unsharded. - * - * The "copydb" command gets sent by the client or the mongos to the destination of the copy - * operation. The node, cluster, or shard that recieves the "copydb" command must then query - * the source of the database to be copied for all the contents and metadata of the database. - * - * - * - * When used with auth, there are two different considerations. - * - * The first is authentication with the target. The only entity that needs to authenticate with - * the target node is the client, so authentication works there the same as it would with any - * other command. - * - * The second is the authentication of the target with the source, which is needed because the - * target must query the source directly for the contents of the database. To do this, the - * client must use the "copydbgetnonce" command, in which the target will get a nonce from the - * source and send it back to the client. The client can then hash its password with the nonce, - * send it to the target when it runs the "copydb" command, which can then use that information - * to authenticate with the source. - * - * NOTE: mongos doesn't know how to call or handle the "copydbgetnonce" command. See - * SERVER-6427. - * - * NOTE: Since internal cluster auth works differently, "copydb" currently doesn't work between - * shards in a cluster when auth is enabled. See SERVER-13080. - */ - class CmdCopyDb : public Command { - public: - CmdCopyDb() : Command("copydb") { } - virtual bool adminOnly() const { - return true; - } - virtual bool slaveOk() const { - return false; - } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - return copydb::checkAuthForCopydbCommand(client, dbname, cmdObj); - } - virtual void help( stringstream &help ) const { - help << "copy a database from another host to this host\n"; - help << "usage: {copydb: 1, fromhost: <connection string>, fromdb: <db>, todb: <db>" - << "[, slaveOk: <bool>, username: <username>, nonce: <nonce>, key: <key>]}"; - } - virtual bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { - string fromhost = cmdObj.getStringField("fromhost"); - bool fromSelf = fromhost.empty(); - if ( fromSelf ) { - /* copy from self */ - stringstream ss; - ss << "localhost:" << serverGlobalParams.port; - fromhost = ss.str(); - } - - CloneOptions cloneOptions; - cloneOptions.fromDB = cmdObj.getStringField("fromdb"); - cloneOptions.logForRepl = !fromRepl; - cloneOptions.slaveOk = cmdObj["slaveOk"].trueValue(); - cloneOptions.useReplAuth = false; - cloneOptions.snapshot = true; - cloneOptions.mayYield = true; - cloneOptions.mayBeInterrupted = false; - - string todb = cmdObj.getStringField("todb"); - if ( fromhost.empty() || todb.empty() || cloneOptions.fromDB.empty() ) { - errmsg = "parms missing - {copydb: 1, fromhost: <connection string>, " - "fromdb: <db>, todb: <db>}"; - return false; - } - - // SERVER-4328 todo lock just the two db's not everything for the fromself case - scoped_ptr<Lock::ScopedLock> lk( fromSelf ? - static_cast<Lock::ScopedLock*>( new Lock::GlobalWrite() ) : - static_cast<Lock::ScopedLock*>( new Lock::DBWrite( todb ) ) ); - - - Cloner cloner; - string username = cmdObj.getStringField( "username" ); - string nonce = cmdObj.getStringField( "nonce" ); - string key = cmdObj.getStringField( "key" ); - if ( !username.empty() && !nonce.empty() && !key.empty() ) { - uassert( 13008, "must call copydbgetnonce first", authConn_.get() ); - BSONObj ret; - { - dbtemprelease t; - if ( !authConn_->runCommand( cloneOptions.fromDB, - BSON( "authenticate" << 1 << "user" << username - << "nonce" << nonce << "key" << key ), ret ) ) { - errmsg = "unable to login " + ret.toString(); - return false; - } - } - cloner.setConnection( authConn_.release() ); - } - else if (!fromSelf) { - // If fromSelf leave the cloner's conn empty, it will use a DBDirectClient instead. - - ConnectionString cs = ConnectionString::parse(fromhost, errmsg); - if (!cs.isValid()) { - return false; - } - DBClientBase* conn = cs.connect(errmsg); - if (!conn) { - return false; - } - cloner.setConnection(conn); - } - Client::Context ctx(todb); - return cloner.go(txn, ctx, fromhost, cloneOptions, NULL, errmsg ); - } - } cmdCopyDB; - } // namespace mongo diff --git a/src/mongo/db/commands/clone.cpp b/src/mongo/db/commands/clone.cpp new file mode 100644 index 00000000000..b75ee5ceb69 --- /dev/null +++ b/src/mongo/db/commands/clone.cpp @@ -0,0 +1,137 @@ +/** +* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. +* +* 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/pch.h" + +#include "mongo/base/init.h" +#include "mongo/base/status.h" +#include "mongo/bson/util/builder.h" +#include "mongo/client/dbclientinterface.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection.h" +#include "mongo/db/cloner.h" +#include "mongo/db/commands.h" +#include "mongo/db/commands/copydb.h" +#include "mongo/db/commands/rename_collection.h" +#include "mongo/db/db.h" +#include "mongo/db/dbhelpers.h" +#include "mongo/db/index_builder.h" +#include "mongo/db/instance.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/kill_current_op.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/oplogreader.h" +#include "mongo/db/pdfile.h" +#include "mongo/db/operation_context_impl.h" +#include "mongo/db/storage_options.h" + +namespace mongo { + + /* Usage: + mydb.$cmd.findOne( { clone: "fromhost" } ); + Note: doesn't work with authentication enabled, except as internal operation or for + old-style users for backwards compatibility. + */ + class CmdClone : public Command { + public: + CmdClone() : Command("clone") { } + + virtual bool slaveOk() const { + return false; + } + + virtual bool isWriteCommandForConfigServer() const { return true; } + + virtual void help( stringstream &help ) const { + help << "clone this database from an instance of the db on another host\n"; + help << "{ clone : \"host13\" }"; + } + + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + ActionSet actions; + actions.addAction(ActionType::insert); + actions.addAction(ActionType::createIndex); + if (!client->getAuthorizationSession()->isAuthorizedForActionsOnResource( + ResourcePattern::forDatabaseName(dbname), actions)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + return Status::OK(); + } + + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + + string from = cmdObj.getStringField("clone"); + if ( from.empty() ) + return false; + + CloneOptions opts; + opts.fromDB = dbname; + opts.logForRepl = ! fromRepl; + + // See if there's any collections we should ignore + if( cmdObj["collsToIgnore"].type() == Array ){ + BSONObjIterator it( cmdObj["collsToIgnore"].Obj() ); + + while( it.more() ){ + BSONElement e = it.next(); + if( e.type() == String ){ + opts.collsToIgnore.insert( e.String() ); + } + } + } + + set<string> clonedColls; + + Lock::DBWrite dbXLock(dbname); + Client::Context context( dbname ); + + Cloner cloner; + bool rval = cloner.go(txn, context, from, opts, &clonedColls, errmsg); + + BSONArrayBuilder barr; + barr.append( clonedColls ); + + result.append( "clonedColls", barr.arr() ); + + return rval; + + } + } cmdClone; + +} // namespace mongo diff --git a/src/mongo/db/commands/clone_collection.cpp b/src/mongo/db/commands/clone_collection.cpp new file mode 100644 index 00000000000..c0a124adbe3 --- /dev/null +++ b/src/mongo/db/commands/clone_collection.cpp @@ -0,0 +1,147 @@ +/** +* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. +* +* 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/pch.h" + +#include "mongo/base/init.h" +#include "mongo/base/status.h" +#include "mongo/bson/util/builder.h" +#include "mongo/client/dbclientinterface.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection.h" +#include "mongo/db/cloner.h" +#include "mongo/db/commands.h" +#include "mongo/db/commands/copydb.h" +#include "mongo/db/commands/rename_collection.h" +#include "mongo/db/db.h" +#include "mongo/db/dbhelpers.h" +#include "mongo/db/index_builder.h" +#include "mongo/db/instance.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/kill_current_op.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/oplogreader.h" +#include "mongo/db/pdfile.h" +#include "mongo/db/operation_context_impl.h" +#include "mongo/db/storage_options.h" + +namespace mongo { + + class CmdCloneCollection : public Command { + public: + CmdCloneCollection() : Command("cloneCollection") { } + + virtual bool slaveOk() const { + return false; + } + + virtual bool isWriteCommandForConfigServer() const { + return false; + } + + virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { + return parseNsFullyQualified(dbname, cmdObj); + } + + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + std::string ns = parseNs(dbname, cmdObj); + + ActionSet actions; + actions.addAction(ActionType::insert); + actions.addAction(ActionType::createIndex); // SERVER-11418 + + if (!client->getAuthorizationSession()->isAuthorizedForActionsOnResource( + ResourcePattern::forExactNamespace(NamespaceString(ns)), actions)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + return Status::OK(); + } + + virtual void help( stringstream &help ) const { + help << "{ cloneCollection: <collection>, from: <host> [,query: <query_filter>] [,copyIndexes:<bool>] }" + "\nCopies a collection from one server to another. Do not use on a single server as the destination " + "is placed at the same db.collection (namespace) as the source.\n" + ; + } + + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + + string fromhost = cmdObj.getStringField("from"); + if ( fromhost.empty() ) { + errmsg = "missing 'from' parameter"; + return false; + } + + { + HostAndPort h(fromhost); + if( h.isSelf() ) { + errmsg = "can't cloneCollection from self"; + return false; + } + } + + string collection = parseNs(dbname, cmdObj); + if ( collection.empty() ) { + errmsg = "bad 'cloneCollection' value"; + return false; + } + BSONObj query = cmdObj.getObjectField("query"); + if ( query.isEmpty() ) + query = BSONObj(); + + BSONElement copyIndexesSpec = cmdObj.getField("copyindexes"); + bool copyIndexes = copyIndexesSpec.isBoolean() ? copyIndexesSpec.boolean() : true; + + log() << "cloneCollection. db:" << dbname << " collection:" << collection << " from: " << fromhost + << " query: " << query << " " << ( copyIndexes ? "" : ", not copying indexes" ) << endl; + + Cloner cloner; + auto_ptr<DBClientConnection> myconn; + myconn.reset( new DBClientConnection() ); + if ( ! myconn->connect( fromhost , errmsg ) ) + return false; + + cloner.setConnection( myconn.release() ); + + return cloner.copyCollection(txn, collection, query, errmsg, true, false, copyIndexes); + } + + } cmdCloneCollection; + +} // namespace mongo diff --git a/src/mongo/db/commands/copydb.cpp b/src/mongo/db/commands/copydb.cpp new file mode 100644 index 00000000000..0db7a7950bb --- /dev/null +++ b/src/mongo/db/commands/copydb.cpp @@ -0,0 +1,198 @@ +/** +* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. +* +* 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/pch.h" + +#include "mongo/base/init.h" +#include "mongo/base/status.h" +#include "mongo/bson/util/builder.h" +#include "mongo/client/dbclientinterface.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection.h" +#include "mongo/db/cloner.h" +#include "mongo/db/commands.h" +#include "mongo/db/commands/copydb.h" +#include "mongo/db/commands/copydb_getnonce.h" +#include "mongo/db/commands/rename_collection.h" +#include "mongo/db/db.h" +#include "mongo/db/dbhelpers.h" +#include "mongo/db/index_builder.h" +#include "mongo/db/instance.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/kill_current_op.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/oplogreader.h" +#include "mongo/db/pdfile.h" +#include "mongo/db/operation_context_impl.h" +#include "mongo/db/storage_options.h" + +namespace mongo { + + /* Usage: + * admindb.$cmd.findOne( { copydb: 1, fromhost: <connection string>, fromdb: <db>, + * todb: <db>[, username: <username>, nonce: <nonce>, key: <key>] } ); + * + * The "copydb" command is used to copy a database. Note that this is a very broad definition. + * This means that the "copydb" command can be used in the following ways: + * + * 1. To copy a database within a single node + * 2. To copy a database within a sharded cluster, possibly to another shard + * 3. To copy a database from one cluster to another + * + * Note that in all cases both the target and source database must be unsharded. + * + * The "copydb" command gets sent by the client or the mongos to the destination of the copy + * operation. The node, cluster, or shard that recieves the "copydb" command must then query + * the source of the database to be copied for all the contents and metadata of the database. + * + * + * + * When used with auth, there are two different considerations. + * + * The first is authentication with the target. The only entity that needs to authenticate with + * the target node is the client, so authentication works there the same as it would with any + * other command. + * + * The second is the authentication of the target with the source, which is needed because the + * target must query the source directly for the contents of the database. To do this, the + * client must use the "copydbgetnonce" command, in which the target will get a nonce from the + * source and send it back to the client. The client can then hash its password with the nonce, + * send it to the target when it runs the "copydb" command, which can then use that information + * to authenticate with the source. + * + * NOTE: mongos doesn't know how to call or handle the "copydbgetnonce" command. See + * SERVER-6427. + * + * NOTE: Since internal cluster auth works differently, "copydb" currently doesn't work between + * shards in a cluster when auth is enabled. See SERVER-13080. + */ + class CmdCopyDb : public Command { + public: + CmdCopyDb() : Command("copydb") { } + + virtual bool adminOnly() const { + return true; + } + + virtual bool slaveOk() const { + return false; + } + + virtual bool isWriteCommandForConfigServer() const { return false; } + + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + return copydb::checkAuthForCopydbCommand(client, dbname, cmdObj); + } + + virtual void help( stringstream &help ) const { + help << "copy a database from another host to this host\n"; + help << "usage: {copydb: 1, fromhost: <connection string>, fromdb: <db>, todb: <db>" + << "[, slaveOk: <bool>, username: <username>, nonce: <nonce>, key: <key>]}"; + } + + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + + string fromhost = cmdObj.getStringField("fromhost"); + bool fromSelf = fromhost.empty(); + if ( fromSelf ) { + /* copy from self */ + stringstream ss; + ss << "localhost:" << serverGlobalParams.port; + fromhost = ss.str(); + } + + CloneOptions cloneOptions; + cloneOptions.fromDB = cmdObj.getStringField("fromdb"); + cloneOptions.logForRepl = !fromRepl; + cloneOptions.slaveOk = cmdObj["slaveOk"].trueValue(); + cloneOptions.useReplAuth = false; + cloneOptions.snapshot = true; + cloneOptions.mayYield = true; + cloneOptions.mayBeInterrupted = false; + + string todb = cmdObj.getStringField("todb"); + if ( fromhost.empty() || todb.empty() || cloneOptions.fromDB.empty() ) { + errmsg = "parms missing - {copydb: 1, fromhost: <connection string>, " + "fromdb: <db>, todb: <db>}"; + return false; + } + + // SERVER-4328 todo lock just the two db's not everything for the fromself case + scoped_ptr<Lock::ScopedLock> lk( fromSelf ? + static_cast<Lock::ScopedLock*>( new Lock::GlobalWrite() ) : + static_cast<Lock::ScopedLock*>( new Lock::DBWrite( todb ) ) ); + + Cloner cloner; + string username = cmdObj.getStringField( "username" ); + string nonce = cmdObj.getStringField( "nonce" ); + string key = cmdObj.getStringField( "key" ); + if ( !username.empty() && !nonce.empty() && !key.empty() ) { + uassert( 13008, "must call copydbgetnonce first", authConn_.get() ); + BSONObj ret; + { + dbtemprelease t; + if ( !authConn_->runCommand( cloneOptions.fromDB, + BSON( "authenticate" << 1 << "user" << username + << "nonce" << nonce << "key" << key ), ret ) ) { + errmsg = "unable to login " + ret.toString(); + return false; + } + } + cloner.setConnection( authConn_.release() ); + } + else if (!fromSelf) { + // If fromSelf leave the cloner's conn empty, it will use a DBDirectClient instead. + + ConnectionString cs = ConnectionString::parse(fromhost, errmsg); + if (!cs.isValid()) { + return false; + } + DBClientBase* conn = cs.connect(errmsg); + if (!conn) { + return false; + } + cloner.setConnection(conn); + } + Client::Context ctx(todb); + return cloner.go(txn, ctx, fromhost, cloneOptions, NULL, errmsg ); + } + + } cmdCopyDB; + +} // namespace mongo diff --git a/src/mongo/db/commands/copydb_getnonce.cpp b/src/mongo/db/commands/copydb_getnonce.cpp new file mode 100644 index 00000000000..a3ea4e00fd9 --- /dev/null +++ b/src/mongo/db/commands/copydb_getnonce.cpp @@ -0,0 +1,133 @@ +/** +* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. +* +* 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/pch.h" + +#include "mongo/base/init.h" +#include "mongo/base/status.h" +#include "mongo/bson/util/builder.h" +#include "mongo/client/dbclientinterface.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/catalog/collection.h" +#include "mongo/db/cloner.h" +#include "mongo/db/commands.h" +#include "mongo/db/commands/copydb.h" +#include "mongo/db/commands/rename_collection.h" +#include "mongo/db/db.h" +#include "mongo/db/dbhelpers.h" +#include "mongo/db/index_builder.h" +#include "mongo/db/instance.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/kill_current_op.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/oplogreader.h" +#include "mongo/db/pdfile.h" +#include "mongo/db/operation_context_impl.h" +#include "mongo/db/storage_options.h" + +namespace mongo { + + // SERVER-4328 todo review for concurrency + // :( + thread_specific_ptr<DBClientBase> authConn_; + + /* Usage: + * admindb.$cmd.findOne( { copydbgetnonce: 1, fromhost: <connection string> } ); + * + * Run against the mongod that is the intended target for the "copydb" command. Used to get a + * nonce from the source of a "copydb" operation for authentication purposes. See the + * description of the "copydb" command below. + */ + class CmdCopyDbGetNonce : public Command { + public: + CmdCopyDbGetNonce() : Command("copydbgetnonce") { } + + virtual bool adminOnly() const { + return true; + } + + virtual bool slaveOk() const { + return false; + } + + virtual bool isWriteCommandForConfigServer() const { return false; } + + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + // No auth required + } + + virtual void help( stringstream &help ) const { + help << "get a nonce for subsequent copy db request from secure server\n"; + help << "usage: {copydbgetnonce: 1, fromhost: <hostname>}"; + } + + virtual bool run(OperationContext* txn, + const string&, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + + string fromhost = cmdObj.getStringField("fromhost"); + if ( fromhost.empty() ) { + /* copy from self */ + stringstream ss; + ss << "localhost:" << serverGlobalParams.port; + fromhost = ss.str(); + } + + BSONObj ret; + + ConnectionString cs = ConnectionString::parse(fromhost, errmsg); + if (!cs.isValid()) { + return false; + } + + authConn_.reset(cs.connect(errmsg)); + if (!authConn_.get()) { + return false; + } + + if( !authConn_->runCommand( "admin", BSON( "getnonce" << 1 ), ret ) ) { + errmsg = "couldn't get nonce " + ret.toString(); + return false; + } + + result.appendElements( ret ); + return true; + } + + } cmdCopyDBGetNonce; + +} // namespace mongo diff --git a/src/mongo/db/commands/copydb_getnonce.h b/src/mongo/db/commands/copydb_getnonce.h new file mode 100644 index 00000000000..315459e44df --- /dev/null +++ b/src/mongo/db/commands/copydb_getnonce.h @@ -0,0 +1,37 @@ +/** +* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. +* +* 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 <boost/thread/tss.hpp> + +#include "mongo/client/dbclientinterface.h" + +namespace mongo { + + extern thread_specific_ptr<DBClientBase> authConn_; + +} // namespace mongo diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h index 4c561192f37..9459b482bca 100644 --- a/src/mongo/db/curop.h +++ b/src/mongo/db/curop.h @@ -31,8 +31,6 @@ #pragma once -#include <vector> - #include "mongo/bson/util/atomic_int.h" #include "mongo/db/client.h" #include "mongo/db/structure/catalog/namespace.h" |