1 __quiet = false;
  2 __magicNoPrint = {
  3     __magicNoPrint: 1111
  4 };
  5 __callLastError = false;
  6 _verboseShell = false;
  7 
  8 chatty = function(s) {
  9     if (!__quiet)
 10         print(s);
 11 };
 12 
 13 function reconnect(db) {
 14     assert.soon(function() {
 15         try {
 16             db.runCommand({ping: 1});
 17             return true;
 18         } catch (x) {
 19             return false;
 20         }
 21     });
 22 }
 23 
 24 function _getErrorWithCode(codeOrObj, message) {
 25     var e = new Error(message);
 26     if (codeOrObj != undefined) {
 27         if (codeOrObj.writeError) {
 28             e.code = codeOrObj.writeError.code;
 29         } else if (codeOrObj.code) {
 30             e.code = codeOrObj.code;
 31         } else {
 32             // At this point assume codeOrObj is a number type
 33             e.code = codeOrObj;
 34         }
 35     }
 36 
 37     return e;
 38 }
 39 
 40 // Please consider using bsonWoCompare instead of this as much as possible.
 41 friendlyEqual = function(a, b) {
 42     if (a == b)
 43         return true;
 44 
 45     a = tojson(a, false, true);
 46     b = tojson(b, false, true);
 47 
 48     if (a == b)
 49         return true;
 50 
 51     var clean = function(s) {
 52         s = s.replace(/NumberInt\((\-?\d+)\)/g, "$1");
 53         return s;
 54     };
 55 
 56     a = clean(a);
 57     b = clean(b);
 58 
 59     if (a == b)
 60         return true;
 61 
 62     return false;
 63 };
 64 
 65 printStackTrace = function() {
 66     try {
 67         throw new Error("Printing Stack Trace");
 68     } catch (e) {
 69         print(e.stack);
 70     }
 71 };
 72 
 73 /**
 74  * <p> Set the shell verbosity. If verbose the shell will display more information about command
 75  * results. </>
 76  * <p> Default is off. <p>
 77  * @param {Bool} verbosity on / off
 78  */
 79 setVerboseShell = function(value) {
 80     if (value == undefined)
 81         value = true;
 82     _verboseShell = value;
 83 };
 84 
 85 // Formats a simple stacked horizontal histogram bar in the shell.
 86 // @param data array of the form [[ratio, symbol], ...] where ratio is between 0 and 1 and
 87 //             symbol is a string of length 1
 88 // @param width width of the bar (excluding the left and right delimiters [ ] )
 89 // e.g. _barFormat([[.3, "="], [.5, '-']], 80) returns
 90 //      "[========================----------------------------------------                ]"
 91 _barFormat = function(data, width) {
 92     var remaining = width;
 93     var res = "[";
 94     for (var i = 0; i < data.length; i++) {
 95         for (var x = 0; x < data[i][0] * width; x++) {
 96             if (remaining-- > 0) {
 97                 res += data[i][1];
 98             }
 99         }
100     }
101     while (remaining-- > 0) {
102         res += " ";
103     }
104     res += "]";
105     return res;
106 };
107 
108 // these two are helpers for Array.sort(func)
109 compare = function(l, r) {
110     return (l == r ? 0 : (l < r ? -1 : 1));
111 };
112 
113 // arr.sort(compareOn('name'))
114 compareOn = function(field) {
115     return function(l, r) {
116         return compare(l[field], r[field]);
117     };
118 };
119 
120 shellPrint = function(x) {
121     it = x;
122     if (x != undefined)
123         shellPrintHelper(x);
124 
125     if (db) {
126         var e = db.getPrevError();
127         if (e.err) {
128             if (e.nPrev <= 1)
129                 print("error on last call: " + tojson(e.err));
130             else
131                 print("an error " + tojson(e.err) + " occurred " + e.nPrev +
132                       " operations back in the command invocation");
133         }
134         db.resetError();
135     }
136 };
137 
138 print.captureAllOutput = function(fn, args) {
139     var res = {};
140     res.output = [];
141     var __orig_print = print;
142     print = function() {
143         Array.prototype.push.apply(res.output,
144                                    Array.prototype.slice.call(arguments).join(" ").split("\n"));
145     };
146     try {
147         res.result = fn.apply(undefined, args);
148     } finally {
149         // Stop capturing print() output
150         print = __orig_print;
151     }
152     return res;
153 };
154 
155 if (typeof TestData == "undefined") {
156     TestData = undefined;
157 }
158 
159 jsTestName = function() {
160     if (TestData)
161         return TestData.testName;
162     return "__unknown_name__";
163 };
164 
165 var _jsTestOptions = {enableTestCommands: true};  // Test commands should be enabled by default
166 
167 jsTestOptions = function() {
168     if (TestData) {
169         return Object.merge(_jsTestOptions, {
170             setParameters: TestData.setParameters,
171             setParametersMongos: TestData.setParametersMongos,
172             storageEngine: TestData.storageEngine,
173             storageEngineCacheSizeGB: TestData.storageEngineCacheSizeGB,
174             wiredTigerEngineConfigString: TestData.wiredTigerEngineConfigString,
175             wiredTigerCollectionConfigString: TestData.wiredTigerCollectionConfigString,
176             wiredTigerIndexConfigString: TestData.wiredTigerIndexConfigString,
177             noJournal: TestData.noJournal,
178             noJournalPrealloc: TestData.noJournalPrealloc,
179             auth: TestData.auth,
180             keyFile: TestData.keyFile,
181             authUser: "__system",
182             authPassword: TestData.keyFileData,
183             authMechanism: TestData.authMechanism,
184             adminUser: TestData.adminUser || "admin",
185             adminPassword: TestData.adminPassword || "password",
186             useLegacyConfigServers: TestData.useLegacyConfigServers || false,
187             useLegacyReplicationProtocol: TestData.useLegacyReplicationProtocol || false,
188             enableMajorityReadConcern: TestData.enableMajorityReadConcern,
189             writeConcernMajorityShouldJournal: TestData.writeConcernMajorityShouldJournal,
190             enableEncryption: TestData.enableEncryption,
191             encryptionKeyFile: TestData.encryptionKeyFile,
192             auditDestination: TestData.auditDestination,
193             minPort: TestData.minPort,
194             maxPort: TestData.maxPort,
195             // Note: does not support the array version
196             mongosBinVersion: TestData.mongosBinVersion || "",
197             shardMixedBinVersions: TestData.shardMixedBinVersions || false,
198             networkMessageCompressors: TestData.networkMessageCompressors
199         });
200     }
201     return _jsTestOptions;
202 };
203 
204 setJsTestOption = function(name, value) {
205     _jsTestOptions[name] = value;
206 };
207 
208 jsTestLog = function(msg) {
209     print("\n\n----\n" + msg + "\n----\n\n");
210 };
211 
212 jsTest = {};
213 
214 jsTest.name = jsTestName;
215 jsTest.options = jsTestOptions;
216 jsTest.setOption = setJsTestOption;
217 jsTest.log = jsTestLog;
218 jsTest.readOnlyUserRoles = ["read"];
219 jsTest.basicUserRoles = ["dbOwner"];
220 jsTest.adminUserRoles = ["root"];
221 
222 jsTest.authenticate = function(conn) {
223     if (!jsTest.options().auth && !jsTest.options().keyFile) {
224         conn.authenticated = true;
225         return true;
226     }
227 
228     try {
229         assert.soon(function() {
230             // Set authenticated to stop an infinite recursion from getDB calling
231             // back into authenticate.
232             conn.authenticated = true;
233             print("Authenticating as internal " + jsTestOptions().authUser +
234                   " user with mechanism " + DB.prototype._defaultAuthenticationMechanism +
235                   " on connection: " + conn);
236             conn.authenticated = conn.getDB('admin').auth({
237                 user: jsTestOptions().authUser,
238                 pwd: jsTestOptions().authPassword,
239             });
240             return conn.authenticated;
241         }, "Authenticating connection: " + conn, 5000, 1000);
242     } catch (e) {
243         print("Caught exception while authenticating connection: " + tojson(e));
244         conn.authenticated = false;
245     }
246     return conn.authenticated;
247 };
248 
249 jsTest.authenticateNodes = function(nodes) {
250     assert.soonNoExcept(function() {
251         for (var i = 0; i < nodes.length; i++) {
252             // Don't try to authenticate to arbiters
253             res = nodes[i].getDB("admin").runCommand({replSetGetStatus: 1});
254             if (res.myState == 7) {
255                 continue;
256             }
257             if (jsTest.authenticate(nodes[i]) != 1) {
258                 return false;
259             }
260         }
261         return true;
262     }, "Authenticate to nodes: " + nodes, 30000);
263 };
264 
265 jsTest.isMongos = function(conn) {
266     return conn.getDB('admin').isMaster().msg == 'isdbgrid';
267 };
268 
269 defaultPrompt = function() {
270     var status = db.getMongo().authStatus;
271     var prefix = db.getMongo().promptPrefix;
272 
273     if (typeof prefix == 'undefined') {
274         prefix = "";
275         var buildInfo = db.runCommand({buildInfo: 1});
276         try {
277             if (buildInfo.modules.indexOf("enterprise") > -1) {
278                 prefix += "MongoDB Enterprise ";
279             }
280         } catch (e) {
281             // Don't do anything here. Just throw the error away.
282         }
283         var isMasterRes = db.runCommand({isMaster: 1, forShell: 1});
284         try {
285             if (isMasterRes.hasOwnProperty("automationServiceDescriptor")) {
286                 prefix += "[automated] ";
287             }
288         } catch (e) {
289             // Don't do anything here. Just throw the error away.
290         }
291         db.getMongo().promptPrefix = prefix;
292     }
293 
294     try {
295         // try to use repl set prompt -- no status or auth detected yet
296         if (!status || !status.authRequired) {
297             try {
298                 var prompt = replSetMemberStatePrompt();
299                 // set our status that it was good
300                 db.getMongo().authStatus = {replSetGetStatus: true, isMaster: true};
301                 return prefix + prompt;
302             } catch (e) {
303                 // don't have permission to run that, or requires auth
304                 // print(e);
305                 status = {authRequired: true, replSetGetStatus: false, isMaster: true};
306             }
307         }
308         // auth detected
309 
310         // try to use replSetGetStatus?
311         if (status.replSetGetStatus) {
312             try {
313                 var prompt = replSetMemberStatePrompt();
314                 // set our status that it was good
315                 status.replSetGetStatus = true;
316                 db.getMongo().authStatus = status;
317                 return prefix + prompt;
318             } catch (e) {
319                 // don't have permission to run that, or requires auth
320                 // print(e);
321                 status.authRequired = true;
322                 status.replSetGetStatus = false;
323             }
324         }
325 
326         // try to use isMaster?
327         if (status.isMaster) {
328             try {
329                 var prompt = isMasterStatePrompt(isMasterRes);
330                 status.isMaster = true;
331                 db.getMongo().authStatus = status;
332                 return prefix + prompt;
333             } catch (e) {
334                 status.authRequired = true;
335                 status.isMaster = false;
336             }
337         }
338     } catch (ex) {
339         printjson(ex);
340         // reset status and let it figure it out next time.
341         status = {isMaster: true};
342     }
343 
344     db.getMongo().authStatus = status;
345     return prefix + "> ";
346 };
347 
348 replSetMemberStatePrompt = function() {
349     var state = '';
350     var stateInfo = db.getSiblingDB('admin').runCommand({replSetGetStatus: 1, forShell: 1});
351     if (stateInfo.ok) {
352         // Report the self member's stateStr if it's present.
353         stateInfo.members.forEach(function(member) {
354             if (member.self) {
355                 state = member.stateStr;
356             }
357         });
358         // Otherwise fall back to reporting the numeric myState field (mongodb 1.6).
359         if (!state) {
360             state = stateInfo.myState;
361         }
362         state = '' + stateInfo.set + ':' + state;
363     } else {
364         var info = stateInfo.info;
365         if (info && info.length < 20) {
366             state = info;  // "mongos", "configsvr"
367         } else {
368             throw _getErrorWithCode(stateInfo, "Failed:" + info);
369         }
370     }
371     return state + '> ';
372 };
373 
374 isMasterStatePrompt = function(isMasterResponse) {
375     var state = '';
376     var isMaster = isMasterResponse || db.runCommand({isMaster: 1, forShell: 1});
377     if (isMaster.ok) {
378         var role = "";
379 
380         if (isMaster.msg == "isdbgrid") {
381             role = "mongos";
382         }
383 
384         if (isMaster.setName) {
385             if (isMaster.ismaster)
386                 role = "PRIMARY";
387             else if (isMaster.secondary)
388                 role = "SECONDARY";
389             else if (isMaster.arbiterOnly)
390                 role = "ARBITER";
391             else {
392                 role = "OTHER";
393             }
394             state = isMaster.setName + ':';
395         }
396         state = state + role;
397     } else {
398         throw _getErrorWithCode(isMaster, "Failed: " + tojson(isMaster));
399     }
400     return state + '> ';
401 };
402 
403 if (typeof(_useWriteCommandsDefault) == 'undefined') {
404     // This is for cases when the v8 engine is used other than the mongo shell, like map reduce.
405     _useWriteCommandsDefault = function() {
406         return false;
407     };
408 }
409 
410 if (typeof(_writeMode) == 'undefined') {
411     // This is for cases when the v8 engine is used other than the mongo shell, like map reduce.
412     _writeMode = function() {
413         return "commands";
414     };
415 }
416 
417 if (typeof(_readMode) == 'undefined') {
418     // This is for cases when the v8 engine is used other than the mongo shell, like map reduce.
419     _readMode = function() {
420         return "legacy";
421     };
422 }
423 
424 shellPrintHelper = function(x) {
425     if (typeof(x) == "undefined") {
426         // Make sure that we have a db var before we use it
427         // TODO: This implicit calling of GLE can cause subtle, hard to track issues - remove?
428         if (__callLastError && typeof(db) != "undefined" && db.getMongo &&
429             db.getMongo().writeMode() == "legacy") {
430             __callLastError = false;
431             // explicit w:1 so that replset getLastErrorDefaults aren't used here which would be bad
432             var err = db.getLastError(1);
433             if (err != null) {
434                 print(err);
435             }
436         }
437         return;
438     }
439 
440     if (x == __magicNoPrint)
441         return;
442 
443     if (x == null) {
444         print("null");
445         return;
446     }
447 
448     if (x === MinKey || x === MaxKey)
449         return x.tojson();
450 
451     if (typeof x != "object")
452         return print(x);
453 
454     var p = x.shellPrint;
455     if (typeof p == "function")
456         return x.shellPrint();
457 
458     var p = x.tojson;
459     if (typeof p == "function")
460         print(x.tojson());
461     else
462         print(tojson(x));
463 };
464 
465 shellAutocomplete = function(
466     /*prefix*/) {  // outer scope function called on init. Actual function at end
467 
468     var universalMethods =
469         "constructor prototype toString valueOf toLocaleString hasOwnProperty propertyIsEnumerable"
470             .split(' ');
471 
472     var builtinMethods = {};  // uses constructor objects as keys
473     builtinMethods[Array] =
474         "length concat join pop push reverse shift slice sort splice unshift indexOf lastIndexOf every filter forEach map some isArray reduce reduceRight"
475             .split(' ');
476     builtinMethods[Boolean] = "".split(' ');  // nothing more than universal methods
477     builtinMethods[Date] =
478         "getDate getDay getFullYear getHours getMilliseconds getMinutes getMonth getSeconds getTime getTimezoneOffset getUTCDate getUTCDay getUTCFullYear getUTCHours getUTCMilliseconds getUTCMinutes getUTCMonth getUTCSeconds getYear parse setDate setFullYear setHours setMilliseconds setMinutes setMonth setSeconds setTime setUTCDate setUTCFullYear setUTCHours setUTCMilliseconds setUTCMinutes setUTCMonth setUTCSeconds setYear toDateString toGMTString toISOString toLocaleDateString toLocaleTimeString toTimeString toUTCString UTC now"
479             .split(' ');
480     if (typeof JSON != "undefined") {  // JSON is new in V8
481         builtinMethods["[object JSON]"] = "parse stringify".split(' ');
482     }
483     builtinMethods[Math] =
484         "E LN2 LN10 LOG2E LOG10E PI SQRT1_2 SQRT2 abs acos asin atan atan2 ceil cos exp floor log max min pow random round sin sqrt tan"
485             .split(' ');
486     builtinMethods[Number] =
487         "MAX_VALUE MIN_VALUE NEGATIVE_INFINITY POSITIVE_INFINITY toExponential toFixed toPrecision"
488             .split(' ');
489     builtinMethods[RegExp] =
490         "global ignoreCase lastIndex multiline source compile exec test".split(' ');
491     builtinMethods[String] =
492         "length charAt charCodeAt concat fromCharCode indexOf lastIndexOf match replace search slice split substr substring toLowerCase toUpperCase trim trimLeft trimRight"
493             .split(' ');
494     builtinMethods[Function] = "call apply bind".split(' ');
495     builtinMethods[Object] =
496         "bsonsize create defineProperty defineProperties getPrototypeOf keys seal freeze preventExtensions isSealed isFrozen isExtensible getOwnPropertyDescriptor getOwnPropertyNames"
497             .split(' ');
498 
499     builtinMethods[Mongo] = "find update insert remove".split(' ');
500     builtinMethods[BinData] = "hex base64 length subtype".split(' ');
501 
502     var extraGlobals =
503         "Infinity NaN undefined null true false decodeURI decodeURIComponent encodeURI encodeURIComponent escape eval isFinite isNaN parseFloat parseInt unescape Array Boolean Date Math Number RegExp String print load gc MinKey MaxKey Mongo NumberInt NumberLong ObjectId DBPointer UUID BinData HexData MD5 Map Timestamp JSON"
504             .split(' ');
505     if (typeof NumberDecimal !== 'undefined') {
506         extraGlobals[extraGlobals.length] = "NumberDecimal";
507     }
508 
509     var isPrivate = function(name) {
510         if (shellAutocomplete.showPrivate)
511             return false;
512         if (name == '_id')
513             return false;
514         if (name[0] == '_')
515             return true;
516         if (name[name.length - 1] == '_')
517             return true;  // some native functions have an extra name_ method
518         return false;
519     };
520 
521     var customComplete = function(obj) {
522         try {
523             if (obj.__proto__.constructor.autocomplete) {
524                 var ret = obj.constructor.autocomplete(obj);
525                 if (ret.constructor != Array) {
526                     print("\nautocompleters must return real Arrays");
527                     return [];
528                 }
529                 return ret;
530             } else {
531                 return [];
532             }
533         } catch (e) {
534             // print( e ); // uncomment if debugging custom completers
535             return [];
536         }
537     };
538 
539     var worker = function(prefix) {
540         var global = (function() {
541                          return this;
542                      }).call();  // trick to get global object
543 
544         var curObj = global;
545         var parts = prefix.split('.');
546         for (var p = 0; p < parts.length - 1; p++) {  // doesn't include last part
547             curObj = curObj[parts[p]];
548             if (curObj == null)
549                 return [];
550         }
551 
552         var lastPrefix = parts[parts.length - 1] || '';
553         var lastPrefixLowercase = lastPrefix.toLowerCase();
554         var beginning = parts.slice(0, parts.length - 1).join('.');
555         if (beginning.length)
556             beginning += '.';
557 
558         var possibilities =
559             new Array().concat(universalMethods,
560                                Object.keySet(curObj),
561                                Object.keySet(curObj.__proto__),
562                                builtinMethods[curObj] || [],  // curObj is a builtin constructor
563                                builtinMethods[curObj.__proto__.constructor] ||
564                                    [],  // curObj is made from a builtin constructor
565                                curObj == global ? extraGlobals : [],
566                                customComplete(curObj));
567 
568         var noDuplicates =
569             {};  // see http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/
570         for (var i = 0; i < possibilities.length; i++) {
571             var p = possibilities[i];
572             if (typeof(curObj[p]) == "undefined" && curObj != global)
573                 continue;  // extraGlobals aren't in the global object
574             if (p.length == 0 || p.length < lastPrefix.length)
575                 continue;
576             if (lastPrefix[0] != '_' && isPrivate(p))
577                 continue;
578             if (p.match(/^[0-9]+$/))
579                 continue;  // don't array number indexes
580             if (p.substr(0, lastPrefix.length).toLowerCase() != lastPrefixLowercase)
581                 continue;
582 
583             var completion = beginning + p;
584             if (curObj[p] && curObj[p].constructor == Function && p != 'constructor')
585                 completion += '(';
586 
587             noDuplicates[completion] = 0;
588         }
589 
590         var ret = [];
591         for (var i in noDuplicates)
592             ret.push(i);
593 
594         return ret;
595     };
596 
597     // this is the actual function that gets assigned to shellAutocomplete
598     return function(prefix) {
599         try {
600             __autocomplete__ = worker(prefix).sort();
601         } catch (e) {
602             print("exception during autocomplete: " + tojson(e.message));
603             __autocomplete__ = [];
604         }
605     };
606 }();
607 
608 shellAutocomplete.showPrivate = false;  // toggle to show (useful when working on internals)
609 
610 shellHelper = function(command, rest, shouldPrint) {
611     command = command.trim();
612     var args = rest.trim().replace(/\s*;$/, "").split("\s+");
613 
614     if (!shellHelper[command])
615         throw Error("no command [" + command + "]");
616 
617     var res = shellHelper[command].apply(null, args);
618     if (shouldPrint) {
619         shellPrintHelper(res);
620     }
621     return res;
622 };
623 
624 shellHelper.use = function(dbname) {
625     var s = "" + dbname;
626     if (s == "") {
627         print("bad use parameter");
628         return;
629     }
630     db = db.getMongo().getDB(dbname);
631     print("switched to db " + db.getName());
632 };
633 
634 shellHelper.set = function(str) {
635     if (str == "") {
636         print("bad use parameter");
637         return;
638     }
639     tokens = str.split(" ");
640     param = tokens[0];
641     value = tokens[1];
642 
643     if (value == undefined)
644         value = true;
645     // value comes in as a string..
646     if (value == "true")
647         value = true;
648     if (value == "false")
649         value = false;
650 
651     if (param == "verbose") {
652         _verboseShell = value;
653     }
654     print("set " + param + " to " + value);
655 };
656 
657 shellHelper.it = function() {
658     if (typeof(___it___) == "undefined" || ___it___ == null) {
659         print("no cursor");
660         return;
661     }
662     shellPrintHelper(___it___);
663 };
664 
665 shellHelper.show = function(what) {
666     assert(typeof what == "string");
667 
668     var args = what.split(/\s+/);
669     what = args[0];
670     args = args.splice(1);
671 
672     if (what == "profile") {
673         if (db.system.profile.count() == 0) {
674             print("db.system.profile is empty");
675             print("Use db.setProfilingLevel(2) will enable profiling");
676             print("Use db.system.profile.find() to show raw profile entries");
677         } else {
678             print();
679             db.system.profile.find({millis: {$gt: 0}})
680                 .sort({$natural: -1})
681                 .limit(5)
682                 .forEach(function(x) {
683                     print("" + x.op + "\t" + x.ns + " " + x.millis + "ms " +
684                           String(x.ts).substring(0, 24));
685                     var l = "";
686                     for (var z in x) {
687                         if (z == "op" || z == "ns" || z == "millis" || z == "ts")
688                             continue;
689 
690                         var val = x[z];
691                         var mytype = typeof(val);
692 
693                         if (mytype == "string" || mytype == "number")
694                             l += z + ":" + val + " ";
695                         else if (mytype == "object")
696                             l += z + ":" + tojson(val) + " ";
697                         else if (mytype == "boolean")
698                             l += z + " ";
699                         else
700                             l += z + ":" + val + " ";
701                     }
702                     print(l);
703                     print("\n");
704                 });
705         }
706         return "";
707     }
708 
709     if (what == "users") {
710         db.getUsers().forEach(printjson);
711         return "";
712     }
713 
714     if (what == "roles") {
715         db.getRoles({showBuiltinRoles: true}).forEach(printjson);
716         return "";
717     }
718 
719     if (what == "collections" || what == "tables") {
720         db.getCollectionNames().forEach(function(x) {
721             print(x);
722         });
723         return "";
724     }
725 
726     if (what == "dbs" || what == "databases") {
727         var dbs = db.getMongo().getDBs();
728         var dbinfo = [];
729         var maxNameLength = 0;
730         var maxGbDigits = 0;
731 
732         dbs.databases.forEach(function(x) {
733             var sizeStr = (x.sizeOnDisk / 1024 / 1024 / 1024).toFixed(3);
734             var nameLength = x.name.length;
735             var gbDigits = sizeStr.indexOf(".");
736 
737             if (nameLength > maxNameLength)
738                 maxNameLength = nameLength;
739             if (gbDigits > maxGbDigits)
740                 maxGbDigits = gbDigits;
741 
742             dbinfo.push({
743                 name: x.name,
744                 size: x.sizeOnDisk,
745                 size_str: sizeStr,
746                 name_size: nameLength,
747                 gb_digits: gbDigits
748             });
749         });
750 
751         dbinfo.sort(compareOn('name'));
752         dbinfo.forEach(function(db) {
753             var namePadding = maxNameLength - db.name_size;
754             var sizePadding = maxGbDigits - db.gb_digits;
755             var padding = Array(namePadding + sizePadding + 3).join(" ");
756             if (db.size > 1) {
757                 print(db.name + padding + db.size_str + "GB");
758             } else {
759                 print(db.name + padding + "(empty)");
760             }
761         });
762 
763         return "";
764     }
765 
766     if (what == "log") {
767         var n = "global";
768         if (args.length > 0)
769             n = args[0];
770 
771         var res = db.adminCommand({getLog: n});
772         if (!res.ok) {
773             print("Error while trying to show " + n + " log: " + res.errmsg);
774             return "";
775         }
776         for (var i = 0; i < res.log.length; i++) {
777             print(res.log[i]);
778         }
779         return "";
780     }
781 
782     if (what == "logs") {
783         var res = db.adminCommand({getLog: "*"});
784         if (!res.ok) {
785             print("Error while trying to show logs: " + res.errmsg);
786             return "";
787         }
788         for (var i = 0; i < res.names.length; i++) {
789             print(res.names[i]);
790         }
791         return "";
792     }
793 
794     if (what == "startupWarnings") {
795         var dbDeclared, ex;
796         try {
797             // !!db essentially casts db to a boolean
798             // Will throw a reference exception if db hasn't been declared.
799             dbDeclared = !!db;
800         } catch (ex) {
801             dbDeclared = false;
802         }
803         if (dbDeclared) {
804             var res = db.adminCommand({getLog: "startupWarnings"});
805             if (res.ok) {
806                 if (res.log.length == 0) {
807                     return "";
808                 }
809                 print("Server has startup warnings: ");
810                 for (var i = 0; i < res.log.length; i++) {
811                     print(res.log[i]);
812                 }
813                 return "";
814             } else if (res.errmsg == "no such cmd: getLog") {
815                 // Don't print if the command is not available
816                 return "";
817             } else if (res.code == 13 /*unauthorized*/ || res.errmsg == "unauthorized" ||
818                        res.errmsg == "need to login") {
819                 // Don't print if startupWarnings command failed due to auth
820                 return "";
821             } else {
822                 print("Error while trying to show server startup warnings: " + res.errmsg);
823                 return "";
824             }
825         } else {
826             print("Cannot show startupWarnings, \"db\" is not set");
827             return "";
828         }
829     }
830 
831     if (what == "automationNotices") {
832         var dbDeclared, ex;
833         try {
834             // !!db essentially casts db to a boolean
835             // Will throw a reference exception if db hasn't been declared.
836             dbDeclared = !!db;
837         } catch (ex) {
838             dbDeclared = false;
839         }
840 
841         if (dbDeclared) {
842             var res = db.runCommand({isMaster: 1, forShell: 1});
843             if (!res.ok) {
844                 print("Note: Cannot determine if automation is active");
845                 return "";
846             }
847 
848             if (res.hasOwnProperty("automationServiceDescriptor")) {
849                 print("Note: This server is managed by automation service '" +
850                       res.automationServiceDescriptor + "'.");
851                 print(
852                     "Note: Many administrative actions are inappropriate, and may be automatically reverted.");
853                 return "";
854             }
855 
856             return "";
857 
858         } else {
859             print("Cannot show automationNotices, \"db\" is not set");
860             return "";
861         }
862     }
863 
864     throw Error("don't know how to show [" + what + "]");
865 
866 };
867 
868 Math.sigFig = function(x, N) {
869     if (!N) {
870         N = 3;
871     }
872     var p = Math.pow(10, N - Math.ceil(Math.log(Math.abs(x)) / Math.log(10)));
873     return Math.round(x * p) / p;
874 };
875 
876 var Random = (function() {
877     var initialized = false;
878     var errorMsg =
879         "The random number generator hasn't been seeded yet; " + "call Random.setRandomSeed()";
880 
881     // Set the random generator seed.
882     function srand(s) {
883         initialized = true;
884         return _srand(s);
885     }
886 
887     // Set the random generator seed & print the result.
888     function setRandomSeed(s) {
889         var seed = srand(s);
890         print("setting random seed: " + seed);
891     }
892 
893     // Generate a random number 0 <= r < 1.
894     function rand() {
895         if (!initialized) {
896             throw new Error(errorMsg);
897         }
898         return _rand();
899     }
900 
901     // Generate a random integer 0 <= r < n.
902     function randInt(n) {
903         if (!initialized) {
904             throw new Error(errorMsg);
905         }
906         return Math.floor(rand() * n);
907     }
908 
909     // Generate a random value from the exponential distribution with the specified mean.
910     function genExp(mean) {
911         if (!initialized) {
912             throw new Error(errorMsg);
913         }
914         var r = rand();
915         if (r == 0) {
916             r = rand();
917             if (r == 0) {
918                 r = 0.000001;
919             }
920         }
921         return -Math.log(r) * mean;
922     }
923 
924     /**
925      * Generate a random value from the normal distribution with specified 'mean' and
926      * 'standardDeviation'.
927      */
928     function genNormal(mean, standardDeviation) {
929         if (!initialized) {
930             throw new Error(errorMsg);
931         }
932         // See http://en.wikipedia.org/wiki/Marsaglia_polar_method
933         while (true) {
934             var x = (2 * rand()) - 1;
935             var y = (2 * rand()) - 1;
936             var s = (x * x) + (y * y);
937 
938             if (s > 0 && s < 1) {
939                 var standardNormal = x * Math.sqrt(-2 * Math.log(s) / s);
940                 return mean + (standardDeviation * standardNormal);
941             }
942         }
943     }
944 
945     return {
946         genExp: genExp,
947         genNormal: genNormal,
948         rand: rand,
949         randInt: randInt,
950         setRandomSeed: setRandomSeed,
951         srand: srand,
952     };
953 
954 })();
955 
956 Geo = {};
957 Geo.distance = function(a, b) {
958     var ax = null;
959     var ay = null;
960     var bx = null;
961     var by = null;
962 
963     for (var key in a) {
964         if (ax == null)
965             ax = a[key];
966         else if (ay == null)
967             ay = a[key];
968     }
969 
970     for (var key in b) {
971         if (bx == null)
972             bx = b[key];
973         else if (by == null)
974             by = b[key];
975     }
976 
977     return Math.sqrt(Math.pow(by - ay, 2) + Math.pow(bx - ax, 2));
978 };
979 
980 Geo.sphereDistance = function(a, b) {
981     var ax = null;
982     var ay = null;
983     var bx = null;
984     var by = null;
985 
986     // TODO swap order of x and y when done on server
987     for (var key in a) {
988         if (ax == null)
989             ax = a[key] * (Math.PI / 180);
990         else if (ay == null)
991             ay = a[key] * (Math.PI / 180);
992     }
993 
994     for (var key in b) {
995         if (bx == null)
996             bx = b[key] * (Math.PI / 180);
997         else if (by == null)
998             by = b[key] * (Math.PI / 180);
999     }
1000 
1001     var sin_x1 = Math.sin(ax), cos_x1 = Math.cos(ax);
1002     var sin_y1 = Math.sin(ay), cos_y1 = Math.cos(ay);
1003     var sin_x2 = Math.sin(bx), cos_x2 = Math.cos(bx);
1004     var sin_y2 = Math.sin(by), cos_y2 = Math.cos(by);
1005 
1006     var cross_prod = (cos_y1 * cos_x1 * cos_y2 * cos_x2) + (cos_y1 * sin_x1 * cos_y2 * sin_x2) +
1007         (sin_y1 * sin_y2);
1008 
1009     if (cross_prod >= 1 || cross_prod <= -1) {
1010         // fun with floats
1011         assert(Math.abs(cross_prod) - 1 < 1e-6);
1012         return cross_prod > 0 ? 0 : Math.PI;
1013     }
1014 
1015     return Math.acos(cross_prod);
1016 };
1017 
1018 rs = function() {
1019     return "try rs.help()";
1020 };
1021 
1022 /**
1023  * This method is intended to aid in the writing of tests. It takes a host's address, desired state,
1024  * and replicaset and waits either timeout milliseconds or until that reaches the desired state.
1025  *
1026  * It should be used instead of awaitRSClientHost when there is no MongoS with a connection to the
1027  * replica set.
1028  */
1029 _awaitRSHostViaRSMonitor = function(hostAddr, desiredState, rsName, timeout) {
1030     timeout = timeout || 60 * 1000;
1031 
1032     if (desiredState == undefined) {
1033         desiredState = {ok: true};
1034     }
1035 
1036     print("Awaiting " + hostAddr + " to be " + tojson(desiredState) + " in " + " rs " + rsName);
1037 
1038     var tests = 0;
1039     assert.soon(
1040         function() {
1041             var stats = _replMonitorStats(rsName);
1042             if (tests++ % 10 == 0) {
1043                 printjson(stats);
1044             }
1045 
1046             for (var i = 0; i < stats.length; i++) {
1047                 var node = stats[i];
1048                 printjson(node);
1049                 if (node["addr"] !== hostAddr)
1050                     continue;
1051 
1052                 // Check that *all* hostAddr properties match desiredState properties
1053                 var stateReached = true;
1054                 for (var prop in desiredState) {
1055                     if (isObject(desiredState[prop])) {
1056                         if (!friendlyEqual(sortDoc(desiredState[prop]), sortDoc(node[prop]))) {
1057                             stateReached = false;
1058                             break;
1059                         }
1060                     } else if (node[prop] !== desiredState[prop]) {
1061                         stateReached = false;
1062                         break;
1063                     }
1064                 }
1065                 if (stateReached) {
1066                     printjson(stats);
1067                     return true;
1068                 }
1069             }
1070             return false;
1071         },
1072         "timed out waiting for replica set member: " + hostAddr + " to reach state: " +
1073             tojson(desiredState),
1074         timeout);
1075 };
1076 
1077 rs.help = function() {
1078     print(
1079         "\trs.status()                                { replSetGetStatus : 1 } checks repl set status");
1080     print(
1081         "\trs.initiate()                              { replSetInitiate : null } initiates set with default settings");
1082     print(
1083         "\trs.initiate(cfg)                           { replSetInitiate : cfg } initiates set with configuration cfg");
1084     print(
1085         "\trs.conf()                                  get the current configuration object from local.system.replset");
1086     print(
1087         "\trs.reconfig(cfg)                           updates the configuration of a running replica set with cfg (disconnects)");
1088     print(
1089         "\trs.add(hostportstr)                        add a new member to the set with default attributes (disconnects)");
1090     print(
1091         "\trs.add(membercfgobj)                       add a new member to the set with extra attributes (disconnects)");
1092     print(
1093         "\trs.addArb(hostportstr)                     add a new member which is arbiterOnly:true (disconnects)");
1094     print("\trs.stepDown([stepdownSecs, catchUpSecs])   step down as primary (disconnects)");
1095     print(
1096         "\trs.syncFrom(hostportstr)                   make a secondary sync from the given member");
1097     print(
1098         "\trs.freeze(secs)                            make a node ineligible to become primary for the time specified");
1099     print(
1100         "\trs.remove(hostportstr)                     remove a host from the replica set (disconnects)");
1101     print("\trs.slaveOk()                               allow queries on secondary nodes");
1102     print();
1103     print("\trs.printReplicationInfo()                  check oplog size and time range");
1104     print(
1105         "\trs.printSlaveReplicationInfo()             check replica set members and replication lag");
1106     print("\tdb.isMaster()                              check who is primary");
1107     print();
1108     print("\treconfiguration helpers disconnect from the database so the shell will display");
1109     print("\tan error, even if the command succeeds.");
1110 };
1111 rs.slaveOk = function(value) {
1112     return db.getMongo().setSlaveOk(value);
1113 };
1114 rs.status = function() {
1115     return db._adminCommand("replSetGetStatus");
1116 };
1117 rs.isMaster = function() {
1118     return db.isMaster();
1119 };
1120 rs.initiate = function(c) {
1121     return db._adminCommand({replSetInitiate: c});
1122 };
1123 rs.printSlaveReplicationInfo = function() {
1124     return db.printSlaveReplicationInfo();
1125 };
1126 rs.printReplicationInfo = function() {
1127     return db.printReplicationInfo();
1128 };
1129 rs._runCmd = function(c) {
1130     // after the command, catch the disconnect and reconnect if necessary
1131     var res = null;
1132     try {
1133         res = db.adminCommand(c);
1134     } catch (e) {
1135         if (("" + e).indexOf("error doing query") >= 0) {
1136             // closed connection.  reconnect.
1137             db.getLastErrorObj();
1138             var o = db.getLastErrorObj();
1139             if (o.ok) {
1140                 print("reconnected to server after rs command (which is normal)");
1141             } else {
1142                 printjson(o);
1143             }
1144         } else {
1145             print("shell got exception during repl set operation: " + e);
1146             print(
1147                 "in some circumstances, the primary steps down and closes connections on a reconfig");
1148         }
1149         return "";
1150     }
1151     return res;
1152 };
1153 rs.reconfig = function(cfg, options) {
1154     cfg.version = rs.conf().version + 1;
1155     cmd = {replSetReconfig: cfg};
1156     for (var i in options) {
1157         cmd[i] = options[i];
1158     }
1159     return this._runCmd(cmd);
1160 };
1161 rs.add = function(hostport, arb) {
1162     var cfg = hostport;
1163 
1164     var local = db.getSisterDB("local");
1165     assert(local.system.replset.count() <= 1,
1166            "error: local.system.replset has unexpected contents");
1167     var c = local.system.replset.findOne();
1168     assert(c, "no config object retrievable from local.system.replset");
1169 
1170     c.version++;
1171 
1172     var max = 0;
1173     for (var i in c.members)
1174         if (c.members[i]._id > max)
1175             max = c.members[i]._id;
1176     if (isString(hostport)) {
1177         cfg = {_id: max + 1, host: hostport};
1178         if (arb)
1179             cfg.arbiterOnly = true;
1180     } else if (arb == true) {
1181         throw Error("Expected first parameter to be a host-and-port string of arbiter, but got " +
1182                     tojson(hostport));
1183     }
1184 
1185     if (cfg._id == null) {
1186         cfg._id = max + 1;
1187     }
1188     c.members.push(cfg);
1189     return this._runCmd({replSetReconfig: c});
1190 };
1191 rs.syncFrom = function(host) {
1192     return db._adminCommand({replSetSyncFrom: host});
1193 };
1194 rs.stepDown = function(stepdownSecs, catchUpSecs) {
1195     var cmdObj = {replSetStepDown: stepdownSecs === undefined ? 60 : stepdownSecs};
1196     if (catchUpSecs !== undefined) {
1197         cmdObj['secondaryCatchUpPeriodSecs'] = catchUpSecs;
1198     }
1199     return db._adminCommand(cmdObj);
1200 };
1201 rs.freeze = function(secs) {
1202     return db._adminCommand({replSetFreeze: secs});
1203 };
1204 rs.addArb = function(hn) {
1205     return this.add(hn, true);
1206 };
1207 
1208 rs.conf = function() {
1209     var resp = db._adminCommand({replSetGetConfig: 1});
1210     if (resp.ok && !(resp.errmsg) && resp.config)
1211         return resp.config;
1212     else if (resp.errmsg && resp.errmsg.startsWith("no such cmd"))
1213         return db.getSisterDB("local").system.replset.findOne();
1214     throw new Error("Could not retrieve replica set config: " + tojson(resp));
1215 };
1216 rs.config = rs.conf;
1217 
1218 rs.remove = function(hn) {
1219     var local = db.getSisterDB("local");
1220     assert(local.system.replset.count() <= 1,
1221            "error: local.system.replset has unexpected contents");
1222     var c = local.system.replset.findOne();
1223     assert(c, "no config object retrievable from local.system.replset");
1224     c.version++;
1225 
1226     for (var i in c.members) {
1227         if (c.members[i].host == hn) {
1228             c.members.splice(i, 1);
1229             return db._adminCommand({replSetReconfig: c});
1230         }
1231     }
1232 
1233     return "error: couldn't find " + hn + " in " + tojson(c.members);
1234 };
1235 
1236 rs.debug = {};
1237 
1238 rs.debug.nullLastOpWritten = function(primary, secondary) {
1239     var p = connect(primary + "/local");
1240     var s = connect(secondary + "/local");
1241     s.getMongo().setSlaveOk();
1242 
1243     var secondToLast = s.oplog.rs.find().sort({$natural: -1}).limit(1).next();
1244     var last = p.runCommand({
1245         findAndModify: "oplog.rs",
1246         query: {ts: {$gt: secondToLast.ts}},
1247         sort: {$natural: 1},
1248         update: {$set: {op: "n"}}
1249     });
1250 
1251     if (!last.value.o || !last.value.o._id) {
1252         print("couldn't find an _id?");
1253     } else {
1254         last.value.o = {_id: last.value.o._id};
1255     }
1256 
1257     print("nulling out this op:");
1258     printjson(last);
1259 };
1260 
1261 rs.debug.getLastOpWritten = function(server) {
1262     var s = db.getSisterDB("local");
1263     if (server) {
1264         s = connect(server + "/local");
1265     }
1266     s.getMongo().setSlaveOk();
1267 
1268     return s.oplog.rs.find().sort({$natural: -1}).limit(1).next();
1269 };
1270 
1271 help = shellHelper.help = function(x) {
1272     if (x == "mr") {
1273         print("\nSee also http://dochub.mongodb.org/core/mapreduce");
1274         print("\nfunction mapf() {");
1275         print("  // 'this' holds current document to inspect");
1276         print("  emit(key, value);");
1277         print("}");
1278         print("\nfunction reducef(key,value_array) {");
1279         print("  return reduced_value;");
1280         print("}");
1281         print("\ndb.mycollection.mapReduce(mapf, reducef[, options])");
1282         print("\noptions");
1283         print("{[query : <query filter object>]");
1284         print(" [, sort : <sort the query.  useful for optimization>]");
1285         print(" [, limit : <number of objects to return from collection>]");
1286         print(" [, out : <output-collection name>]");
1287         print(" [, keeptemp: <true|false>]");
1288         print(" [, finalize : <finalizefunction>]");
1289         print(" [, scope : <object where fields go into javascript global scope >]");
1290         print(" [, verbose : true]}\n");
1291         return;
1292     } else if (x == "connect") {
1293         print(
1294             "\nNormally one specifies the server on the mongo shell command line.  Run mongo --help to see those options.");
1295         print("Additional connections may be opened:\n");
1296         print("    var x = new Mongo('host[:port]');");
1297         print("    var mydb = x.getDB('mydb');");
1298         print("  or");
1299         print("    var mydb = connect('host[:port]/mydb');");
1300         print(
1301             "\nNote: the REPL prompt only auto-reports getLastError() for the shell command line connection.\n");
1302         return;
1303     } else if (x == "keys") {
1304         print("Tab completion and command history is available at the command prompt.\n");
1305         print("Some emacs keystrokes are available too:");
1306         print("  Ctrl-A start of line");
1307         print("  Ctrl-E end of line");
1308         print("  Ctrl-K del to end of line");
1309         print("\nMulti-line commands");
1310         print(
1311             "You can enter a multi line javascript expression.  If parens, braces, etc. are not closed, you will see a new line ");
1312         print(
1313             "beginning with '...' characters.  Type the rest of your expression.  Press Ctrl-C to abort the data entry if you");
1314         print("get stuck.\n");
1315     } else if (x == "misc") {
1316         print("\tb = new BinData(subtype,base64str)  create a BSON BinData value");
1317         print("\tb.subtype()                         the BinData subtype (0..255)");
1318         print("\tb.length()                          length of the BinData data in bytes");
1319         print("\tb.hex()                             the data as a hex encoded string");
1320         print("\tb.base64()                          the data as a base 64 encoded string");
1321         print("\tb.toString()");
1322         print();
1323         print(
1324             "\tb = HexData(subtype,hexstr)         create a BSON BinData value from a hex string");
1325         print("\tb = UUID(hexstr)                    create a BSON BinData value of UUID subtype");
1326         print("\tb = MD5(hexstr)                     create a BSON BinData value of MD5 subtype");
1327         print(
1328             "\t\"hexstr\"                            string, sequence of hex characters (no 0x prefix)");
1329         print();
1330         print("\to = new ObjectId()                  create a new ObjectId");
1331         print(
1332             "\to.getTimestamp()                    return timestamp derived from first 32 bits of the OID");
1333         print("\to.isObjectId");
1334         print("\to.toString()");
1335         print("\to.equals(otherid)");
1336         print();
1337         print(
1338             "\td = ISODate()                       like Date() but behaves more intuitively when used");
1339         print(
1340             "\td = ISODate('YYYY-MM-DD hh:mm:ss')    without an explicit \"new \" prefix on construction");
1341         return;
1342     } else if (x == "admin") {
1343         print("\tls([path])                      list files");
1344         print("\tpwd()                           returns current directory");
1345         print("\tlistFiles([path])               returns file list");
1346         print("\thostname()                      returns name of this host");
1347         print("\tcat(fname)                      returns contents of text file as a string");
1348         print("\tremoveFile(f)                   delete a file or directory");
1349         print("\tload(jsfilename)                load and execute a .js file");
1350         print("\trun(program[, args...])         spawn a program and wait for its completion");
1351         print("\trunProgram(program[, args...])  same as run(), above");
1352         print("\tsleep(m)                        sleep m milliseconds");
1353         print("\tgetMemInfo()                    diagnostic");
1354         return;
1355     } else if (x == "test") {
1356         print("\tMongoRunner.runMongod(args)   DELETES DATA DIR and then starts mongod");
1357         print("\t                              returns a connection to the new server");
1358         return;
1359     } else if (x == "") {
1360         print("\t" + "db.help()                    help on db methods");
1361         print("\t" + "db.mycoll.help()             help on collection methods");
1362         print("\t" + "sh.help()                    sharding helpers");
1363         print("\t" + "rs.help()                    replica set helpers");
1364         print("\t" + "help admin                   administrative help");
1365         print("\t" + "help connect                 connecting to a db help");
1366         print("\t" + "help keys                    key shortcuts");
1367         print("\t" + "help misc                    misc things to know");
1368         print("\t" + "help mr                      mapreduce");
1369         print();
1370         print("\t" + "show dbs                     show database names");
1371         print("\t" + "show collections             show collections in current database");
1372         print("\t" + "show users                   show users in current database");
1373         print(
1374             "\t" +
1375             "show profile                 show most recent system.profile entries with time >= 1ms");
1376         print("\t" + "show logs                    show the accessible logger names");
1377         print(
1378             "\t" +
1379             "show log [name]              prints out the last segment of log in memory, 'global' is default");
1380         print("\t" + "use <db_name>                set current database");
1381         print("\t" + "db.foo.find()                list objects in collection foo");
1382         print("\t" + "db.foo.find( { a : 1 } )     list objects in foo where a == 1");
1383         print(
1384             "\t" +
1385             "it                           result of the last line evaluated; use to further iterate");
1386         print("\t" +
1387               "DBQuery.shellBatchSize = x   set default number of items to display on shell");
1388         print("\t" + "exit                         quit the mongo shell");
1389     } else
1390         print("unknown help option");
1391 };
1392