// mongo/shell/shell_utils_extended.cpp /* * Copyright 2010 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pch.h" #include #include #include "mongo/scripting/engine.h" #include "mongo/shell/shell_utils.h" #include "mongo/shell/shell_utils_launcher.h" #include "mongo/util/file.h" #include "mongo/util/md5.hpp" #include "mongo/util/net/sock.h" #include "mongo/util/scopeguard.h" #include "mongo/util/text.h" namespace mongo { /** * These utilities are thread safe but do not provide mutually exclusive access to resources * identified by the caller. Dependent filesystem paths should not be accessed by different * threads. */ namespace shell_utils { BSONObj listFiles(const BSONObj& _args, void* data) { BSONObj cd = BSON( "0" << "." ); BSONObj args = _args.isEmpty() ? cd : _args; uassert( 10257 , "need to specify 1 argument to listFiles" , args.nFields() == 1 ); BSONArrayBuilder lst; string rootname = args.firstElement().valuestrsafe(); boost::filesystem::path root( rootname ); stringstream ss; ss << "listFiles: no such directory: " << rootname; string msg = ss.str(); uassert( 12581, msg.c_str(), boost::filesystem::exists( root ) ); boost::filesystem::directory_iterator end; boost::filesystem::directory_iterator i( root); while ( i != end ) { boost::filesystem::path p = *i; BSONObjBuilder b; b << "name" << p.generic_string(); b.appendBool( "isDirectory", is_directory( p ) ); if ( ! boost::filesystem::is_directory( p ) ) { try { b.append( "size" , (double)boost::filesystem::file_size( p ) ); } catch ( ... ) { i++; continue; } } lst.append( b.obj() ); i++; } BSONObjBuilder ret; ret.appendArray( "", lst.done() ); return ret.obj(); } BSONObj ls(const BSONObj& args, void* data) { BSONArrayBuilder ret; BSONObj o = listFiles(args, data); if( !o.isEmpty() ) { for( BSONObj::iterator i = o.firstElement().Obj().begin(); i.more(); ) { BSONObj f = i.next().Obj(); string name = f["name"].String(); if( f["isDirectory"].trueValue() ) { name += '/'; } ret << name; } } return BSON( "" << ret.arr() ); } /** Set process wide current working directory. */ BSONObj cd(const BSONObj& args, void* data) { #if defined(_WIN32) std::wstring dir = toWideString( args.firstElement().String().c_str() ); if( SetCurrentDirectory(dir.c_str()) ) return BSONObj(); #else string dir = args.firstElement().String(); if( chdir( dir.c_str() ) == 0 ) return BSONObj(); #endif return BSON( "" << "change directory failed" ); } BSONObj pwd(const BSONObj&, void* data) { boost::filesystem::path p = boost::filesystem::current_path(); return BSON( "" << p.string() ); } BSONObj hostname(const BSONObj&, void* data) { return BSON( "" << getHostName() ); } const int CANT_OPEN_FILE = 13300; BSONObj cat(const BSONObj& args, void* data) { BSONElement e = singleArg(args); stringstream ss; ifstream f(e.valuestrsafe()); uassert(CANT_OPEN_FILE, "couldn't open file", f.is_open() ); streamsize sz = 0; while( 1 ) { char ch = 0; // slow...maybe change one day f.get(ch); if( ch == 0 ) break; ss << ch; sz += 1; uassert(13301, "cat() : file to big to load as a variable", sz < 1024 * 1024 * 16); } return BSON( "" << ss.str() ); } BSONObj md5sumFile(const BSONObj& args, void* data) { BSONElement e = singleArg(args); stringstream ss; FILE* f = fopen(e.valuestrsafe(), "rb"); uassert(CANT_OPEN_FILE, "couldn't open file", f ); ON_BLOCK_EXIT(fclose, f); md5digest d; md5_state_t st; md5_init(&st); enum {BUFLEN = 4*1024}; char buffer[BUFLEN]; int bytes_read; while( (bytes_read = fread(buffer, 1, BUFLEN, f)) ) { md5_append( &st , (const md5_byte_t*)(buffer) , bytes_read ); } md5_finish(&st, d); return BSON( "" << digestToString( d ) ); } BSONObj mkdir(const BSONObj& args, void* data) { boost::filesystem::create_directories(args.firstElement().String()); return BSON( "" << true ); } BSONObj removeFile(const BSONObj& args, void* data) { BSONElement e = singleArg(args); bool found = false; boost::filesystem::path root( e.valuestrsafe() ); if ( boost::filesystem::exists( root ) ) { found = true; boost::filesystem::remove_all( root ); } BSONObjBuilder b; b.appendBool( "removed" , found ); return b.obj(); } /** * @param args - [ source, destination ] * copies file 'source' to 'destination'. Errors if the 'destination' file already exists. */ BSONObj copyFile(const BSONObj& args, void* data) { uassert(13619, "copyFile takes 2 arguments", args.nFields() == 2); BSONObjIterator it(args); const std::string source = it.next().str(); const std::string destination = it.next().str(); boost::filesystem::copy_file(source, destination); return undefinedReturn; } BSONObj getHostName(const BSONObj& a, void* data) { uassert( 13411, "getHostName accepts no arguments", a.nFields() == 0 ); char buf[260]; // HOST_NAME_MAX is usually 255 verify(gethostname(buf, 260) == 0); buf[259] = '\0'; return BSON("" << buf); } void installShellUtilsExtended( Scope& scope ) { scope.injectNative( "getHostName" , getHostName ); scope.injectNative( "removeFile" , removeFile ); scope.injectNative( "copyFile" , copyFile ); scope.injectNative( "listFiles" , listFiles ); scope.injectNative( "ls" , ls ); scope.injectNative( "pwd", pwd ); scope.injectNative( "cd", cd ); scope.injectNative( "cat", cat ); scope.injectNative( "hostname", hostname); scope.injectNative( "md5sumFile", md5sumFile ); scope.injectNative( "mkdir" , mkdir ); } } }