/** * Copyright (C) 2015 MongoDB 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 "mongo/scripting/mozjs/mongo.h" #include "mongo/client/dbclientinterface.h" #include "mongo/client/global_conn_pool.h" #include "mongo/client/mongo_uri.h" #include "mongo/client/native_sasl_client_session.h" #include "mongo/client/sasl_client_authenticate.h" #include "mongo/client/sasl_client_session.h" #include "mongo/db/namespace_string.h" #include "mongo/db/operation_context.h" #include "mongo/scripting/dbdirectclient_factory.h" #include "mongo/scripting/mozjs/cursor.h" #include "mongo/scripting/mozjs/implscope.h" #include "mongo/scripting/mozjs/objectwrapper.h" #include "mongo/scripting/mozjs/valuereader.h" #include "mongo/scripting/mozjs/valuewriter.h" #include "mongo/scripting/mozjs/wrapconstrainedmethod.h" #include "mongo/stdx/memory.h" #include "mongo/util/assert_util.h" namespace mongo { namespace mozjs { const JSFunctionSpec MongoBase::methods[] = { MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(auth, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(close, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( copyDatabaseWithSCRAM, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(cursorFromId, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( cursorHandleFromId, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(find, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( getClientRPCProtocols, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( getServerRPCProtocols, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(insert, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( isReplicaSetConnection, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(logout, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(remove, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(runCommand, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( runCommandWithMetadata, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( setClientRPCProtocols, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO(update, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( getMinWireVersion, MongoLocalInfo, MongoExternalInfo), MONGO_ATTACH_JS_CONSTRAINED_METHOD_NO_PROTO( getMaxWireVersion, MongoLocalInfo, MongoExternalInfo), JS_FS_END, }; const char* const MongoBase::className = "Mongo"; const JSFunctionSpec MongoExternalInfo::freeFunctions[4] = { MONGO_ATTACH_JS_FUNCTION(_forgetReplSet), MONGO_ATTACH_JS_FUNCTION(load), MONGO_ATTACH_JS_FUNCTION(quit), JS_FS_END, }; namespace { DBClientBase* getConnection(JS::CallArgs& args) { auto ret = static_cast*>(JS_GetPrivate(args.thisv().toObjectOrNull())) ->get(); uassert( ErrorCodes::BadValue, "Trying to get connection for closed Mongo object", ret != nullptr); return ret; } void setCursor(JS::HandleObject target, std::unique_ptr cursor, JS::CallArgs& args) { auto client = static_cast*>(JS_GetPrivate(args.thisv().toObjectOrNull())); // Copy the client shared pointer to up the refcount JS_SetPrivate(target, new CursorInfo::CursorHolder(std::move(cursor), *client)); } void setCursorHandle(JS::HandleObject target, long long cursorId, JS::CallArgs& args) { auto client = static_cast*>(JS_GetPrivate(args.thisv().toObjectOrNull())); // Copy the client shared pointer to up the refcount. JS_SetPrivate(target, new CursorHandleInfo::CursorTracker(cursorId, *client)); } void setHiddenMongo(JSContext* cx, DBClientWithCommands* resPtr, DBClientWithCommands* origConn, JS::CallArgs& args) { ObjectWrapper o(cx, args.rval()); // If the connection that ran the command is the same as conn, then we set a hidden "_mongo" // property on the returned object that is just "this" Mongo object. if (resPtr == origConn) { o.defineProperty(InternedString::_mongo, args.thisv(), JSPROP_READONLY | JSPROP_PERMANENT); } else { // Otherwise, we construct a new Mongo object that is a copy of "this", but has a different // private value which is the specific DBClientBase that should be used for getMore calls. auto& connSharedPtr = *(static_cast*>( JS_GetPrivate(args.thisv().toObjectOrNull()))); JS::RootedObject newMongo(cx); auto scope = getScope(cx); auto isLocalInfo = scope->getProto().instanceOf(args.thisv()); if (isLocalInfo) { scope->getProto().newObject(&newMongo); } else { scope->getProto().newObject(&newMongo); } JS_SetPrivate( newMongo, new std::shared_ptr(connSharedPtr, static_cast(resPtr))); ObjectWrapper from(cx, args.thisv()); ObjectWrapper to(cx, newMongo); for (const auto& k : {InternedString::slaveOk, InternedString::defaultDB, InternedString::host}) { JS::RootedValue tmpValue(cx); from.getValue(k, &tmpValue); to.setValue(k, tmpValue); } JS::RootedValue value(cx); value.setObjectOrNull(newMongo); o.defineProperty(InternedString::_mongo, value, JSPROP_READONLY | JSPROP_PERMANENT); } } } // namespace void MongoBase::finalize(JSFreeOp* fop, JSObject* obj) { auto conn = static_cast*>(JS_GetPrivate(obj)); if (conn) { delete conn; } } void MongoBase::Functions::close::call(JSContext* cx, JS::CallArgs args) { getConnection(args); auto thisv = args.thisv().toObjectOrNull(); auto conn = static_cast*>(JS_GetPrivate(thisv)); conn->reset(); args.rval().setUndefined(); } void MongoBase::Functions::runCommand::call(JSContext* cx, JS::CallArgs args) { if (args.length() != 3) uasserted(ErrorCodes::BadValue, "runCommand needs 3 args"); if (!args.get(0).isString()) uasserted(ErrorCodes::BadValue, "the database parameter to runCommand must be a string"); if (!args.get(1).isObject()) uasserted(ErrorCodes::BadValue, "the cmdObj parameter to runCommand must be an object"); if (!args.get(2).isNumber()) uasserted(ErrorCodes::BadValue, "the options parameter to runCommand must be a number"); auto conn = getConnection(args); std::string database = ValueWriter(cx, args.get(0)).toString(); BSONObj cmdObj = ValueWriter(cx, args.get(1)).toBSON(); int queryOptions = ValueWriter(cx, args.get(2)).toInt32(); BSONObj cmdRes; auto resTuple = conn->runCommandWithTarget(database, cmdObj, cmdRes, queryOptions); // the returned object is not read only as some of our tests depend on modifying it. // // Also, we make a copy here because we want a copy after we dump cmdRes ValueReader(cx, args.rval()).fromBSON(cmdRes.getOwned(), nullptr, false /* read only */); setHiddenMongo(cx, std::get<1>(resTuple), conn, args); } void MongoBase::Functions::runCommandWithMetadata::call(JSContext* cx, JS::CallArgs args) { if (args.length() != 4) uasserted(ErrorCodes::BadValue, "runCommandWithMetadata needs 4 args"); if (!args.get(0).isString()) uasserted(ErrorCodes::BadValue, "the database parameter to runCommandWithMetadata must be a string"); if (!args.get(1).isString()) uasserted(ErrorCodes::BadValue, "the commandName parameter to runCommandWithMetadata must be a string"); if (!args.get(2).isObject()) uasserted(ErrorCodes::BadValue, "the metadata argument to runCommandWithMetadata must be an object"); if (!args.get(3).isObject()) uasserted(ErrorCodes::BadValue, "the commandArgs argument to runCommandWithMetadata must be an object"); std::string database = ValueWriter(cx, args.get(0)).toString(); std::string commandName = ValueWriter(cx, args.get(1)).toString(); BSONObj metadata = ValueWriter(cx, args.get(2)).toBSON(); BSONObj commandArgs = ValueWriter(cx, args.get(3)).toBSON(); auto conn = getConnection(args); auto resTuple = conn->runCommandWithMetadataAndTarget(database, commandName, metadata, commandArgs); auto res = std::move(std::get<0>(resTuple)); BSONObjBuilder mergedResultBob; mergedResultBob.append("commandReply", res->getCommandReply()); mergedResultBob.append("metadata", res->getMetadata()); ValueReader(cx, args.rval()).fromBSON(mergedResultBob.obj(), nullptr, false); setHiddenMongo(cx, std::get<1>(resTuple), conn, args); } void MongoBase::Functions::find::call(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); if (args.length() != 7) uasserted(ErrorCodes::BadValue, "find needs 7 args"); if (!args.get(1).isObject()) uasserted(ErrorCodes::BadValue, "needs to be an object"); auto conn = getConnection(args); std::string ns = ValueWriter(cx, args.get(0)).toString(); BSONObj fields; BSONObj q = ValueWriter(cx, args.get(1)).toBSON(); bool haveFields = false; if (args.get(2).isObject()) { JS::RootedObject obj(cx, args.get(2).toObjectOrNull()); ObjectWrapper(cx, obj).enumerate([&](jsid) { haveFields = true; return false; }); } if (haveFields) fields = ValueWriter(cx, args.get(2)).toBSON(); int nToReturn = ValueWriter(cx, args.get(3)).toInt32(); int nToSkip = ValueWriter(cx, args.get(4)).toInt32(); int batchSize = ValueWriter(cx, args.get(5)).toInt32(); int options = ValueWriter(cx, args.get(6)).toInt32(); std::unique_ptr cursor( conn->query(ns, q, nToReturn, nToSkip, haveFields ? &fields : NULL, options, batchSize)); if (!cursor.get()) { uasserted(ErrorCodes::InternalError, "error doing query: failed"); } JS::RootedObject c(cx); scope->getProto().newObject(&c); setCursor(c, std::move(cursor), args); args.rval().setObjectOrNull(c); } void MongoBase::Functions::insert::call(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); if (args.length() != 3) uasserted(ErrorCodes::BadValue, "insert needs 3 args"); if (!args.get(1).isObject()) uasserted(ErrorCodes::BadValue, "attempted to insert a non-object"); ObjectWrapper o(cx, args.thisv()); if (o.hasField(InternedString::readOnly) && o.getBoolean(InternedString::readOnly)) uasserted(ErrorCodes::BadValue, "js db in read only mode"); auto conn = getConnection(args); std::string ns = ValueWriter(cx, args.get(0)).toString(); int flags = ValueWriter(cx, args.get(2)).toInt32(); auto addId = [cx, scope](JS::HandleValue value) { if (!value.isObject()) uasserted(ErrorCodes::BadValue, "attempted to insert a non-object type"); JS::RootedObject elementObj(cx, value.toObjectOrNull()); ObjectWrapper ele(cx, elementObj); if (!ele.hasField(InternedString::_id)) { JS::RootedValue value(cx); scope->getProto().newInstance(&value); ele.setValue(InternedString::_id, value); } return ValueWriter(cx, value).toBSON(); }; if (args.get(1).isObject()) { bool isArray; if (!JS_IsArrayObject(cx, args.get(1), &isArray)) { uasserted(ErrorCodes::BadValue, "Failure to check is object an array"); } if (isArray) { JS::RootedObject obj(cx, args.get(1).toObjectOrNull()); ObjectWrapper array(cx, obj); std::vector bos; bool foundElement = false; array.enumerate([&](JS::HandleId id) { foundElement = true; JS::RootedValue value(cx); array.getValue(id, &value); bos.push_back(addId(value)); return true; }); if (!foundElement) uasserted(ErrorCodes::BadValue, "attempted to insert an empty array"); conn->insert(ns, bos, flags); } else { conn->insert(ns, addId(args.get(1))); } } else { conn->insert(ns, addId(args.get(1))); } args.rval().setUndefined(); } void MongoBase::Functions::remove::call(JSContext* cx, JS::CallArgs args) { if (!(args.length() == 2 || args.length() == 3)) uasserted(ErrorCodes::BadValue, "remove needs 2 or 3 args"); if (!(args.get(1).isObject())) uasserted(ErrorCodes::BadValue, "attempted to remove a non-object"); ObjectWrapper o(cx, args.thisv()); if (o.hasOwnField(InternedString::readOnly) && o.getBoolean(InternedString::readOnly)) uasserted(ErrorCodes::BadValue, "js db in read only mode"); auto conn = getConnection(args); std::string ns = ValueWriter(cx, args.get(0)).toString(); BSONObj bson = ValueWriter(cx, args.get(1)).toBSON(); bool justOne = false; if (args.length() > 2) { justOne = args.get(2).toBoolean(); } conn->remove(ns, bson, justOne); args.rval().setUndefined(); } void MongoBase::Functions::update::call(JSContext* cx, JS::CallArgs args) { if (args.length() < 3) uasserted(ErrorCodes::BadValue, "update needs at least 3 args"); if (!args.get(1).isObject()) uasserted(ErrorCodes::BadValue, "1st param to update has to be an object"); if (!args.get(2).isObject()) uasserted(ErrorCodes::BadValue, "2nd param to update has to be an object"); ObjectWrapper o(cx, args.thisv()); if (o.hasOwnField(InternedString::readOnly) && o.getBoolean(InternedString::readOnly)) uasserted(ErrorCodes::BadValue, "js db in read only mode"); auto conn = getConnection(args); std::string ns = ValueWriter(cx, args.get(0)).toString(); BSONObj q1 = ValueWriter(cx, args.get(1)).toBSON(); BSONObj o1 = ValueWriter(cx, args.get(2)).toBSON(); bool upsert = args.length() > 3 && args.get(3).isBoolean() && args.get(3).toBoolean(); bool multi = args.length() > 4 && args.get(4).isBoolean() && args.get(4).toBoolean(); conn->update(ns, q1, o1, upsert, multi); args.rval().setUndefined(); } void MongoBase::Functions::auth::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); if (!conn) uasserted(ErrorCodes::BadValue, "no connection"); BSONObj params; switch (args.length()) { case 1: params = ValueWriter(cx, args.get(0)).toBSON(); break; case 3: params = BSON(saslCommandMechanismFieldName << "MONGODB-CR" << saslCommandUserDBFieldName << ValueWriter(cx, args[0]).toString() << saslCommandUserFieldName << ValueWriter(cx, args[1]).toString() << saslCommandPasswordFieldName << ValueWriter(cx, args[2]).toString()); break; default: uasserted(ErrorCodes::BadValue, "mongoAuth takes 1 object or 3 string arguments"); } conn->auth(params); args.rval().setBoolean(true); } void MongoBase::Functions::logout::call(JSContext* cx, JS::CallArgs args) { if (args.length() != 1) uasserted(ErrorCodes::BadValue, "logout needs 1 arg"); BSONObj ret; std::string db = ValueWriter(cx, args.get(0)).toString(); auto conn = getConnection(args); if (conn) { conn->logout(db, ret); } // Make a copy because I want to insulate us from whether conn->logout // writes an owned bson or not ValueReader(cx, args.rval()).fromBSON(ret.getOwned(), nullptr, false); } void MongoBase::Functions::cursorFromId::call(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); if (!(args.length() == 2 || args.length() == 3)) uasserted(ErrorCodes::BadValue, "cursorFromId needs 2 or 3 args"); if (!scope->getProto().instanceOf(args.get(1))) uasserted(ErrorCodes::BadValue, "2nd arg must be a NumberLong"); if (!(args.get(2).isNumber() || args.get(2).isUndefined())) uasserted(ErrorCodes::BadValue, "3rd arg must be a js Number"); auto conn = getConnection(args); std::string ns = ValueWriter(cx, args.get(0)).toString(); long long cursorId = NumberLongInfo::ToNumberLong(cx, args.get(1)); auto cursor = stdx::make_unique(conn, ns, cursorId, 0, 0); if (args.get(2).isNumber()) cursor->setBatchSize(ValueWriter(cx, args.get(2)).toInt32()); JS::RootedObject c(cx); scope->getProto().newObject(&c); setCursor(c, std::move(cursor), args); args.rval().setObjectOrNull(c); } void MongoBase::Functions::cursorHandleFromId::call(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); if (args.length() != 1) { uasserted(ErrorCodes::BadValue, "cursorHandleFromId needs 1 arg"); } if (!scope->getProto().instanceOf(args.get(0))) { uasserted(ErrorCodes::BadValue, "1st arg must be a NumberLong"); } long long cursorId = NumberLongInfo::ToNumberLong(cx, args.get(0)); JS::RootedObject c(cx); scope->getProto().newObject(&c); setCursorHandle(c, cursorId, args); args.rval().setObjectOrNull(c); } void MongoBase::Functions::copyDatabaseWithSCRAM::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); if (!conn) uasserted(ErrorCodes::BadValue, "no connection"); if (args.length() != 6) uasserted(ErrorCodes::BadValue, "copyDatabase needs 6 arg"); // copyDatabase(fromdb, todb, fromhost, username, password); std::string fromDb = ValueWriter(cx, args.get(0)).toString(); std::string toDb = ValueWriter(cx, args.get(1)).toString(); std::string fromHost = ValueWriter(cx, args.get(2)).toString(); std::string user = ValueWriter(cx, args.get(3)).toString(); std::string password = ValueWriter(cx, args.get(4)).toString(); bool slaveOk = ValueWriter(cx, args.get(5)).toBoolean(); std::string hashedPwd = DBClientWithCommands::createPasswordDigest(user, password); std::unique_ptr session(new NativeSaslClientSession()); session->setParameter(SaslClientSession::parameterMechanism, "SCRAM-SHA-1"); session->setParameter(SaslClientSession::parameterUser, user); session->setParameter(SaslClientSession::parameterPassword, hashedPwd); session->initialize(); BSONObj saslFirstCommandPrefix = BSON("copydbsaslstart" << 1 << "fromhost" << fromHost << "fromdb" << fromDb << saslCommandMechanismFieldName << "SCRAM-SHA-1"); BSONObj saslFollowupCommandPrefix = BSON( "copydb" << 1 << "fromhost" << fromHost << "fromdb" << fromDb << "todb" << toDb << "slaveOk" << slaveOk); BSONObj saslCommandPrefix = saslFirstCommandPrefix; BSONObj inputObj = BSON(saslCommandPayloadFieldName << ""); bool isServerDone = false; while (!session->isDone()) { std::string payload; BSONType type; Status status = saslExtractPayload(inputObj, &payload, &type); uassertStatusOK(status); std::string responsePayload; status = session->step(payload, &responsePayload); uassertStatusOK(status); BSONObjBuilder commandBuilder; commandBuilder.appendElements(saslCommandPrefix); commandBuilder.appendBinData(saslCommandPayloadFieldName, static_cast(responsePayload.size()), BinDataGeneral, responsePayload.c_str()); BSONElement conversationId = inputObj[saslCommandConversationIdFieldName]; if (!conversationId.eoo()) commandBuilder.append(conversationId); BSONObj command = commandBuilder.obj(); bool ok = conn->runCommand("admin", command, inputObj); ErrorCodes::Error code = ErrorCodes::fromInt(inputObj[saslCommandCodeFieldName].numberInt()); if (!ok || code != ErrorCodes::OK) { if (code == ErrorCodes::OK) code = ErrorCodes::UnknownError; ValueReader(cx, args.rval()).fromBSON(inputObj, nullptr, true); return; } isServerDone = inputObj[saslCommandDoneFieldName].trueValue(); saslCommandPrefix = saslFollowupCommandPrefix; } if (!isServerDone) { uasserted(ErrorCodes::InternalError, "copydb client finished before server."); } ValueReader(cx, args.rval()).fromBSON(inputObj, nullptr, true); } void MongoBase::Functions::getClientRPCProtocols::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); if (args.length() != 0) uasserted(ErrorCodes::BadValue, "getClientRPCProtocols takes no args"); auto clientRPCProtocols = rpc::toString(conn->getClientRPCProtocols()); uassertStatusOK(clientRPCProtocols); auto protoStr = clientRPCProtocols.getValue().toString(); ValueReader(cx, args.rval()).fromStringData(protoStr); } void MongoBase::Functions::setClientRPCProtocols::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); if (args.length() != 1) uasserted(ErrorCodes::BadValue, "setClientRPCProtocols needs 1 arg"); if (!args.get(0).isString()) uasserted(ErrorCodes::BadValue, "first argument to setClientRPCProtocols must be a string"); std::string rpcProtosStr = ValueWriter(cx, args.get(0)).toString(); auto clientRPCProtocols = rpc::parseProtocolSet(rpcProtosStr); uassertStatusOK(clientRPCProtocols); conn->setClientRPCProtocols(clientRPCProtocols.getValue()); args.rval().setUndefined(); } void MongoBase::Functions::getServerRPCProtocols::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); if (args.length() != 0) uasserted(ErrorCodes::BadValue, "getServerRPCProtocols takes no args"); auto serverRPCProtocols = rpc::toString(conn->getServerRPCProtocols()); uassertStatusOK(serverRPCProtocols); auto protoStr = serverRPCProtocols.getValue().toString(); ValueReader(cx, args.rval()).fromStringData(protoStr); } void MongoBase::Functions::isReplicaSetConnection::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); if (args.length() != 0) { uasserted(ErrorCodes::BadValue, "isReplicaSetConnection takes no args"); } args.rval().setBoolean(conn->type() == ConnectionString::ConnectionType::SET); } void MongoLocalInfo::construct(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); if (args.length() != 0) uasserted(ErrorCodes::BadValue, "local Mongo constructor takes no args"); auto opCtx = scope->getOpContext(); auto conn = DBDirectClientFactory::get(opCtx).create(opCtx); JS::RootedObject thisv(cx); scope->getProto().newObject(&thisv); ObjectWrapper o(cx, thisv); JS_SetPrivate(thisv, new std::shared_ptr(conn.release())); o.setBoolean(InternedString::slaveOk, false); o.setString(InternedString::host, "EMBEDDED"); args.rval().setObjectOrNull(thisv); } void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); std::string host("127.0.0.1"); if (args.length() > 0 && args.get(0).isString()) { host = ValueWriter(cx, args.get(0)).toString(); } auto statusWithHost = MongoURI::parse(host); auto cs = uassertStatusOK(statusWithHost); std::string errmsg; std::unique_ptr conn(cs.connect("MongoDB Shell", errmsg)); if (!conn.get()) { uasserted(ErrorCodes::InternalError, errmsg); } ScriptEngine::runConnectCallback(*conn); JS::RootedObject thisv(cx); scope->getProto().newObject(&thisv); ObjectWrapper o(cx, thisv); JS_SetPrivate(thisv, new std::shared_ptr(conn.release())); o.setBoolean(InternedString::slaveOk, false); o.setString(InternedString::host, cs.toString()); auto defaultDB = cs.getDatabase() == "" ? "test" : cs.getDatabase(); o.setString(InternedString::defaultDB, defaultDB); args.rval().setObjectOrNull(thisv); } void MongoBase::Functions::getMinWireVersion::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); args.rval().setInt32(conn->getMinWireVersion()); } void MongoBase::Functions::getMaxWireVersion::call(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); args.rval().setInt32(conn->getMaxWireVersion()); } void MongoExternalInfo::Functions::load::call(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); for (unsigned i = 0; i < args.length(); ++i) { std::string filename = ValueWriter(cx, args.get(i)).toString(); if (!scope->execFile(filename, false, true)) { uasserted(ErrorCodes::BadValue, std::string("error loading js file: ") + filename); } } args.rval().setBoolean(true); } void MongoExternalInfo::Functions::quit::call(JSContext* cx, JS::CallArgs args) { auto scope = getScope(cx); scope->setQuickExit(args.get(0).isNumber() ? args.get(0).toNumber() : 0); uasserted(ErrorCodes::JSUncatchableError, "Calling Quit"); } void MongoExternalInfo::Functions::_forgetReplSet::call(JSContext* cx, JS::CallArgs args) { if (args.length() != 1) { uasserted(ErrorCodes::BadValue, str::stream() << "_forgetReplSet takes exactly 1 argument, but was given " << args.length()); } std::string rsName = ValueWriter(cx, args.get(0)).toString(); globalRSMonitorManager.removeMonitor(rsName); args.rval().setUndefined(); } } // namespace mozjs } // namespace mongo