diff options
| author | Antonin Kral <a.kral@bobek.cz> | 2013-07-17 16:21:49 +0200 |
|---|---|---|
| committer | Antonin Kral <a.kral@bobek.cz> | 2013-07-17 16:21:49 +0200 |
| commit | e3904bf97e74cbbe5dde6b3abc3e797ad7d26b5d (patch) | |
| tree | f2d76fa86bfce0e897a616e02551bdd7d9913682 /src | |
| parent | bef4346b845150d5318fd7a0acb43554f0d40a06 (diff) | |
Imported Upstream version 2.4.5upstream/2.4.5
Diffstat (limited to 'src')
90 files changed, 4785 insertions, 1338 deletions
diff --git a/src/SConscript.client b/src/SConscript.client index 74bd73c9c66..e30c73fcbb4 100644 --- a/src/SConscript.client +++ b/src/SConscript.client @@ -2,14 +2,19 @@ # This SConscript describes build and install rules for the Mongo C++ driver and associated exmaple # programs. +Import('env has_option installSetup use_system_version_of_library') -Import('env clientEnv') +Import('nix linux darwin windows') + +buildShared = False +if has_option("sharedclient"): + buildShared = True env.Command(['mongo/base/error_codes.h', 'mongo/base/error_codes.cpp',], ['mongo/base/generate_error_codes.py', 'mongo/base/error_codes.err'], '$PYTHON $SOURCES $TARGETS') -env.Command(['mongo/db/auth/action_type.h', 'mongo/db/auth/action_type.cpp'], +env.Command(['mongo/db/auth/action_type.h', 'mongo/db/auth/action_type.cpp'], ['mongo/db/auth/generate_action_types.py', 'mongo/db/auth/action_types.txt'], '$PYTHON $SOURCES $TARGETS') @@ -37,7 +42,6 @@ clientSourceBasic = [ 'mongo/client/dbclient.cpp', 'mongo/client/dbclient_rs.cpp', 'mongo/client/dbclientcursor.cpp', - 'mongo/client/distlock.cpp', 'mongo/client/gridfs.cpp', 'mongo/client/model.cpp', 'mongo/client/sasl_client_authenticate.cpp', @@ -60,6 +64,7 @@ clientSourceBasic = [ 'mongo/util/concurrency/mutexdebugger.cpp', 'mongo/util/debug_util.cpp', 'mongo/util/stacktrace.cpp', + 'mongo/util/file.cpp', 'mongo/util/file_allocator.cpp', 'mongo/util/fail_point.cpp', 'mongo/util/fail_point_registry.cpp', @@ -76,6 +81,8 @@ clientSourceBasic = [ 'mongo/util/net/sock.cpp', 'mongo/util/net/ssl_manager.cpp', 'mongo/util/password.cpp', + 'mongo/util/processinfo.cpp', + env.File('mongo/util/processinfo_${PYSYSPLATFORM}.cpp'), 'mongo/util/ramlog.cpp', 'mongo/util/signal_handlers.cpp', 'mongo/util/stringutils.cpp', @@ -85,14 +92,17 @@ clientSourceBasic = [ 'mongo/util/trace.cpp', 'mongo/util/util.cpp', 'mongo/util/version.cpp', + 'third_party/murmurhash3/MurmurHash3.cpp', ] clientSourceSasl = ['mongo/client/sasl_client_authenticate_impl.cpp', - 'mongo/util/gsasl_session.cpp'] + 'mongo/client/sasl_client_session.cpp'] clientSourceAll = clientSourceBasic + clientSourceSasl -if env['MONGO_BUILD_SASL_CLIENT']: +usingSasl = env['MONGO_BUILD_SASL_CLIENT'] + +if usingSasl: clientSource = clientSourceAll else: clientSource = clientSourceBasic @@ -125,8 +135,8 @@ clientHeaderDirectories = [ "util/", "util/concurrency/", "util/mongoutils/", - "util/net/", - "" + "util/net/", + "" ] clientHeaders = [] @@ -134,16 +144,145 @@ for path in clientHeaderDirectories: clientHeaders.extend(Glob('mongo/%s/*.h' % path)) clientHeaders.extend(Glob('mongo/%s/*.hpp' % path)) -mongoclient_lib = env.Library('mongoclient', clientSource), -mongoclient_install = env.Install('#/', [ - mongoclient_lib, - #env.SharedLibrary('mongoclient', clientSource), - ]) -env.Alias('mongoclient', mongoclient_install) +# This relies on static and shared objects being the same, since we will link these object +# files twice: once into a .a, and another time into a .so +clientObjects = [env.Object(source) for source in clientSource] + +mongoClientLibs = [] +mongoClientLibDeps = [] +mongoClientSysLibDeps = [] + +if usingSasl: + mongoClientSysLibDeps += ["sasl2"] + +if not use_system_version_of_library("boost"): + mongoClientLibDeps.append(['$BUILD_DIR/third_party/shim_boost']) + +mongoClientInstalls = [] +mongoClientPrefixInstalls = [] + +if windows and buildShared: + print("Building the client driver as a DLL is not supported, see SERVER-5650") + Exit(1) + +staticLibEnv = env.Clone() +staticLibEnv.AppendUnique( + LIBDEPS=mongoClientLibDeps, + SYSLIBDEPS=mongoClientSysLibDeps) + +mongoClientStaticLib = staticLibEnv.StaticLibrary( + 'mongoclient', clientObjects), +mongoClientInstalls.append(staticLibEnv.Install('#/', mongoClientStaticLib)) + +if installSetup.libraries: + mongoClientPrefixInstalls.append(env.Install("$INSTALL_DIR/lib", mongoClientStaticLib)) + +mongoClientSharedLib = None + +if buildShared: + + # TODO: When we are ready to set a SONAME for mongoclient, set SHLIBVERSION=x.y.z in this + # environment to enable SCons versioned shared library support, and then change the two + # 'Install' calls in this block to 'InstallVersionedLibrary'. SHLIBVERSION and + # InstallVersionedLibrary support is only stable in SCons > 2.3.0, so if you add support + # here, be sure to add an EnsuredSconsVersion here as well. + sharedLibEnv = env.Clone() + sharedLibEnv.AppendUnique( + LIBS=mongoClientLibs + mongoClientSysLibDeps, + # TODO: This currently causes the files for the libdep to get dragged into dependents + # of this shared library, incorrectly. We need to patch up libdeps to treat shared + # libraries as dependency terminals. + LIBDEPS=mongoClientLibDeps) + + if linux: + sharedLibEnv.AppendUnique(SHLINKFLAGS=["-Wl,--as-needed", "-Wl,-zdefs"]) + + mongoClientSharedLib = sharedLibEnv.SharedLibrary('mongoclient', clientObjects) + + mongoClientSharedLibInstall = sharedLibEnv.Install( + '#/sharedclient', mongoClientSharedLib) + + if darwin: + # Set up the copy of the client library in #/sharedclient so that things that link + # against it record the local directory as the install_name. + sharedLibEnv.AddPostAction( + mongoClientSharedLibInstall, + "install_name_tool -id @executable_path/%s %s" % ( + mongoClientSharedLibInstall[0].name, + mongoClientSharedLibInstall[0] + )) + mongoClientInstalls.append(mongoClientSharedLibInstall) -clientTests = clientEnv.Install('#/', [ - clientEnv.Program(target, - [source, mongoclient_lib]) for (target, source) in exampleSourceMap]) + if installSetup.libraries: + mongoClientSharedLibPrefixInstall = sharedLibEnv.Install( + '$INSTALL_DIR/lib', mongoClientSharedLib) + if darwin: + sharedLibEnv.AddPostAction( + mongoClientSharedLibPrefixInstall, + "install_name_tool -id %s %s" % ( + mongoClientSharedLibPrefixInstall[0], + mongoClientSharedLibPrefixInstall[0] + )) + mongoClientPrefixInstalls.append(mongoClientSharedLibPrefixInstall) + +env.Alias('mongoclient', mongoClientInstalls) + +if installSetup.headers: + for x in clientHeaderDirectories: + inst = env.Install("$INSTALL_DIR/include/mongo/" + x, + [Glob('mongo/%s*.h' % x), Glob('mongo/%s*.hpp' % x)]) + env.AddPostAction(inst, Chmod('$TARGET', 0644)) + mongoClientPrefixInstalls.append(inst) + +if installSetup.headers or installSetup.libraries: + env.Alias('install-mongoclient', mongoClientPrefixInstalls) + +clientEnv = env.Clone() +clientEnv['CPPDEFINES'].remove('MONGO_EXPOSE_MACROS') + +# Compile the example files to .o so that we can link them twice: once statically, once shared. +exampleObjMap = [(target, clientEnv.Object(source)) for (target, source) in exampleSourceMap] + +# Create an environment for linking the examples to the static library. For out of tree builds, +# we need to use LIBS. For in-tree builds we need LIBDEPS. +staticClientEnv = clientEnv.Clone() +if '_LIBDEPS' in clientEnv: + staticClientEnv.PrependUnique(LIBDEPS=[mongoClientStaticLib]) +else: + # We need the mongo client library to preceed the boost libraries. + staticClientEnv.PrependUnique(LIBS=[mongoClientStaticLib]) + +# Build each statically linked client program +staticClientPrograms = [staticClientEnv.Program(target, obj) for (target, obj) in exampleObjMap] + +# Install them to the root, and append the install targets to the list of client tests +clientTests = staticClientEnv.Install("#/", staticClientPrograms) + +# Do the same for the shared library case, if we are doing that. +if buildShared: + sharedClientEnv = clientEnv.Clone() + + # Arrange for the tests to link against the mongoclient in the #/sharedclient directory. + sharedClientEnv.PrependUnique( + LIBS=['mongoclient'], + LIBPATH=["#/sharedclient"] + ) + + # Deal with the different lookup models between regular UNIX and Darwin. For regular unix, + # we set $ORIGIN to pull the copy we run against from the current directory + # (#/sharedclient). On Darwin, the staged copy of the mongoclient dylib in #sharedclient + # has @executable path set as its install_name, so we pick up the same behavior that way. + if nix and not darwin: + sharedClientEnv.PrependUnique( + LINKFLAGS="-Wl,-z,origin", + RPATH=[sharedClientEnv.Literal("\\$$ORIGIN")]) + + sharedClientPrograms = [ + sharedClientEnv.Program("sharedclient/" + target, obj) for (target, obj) in exampleObjMap] + env.Depends(sharedClientPrograms, mongoClientInstalls) + + sharedClientProgramInstalls = sharedClientEnv.Install("#/sharedclient", sharedClientPrograms) + clientTests.append(sharedClientProgramInstalls) clientTests.append( clientEnv.Install('#/', clientEnv.Program('bsondemo', 'mongo/bson/bsondemo/bsondemo.cpp'))) @@ -161,6 +300,7 @@ env.Install( 'mongo/base/error_codes.err', 'mongo/db/auth/generate_action_types.py', 'mongo/db/auth/action_types.txt', + 'third_party/murmurhash3/MurmurHash3.h', '#buildscripts/make_archive.py', clientSourceAll, clientHeaders, @@ -173,13 +313,3 @@ env.Install( '--transform distsrc/client=$CLIENT_DIST_BASENAME ' '--transform =$CLIENT_DIST_BASENAME/ ' '${TEMPFILE(SOURCES[1:])}')) - -# install -prefix = GetOption("prefix") - -env.Install(prefix + "/lib", '${LIBPREFIX}mongoclient${LIBSUFFIX}') - -for x in clientHeaderDirectories: - inst = env.Install(prefix + "/include/mongo/" + x, - [Glob('mongo/%s*.h' % x), Glob('mongo/%s*.hpp' % x)]) - env.AddPostAction(inst, Chmod('$TARGET', 0644)) diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 9335eaa39a2..27067e32e9d 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -166,12 +166,15 @@ commonFiles = [ "pch.cpp", "db/dbmessage.cpp" ] -commonSysLibdeps = [] +extraCommonLibdeps = [] if env['MONGO_BUILD_SASL_CLIENT']: - commonFiles.extend(['client/sasl_client_authenticate_impl.cpp', - 'util/gsasl_session.cpp']) - commonSysLibdeps.append('gsasl') + env.StaticLibrary('sasl_client_session', + ['client/sasl_client_session.cpp'], + LIBDEPS=['foundation'], + SYSLIBDEPS=['sasl2']) + commonFiles.extend(['client/sasl_client_authenticate_impl.cpp']) + extraCommonLibdeps.append('sasl_client_session') # handle processinfo* processInfoFiles = [ "util/processinfo.cpp" ] @@ -218,8 +221,8 @@ env.StaticLibrary('mongocommon', commonFiles, 'fail_point', '$BUILD_DIR/third_party/pcrecpp', '$BUILD_DIR/third_party/murmurhash3/murmurhash3', - '$BUILD_DIR/third_party/shim_boost'], - SYSLIBDEPS=commonSysLibdeps) + '$BUILD_DIR/third_party/shim_boost'] + + extraCommonLibdeps) env.StaticLibrary("coredb", [ "client/parallel.cpp", @@ -656,9 +659,6 @@ env.Library("clientandshell", ["client/clientAndShell.cpp"], "notmongodormongos"]) env.Library("allclient", "client/clientOnly.cpp", LIBDEPS=["clientandshell"]) -if has_option( "sharedclient" ): - sharedClientLibName = str( env.SharedLibrary( "mongoclient", [], LIBDEPS=["allclient"] )[0] ) - # dbtests test binary env.StaticLibrary('testframework', ['dbtests/framework.cpp'], LIBDEPS=['unittest/unittest']) @@ -798,20 +798,6 @@ if shellEnv is not None: env.Alias( "core", [ '#/%s' % b for b in [ add_exe( "mongo" ), add_exe( "mongod" ), add_exe( "mongos" ) ] ] ) -#headers -if installSetup.headers: - for id in [ "", "util/", "util/net/", "util/mongoutils/", "util/concurrency/", "db/", - "db/stats/", "db/repl/", "db/ops/", "client/", "bson/", "bson/util/", "s/", - "scripting/", "base/", "platform/" ]: - env.Install( "$INSTALL_DIR/include/" + id, Glob( id + "*.h" ) ) - env.Install( "$INSTALL_DIR/include/" + id, Glob( id + "*.hpp" ) ) - -#lib -if installSetup.libraries: - env.Install('$INSTALL_DIR/$NIX_LIB_DIR', '#${LIBPREFIX}mongoclient${LIBSUFFIX}') - if has_option( "sharedclient" ): - env.Install( "$INSTALL_DIR/$NIX_LIB_DIR", '#${SHLIBPREFIX}mongoclient${SHLIBSUFFIX}') - # Stage the top-level mongodb banners distsrc = env.Dir('#distsrc') env.Append(MODULE_BANNERS = [distsrc.File('README'), diff --git a/src/mongo/base/SConscript b/src/mongo/base/SConscript index a2a001adf4c..725a0f6286f 100644 --- a/src/mongo/base/SConscript +++ b/src/mongo/base/SConscript @@ -16,7 +16,11 @@ env.StaticLibrary('base', ['configuration_variable_manager.cpp', 'make_string_vector.cpp', 'parse_number.cpp', 'status.cpp', - 'string_data.cpp']) + 'string_data.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/third_party/murmurhash3/murmurhash3', + ]) env.CppUnitTest('configuration_variable_manager_test', ['configuration_variable_manager_test.cpp'], diff --git a/src/mongo/base/string_data-inl.h b/src/mongo/base/string_data-inl.h index 9d6d024df9c..efad95154ea 100644 --- a/src/mongo/base/string_data-inl.h +++ b/src/mongo/base/string_data-inl.h @@ -90,6 +90,18 @@ namespace mongo { } + inline size_t StringData::rfind( char c, size_t fromPos ) const { + const size_t sz = size(); + if ( fromPos > sz ) + fromPos = sz; + + for ( const char* cur = _data + fromPos; cur > _data; --cur ) { + if ( *(cur - 1) == c ) + return (cur - _data) - 1; + } + return string::npos; + } + inline StringData StringData::substr( size_t pos, size_t n ) const { if ( pos > size() ) throw std::out_of_range( "out of range" ); diff --git a/src/mongo/base/string_data.cpp b/src/mongo/base/string_data.cpp index 8a65e30c9fa..31ab21f1144 100644 --- a/src/mongo/base/string_data.cpp +++ b/src/mongo/base/string_data.cpp @@ -16,6 +16,7 @@ #include "mongo/base/string_data.h" #include <ostream> +#include <third_party/murmurhash3/MurmurHash3.h> namespace mongo { @@ -23,4 +24,10 @@ namespace mongo { return stream.write(value.rawData(), value.size()); } + size_t StringData::Hasher::operator() (const StringData& str) const { + unsigned out; + MurmurHash3_x86_32(str.rawData(), str.size(), 0, &out); + return out; + } + } // namespace mongo diff --git a/src/mongo/base/string_data.h b/src/mongo/base/string_data.h index dca41fa1e9a..3ffbba834e8 100644 --- a/src/mongo/base/string_data.h +++ b/src/mongo/base/string_data.h @@ -98,6 +98,7 @@ namespace mongo { size_t find( char c , size_t fromPos = 0 ) const; size_t find( const StringData& needle ) const; + size_t rfind( char c, size_t fromPos = string::npos ) const; /** * Returns true if 'prefix' is a substring of this instance, anchored at position 0. @@ -125,6 +126,15 @@ namespace mongo { string toString() const { return string(_data, size()); } char operator[] ( unsigned pos ) const { return _data[pos]; } + /** + * Functor compatible with std::hash for std::unordered_{map,set} + * Warning: The hash function is subject to change. Do not use in cases where hashes need + * to be consistent across versions. + */ + struct Hasher { + size_t operator() (const StringData& str) const; + }; + private: const char* _data; // is not guaranted to be null terminated (see "notes" above) mutable size_t _size; // 'size' does not include the null terminator diff --git a/src/mongo/base/string_data_test.cpp b/src/mongo/base/string_data_test.cpp index c8ca72ca76c..bbd40011002 100644 --- a/src/mongo/base/string_data_test.cpp +++ b/src/mongo/base/string_data_test.cpp @@ -123,6 +123,22 @@ namespace { ASSERT_EQUALS( string("foo").find( "" ), StringData("foo").find( "" ) ); } + TEST(Rfind, Char1) { + ASSERT_EQUALS( string::npos, StringData( "foo" ).rfind( 'a' ) ); + + ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f' ) ); + ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f', 3 ) ); + ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f', 2 ) ); + ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f', 1 ) ); + ASSERT_EQUALS( string::npos, StringData( "foo", 0 ).rfind( 'f' ) ); + + ASSERT_EQUALS( 2U, StringData( "foo" ).rfind( 'o' ) ); + ASSERT_EQUALS( 2U, StringData( "foo", 3 ).rfind( 'o' ) ); + ASSERT_EQUALS( 1U, StringData( "foo", 2 ).rfind( 'o' ) ); + ASSERT_EQUALS( string::npos, StringData( "foo", 1 ).rfind( 'o' ) ); + ASSERT_EQUALS( string::npos, StringData( "foo", 0 ).rfind( 'o' ) ); + } + // this is to verify we match std::string void SUBSTR_TEST_HELP(StringData big, StringData small, size_t start, size_t len) { ASSERT_EQUALS(small.toString(), big.toString().substr(start, len)); diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h index a4e51b75b61..f094ab91994 100644 --- a/src/mongo/bson/bsonelement.h +++ b/src/mongo/bson/bsonelement.h @@ -23,6 +23,7 @@ #include "mongo/bson/bsontypes.h" #include "mongo/bson/oid.h" +#include "mongo/platform/cstdint.h" #include "mongo/platform/float_utils.h" namespace mongo { @@ -237,8 +238,8 @@ namespace mongo { } // for objects the size *includes* the size of the size field - int objsize() const { - return *reinterpret_cast< const int* >( value() ); + size_t objsize() const { + return static_cast< const size_t >( *reinterpret_cast< const uint32_t* >( value() ) ); } /** Get a string's value. Also gives you start of the real data for an embedded object. diff --git a/src/mongo/bson/util/builder.h b/src/mongo/bson/util/builder.h index cfff6114980..a66a736bb5c 100644 --- a/src/mongo/bson/util/builder.h +++ b/src/mongo/bson/util/builder.h @@ -202,18 +202,19 @@ namespace mongo { /* returns the pre-grow write position */ inline char* grow(int by) { int oldlen = l; - l += by; - if ( l > size ) { - grow_reallocate(); + int newLen = l + by; + if ( newLen > size ) { + grow_reallocate(newLen); } + l = newLen; return data + oldlen; } private: /* "slow" portion of 'grow()' */ - void NOINLINE_DECL grow_reallocate() { + void NOINLINE_DECL grow_reallocate(int newLen) { int a = 64; - while( a < l ) + while( a < newLen ) a = a * 2; if ( a > BufferMaxSize ) { std::stringstream ss; diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 88abdb77a83..badd292124b 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -568,7 +568,7 @@ namespace mongo { _authMongoCR(userSource, user, password, errmsg, digestPassword)); } else if (saslClientAuthenticate != NULL) { - uassertStatusOK(saslClientAuthenticate(this, params, NULL)); + uassertStatusOK(saslClientAuthenticate(this, params)); } else { uasserted(ErrorCodes::BadValue, diff --git a/src/mongo/client/examples/httpClientTest.cpp b/src/mongo/client/examples/httpClientTest.cpp index d7d0659faa3..43617b592de 100644 --- a/src/mongo/client/examples/httpClientTest.cpp +++ b/src/mongo/client/examples/httpClientTest.cpp @@ -17,6 +17,7 @@ #include <iostream> +#include "mongo/base/init.h" #include "mongo/client/dbclient.h" #include "util/net/httpclient.h" @@ -39,7 +40,12 @@ void play( string url ) { } -int main( int argc, const char **argv ) { +int main( int argc, const char **argv, char **envp) { + +#ifdef MONGO_SSL + cmdLine.sslOnNormalPorts = true; + runGlobalInitializersOrDie(argc, argv, envp); +#endif int port = 27017; if ( argc != 1 ) { diff --git a/src/mongo/client/sasl_client_authenticate.cpp b/src/mongo/client/sasl_client_authenticate.cpp index b97e97583a4..24cd5000fd7 100644 --- a/src/mongo/client/sasl_client_authenticate.cpp +++ b/src/mongo/client/sasl_client_authenticate.cpp @@ -27,8 +27,7 @@ namespace mongo { using namespace mongoutils; Status (*saslClientAuthenticate)(DBClientWithCommands* client, - const BSONObj& saslParameters, - void* sessionHook) = NULL; + const BSONObj& saslParameters) = NULL; const char* const saslStartCommandName = "saslStart"; const char* const saslContinueCommandName = "saslContinue"; diff --git a/src/mongo/client/sasl_client_authenticate.h b/src/mongo/client/sasl_client_authenticate.h index f6d550abe87..c95cdc42660 100644 --- a/src/mongo/client/sasl_client_authenticate.h +++ b/src/mongo/client/sasl_client_authenticate.h @@ -34,34 +34,28 @@ namespace mongo { * * The "saslParameters" BSONObj should be initialized with zero or more of the * fields below. Which fields are required depends on the mechanism. Consult the - * libgsasl documentation. + * relevant IETF standards. * * "mechanism": The string name of the sasl mechanism to use. Mandatory. * "autoAuthorize": Truthy values tell the server to automatically acquire privileges on * all resources after successful authentication, which is the default. Falsey values * instruct the server to await separate privilege-acquisition commands. - * "user": The string name of the principal to authenticate, GSASL_AUTHID. + * "user": The string name of the principal to authenticate. * "userSource": The database target of the auth command, which identifies the location * of the credential information for the principal. May be "$external" if credential * information is stored outside of the mongo cluster. - * "pwd": The password data, GSASL_PASSWORD. + * "pwd": The password. * "serviceName": The GSSAPI service name to use. Defaults to "mongodb". * "serviceHostname": The GSSAPI hostname to use. Defaults to the name of the remote host. * * Other fields in saslParameters are silently ignored. * - * "sessionHook" is a pointer to optional data, which may be used by the gsasl_callback - * previously set on "gsasl". The session hook is set on an underlying Gsasl_session using - * gsasl_session_hook_set, and may be accessed by callbacks using gsasl_session_hook_get. - * See the gsasl documentation. - * * Returns an OK status on success, and ErrorCodes::AuthenticationFailed if authentication is * rejected. Other failures, all of which are tantamount to authentication failure, may also be * returned. */ extern Status (*saslClientAuthenticate)(DBClientWithCommands* client, - const BSONObj& saslParameters, - void* sessionHook); + const BSONObj& saslParameters); /** * Extracts the payload field from "cmdObj", and store it into "*payload". diff --git a/src/mongo/client/sasl_client_authenticate_impl.cpp b/src/mongo/client/sasl_client_authenticate_impl.cpp index b7aaa218d10..30ee4bc9b40 100644 --- a/src/mongo/client/sasl_client_authenticate_impl.cpp +++ b/src/mongo/client/sasl_client_authenticate_impl.cpp @@ -13,6 +13,14 @@ * limitations under the License. */ +/** + * This module implements the client side of SASL authentication in MongoDB, in terms of the Cyrus + * SASL library. See <sasl/sasl.h> and http://cyrusimap.web.cmu.edu/ for relevant documentation. + * + * The primary entry point at runtime is saslClientAuthenticateImpl(). + */ + +#include <boost/scoped_ptr.hpp> #include <string> #include "mongo/base/init.h" @@ -20,15 +28,13 @@ #include "mongo/base/string_data.h" #include "mongo/bson/util/bson_extract.h" #include "mongo/client/sasl_client_authenticate.h" +#include "mongo/client/sasl_client_session.h" #include "mongo/platform/cstdint.h" #include "mongo/util/base64.h" -#include "mongo/util/gsasl_session.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/net/hostandport.h" -#include <gsasl.h> // Must be included after "mongo/platform/cstdint.h" because of SERVER-8086. - namespace mongo { namespace { @@ -37,134 +43,145 @@ namespace { const char* const saslClientLogFieldName = "clientLogLevel"; - Gsasl* _gsaslLibraryContext = NULL; + int getSaslClientLogLevel(const BSONObj& saslParameters) { + int saslLogLevel = defaultSaslClientLogLevel; + BSONElement saslLogElement = saslParameters[saslClientLogFieldName]; + if (saslLogElement.trueValue()) + saslLogLevel = 1; + if (saslLogElement.isNumber()) + saslLogLevel = saslLogElement.numberInt(); + return saslLogLevel; + } + + /** + * Gets the password data from "saslParameters" and stores it to "outPassword". + * + * If "saslParameters" indicates that the password needs to be "digested" via + * DBClientWithCommands::createPasswordDigest(), this method takes care of that. + * On success, the value of "*outPassword" is always the correct value to set + * as the password on the SaslClientSession. + * + * Returns Status::OK() on success, and ErrorCodes::NoSuchKey if the password data is not + * present in "saslParameters". Other ErrorCodes returned indicate other errors. + */ + Status extractPassword(DBClientWithCommands* client, + const BSONObj& saslParameters, + std::string* outPassword) { - MONGO_INITIALIZER(SaslClientContext)(InitializerContext* context) { - fassert(16710, _gsaslLibraryContext == NULL); + std::string rawPassword; + Status status = bsonExtractStringField(saslParameters, + saslCommandPasswordFieldName, + &rawPassword); + if (!status.isOK()) + return status; + + bool digest; + status = bsonExtractBooleanFieldWithDefault(saslParameters, + saslCommandDigestPasswordFieldName, + true, + &digest); + if (!status.isOK()) + return status; - if (!gsasl_check_version(GSASL_VERSION)) - return Status(ErrorCodes::UnknownError, "Incompatible gsasl library."); + if (digest) { + std::string user; + status = bsonExtractStringField(saslParameters, + saslCommandPrincipalFieldName, + &user); + if (!status.isOK()) + return status; - int rc = gsasl_init(&_gsaslLibraryContext); - if (GSASL_OK != rc) - return Status(ErrorCodes::UnknownError, gsasl_strerror(rc)); + *outPassword = client->createPasswordDigest(user, rawPassword); + } + else { + *outPassword = rawPassword; + } return Status::OK(); } /** - * Configure "*session" as a client gsasl session for authenticating on the connection - * "*client", with the given "saslParameters". "gsasl" and "sessionHook" are passed through - * to GsaslSession::initializeClientSession, where they are documented. + * Configures "session" to perform the client side of a SASL conversation over connection + * "client". + * + * "saslParameters" is a BSON document providing the necessary configuration information. + * + * Returns Status::OK() on success. */ - Status configureSession(Gsasl* gsasl, + Status configureSession(SaslClientSession* session, DBClientWithCommands* client, - const BSONObj& saslParameters, - void* sessionHook, - GsaslSession* session) { + const BSONObj& saslParameters) { - std::string mechanism; + std::string value; Status status = bsonExtractStringField(saslParameters, saslCommandMechanismFieldName, - &mechanism); + &value); if (!status.isOK()) return status; + session->setParameter(SaslClientSession::parameterMechanism, value); - status = session->initializeClientSession(gsasl, mechanism, sessionHook); - if (!status.isOK()) - return status; - - std::string service; status = bsonExtractStringFieldWithDefault(saslParameters, saslCommandServiceNameFieldName, saslDefaultServiceName, - &service); + &value); if (!status.isOK()) return status; - session->setProperty(GSASL_SERVICE, service); + session->setParameter(SaslClientSession::parameterServiceName, value); - std::string hostname; status = bsonExtractStringFieldWithDefault(saslParameters, saslCommandServiceHostnameFieldName, HostAndPort(client->getServerAddress()).host(), - &hostname); + &value); if (!status.isOK()) return status; - session->setProperty(GSASL_HOSTNAME, hostname); - - BSONElement principalElement = saslParameters[saslCommandPrincipalFieldName]; - if (principalElement.type() == String) { - session->setProperty(GSASL_AUTHID, principalElement.str()); - } - else if (!principalElement.eoo()) { - return Status(ErrorCodes::TypeMismatch, - str::stream() << "Expected string for " << principalElement); - } + session->setParameter(SaslClientSession::parameterServiceHostname, value); - BSONElement passwordElement = saslParameters[saslCommandPasswordFieldName]; - if (passwordElement.type() == String) { - bool digest; - status = bsonExtractBooleanFieldWithDefault(saslParameters, - saslCommandDigestPasswordFieldName, - true, - &digest); - if (!status.isOK()) - return status; + status = bsonExtractStringField(saslParameters, + saslCommandPrincipalFieldName, + &value); + if (!status.isOK()) + return status; + session->setParameter(SaslClientSession::parameterUser, value); - std::string passwordHash; - if (digest) { - passwordHash = client->createPasswordDigest(principalElement.str(), - passwordElement.str()); - } - else { - passwordHash = passwordElement.str(); - } - session->setProperty(GSASL_PASSWORD, passwordHash); + status = extractPassword(client, saslParameters, &value); + if (status.isOK()) { + session->setParameter(SaslClientSession::parameterPassword, value); } - else if (!passwordElement.eoo()) { - return Status(ErrorCodes::TypeMismatch, - str::stream() << "Expected string for " << passwordElement); + else if (status != ErrorCodes::NoSuchKey) { + return status; } - return Status::OK(); + return session->initialize(); } - int getSaslClientLogLevel(const BSONObj& saslParameters) { - int saslLogLevel = defaultSaslClientLogLevel; - BSONElement saslLogElement = saslParameters[saslClientLogFieldName]; - if (saslLogElement.trueValue()) - saslLogLevel = 1; - if (saslLogElement.isNumber()) - saslLogLevel = saslLogElement.numberInt(); - return saslLogLevel; - } - - Status saslClientAuthenticateImpl(DBClientWithCommands* client, - const BSONObj& saslParameters, - void* sessionHook) { - - GsaslSession session; + /** + * Driver for the client side of a sasl authentication session, conducted synchronously over + * "client". + */ + Status saslClientAuthenticateImpl(DBClientWithCommands* client, const BSONObj& saslParameters) { int saslLogLevel = getSaslClientLogLevel(saslParameters); - Status status = configureSession(_gsaslLibraryContext, - client, - saslParameters, - sessionHook, - &session); + SaslClientSession session; + Status status = configureSession(&session, client, saslParameters); if (!status.isOK()) return status; std::string targetDatabase; - status = bsonExtractStringFieldWithDefault(saslParameters, - saslCommandPrincipalSourceFieldName, - saslDefaultDBName, - &targetDatabase); + try { + status = bsonExtractStringFieldWithDefault(saslParameters, + saslCommandPrincipalSourceFieldName, + saslDefaultDBName, + &targetDatabase); + } catch (const DBException& ex) { + return ex.toStatus(); + } if (!status.isOK()) return status; BSONObj saslFirstCommandPrefix = BSON( saslStartCommandName << 1 << - saslCommandMechanismFieldName << session.getMechanism()); + saslCommandMechanismFieldName << + session.getParameter(SaslClientSession::parameterMechanism)); BSONObj saslFollowupCommandPrefix = BSON(saslContinueCommandName << 1); BSONObj saslCommandPrefix = saslFirstCommandPrefix; diff --git a/src/mongo/client/sasl_client_session.cpp b/src/mongo/client/sasl_client_session.cpp new file mode 100644 index 00000000000..24c1f343ed9 --- /dev/null +++ b/src/mongo/client/sasl_client_session.cpp @@ -0,0 +1,306 @@ +/* Copyright 2012 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 "mongo/client/sasl_client_session.h" + +#include "mongo/base/init.h" +#include "mongo/util/allocator.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/concurrency/mutex.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { +namespace { + + /* + * Allocator functions to be used by the SASL library, if the client + * doesn't initialize the library for us. + */ + + void* saslOurMalloc(unsigned long sz) { + return ourmalloc(sz); + } + + void* saslOurCalloc(unsigned long count, unsigned long size) { + void* ptr = calloc(count, size); + if (!ptr) printStackAndExit(0); + return ptr; + } + + void* saslOurRealloc(void* ptr, unsigned long sz) { + return ourrealloc(ptr, sz); + } + + /* + * Mutex functions to be used by the SASL library, if the client doesn't initialize the library + * for us. + */ + + void* saslMutexAlloc(void) { + return new SimpleMutex("sasl"); + } + + int saslMutexLock(void* mutex) { + static_cast<SimpleMutex*>(mutex)->lock(); + return SASL_OK; + } + + int saslMutexUnlock(void* mutex) { + static_cast<SimpleMutex*>(mutex)->unlock(); + return SASL_OK; + } + + void saslMutexFree(void* mutex) { + delete static_cast<SimpleMutex*>(mutex); + } + + /** + * Configures the SASL library to use allocator and mutex functions we specify, + * unless the client application has previously initialized the SASL library. + */ + MONGO_INITIALIZER(CyrusSaslAllocatorsAndMutexes)(InitializerContext*) { + sasl_set_alloc(saslOurMalloc, + saslOurCalloc, + saslOurRealloc, + free); + + sasl_set_mutex(saslMutexAlloc, + saslMutexLock, + saslMutexUnlock, + saslMutexFree); + return Status::OK(); + } + + /** + * Initializes the client half of the SASL library, but is effectively a no-op if the client + * application has already done it. + * + * If a client wishes to override this initialization but keep the allocator and mutex + * initialization, it should implement a MONGO_INITIALIZER_GENERAL with + * CyrusSaslAllocatorsAndMutexes as a prerequisite and SaslClientContext as a dependent. If it + * wishes to override both, it should implement a MONGO_INITIALIZER_GENERAL with + * CyrusSaslAllocatorsAndMutexes and SaslClientContext as dependents, or initialize the library + * before calling mongo::runGlobalInitializersOrDie(). + */ + MONGO_INITIALIZER_WITH_PREREQUISITES(SaslClientContext, ("CyrusSaslAllocatorsAndMutexes"))( + InitializerContext* context) { + + static sasl_callback_t saslClientGlobalCallbacks[] = { { SASL_CB_LIST_END } }; + + // If the client application has previously called sasl_client_init(), the callbacks passed + // in here are ignored. + // + // TODO: Call sasl_client_done() at shutdown when we have a story for orderly shutdown. + int result = sasl_client_init(saslClientGlobalCallbacks); + if (result != SASL_OK) { + return Status(ErrorCodes::UnknownError, + mongoutils::str::stream() << + "Could not initialize sasl client components (" << + sasl_errstring(result, NULL, NULL) << + ")"); + } + return Status::OK(); + } + + /** + * Callback registered on the sasl_conn_t underlying a SaslClientSession to allow the Cyrus SASL + * library to query for the authentication id and other simple string configuration parameters. + * + * Note that in Mongo, the authentication and authorization ids (authid and authzid) are always + * the same. These correspond to SASL_CB_AUTHNAME and SASL_CB_USER. + */ + int saslClientGetSimple(void* context, + int id, + const char** result, + unsigned* resultLen) throw () { + SaslClientSession* session = static_cast<SaslClientSession*>(context); + if (!session || !result) + return SASL_BADPARAM; + + SaslClientSession::Parameter requiredParameterId; + switch (id) { + case SASL_CB_AUTHNAME: + case SASL_CB_USER: + requiredParameterId = SaslClientSession::parameterUser; + break; + default: + return SASL_FAIL; + } + + if (!session->hasParameter(requiredParameterId)) + return SASL_FAIL; + StringData value = session->getParameter(requiredParameterId); + *result = value.rawData(); + if (resultLen) + *resultLen = static_cast<unsigned>(value.size()); + return SASL_OK; + } + + /** + * Callback registered on the sasl_conn_t underlying a SaslClientSession to allow the Cyrus SASL + * library to query for the password data. + */ + int saslClientGetPassword(sasl_conn_t* conn, + void* context, + int id, + sasl_secret_t** outSecret) throw () { + + SaslClientSession* session = static_cast<SaslClientSession*>(context); + if (!session || !outSecret) + return SASL_BADPARAM; + + sasl_secret_t* secret = session->getPasswordAsSecret(); + if (secret == NULL) { + sasl_seterror(conn, 0, "No password data provided"); + return SASL_FAIL; + } + + *outSecret = secret; + return SASL_OK; + } + +} // namespace + + SaslClientSession::SaslClientSession() : + _saslConnection(NULL), + _step(0), + _done(false) { + + typedef int(*SaslCallbackFn)(); + + const sasl_callback_t callbackTemplate[maxCallbacks] = { + { SASL_CB_AUTHNAME, SaslCallbackFn(saslClientGetSimple), this }, + { SASL_CB_USER, SaslCallbackFn(saslClientGetSimple), this }, + { SASL_CB_PASS, SaslCallbackFn(saslClientGetPassword), this }, + { SASL_CB_LIST_END } + }; + std::copy(callbackTemplate, callbackTemplate + maxCallbacks, _callbacks); + } + + SaslClientSession::~SaslClientSession() { + sasl_dispose(&_saslConnection); + } + + void SaslClientSession::setParameter(Parameter id, const StringData& value) { + fassert(16807, id >= 0 && id < numParameters); + DataBuffer& buffer = _parameters[id]; + if (id == parameterPassword) { + // The parameterPassword is stored as a sasl_secret_t inside its DataBuffer, while other + // parameters are stored directly. This facilitates memory ownership management for + // getPasswordAsSecret(). + buffer.size = sizeof(sasl_secret_t) + value.size(); + buffer.data.reset(new char[buffer.size + 1]); + sasl_secret_t* secret = + static_cast<sasl_secret_t*>(static_cast<void*>(buffer.data.get())); + secret->len = value.size(); + value.copyTo(static_cast<char*>(static_cast<void*>(&secret->data[0])), false); + } + else { + buffer.size = value.size(); + buffer.data.reset(new char[buffer.size + 1]); + // Note that we append a terminal NUL to buffer.data, so it may be treated as a C-style + // string. This is required for parameterServiceName, parameterServiceHostname, + // parameterMechanism and parameterUser. + value.copyTo(buffer.data.get(), true); + } + } + + bool SaslClientSession::hasParameter(Parameter id) { + if (id < 0 || id >= numParameters) + return false; + return _parameters[id].data; + } + + StringData SaslClientSession::getParameter(Parameter id) { + if (!hasParameter(id)) + return StringData(); + + if (id == parameterPassword) { + // See comment in setParameter() about the special storage of parameterPassword. + sasl_secret_t* secret = getPasswordAsSecret(); + return StringData(static_cast<char*>(static_cast<void*>(secret->data)), secret->len); + } + else { + DataBuffer& buffer = _parameters[id]; + return StringData(buffer.data.get(), buffer.size); + } + } + + sasl_secret_t* SaslClientSession::getPasswordAsSecret() { + // See comment in setParameter() about the special storage of parameterPassword. + return static_cast<sasl_secret_t*>( + static_cast<void*>(_parameters[parameterPassword].data.get())); + } + + Status SaslClientSession::initialize() { + if (_saslConnection != NULL) + return Status(ErrorCodes::AlreadyInitialized, "Cannot reinitialize SaslClientSession."); + + int result = sasl_client_new(_parameters[parameterServiceName].data.get(), + _parameters[parameterServiceHostname].data.get(), + NULL, + NULL, + _callbacks, + 0, + &_saslConnection); + + if (SASL_OK != result) { + return Status(ErrorCodes::UnknownError, + mongoutils::str::stream() << sasl_errstring(result, NULL, NULL)); + } + + return Status::OK(); + } + + Status SaslClientSession::step(const StringData& inputData, std::string* outputData) { + const char* output = NULL; + unsigned outputSize = 0xFFFFFFFF; + + int result; + if (_step == 0) { + const char* actualMechanism; + result = sasl_client_start(_saslConnection, + getParameter(parameterMechanism).toString().c_str(), + NULL, + &output, + &outputSize, + &actualMechanism); + } + else { + result = sasl_client_step(_saslConnection, + inputData.rawData(), + static_cast<unsigned>(inputData.size()), + NULL, + &output, + &outputSize); + } + ++_step; + switch (result) { + case SASL_OK: + _done = true; + // Fall through + case SASL_CONTINUE: + *outputData = std::string(output, outputSize); + return Status::OK(); + case SASL_NOMECH: + return Status(ErrorCodes::BadValue, sasl_errdetail(_saslConnection)); + case SASL_BADAUTH: + return Status(ErrorCodes::AuthenticationFailed, sasl_errdetail(_saslConnection)); + default: + return Status(ErrorCodes::ProtocolError, sasl_errdetail(_saslConnection)); + } + } + +} // namespace mongo diff --git a/src/mongo/client/sasl_client_session.h b/src/mongo/client/sasl_client_session.h new file mode 100644 index 00000000000..9316b7c84a1 --- /dev/null +++ b/src/mongo/client/sasl_client_session.h @@ -0,0 +1,152 @@ +/* Copyright 2012 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 <boost/scoped_array.hpp> +#include <sasl/sasl.h> +#include <string> +#include <vector> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/status.h" +#include "mongo/base/string_data.h" + +namespace mongo { + + /** + * Implementation of the client side of a SASL authentication conversation. + * + * To use, create an instance, then use setParameter() to configure the authentication + * parameters. Once all parameters are set, call initialize() to initialize the client state + * machine. Finally, use repeated calls to step() to generate messages to send to the server + * and process server responses. + * + * The required parameters vary by mechanism, but all mechanisms require parameterServiceName, + * parameterServiceHostname, parameterMechanism and parameterUser. All of the required + * parameters must be UTF-8 encoded strings with no embedded NUL characters. The + * parameterPassword parameter is not constrained. + */ + class SaslClientSession { + MONGO_DISALLOW_COPYING(SaslClientSession); + public: + /** + * Identifiers of parameters used to configure a SaslClientSession. + */ + enum Parameter { + parameterServiceName = 0, + parameterServiceHostname, + parameterMechanism, + parameterUser, + parameterPassword, + numParameters // Must be last + }; + + SaslClientSession(); + ~SaslClientSession(); + + /** + * Sets the parameter identified by "id" to "value". + * + * The value of "id" must be one of the legal values of Parameter less than numParameters. + * May be called repeatedly for the same value of "id", with the last "value" replacing + * previous values. + * + * The session object makes and owns a copy of the data in "value". + */ + void setParameter(Parameter id, const StringData& value); + + /** + * Returns true if "id" identifies a parameter previously set by a call to setParameter(). + */ + bool hasParameter(Parameter id); + + /** + * Returns the value of a previously set parameter. + * + * If parameter "id" was never set, returns an empty StringData. Note that a parameter may + * be explicitly set to StringData(), so use hasParameter() to distinguish those cases. + * + * The session object owns the storage behind the returned StringData, which will remain + * valid until setParameter() is called with the same value of "id", or the session object + * goes out of scope. + */ + StringData getParameter(Parameter id); + + /** + * Returns the value of the parameterPassword parameter in the form of a sasl_secret_t, used + * by the Cyrus SASL library's SASL_CB_PASS callback. The session object owns the storage + * referenced by the returned sasl_secret_t*, which will remain in scope according to the + * same rules as given for getParameter(), above. + */ + sasl_secret_t* getPasswordAsSecret(); + + /** + * Initializes a session for use. + * + * Call exactly once, after setting any parameters you intend to set via setParameter(). + */ + Status initialize(); + + /** + * Takes one step of the SASL protocol on behalf of the client. + * + * Caller should provide data from the server side of the conversation in "inputData", or an + * empty StringData() if none is available. If the client should make a response to the + * server, stores the response into "*outputData". + * + * Returns Status::OK() on success. Any other return value indicates a failed + * authentication, though the specific return value may provide insight into the cause of + * the failure (e.g., ProtocolError, AuthenticationFailed). + * + * In the event that this method returns Status::OK(), consult the value of isDone() to + * determine if the conversation has completed. When step() returns Status::OK() and + * isDone() returns true, authentication has completed successfully. + */ + Status step(const StringData& inputData, std::string* outputData); + + /** + * Returns true if the authentication completed successfully. + */ + bool isDone() const { return _done; } + + private: + /** + * Buffer object that owns data for a single parameter. + */ + struct DataBuffer { + boost::scoped_array<char> data; + size_t size; + }; + + /// Maximum number of Cyrus SASL callbacks stored in _callbacks. + static const int maxCallbacks = 4; + + /// Underlying Cyrus SASL library connection object. + sasl_conn_t* _saslConnection; + + /// Callbacks registered on _saslConnection for providing the Cyrus SASL library with + /// parameter values, etc. + sasl_callback_t _callbacks[maxCallbacks]; + + /// Buffers for each of the settable parameters. + DataBuffer _parameters[numParameters]; + + /// Number of successfully completed conversation steps. + int _step; + + /// See isDone(). + bool _done; + }; + +} // namespace mongo diff --git a/src/mongo/client/syncclusterconnection.cpp b/src/mongo/client/syncclusterconnection.cpp index 7bb538079eb..3a927a55777 100644 --- a/src/mongo/client/syncclusterconnection.cpp +++ b/src/mongo/client/syncclusterconnection.cpp @@ -82,22 +82,23 @@ namespace mongo { bool ok = true; errmsg = ""; for ( size_t i=0; i<_conns.size(); i++ ) { - BSONObj res; + string singleErr; try { - if ( _conns[i]->simpleCommand( "admin" , &res , "fsync" ) ) + // this is fsync=true + // which with journalling on is a journal commit + // without journalling, is a full fsync + _conns[i]->simpleCommand( "admin", NULL, "resetError" ); + singleErr = _conns[i]->getLastError( true ); + + if ( singleErr.size() == 0 ) continue; + } catch ( DBException& e ) { - errmsg += e.toString(); - } - catch ( std::exception& e ) { - errmsg += e.what(); - } - catch ( ... ) { - warning() << "unknown exception in SyncClusterConnection::fsync" << endl; + singleErr = e.toString(); } ok = false; - errmsg += " " + _conns[i]->toString() + ":" + res.toString(); + errmsg += " " + _conns[i]->toString() + ":" + singleErr; } return ok; } diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index ab2f5cd26a4..1cf8efede39 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -173,6 +173,7 @@ namespace { clusterAdminRoleReadActions.addAction(ActionType::touch); clusterAdminRoleReadActions.addAction(ActionType::unlock); clusterAdminRoleReadActions.addAction(ActionType::unsetSharding); + clusterAdminRoleReadActions.addAction(ActionType::writeBacksQueued); clusterAdminRoleWriteActions.addAction(ActionType::addShard); clusterAdminRoleWriteActions.addAction(ActionType::closeAllDatabases); @@ -228,7 +229,6 @@ namespace { internalActions.addAction(ActionType::replSetGetRBID); internalActions.addAction(ActionType::replSetHeartbeat); internalActions.addAction(ActionType::writebacklisten); - internalActions.addAction(ActionType::writeBacksQueued); internalActions.addAction(ActionType::_migrateClone); internalActions.addAction(ActionType::_recvChunkAbort); internalActions.addAction(ActionType::_recvChunkCommit); @@ -394,9 +394,21 @@ namespace { _authenticatedPrincipals.add(principal); if (!principal->isImplicitPrivilegeAcquisitionEnabled()) return; + + const std::string dbname = principal->getName().getDB().toString(); + if (dbname == StringData("local", StringData::LiteralTag()) && + principal->getName().getUser() == internalSecurity.user) { + + // Grant full access to internal user + ActionSet allActions; + allActions.addAllActions(); + acquirePrivilege(Privilege(PrivilegeSet::WILDCARD_RESOURCE, allActions), + principal->getName()); + return; + } + _acquirePrivilegesForPrincipalFromDatabase(ADMIN_DBNAME, principal->getName()); principal->markDatabaseAsProbed(ADMIN_DBNAME); - const std::string dbname = principal->getName().getDB().toString(); _acquirePrivilegesForPrincipalFromDatabase(dbname, principal->getName()); principal->markDatabaseAsProbed(dbname); } @@ -491,13 +503,6 @@ namespace { << principal.getDB(), 0); } - if (principal.getUser() == internalSecurity.user) { - // Grant full access to internal user - ActionSet allActions; - allActions.addAllActions(); - return acquirePrivilege(Privilege(PrivilegeSet::WILDCARD_RESOURCE, allActions), - principal); - } return buildPrivilegeSet(dbname, principal, privilegeDocument, &_acquiredPrivileges); } diff --git a/src/mongo/db/btree.cpp b/src/mongo/db/btree.cpp index f6d6a3b668d..f669bdb142c 100644 --- a/src/mongo/db/btree.cpp +++ b/src/mongo/db/btree.cpp @@ -745,7 +745,7 @@ namespace mongo { const Ordering &order, int& pos, bool assertIfDup) const { Loc recordLoc; recordLoc = rl; - globalIndexCounters.btree( (char*)this ); + globalIndexCounters->btree( reinterpret_cast<const char*>(this) ); // binary search for this key bool dupsChecked = false; diff --git a/src/mongo/db/btree_stats.cpp b/src/mongo/db/btree_stats.cpp index 091726c0b0a..0ec80b77646 100644 --- a/src/mongo/db/btree_stats.cpp +++ b/src/mongo/db/btree_stats.cpp @@ -18,6 +18,7 @@ #include "mongo/pch.h" +#include "mongo/base/init.h" #include "mongo/db/btree_stats.h" namespace mongo { @@ -64,6 +65,14 @@ namespace mongo { _resets++; } - IndexCounters globalIndexCounters; + IndexCounters* globalIndexCounters = NULL; + + MONGO_INITIALIZER_WITH_PREREQUISITES(BtreeIndexCountersBlockSupported, + ("SystemInfo"))(InitializerContext* cx) { + if (globalIndexCounters == NULL) { + globalIndexCounters = new IndexCounters(); + } + return Status::OK(); + } } diff --git a/src/mongo/db/btree_stats.h b/src/mongo/db/btree_stats.h index 903286cdbc0..9d9e638668c 100644 --- a/src/mongo/db/btree_stats.h +++ b/src/mongo/db/btree_stats.h @@ -38,7 +38,7 @@ namespace mongo { // used without a mutex intentionally (can race) - void btree( char * node ) { + void btree( const char* node ) { if ( ! _memSupported ) return; btree( Record::likelyInPhysicalMemory( node ) ); @@ -70,5 +70,5 @@ namespace mongo { long long _btreeAccesses; }; - extern IndexCounters globalIndexCounters; + extern IndexCounters* globalIndexCounters; } diff --git a/src/mongo/db/client.cpp b/src/mongo/db/client.cpp index 713e8da07a4..a3ec1c5c674 100644 --- a/src/mongo/db/client.cpp +++ b/src/mongo/db/client.cpp @@ -388,7 +388,7 @@ namespace mongo { _handshake = b.obj(); if (theReplSet && o.hasField("member")) { - theReplSet->ghost->associateSlave(_remoteId, o["member"].Int()); + theReplSet->registerSlave(_remoteId, o["member"].Int()); } } diff --git a/src/mongo/db/clientcursor.cpp b/src/mongo/db/clientcursor.cpp index 764abb3f2e7..a0693dd8c7d 100644 --- a/src/mongo/db/clientcursor.cpp +++ b/src/mongo/db/clientcursor.cpp @@ -571,7 +571,11 @@ namespace mongo { // This sleep helps reader threads yield to writer threads. // Without this, the underlying reader/writer lock implementations // are not sufficiently writer-greedy. +#ifdef _WIN32 + SwitchToThread(); +#else sleepmicros(1); +#endif } else { if ( micros == -1 ) diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp index 742392f04ee..9528e495ded 100644 --- a/src/mongo/db/commands/mr.cpp +++ b/src/mongo/db/commands/mr.cpp @@ -73,8 +73,8 @@ namespace mongo { void JSMapper::map( const BSONObj& o ) { Scope * s = _func.scope(); verify( s ); - if ( s->invoke( _func.func() , &_params, &o , 0 , true, false, true ) ) - throw UserException( 9014, str::stream() << "map invoke failed: " + s->getError() ); + if (s->invoke(_func.func(), &_params, &o, 0, true)) + uasserted(9014, str::stream() << "map invoke failed: " << s->getError()); } /** @@ -193,7 +193,7 @@ namespace mongo { Scope * s = _func.scope(); - s->invokeSafe( _func.func() , &args, 0, 0, false, false, true ); + s->invokeSafe(_func.func(), &args, 0); ++numReduces; if ( s->type( "__returnValue" ) == Array ) { @@ -1191,6 +1191,11 @@ namespace mongo { Timer mt; // go through each doc while ( cursor->ok() ) { + if ( ! cursor->yieldSometimes( ClientCursor::WillNeed ) ) { + cursor.release(); + break; + } + if ( ! cursor->currentMatches() ) { cursor->advance(); continue; diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h index 8721484b684..30d94d2a63e 100644 --- a/src/mongo/db/curop.h +++ b/src/mongo/db/curop.h @@ -121,8 +121,8 @@ namespace mongo { void set( const BSONObj& o ) { scoped_spinlock lk(_lock); - int sz = o.objsize(); - if ( sz > (int) sizeof(_buf) ) { + size_t sz = o.objsize(); + if ( sz > sizeof(_buf) ) { _reset(TOO_BIG_SENTINEL); } else { diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index def2e459c70..2361ec2a945 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -719,7 +719,6 @@ using namespace mongo; namespace po = boost::program_options; void show_help_text(po::options_description options) { - show_warnings(); cout << options << endl; }; diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 2b345c70200..33b77f455d6 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -231,6 +231,15 @@ namespace mongo { } while ( 1 ) { + + if ( !_isMaster() ) { + // this should be in the while loop in case we step down + errmsg = "not master"; + result.append( "wnote", "no longer primary" ); + result.append( "code" , 10990 ); + return false; + } + // check this first for w=0 or w=1 if ( opReplicatedEnough( op, e ) ) { break; @@ -1694,6 +1703,21 @@ namespace mongo { out->push_back(Privilege(dbname, actions)); } virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) { + Timer timer; + + set<string> desiredCollections; + if ( cmdObj["collections"].type() == Array ) { + BSONObjIterator i( cmdObj["collections"].Obj() ); + while ( i.more() ) { + BSONElement e = i.next(); + if ( e.type() != String ) { + errmsg = "collections entries have to be strings"; + return false; + } + desiredCollections.insert( e.String() ); + } + } + list<string> colls; Database* db = cc().database(); if ( db ) @@ -1708,20 +1732,26 @@ namespace mongo { BSONObjBuilder bb( result.subobjStart( "collections" ) ); for ( list<string>::iterator i=colls.begin(); i != colls.end(); i++ ) { - string c = *i; - if ( c.find( ".system.profile" ) != string::npos ) + string fullCollectionName = *i; + string shortCollectionName = fullCollectionName.substr( dbname.size() + 1 ); + + if ( shortCollectionName.find( "system." ) == 0 ) + continue; + + if ( desiredCollections.size() > 0 && + desiredCollections.count( shortCollectionName ) == 0 ) continue; shared_ptr<Cursor> cursor; - NamespaceDetails * nsd = nsdetails( c ); + NamespaceDetails * nsd = nsdetails( fullCollectionName ); // debug SERVER-761 NamespaceDetails::IndexIterator ii = nsd->ii(); while( ii.more() ) { const IndexDetails &idx = ii.next(); if ( !idx.head.isValid() || !idx.info.isValid() ) { - log() << "invalid index for ns: " << c << " " << idx.head << " " << idx.info; + log() << "invalid index for ns: " << fullCollectionName << " " << idx.head << " " << idx.info; if ( idx.info.isValid() ) log() << " " << idx.info.obj(); log() << endl; @@ -1737,14 +1767,11 @@ namespace mongo { false, 1 ) ); } - else if ( c.find( ".system." ) != string::npos ) { - continue; - } else if ( nsd->isCapped() ) { - cursor = findTableScan( c.c_str() , BSONObj() ); + cursor = findTableScan( fullCollectionName.c_str() , BSONObj() ); } else { - log() << "can't find _id index for: " << c << endl; + log() << "can't find _id index for: " << fullCollectionName << endl; continue; } @@ -1762,7 +1789,7 @@ namespace mongo { md5_finish(&st, d); string hash = digestToString( d ); - bb.append( c.c_str() + ( dbname.size() + 1 ) , hash ); + bb.append( shortCollectionName, hash ); md5_append( &globalState , (const md5_byte_t*)hash.c_str() , hash.size() ); } @@ -1773,7 +1800,7 @@ namespace mongo { string hash = digestToString( d ); result.append( "md5" , hash ); - + result.appendNumber( "timeMillis", timer.millis() ); return 1; } diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp index 95fbb15e0db..262c2480a4f 100644 --- a/src/mongo/db/dbhelpers.cpp +++ b/src/mongo/db/dbhelpers.cpp @@ -374,7 +374,6 @@ namespace mongo { stringstream ss; ss << why << "." << terseCurrentTime(false) << "." << NUM++ << ".bson"; _file /= ss.str(); - } RemoveSaver::~RemoveSaver() { @@ -391,7 +390,8 @@ namespace mongo { _out = new ofstream(); _out->open( _file.string().c_str() , ios_base::out | ios_base::binary ); if ( ! _out->good() ) { - LOG( LL_WARNING ) << "couldn't create file: " << _file.string() << " for remove saving" << endl; + error() << "couldn't create file: " << _file.string() << + " for remove saving" << endl; delete _out; _out = 0; return; diff --git a/src/mongo/db/fts/fts_spec.cpp b/src/mongo/db/fts/fts_spec.cpp index 238065983cb..eb534f10a3e 100644 --- a/src/mongo/db/fts/fts_spec.cpp +++ b/src/mongo/db/fts/fts_spec.cpp @@ -301,7 +301,8 @@ namespace mongo { BSONElement e = i.next(); if ( str::equals( e.fieldName(), "_fts" ) || str::equals( e.fieldName(), "_ftsx" ) ) { - continue; + addedFtsStuff = true; + b.append( e ); } else if ( e.type() == String && ( str::equals( "fts", e.valuestr() ) || diff --git a/src/mongo/db/fts/fts_spec_test.cpp b/src/mongo/db/fts/fts_spec_test.cpp index 541bd4a56d8..df4ff719f02 100644 --- a/src/mongo/db/fts/fts_spec_test.cpp +++ b/src/mongo/db/fts/fts_spec_test.cpp @@ -97,10 +97,14 @@ namespace mongo { TEST( FTSSpec, Extra2 ) { BSONObj user = BSON( "key" << BSON( "data" << "fts" << "x" << 1 ) ); - FTSSpec spec( FTSSpec::fixSpec( user ) ); + BSONObj fixed = FTSSpec::fixSpec( user ); + FTSSpec spec( fixed ); ASSERT_EQUALS( 0U, spec.numExtraBefore() ); ASSERT_EQUALS( 1U, spec.numExtraAfter() ); ASSERT_EQUALS( StringData("x"), spec.extraAfter(0) ); + + BSONObj fixed2 = FTSSpec::fixSpec( fixed ); + ASSERT_EQUALS( fixed, fixed2 ); } TEST( FTSSpec, Extra3 ) { diff --git a/src/mongo/db/geo/geoparser.cpp b/src/mongo/db/geo/geoparser.cpp index f74022dde76..3de520be0be 100644 --- a/src/mongo/db/geo/geoparser.cpp +++ b/src/mongo/db/geo/geoparser.cpp @@ -123,6 +123,16 @@ namespace mongo { *out = coordsToPoint(coords); } + void eraseDuplicatePoints(vector<S2Point>* vertices) { + for (size_t i = 1; i < vertices->size(); ++i) { + if ((*vertices)[i - 1] == (*vertices)[i]) { + vertices->erase(vertices->begin() + i); + // We could have > 2 adjacent identical vertices, and must examine i again. + --i; + } + } + } + bool GeoParser::isGeoJSONLineString(const BSONObj& obj) { BSONElement type = obj.getFieldDotted(GEOJSON_TYPE); if (type.eoo() || (String != type.type())) { return false; } @@ -141,12 +151,14 @@ namespace mongo { if (!isArrayOfCoordinates(coordinateArray)) { return false; } vector<S2Point> vertices; parsePoints(obj.getFieldDotted(GEOJSON_COORDINATES).Array(), &vertices); + eraseDuplicatePoints(&vertices); return S2Polyline::IsValid(vertices); } void GeoParser::parseGeoJSONLineString(const BSONObj& obj, S2Polyline* out) { vector<S2Point> vertices; parsePoints(obj.getFieldDotted(GEOJSON_COORDINATES).Array(), &vertices); + eraseDuplicatePoints(&vertices); out->Init(vertices); } diff --git a/src/mongo/db/geo/s2cursor.cpp b/src/mongo/db/geo/s2cursor.cpp index 56bb9230ce2..a0e07f56d8e 100644 --- a/src/mongo/db/geo/s2cursor.cpp +++ b/src/mongo/db/geo/s2cursor.cpp @@ -49,7 +49,13 @@ namespace mongo { BSONObjIterator i(_keyPattern); while (i.more()) { BSONElement e = i.next(); - specBuilder.append(e.fieldName(), 1); + // Checked in AccessMethod already, so we know this spec has only numbers and 2dsphere + if ( e.type() == String ) { + specBuilder.append( e.fieldName(), 1 ); + } + else { + specBuilder.append( e.fieldName(), e.numberInt() ); + } } BSONObj spec = specBuilder.obj(); IndexSpec specForFRV(spec); diff --git a/src/mongo/db/geo/s2index.cpp b/src/mongo/db/geo/s2index.cpp index 0a030b28c22..d52f0ad2991 100644 --- a/src/mongo/db/geo/s2index.cpp +++ b/src/mongo/db/geo/s2index.cpp @@ -365,6 +365,26 @@ namespace mongo { uassert(16688, "finestIndexedLevel must be <= 30", params.finestIndexedLevel <= 30); uassert(16689, "finestIndexedLevel must be >= coarsestIndexedLevel", params.finestIndexedLevel >= params.coarsestIndexedLevel); + + // Categorize the fields we're indexing and make sure we have a geo field. + int geoFields = 0; + BSONObjIterator i( spec->keyPattern ); + while ( i.more() ) { + BSONElement e = i.next(); + if ( e.type() == String && SPHERE_2D_NAME == e.String() ) { + ++geoFields; + } + else { + // We check for numeric in 2d, so that's the check here + uassert( 16823, (string)"Cannot use " + SPHERE_2D_NAME + + " index with other special index types: " + e.toString(), + e.isNumber() ); + } + } + uassert(16750, (string)"Expect at least one geo field, spec=" + + spec->keyPattern.toString(), + geoFields >= 1); + return new S2IndexType(SPHERE_2D_NAME, this, spec, params); } diff --git a/src/mongo/db/geo/s2nearcursor.cpp b/src/mongo/db/geo/s2nearcursor.cpp index 5466fe00b50..e4ef547c5d8 100644 --- a/src/mongo/db/geo/s2nearcursor.cpp +++ b/src/mongo/db/geo/s2nearcursor.cpp @@ -44,7 +44,13 @@ namespace mongo { BSONObjIterator specIt(_keyPattern); while (specIt.more()) { BSONElement e = specIt.next(); - specBuilder.append(e.fieldName(), 1); + // Checked in AccessMethod already, so we know this spec has only numbers and 2dsphere + if ( e.type() == String ) { + specBuilder.append( e.fieldName(), 1 ); + } + else { + specBuilder.append( e.fieldName(), e.numberInt() ); + } } BSONObj spec = specBuilder.obj(); _specForFRV = IndexSpec(spec); @@ -258,8 +264,6 @@ namespace mongo { continue; } - seen.insert(cursor->currLoc()); - // Get distance interval from our query point to the cell. // If it doesn't overlap with our current shell, toss. BSONObj currKey(cursor->currKey()); @@ -282,13 +286,20 @@ namespace mongo { continue; } + // We have to add this document to seen *AFTER* the key intersection test. + // A geometry may have several keys, one of which may be in our search shell and one + // of which may be outside of it. We don't want to ignore a document just because + // one of its covers isn't inside this annulus. + seen.insert(cursor->currLoc()); + + // At this point forward, we will not examine the document again in this annulus. + const BSONObj& indexedObj = cursor->currLoc().obj(); // Match against indexed geo fields. ++_stats._geoMatchTested; size_t geoFieldsMatched = 0; - // OK, cool, non-geo match satisfied. See if the object actually overlaps w/the geo - // query fields. + // See if the object actually overlaps w/the geo query fields. for (size_t i = 0; i < _indexedGeoFields.size(); ++i) { BSONElementSet geoFieldElements; indexedObj.getFieldsDotted(_indexedGeoFields[i].getField(), geoFieldElements, diff --git a/src/mongo/db/index.cpp b/src/mongo/db/index.cpp index 377679806a4..f7346978313 100644 --- a/src/mongo/db/index.cpp +++ b/src/mongo/db/index.cpp @@ -369,11 +369,14 @@ namespace mongo { verify( sourceCollection ); } - if ( sourceCollection->findIndexByName(name) >= 0 ) { + // Check both existing and in-progress indexes (2nd param = true) + if ( sourceCollection->findIndexByName(name, true) >= 0 ) { // index already exists. return false; } - if( sourceCollection->findIndexByKeyPattern(key) >= 0 ) { + + // Check both existing and in-progress indexes (2nd param = true) + if( sourceCollection->findIndexByKeyPattern(key, true) >= 0 ) { LOG(2) << "index already exists with diff name " << name << ' ' << key.toString() << endl; return false; } diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index 2daee09b14d..926cdeecdb6 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -777,13 +777,21 @@ namespace mongo { void checkAndInsert(const char *ns, /*modifies*/BSONObj& js) { uassert( 10059 , "object to insert too large", js.objsize() <= BSONObjMaxUserSize); { - // check no $ modifiers. note we only check top level. (scanning deep would be quite expensive) BSONObjIterator i( js ); while ( i.more() ) { BSONElement e = i.next(); - uassert( 13511 , "document to insert can't have $ fields" , e.fieldName()[0] != '$' ); + + // check no $ modifiers. note we only check top level. + // (scanning deep would be quite expensive) + uassert( 13511, "document to insert can't have $ fields", e.fieldName()[0] != '$' ); + + // check no regexp for _id (SERVER-9502) + if (str::equals(e.fieldName(), "_id")) { + uassert(16824, "can't use a regex for _id", e.type() != RegEx); + } } } + theDataFileMgr.insertWithObjMod(ns, // May be modified in the call to add an _id field. js, diff --git a/src/mongo/db/jsobj.cpp b/src/mongo/db/jsobj.cpp index 67967ba91d5..ea3248e42da 100644 --- a/src/mongo/db/jsobj.cpp +++ b/src/mongo/db/jsobj.cpp @@ -893,6 +893,13 @@ namespace mongo { ; } + // check no regexp for _id (SERVER-9502) + if (mongoutils::str::equals(e.fieldName(), "_id")) { + if (e.type() == RegEx) { + return false; + } + } + if ( e.mayEncapsulate() ) { switch ( e.type() ) { case Object: diff --git a/src/mongo/db/namespace_details-inl.h b/src/mongo/db/namespace_details-inl.h index 0e64b961ba8..9e8ccbb37e6 100644 --- a/src/mongo/db/namespace_details-inl.h +++ b/src/mongo/db/namespace_details-inl.h @@ -56,8 +56,9 @@ namespace mongo { return -1; } - inline int NamespaceDetails::findIndexByKeyPattern(const BSONObj& keyPattern) { - IndexIterator i = ii(); + inline int NamespaceDetails::findIndexByKeyPattern(const BSONObj& keyPattern, + bool includeBackgroundInProgress) { + IndexIterator i = ii(includeBackgroundInProgress); while( i.more() ) { if( i.next().keyPattern() == keyPattern ) return i.pos()-1; @@ -83,8 +84,9 @@ namespace mongo { } // @return offset in indexes[] - inline int NamespaceDetails::findIndexByName(const char *name) { - IndexIterator i = ii(); + inline int NamespaceDetails::findIndexByName(const char *name, + bool includeBackgroundInProgress) { + IndexIterator i = ii(includeBackgroundInProgress); while( i.more() ) { if ( strcmp(i.next().info.obj().getStringField("name"),name) == 0 ) return i.pos()-1; diff --git a/src/mongo/db/namespace_details.h b/src/mongo/db/namespace_details.h index 28416f4abf0..0e7e324da67 100644 --- a/src/mongo/db/namespace_details.h +++ b/src/mongo/db/namespace_details.h @@ -291,10 +291,11 @@ namespace mongo { } // @return offset in indexes[] - int findIndexByName(const char *name); + int findIndexByName(const char *name, bool includeBackgroundInProgress = false); // @return offset in indexes[] - int findIndexByKeyPattern(const BSONObj& keyPattern); + int findIndexByKeyPattern(const BSONObj& keyPattern, + bool includeBackgroundInProgress = false); void findIndexByType( const string& name , vector<int>& matches ) { IndexIterator i = ii(); diff --git a/src/mongo/db/oplog.cpp b/src/mongo/db/oplog.cpp index e1e6e370d4f..870601c9b4b 100644 --- a/src/mongo/db/oplog.cpp +++ b/src/mongo/db/oplog.cpp @@ -695,19 +695,51 @@ namespace mongo { return BSONObj(); } - uassert(15916, str::stream() << "Can no longer connect to initial sync source: " << hn, missingObjReader.connect(hn)); + const int retryMax = 3; + for (int retryCount = 1; retryCount <= retryMax; ++retryCount) { + if (retryCount != 1) { + // if we are retrying, sleep a bit to let the network possibly recover + sleepsecs(retryCount * retryCount); + } + try { + bool ok = missingObjReader.connect(hn); + if (!ok) { + warning() << "network problem detected while connecting to the " + << "sync source, attempt " << retryCount << " of " + << retryMax << endl; + continue; // try again + } + } + catch (const SocketException& exc) { + warning() << "network problem detected while connecting to the " + << "sync source, attempt " << retryCount << " of " + << retryMax << endl; + continue; // try again + } - // might be more than just _id in the update criteria - BSONObj query = BSONObjBuilder().append(o.getObjectField("o2")["_id"]).obj(); - BSONObj missingObj; - try { - missingObj = missingObjReader.findOne(ns, query); - } catch(DBException& e) { - log() << "replication assertion fetching missing object: " << e.what() << endl; - throw; - } + // might be more than just _id in the update criteria + BSONObj query = BSONObjBuilder().append(o.getObjectField("o2")["_id"]).obj(); + BSONObj missingObj; + try { + missingObj = missingObjReader.findOne(ns, query); + } + catch (const SocketException& exc) { + warning() << "network problem detected while fetching a missing document from the " + << "sync source, attempt " << retryCount << " of " + << retryMax << endl; + continue; // try again + } + catch (DBException& e) { + log() << "replication assertion fetching missing object: " << e.what() << endl; + throw; + } - return missingObj; + // success! + return missingObj; + } + // retry count exceeded + msgasserted(15916, + str::stream() << "Can no longer connect to initial sync source: " << hn); } bool Sync::shouldRetry(const BSONObj& o) { diff --git a/src/mongo/db/ops/delete.cpp b/src/mongo/db/ops/delete.cpp index 790dd543cd0..8d7b1cf9d3b 100644 --- a/src/mongo/db/ops/delete.cpp +++ b/src/mongo/db/ops/delete.cpp @@ -129,9 +129,6 @@ namespace mongo { } } - if ( rs ) - rs->goingToDelete( rloc.obj() /*cc->c->current()*/ ); - theDataFileMgr.deleteRecord(ns, rloc.rec(), rloc); nDeleted++; if ( foundAllResults ) { diff --git a/src/mongo/db/ops/update.cpp b/src/mongo/db/ops/update.cpp index 034b0849759..835ecc4723c 100644 --- a/src/mongo/db/ops/update.cpp +++ b/src/mongo/db/ops/update.cpp @@ -357,9 +357,6 @@ namespace mongo { d->paddingFits(); } else { - if ( rs ) - rs->goingToDelete( onDisk ); - BSONObj newObj = mss->createNewFromMods(); checkTooLarge(newObj); DiskLoc newLoc = theDataFileMgr.updateRecord(ns, diff --git a/src/mongo/db/pdfile.h b/src/mongo/db/pdfile.h index 094c2e28246..67343b686b9 100644 --- a/src/mongo/db/pdfile.h +++ b/src/mongo/db/pdfile.h @@ -309,8 +309,6 @@ namespace mongo { Record* accessed(); static bool likelyInPhysicalMemory( const char* data ); - - static bool blockCheckSupported(); /** * this adds stats about page fault exceptions currently diff --git a/src/mongo/db/record.cpp b/src/mongo/db/record.cpp index c98c9417ab9..d55d6d40f68 100644 --- a/src/mongo/db/record.cpp +++ b/src/mongo/db/record.cpp @@ -17,6 +17,7 @@ */ #include "pch.h" +#include "mongo/base/init.h" #include "mongo/db/curop.h" #include "mongo/db/databaseholder.h" #include "mongo/db/pagefault.h" @@ -420,7 +421,13 @@ namespace mongo { } } - const bool blockSupported = ProcessInfo::blockCheckSupported(); + static bool blockSupported = false; + + MONGO_INITIALIZER_WITH_PREREQUISITES(RecordBlockSupported, + ("SystemInfo"))(InitializerContext* cx) { + blockSupported = ProcessInfo::blockCheckSupported(); + return Status::OK(); + } void Record::appendWorkingSetInfo( BSONObjBuilder& b ) { if ( ! blockSupported ) { @@ -431,10 +438,6 @@ namespace mongo { ps::appendWorkingSetInfo( b ); } - bool Record::blockCheckSupported() { - return ProcessInfo::blockCheckSupported(); - } - bool Record::likelyInPhysicalMemory() const { return likelyInPhysicalMemory( _data ); } @@ -477,7 +480,7 @@ namespace mongo { #ifdef _DEBUG if ( blockSupported && ! ProcessInfo::blockInMemory(data) ) { - warning() << "we think data is in ram but system says no" << endl; + RARELY warning() << "we think data is in ram but system says no" << endl; } #endif return true; diff --git a/src/mongo/db/repl/bgsync.cpp b/src/mongo/db/repl/bgsync.cpp index 626a575afae..67c1eca8555 100644 --- a/src/mongo/db/repl/bgsync.cpp +++ b/src/mongo/db/repl/bgsync.cpp @@ -312,10 +312,6 @@ namespace replset { return; } - while (MONGO_FAIL_POINT(rsBgSyncProduce)) { - sleepmillis(0); - } - uassert(1000, "replSet source for syncing doesn't seem to be await capable -- is it an older version of mongodb?", r.awaitCapable() ); if (isRollbackRequired(r)) { @@ -472,6 +468,10 @@ namespace replset { } } + while (MONGO_FAIL_POINT(rsBgSyncProduce)) { + sleepmillis(0); + } + verify(r.conn() == NULL); while ((target = theReplSet->getMemberToSyncTo()) != NULL) { diff --git a/src/mongo/db/repl/consensus.cpp b/src/mongo/db/repl/consensus.cpp index dbb36efaaa3..f056befb605 100644 --- a/src/mongo/db/repl/consensus.cpp +++ b/src/mongo/db/repl/consensus.cpp @@ -233,16 +233,14 @@ namespace mongo { log() << "replSet electCmdReceived couldn't find member with id " << whoid << rsLog; vote = -10000; } - else if( primary && primary == rs._self && rs.lastOpTimeWritten >= hopeful->hbinfo().opTime ) { - // hbinfo is not updated, so we have to check the primary's last optime separately + else if( primary && primary == rs._self) { log() << "I am already primary, " << hopeful->fullName() << " can try again once I've stepped down" << rsLog; vote = -10000; } - else if( primary && primary->hbinfo().opTime >= hopeful->hbinfo().opTime ) { - // other members might be aware of more up-to-date nodes + else if (primary) { log() << hopeful->fullName() << " is trying to elect itself but " << - primary->fullName() << " is already primary and more up-to-date" << rsLog; + primary->fullName() << " is already primary" << rsLog; vote = -10000; } else if( highestPriority && highestPriority->config().priority > hopeful->config().priority) { diff --git a/src/mongo/db/repl/heartbeat.cpp b/src/mongo/db/repl/heartbeat.cpp index 5ba3d5f3427..f1bc18168f6 100644 --- a/src/mongo/db/repl/heartbeat.cpp +++ b/src/mongo/db/repl/heartbeat.cpp @@ -49,6 +49,22 @@ namespace mongo { return jsTime() - downSince; } + void HeartbeatInfo::updateFromLastPoll(const HeartbeatInfo& newInfo) { + hbstate = newInfo.hbstate; + health = newInfo.health; + upSince = newInfo.upSince; + downSince = newInfo.downSince; + lastHeartbeat = newInfo.lastHeartbeat; + lastHeartbeatMsg = newInfo.lastHeartbeatMsg; + // Note: lastHeartbeatRecv is updated through CmdReplSetHeartbeat::run(). + + syncingTo = newInfo.syncingTo; + opTime = newInfo.opTime; + skew = newInfo.skew; + authIssue = newInfo.authIssue; + ping = newInfo.ping; + } + /* { replSetHeartbeat : <setname> } */ class CmdReplSetHeartbeat : public ReplSetCommand { public: @@ -143,7 +159,9 @@ namespace mongo { } // note that we got a heartbeat from this node - from->get_hbinfo().recvHeartbeat(); + theReplSet->mgr->send(boost::bind(&ReplSet::msgUpdateHBRecv, + theReplSet, from->hbinfo().id(), time(0))); + return true; } @@ -445,10 +463,6 @@ namespace mongo { time_t _timeout; }; - void HeartbeatInfo::recvHeartbeat() { - lastHeartbeatRecv = time(0); - } - int ReplSetHealthPollTask::s_try_offset = 0; void ReplSetImpl::endOldHealthTasks() { diff --git a/src/mongo/db/repl/rs.cpp b/src/mongo/db/repl/rs.cpp index f6ef3dca558..8646b6eb2d3 100644 --- a/src/mongo/db/repl/rs.cpp +++ b/src/mongo/db/repl/rs.cpp @@ -244,7 +244,16 @@ namespace mongo { void ReplSetImpl::msgUpdateHBInfo(HeartbeatInfo h) { for( Member *m = _members.head(); m; m=m->next() ) { if( m->id() == h.id() ) { - m->_hbinfo = h; + m->_hbinfo.updateFromLastPoll(h); + return; + } + } + } + + void ReplSetImpl::msgUpdateHBRecv(unsigned id, time_t newTime) { + for (Member *m = _members.head(); m; m = m->next()) { + if (m->id() == id) { + m->_hbinfo.lastHeartbeatRecv = newTime; return; } } @@ -584,7 +593,6 @@ namespace mongo { verify( _name.empty() || _name == config()._id ); _name = config()._id; verify( !_name.empty() ); - // this is a shortcut for simple changes if( additive ) { log() << "replSet info : additive change to configuration" << rsLog; @@ -613,6 +621,10 @@ namespace mongo { _members.orphanAll(); endOldHealthTasks(); + + // Clear out our memory of who might have been syncing from us. + // Any incoming handshake connections after this point will be newly registered. + ghost->clearCache(); int oldPrimaryId = -1; { @@ -907,6 +919,13 @@ namespace mongo { return OpTime(); } + void ReplSetImpl::registerSlave(const BSONObj& rid, const int memberId) { + // To prevent race conditions with clearing the cache at reconfig time, + // we lock the replset mutex here. + lock lk(this); + ghost->associateSlave(rid, memberId); + } + class ReplIndexPrefetch : public ServerParameter { public: ReplIndexPrefetch() diff --git a/src/mongo/db/repl/rs.h b/src/mongo/db/repl/rs.h index cd353752e82..18576106e3c 100644 --- a/src/mongo/db/repl/rs.h +++ b/src/mongo/db/repl/rs.h @@ -149,6 +149,7 @@ namespace mongo { void percolate(const BSONObj& rid, const OpTime& last); void associateSlave(const BSONObj& rid, const int memberId); void updateSlave(const mongo::OID& id, const OpTime& last); + void clearCache(); }; class Consensus { @@ -331,6 +332,11 @@ namespace mongo { /* todo thread */ void msgUpdateHBInfo(HeartbeatInfo); + /** + * Updates the lastHeartbeatRecv of Member with the given id. + */ + void msgUpdateHBRecv(unsigned id, time_t newTime); + StateBox box; OpTime lastOpTimeWritten; @@ -489,6 +495,9 @@ namespace mongo { * have called it again, passing in false. */ bool setMaintenanceMode(const bool inc); + + // Records a new slave's id in the GhostSlave map, at handshake time. + void registerSlave(const BSONObj& rid, const int memberId); private: Member* head() const { return _members.head(); } public: diff --git a/src/mongo/db/repl/rs_member.h b/src/mongo/db/repl/rs_member.h index 100a2013314..b59d958e584 100644 --- a/src/mongo/db/repl/rs_member.h +++ b/src/mongo/db/repl/rs_member.h @@ -101,7 +101,11 @@ namespace mongo { /* true if changed in a way of interest to the repl set manager. */ bool changed(const HeartbeatInfo& old) const; - void recvHeartbeat(); + /** + * Updates this with the info received from the command result we got from + * the last replSetHeartbeat. + */ + void updateFromLastPoll(const HeartbeatInfo& newInfo); }; inline HeartbeatInfo::HeartbeatInfo(unsigned id) : diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp index 80335bea702..8c8e89c20c6 100644 --- a/src/mongo/db/repl/rs_rollback.cpp +++ b/src/mongo/db/repl/rs_rollback.cpp @@ -466,6 +466,16 @@ namespace mongo { // todo: lots of overhead in context, this can be faster Client::Context c(d.ns); + + // Add the doc to our rollback file + BSONObj obj; + bool found = Helpers::findOne(d.ns, pattern, obj, false); + if ( found ) { + rs->goingToDelete( obj ); + } else { + error() << "rollback cannot find object by id" << endl; + } + if( i->second.isEmpty() ) { // wasn't on the primary; delete. /* TODO1.6 : can't delete from a capped collection. need to handle that here. */ diff --git a/src/mongo/db/repl/rs_sync.cpp b/src/mongo/db/repl/rs_sync.cpp index 7d43b3ac466..f1c531b1c7d 100644 --- a/src/mongo/db/repl/rs_sync.cpp +++ b/src/mongo/db/repl/rs_sync.cpp @@ -45,6 +45,11 @@ namespace mongo { const int ReplSetImpl::maxSyncSourceLagSecs = 30; + // For testing network failures in percolate() for chaining + MONGO_FP_DECLARE(rsChaining1); + MONGO_FP_DECLARE(rsChaining2); + MONGO_FP_DECLARE(rsChaining3); + namespace replset { MONGO_FP_DECLARE(rsSyncApplyStop); @@ -784,6 +789,11 @@ namespace replset { } } + void GhostSync::clearCache() { + rwlock lk(_lock, true); + _ghostCache.clear(); + } + void GhostSync::associateSlave(const BSONObj& id, const int memberId) { const OID rid = id["_id"].OID(); rwlock lk( _lock , true ); @@ -827,7 +837,7 @@ namespace replset { void GhostSync::percolate(const BSONObj& id, const OpTime& last) { const OID rid = id["_id"].OID(); - GhostSlave* slave; + shared_ptr<GhostSlave> slave; { rwlock lk( _lock , false ); @@ -837,63 +847,89 @@ namespace replset { return; } - slave = i->second.get(); + slave = i->second; if (!slave->init) { OCCASIONALLY log() << "couldn't percolate slave " << rid << " not init" << rsLog; return; } } - verify(slave->slave); - const Member *target = replset::BackgroundSync::get()->getSyncTarget(); - if (!target || rs->box.getState().primary() - // we are currently syncing from someone who's syncing from us - // the target might end up with a new Member, but s.slave never - // changes so we'll compare the names - || target == slave->slave || target->fullName() == slave->slave->fullName()) { - LOG(1) << "replica set ghost target no good" << endl; - return; - } + // Keep trying to update until we either succeed or we become primary. + // Note that this can block the ghostsync thread for quite a while if there + // are connection problems to the current sync source ("sync target") + while (true) { + const Member *target = replset::BackgroundSync::get()->getSyncTarget(); + if (!target || rs->box.getState().primary() + // we are currently syncing from someone who's syncing from us + // the target might end up with a new Member, but s.slave never + // changes so we'll compare the names + || target == slave->slave || target->fullName() == slave->slave->fullName()) { + LOG(1) << "replica set ghost target no good" << endl; + return; + } - try { - // haveCursor() does not necessarily tell us if we have a non-dead cursor, so we check - // tailCheck() as well; see SERVER-8420 - slave->reader.tailCheck(); - if (!slave->reader.haveCursor()) { - if (!slave->reader.connect(id, slave->slave->id(), target->fullName())) { - // error message logged in OplogReader::connect - return; + try { + if (MONGO_FAIL_POINT(rsChaining1)) { + mongo::getGlobalFailPointRegistry()->getFailPoint("throwSockExcep")-> + setMode(FailPoint::nTimes, 1); } - slave->reader.ghostQueryGTE(rsoplog, last); - // if we lose the connection between connecting and querying, the cursor may not - // exist so we have to check again before using it. + + // haveCursor() does not necessarily tell us if we have a non-dead cursor, + // so we check tailCheck() as well; see SERVER-8420 + slave->reader.tailCheck(); if (!slave->reader.haveCursor()) { - return; - } - } + if (!slave->reader.connect(id, slave->slave->id(), target->fullName())) { + // error message logged in OplogReader::connect + sleepsecs(1); + continue; + } - LOG(1) << "replSet last: " << slave->last.toString() << " to " << last.toString() << rsLog; - if (slave->last > last) { - return; - } + if (MONGO_FAIL_POINT(rsChaining2)) { + mongo::getGlobalFailPointRegistry()->getFailPoint("throwSockExcep")-> + setMode(FailPoint::nTimes, 1); + } + + slave->reader.ghostQueryGTE(rsoplog, last); + // if we lose the connection between connecting and querying, the cursor may not + // exist so we have to check again before using it. + if (!slave->reader.haveCursor()) { + sleepsecs(1); + continue; + } + } - while (slave->last <= last) { - if (!slave->reader.more()) { - // we'll be back + LOG(1) << "replSet last: " << slave->last.toString() << " to " + << last.toString() << rsLog; + if (slave->last > last) { + // Nothing to do; already up to date. return; } - BSONObj o = slave->reader.nextSafe(); - slave->last = o["ts"]._opTime(); + while (slave->last <= last) { + if (MONGO_FAIL_POINT(rsChaining3)) { + mongo::getGlobalFailPointRegistry()->getFailPoint("throwSockExcep")-> + setMode(FailPoint::nTimes, 1); + } + + if (!slave->reader.more()) { + // Hit the end of the oplog on the sync source; we're fully up to date now. + return; + } + + BSONObj o = slave->reader.nextSafe(); + slave->last = o["ts"]._opTime(); + } + LOG(2) << "now last is " << slave->last.toString() << rsLog; + // We moved the cursor forward enough; we're done. + return; + } + catch (const DBException& e) { + // This captures SocketExceptions as well. + log() << "replSet ghost sync error: " << e.what() << " for " + << slave->slave->fullName() << rsLog; + slave->reader.resetConnection(); } - LOG(2) << "now last is " << slave->last.toString() << rsLog; - } - catch (DBException& e) { - // we'll be back - LOG(2) << "replSet ghost sync error: " << e.what() << " for " - << slave->slave->fullName() << rsLog; - slave->reader.resetConnection(); } } } diff --git a/src/mongo/db/repl_block.cpp b/src/mongo/db/repl_block.cpp index f32e1ffe90c..4eacbcdb1be 100644 --- a/src/mongo/db/repl_block.cpp +++ b/src/mongo/db/repl_block.cpp @@ -100,6 +100,7 @@ namespace mongo { _currentlyUpdatingCache = true; for ( list< pair<BSONObj,BSONObj> >::iterator i=todo.begin(); i!=todo.end(); i++ ) { + Client::GodScope gs; db.update( NS , i->first , i->second , true ); } _currentlyUpdatingCache = false; @@ -168,7 +169,9 @@ namespace mongo { } bool replicatedToNum(OpTime& op, int w) { - if ( w <= 1 || ! _isMaster() ) + massert( 16805, "replicatedToNum called but not master anymore", _isMaster() ); + + if ( w <= 1 ) return true; w--; // now this is the # of slaves i need @@ -177,7 +180,11 @@ namespace mongo { } bool waitForReplication(OpTime& op, int w, int maxSecondsToWait) { - if ( w <= 1 || ! _isMaster() ) + static const int noLongerMasterAssertCode = 16806; + massert(noLongerMasterAssertCode, + "waitForReplication called but not master anymore", _isMaster() ); + + if ( w <= 1 ) return true; w--; // now this is the # of slaves i need @@ -188,8 +195,13 @@ namespace mongo { scoped_lock mylk(_mutex); while ( ! _replicatedToNum_slaves_locked( op, w ) ) { - if ( ! _threadsWaitingForReplication.timed_wait( mylk.boost() , xt ) ) + if ( ! _threadsWaitingForReplication.timed_wait( mylk.boost() , xt ) ) { + massert(noLongerMasterAssertCode, + "waitForReplication called but not master anymore", _isMaster()); return false; + } + massert(noLongerMasterAssertCode, + "waitForReplication called but not master anymore", _isMaster()); } return true; } diff --git a/src/mongo/dbtests/jsobjtests.cpp b/src/mongo/dbtests/jsobjtests.cpp index a2ea39bcb82..80b88831c47 100644 --- a/src/mongo/dbtests/jsobjtests.cpp +++ b/src/mongo/dbtests/jsobjtests.cpp @@ -2065,6 +2065,27 @@ namespace JsobjTests { } }; + class NestedBuilderOversize { + public: + void run() { + try { + BSONObjBuilder outer; + BSONObjBuilder inner(outer.subobjStart("inner")); + + string bigStr(1000, 'x'); + while (true) { + ASSERT_LESS_THAN_OR_EQUALS(inner.len(), BufferMaxSize); + inner.append("", bigStr); + } + + ASSERT(!"Expected Throw"); + } catch (const DBException& e) { + if (e.getCode() != 13548) // we expect the code for oversized buffer + throw; + } + } + }; + class All : public Suite { public: All() : Suite( "jsobj" ) { @@ -2166,6 +2187,7 @@ namespace JsobjTests { add< BSONForEachTest >(); add< CompareOps >(); add< HashingTest >(); + add< NestedBuilderOversize >(); } } myall; diff --git a/src/mongo/dbtests/jstests.cpp b/src/mongo/dbtests/jstests.cpp index 67262de778b..b1ba53cc1f1 100644 --- a/src/mongo/dbtests/jstests.cpp +++ b/src/mongo/dbtests/jstests.cpp @@ -438,6 +438,19 @@ namespace JSTests { ASSERT_EQUALS( (string)"^a" , out["a"].regex() ); ASSERT_EQUALS( (string)"i" , out["a"].regexFlags() ); + // This regex used to cause a segfault because x isn't a valid flag for a js RegExp. + // Now it throws a JS exception. + BSONObj invalidRegex = BSON_ARRAY(BSON("regex" << BSONRegEx("asdf", "x"))); + const char* code = "function (obj) {" + " var threw = false;" + " try {" + " obj.regex;" // should throw + " } catch(e) {" + " threw = true;" + " }" + " assert(threw);" + "}"; + ASSERT_EQUALS(s->invoke(code, &invalidRegex, NULL), 0); } // array diff --git a/src/mongo/platform/atomic_intrinsics_win32.h b/src/mongo/platform/atomic_intrinsics_win32.h index 91ecc8fce2c..f66e648d0c5 100644 --- a/src/mongo/platform/atomic_intrinsics_win32.h +++ b/src/mongo/platform/atomic_intrinsics_win32.h @@ -87,7 +87,12 @@ namespace mongo { template <typename T> class AtomicIntrinsics<T, typename boost::enable_if_c<sizeof(T) == sizeof(LONGLONG)>::type> { public: - static const bool kHaveInterlocked64 = (_WIN32_WINNT >= _WIN32_WINNT_VISTA); + +#if defined(_WIN32_WINNT_VISTA) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA) + static const bool kHaveInterlocked64 = true; +#else + static const bool kHaveInterlocked64 = false; +#endif static T compareAndSwap(volatile T* dest, T expected, T newValue) { return InterlockedImpl<kHaveInterlocked64>::compareAndSwap(dest, expected, newValue); diff --git a/src/mongo/s/balance.cpp b/src/mongo/s/balance.cpp index 13f764f608d..de72b25dada 100644 --- a/src/mongo/s/balance.cpp +++ b/src/mongo/s/balance.cpp @@ -52,58 +52,70 @@ namespace mongo { for ( vector<CandidateChunkPtr>::const_iterator it = candidateChunks->begin(); it != candidateChunks->end(); ++it ) { const CandidateChunk& chunkInfo = *it->get(); - DBConfigPtr cfg = grid.getDBConfig( chunkInfo.ns ); - verify( cfg ); + // Changes to metadata, borked metadata, and connectivity problems should cause us to + // abort this chunk move, but shouldn't cause us to abort the entire round of chunks. + // TODO: Handle all these things more cleanly, since they're expected problems + try { - ChunkManagerPtr cm = cfg->getChunkManager( chunkInfo.ns ); - verify( cm ); + DBConfigPtr cfg = grid.getDBConfig( chunkInfo.ns ); + verify( cfg ); - ChunkPtr c = cm->findIntersectingChunk( chunkInfo.chunk.min ); - if ( c->getMin().woCompare( chunkInfo.chunk.min ) || c->getMax().woCompare( chunkInfo.chunk.max ) ) { - // likely a split happened somewhere - cm = cfg->getChunkManager( chunkInfo.ns , true /* reload */); + // NOTE: We purposely do not reload metadata here, since _doBalanceRound already + // tried to do so once. + ChunkManagerPtr cm = cfg->getChunkManager( chunkInfo.ns ); verify( cm ); - c = cm->findIntersectingChunk( chunkInfo.chunk.min ); + ChunkPtr c = cm->findIntersectingChunk( chunkInfo.chunk.min ); if ( c->getMin().woCompare( chunkInfo.chunk.min ) || c->getMax().woCompare( chunkInfo.chunk.max ) ) { - log() << "chunk mismatch after reload, ignoring will retry issue " << chunkInfo.chunk.toString() << endl; + // likely a split happened somewhere + cm = cfg->getChunkManager( chunkInfo.ns , true /* reload */); + verify( cm ); + + c = cm->findIntersectingChunk( chunkInfo.chunk.min ); + if ( c->getMin().woCompare( chunkInfo.chunk.min ) || c->getMax().woCompare( chunkInfo.chunk.max ) ) { + log() << "chunk mismatch after reload, ignoring will retry issue " << chunkInfo.chunk.toString() << endl; + continue; + } + } + + BSONObj res; + if (c->moveAndCommit(Shard::make(chunkInfo.to), + Chunk::MaxChunkSize, + secondaryThrottle, + waitForDelete, + res)) { + movedCount++; continue; } - } - BSONObj res; - if (c->moveAndCommit(Shard::make(chunkInfo.to), - Chunk::MaxChunkSize, - secondaryThrottle, - waitForDelete, - res)) { - movedCount++; - continue; - } + // the move requires acquiring the collection metadata's lock, which can fail + log() << "balancer move failed: " << res << " from: " << chunkInfo.from << " to: " << chunkInfo.to + << " chunk: " << chunkInfo.chunk << endl; - // the move requires acquiring the collection metadata's lock, which can fail - log() << "balancer move failed: " << res << " from: " << chunkInfo.from << " to: " << chunkInfo.to - << " chunk: " << chunkInfo.chunk << endl; + if ( res["chunkTooBig"].trueValue() ) { + // reload just to be safe + cm = cfg->getChunkManager( chunkInfo.ns ); + verify( cm ); + c = cm->findIntersectingChunk( chunkInfo.chunk.min ); - if ( res["chunkTooBig"].trueValue() ) { - // reload just to be safe - cm = cfg->getChunkManager( chunkInfo.ns ); - verify( cm ); - c = cm->findIntersectingChunk( chunkInfo.chunk.min ); - - log() << "forcing a split because migrate failed for size reasons" << endl; - - res = BSONObj(); - c->singleSplit( true , res ); - log() << "forced split results: " << res << endl; - - if ( ! res["ok"].trueValue() ) { - log() << "marking chunk as jumbo: " << c->toString() << endl; - c->markAsJumbo(); - // we increment moveCount so we do another round right away - movedCount++; - } + log() << "forcing a split because migrate failed for size reasons" << endl; + + res = BSONObj(); + c->singleSplit( true , res ); + log() << "forced split results: " << res << endl; + if ( ! res["ok"].trueValue() ) { + log() << "marking chunk as jumbo: " << c->toString() << endl; + c->markAsJumbo(); + // we increment moveCount so we do another round right away + movedCount++; + } + + } + } + catch( const DBException& ex ) { + warning() << "could not move chunk " << chunkInfo.chunk.toString() + << ", continuing balancing round" << causedBy( ex ) << endl; } } @@ -295,9 +307,18 @@ namespace mongo { cursor.reset(); DBConfigPtr cfg = grid.getDBConfig( ns ); - verify( cfg ); - ChunkManagerPtr cm = cfg->getChunkManager( ns ); - verify( cm ); + if ( !cfg ) { + warning() << "could not load db config to balance " << ns << " collection" << endl; + continue; + } + + // This line reloads the chunk manager once if this process doesn't know the collection + // is sharded yet. + ChunkManagerPtr cm = cfg->getChunkManagerIfExists( ns, true ); + if ( !cm ) { + warning() << "could not load chunks to balance " << ns << " collection" << endl; + continue; + } // loop through tags to make sure no chunk spans tags; splits on tag min. for all chunks bool didAnySplits = false; diff --git a/src/mongo/s/balancer_policy.cpp b/src/mongo/s/balancer_policy.cpp index 6672c894366..184451637ed 100644 --- a/src/mongo/s/balancer_policy.cpp +++ b/src/mongo/s/balancer_policy.cpp @@ -447,7 +447,7 @@ namespace mongo { string ChunkInfo::toString() const { StringBuilder buf; buf << " min: " << min; - buf << " max: " << min; + buf << " max: " << max; return buf.str(); } diff --git a/src/mongo/s/chunk_version.h b/src/mongo/s/chunk_version.h index 48986a10c28..36d5ac140ef 100644 --- a/src/mongo/s/chunk_version.h +++ b/src/mongo/s/chunk_version.h @@ -116,7 +116,7 @@ namespace mongo { } bool operator<=( const ChunkVersion& otherVersion ) const { - return this->_combined < otherVersion._combined; + return this->_combined <= otherVersion._combined; } // diff --git a/src/mongo/s/collection_manager_test.cpp b/src/mongo/s/collection_manager_test.cpp index f4525860654..c385d894d3d 100644 --- a/src/mongo/s/collection_manager_test.cpp +++ b/src/mongo/s/collection_manager_test.cpp @@ -406,7 +406,7 @@ namespace { chunk.setMax(BSON("a" << 20 << "b" << 0)); string errMsg; - ChunkVersion version(1, 0, OID()); + ChunkVersion version(2, 0, OID()); scoped_ptr<CollectionManager> cloned(getCollManager()->cloneMinus( chunk, version, &errMsg)); diff --git a/src/mongo/s/config.cpp b/src/mongo/s/config.cpp index 0e15a612d2f..b8698a10687 100644 --- a/src/mongo/s/config.cpp +++ b/src/mongo/s/config.cpp @@ -771,26 +771,33 @@ namespace mongo { unsigned firstGood = 0; int up = 0; vector<BSONObj> res; + // The last error we saw on a config server + string error; for ( unsigned i=0; i<_config.size(); i++ ) { - BSONObj x; + BSONObj result; scoped_ptr<ScopedDbConnection> conn; try { conn.reset( ScopedDbConnection::getInternalScopedDbConnection( _config[i], 30.0 ) ); - // check auth - conn->get()->update("config.foo.bar", BSONObj(), BSON("x" << 1)); - conn->get()->simpleCommand( "admin", &x, "getlasterror"); - if (x["err"].type() == String && x["err"].String() == "unauthorized") { - errmsg = "not authorized, did you start with --keyFile?"; - return false; - } + if ( ! conn->get()->runCommand( "config", + BSON( "dbhash" << 1 << + "collections" << BSON_ARRAY( "chunks" << + "databases" ) ), + result ) ) { + + // TODO: Make this a helper + error = result["errmsg"].eoo() ? "" : result["errmsg"].String(); + if (!result["assertion"].eoo()) error = result["assertion"].String(); - if ( ! conn->get()->simpleCommand( "config" , &x , "dbhash" ) ) - x = BSONObj(); + warning() << "couldn't check dbhash on config server " << _config[i] + << causedBy(result.toString()) << endl; + + result = BSONObj(); + } else { - x = x.getOwned(); + result = result.getOwned(); if ( up == 0 ) firstGood = i; up++; @@ -805,16 +812,18 @@ namespace mongo { // We need to catch DBExceptions b/c sometimes we throw them // instead of socket exceptions when findN fails - warning() << " couldn't check on config server:" << _config[i] << " ok for now : " << e.toString() << endl; + error = e.toString(); + warning() << " couldn't check dbhash on config server " << _config[i] << causedBy(e) << endl; } - res.push_back(x); + res.push_back(result); } if ( _config.size() == 1 ) return true; if ( up == 0 ) { - errmsg = "no config servers reachable"; + // Use a ptr to error so if empty we won't add causedby + errmsg = str::stream() << "no config servers successfully contacted" << causedBy(&error); return false; } @@ -859,7 +868,7 @@ namespace mongo { if ( checkConsistency ) { string errmsg; if ( ! checkConfigServersConsistent( errmsg ) ) { - LOG( LL_ERROR ) << "config servers not in sync! " << errmsg << warnings; + LOG( LL_ERROR ) << "could not verify that config servers are in sync" << causedBy(errmsg) << warnings; return false; } } diff --git a/src/mongo/s/d_migrate.cpp b/src/mongo/s/d_migrate.cpp index a9507e0b96c..139a640f5ce 100644 --- a/src/mongo/s/d_migrate.cpp +++ b/src/mongo/s/d_migrate.cpp @@ -1600,11 +1600,10 @@ namespace mongo { verify( ! min.isEmpty() ); verify( ! max.isEmpty() ); - slaveCount = ( getSlaveCount() / 2 ) + 1; + replSetMajorityCount = theReplSet ? theReplSet->config().getMajority() : 0; log() << "starting receiving-end of migration of chunk " << min << " -> " << max << - " for collection " << ns << " from " << from << - " (" << getSlaveCount() << " slaves detected)" << endl; + " for collection " << ns << " from " << from << endl; string errmsg; MoveTimingHelper timing( "to" , ns , min , max , 5 /* steps */ , errmsg ); @@ -1799,7 +1798,15 @@ namespace mongo { // 5. wait for commit state = STEADY; + bool transferAfterCommit = false; while ( state == STEADY || state == COMMIT_START ) { + + // Make sure we do at least one transfer after recv'ing the commit message + // If we aren't sure that at least one transfer happens *after* our state + // changes to COMMIT_START, there could be mods still on the FROM shard that + // got logged *after* our _transferMods but *before* the critical section. + if ( state == COMMIT_START ) transferAfterCommit = true; + BSONObj res; if ( ! conn->runCommand( "admin" , BSON( "_transferMods" << 1 ) , res ) ) { log() << "_transferMods failed in STEADY state: " << res << migrateLog; @@ -1817,12 +1824,16 @@ namespace mongo { return; } - if ( state == COMMIT_START ) { + // We know we're finished when: + // 1) The from side has told us that it has locked writes (COMMIT_START) + // 2) We've checked at least one more time for un-transmitted mods + if ( state == COMMIT_START && transferAfterCommit == true ) { if ( flushPendingWrites( lastOpApplied ) ) break; } - sleepmillis( 10 ); + // Only sleep if we aren't committing + if ( state == STEADY ) sleepmillis( 10 ); } if ( state == FAIL ) { @@ -1928,13 +1939,13 @@ namespace mongo { // if replication is on, try to force enough secondaries to catch up // TODO opReplicatedEnough should eventually honor priorities and geo-awareness // for now, we try to replicate to a sensible number of secondaries - return mongo::opReplicatedEnough( lastOpApplied , slaveCount ); + return mongo::opReplicatedEnough( lastOpApplied , replSetMajorityCount ); } bool flushPendingWrites( const ReplTime& lastOpApplied ) { if ( ! opReplicatedEnough( lastOpApplied ) ) { OpTime op( lastOpApplied ); - OCCASIONALLY warning() << "migrate commit waiting for " << slaveCount + OCCASIONALLY warning() << "migrate commit waiting for " << replSetMajorityCount << " slaves for '" << ns << "' " << min << " -> " << max << " waiting for: " << op << migrateLog; @@ -2012,7 +2023,7 @@ namespace mongo { long long numSteady; bool secondaryThrottle; - int slaveCount; + int replSetMajorityCount; enum State { READY , CLONE , CATCHUP , STEADY , COMMIT_START , DONE , FAIL , ABORT } state; string errmsg; diff --git a/src/mongo/s/d_split.cpp b/src/mongo/s/d_split.cpp index 2370fba0530..5231e9e571b 100644 --- a/src/mongo/s/d_split.cpp +++ b/src/mongo/s/d_split.cpp @@ -475,7 +475,7 @@ namespace mongo { string ChunkInfo::toString() const { ostringstream os; - os << "lastmod: " << lastmod.toString() << " min: " << min << " max: " << endl; + os << "lastmod: " << lastmod.toString() << " min: " << min << " max: " << max << endl; return os.str(); } // ** end temporary ** diff --git a/src/mongo/s/grid.cpp b/src/mongo/s/grid.cpp index ef6046c558b..4e48113c05f 100644 --- a/src/mongo/s/grid.cpp +++ b/src/mongo/s/grid.cpp @@ -30,11 +30,14 @@ #include "mongo/s/type_database.h" #include "mongo/s/type_settings.h" #include "mongo/s/type_shard.h" +#include "mongo/util/fail_point_service.h" #include "mongo/util/startup_test.h" #include "mongo/util/stringutils.h" namespace mongo { + MONGO_FP_DECLARE(neverBalance); + DBConfigPtr Grid::getDBConfig( string database , bool create , const string& shardNameHint ) { { string::size_type i = database.find( "." ); @@ -471,6 +474,9 @@ namespace mongo { */ bool Grid::shouldBalance( const string& ns, BSONObj* balancerDocOut ) const { + // Allow disabling the balancer for testing + if ( MONGO_FAIL_POINT(neverBalance) ) return false; + scoped_ptr<ScopedDbConnection> conn( ScopedDbConnection::getInternalScopedDbConnection( configServer.getPrimary().getConnString(), 30)); BSONObj balancerDoc; diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index 7aadbc31296..6f5f4ed52f7 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -222,7 +222,7 @@ namespace mongo { void init() { serverID.init(); - setupSignalHandlers(); + Logstream::get().addGlobalTee( new RamLog("global") ); } @@ -265,7 +265,7 @@ namespace mongo { using namespace mongo; static bool runMongosServer( bool doUpgrade ) { - + setupSignalHandlers(); setThreadName( "mongosMain" ); printShardingVersionInfo( false ); diff --git a/src/mongo/s/type_collection.cpp b/src/mongo/s/type_collection.cpp index c655d4f7f0d..f2fc3f21dfe 100644 --- a/src/mongo/s/type_collection.cpp +++ b/src/mongo/s/type_collection.cpp @@ -73,8 +73,11 @@ namespace mongo { return false; } - // Sharding related fields may only be set if the sharding key pattern is present. - if ((_unique || _noBalance) && (_keyPattern.nFields() == 0)) { + // Sharding related fields may only be set if the sharding key pattern is present, unless + // we're dropped. + if ( ( _unique || _noBalance ) && ( !_isDroppedSet || !_dropped ) + && ( _keyPattern.nFields() == 0 ) ) + { *errMsg = stream() << "missing " << keyPattern.name() << " field"; return false; } diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp index 487bec5996d..7ffebec383e 100644 --- a/src/mongo/scripting/engine_v8.cpp +++ b/src/mongo/scripting/engine_v8.cpp @@ -33,38 +33,47 @@ namespace mongo { extern const JSFile assert; } - /** - * Unwraps a BSONObj from the JS wrapper - */ - static BSONObj unwrapBSONObj(const v8::Handle<v8::Object>& obj) { + // The unwrapXXX functions extract internal fields from an object wrapped by wrapBSONObject. + // These functions are currently only used in places that should always have the correct + // type of object, however it may be possible for users to come up with a way to make these + // called with the wrong type so calling code should always check the returns. + static BSONHolder* unwrapHolder(V8Scope* scope, const v8::Handle<v8::Object>& obj) { + // Warning: can't throw exceptions in this context. + if (!scope->LazyBsonFT()->HasInstance(obj)) + return NULL; + v8::Handle<v8::External> field = v8::Handle<v8::External>::Cast(obj->GetInternalField(0)); - if (field.IsEmpty() || !field->IsExternal()) { - return BSONObj(); - } + if (field.IsEmpty() || !field->IsExternal()) + return 0; void* ptr = field->Value(); - return ((BSONHolder*)ptr)->_obj; + return (BSONHolder*)ptr; } - static BSONHolder* unwrapHolder(const v8::Handle<v8::Object>& obj) { - v8::Handle<v8::External> field = v8::Handle<v8::External>::Cast(obj->GetInternalField(0)); - if (field.IsEmpty() || !field->IsExternal()) - return 0; - void* ptr = field->Value(); - return (BSONHolder*)ptr; + static BSONObj unwrapBSONObj(V8Scope* scope, const v8::Handle<v8::Object>& obj) { + // Warning: can't throw exceptions in this context. + BSONHolder* holder = unwrapHolder(scope, obj); + return holder ? holder->_obj : BSONObj(); } - static v8::Handle<v8::Object> unwrapObject(const v8::Handle<v8::Object>& obj) { - return obj->GetInternalField(1).As<v8::Object>(); + static v8::Handle<v8::Object> unwrapObject(V8Scope* scope, const v8::Handle<v8::Object>& obj) { + // Warning: can't throw exceptions in this context. + if (!scope->LazyBsonFT()->HasInstance(obj)) + return v8::Handle<v8::Object>(); + + return obj->GetInternalField(1).As<v8::Object>(); } - v8::Persistent<v8::Object> V8Scope::wrapBSONObject(v8::Local<v8::Object> obj, - BSONHolder* data) { - data->_scope = this; - obj->SetInternalField(0, v8::External::New(data)); // Holder + void V8Scope::wrapBSONObject(v8::Handle<v8::Object> obj, BSONObj data, bool readOnly) { + verify(LazyBsonFT()->HasInstance(obj)); + + // Nothing below throws + BSONHolder* holder = new BSONHolder(data); + holder->_readOnly = readOnly; + holder->_scope = this; + obj->SetInternalField(0, v8::External::New(holder)); // Holder obj->SetInternalField(1, v8::Object::New()); // Object v8::Persistent<v8::Object> p = v8::Persistent<v8::Object>::New(obj); - bsonHolderTracker.track(p, data); - return p; + bsonHolderTracker.track(p, holder); } static v8::Handle<v8::Value> namedGet(v8::Local<v8::String> name, @@ -72,14 +81,16 @@ namespace mongo { v8::HandleScope handle_scope; v8::Handle<v8::Value> val; try { - v8::Handle<v8::Object> realObject = unwrapObject(info.Holder()); + V8Scope* scope = getScope(info.GetIsolate()); + v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder()); + if (realObject.IsEmpty()) return v8::Handle<v8::Value>(); if (realObject->HasOwnProperty(name)) { // value already cached or added return handle_scope.Close(realObject->Get(name)); } string key = toSTLString(name); - BSONHolder* holder = unwrapHolder(info.Holder()); + BSONHolder* holder = unwrapHolder(scope, info.Holder()); if (!holder || holder->_removed.count(key)) return handle_scope.Close(v8::Handle<v8::Value>()); @@ -88,8 +99,6 @@ namespace mongo { if (elmt.eoo()) return handle_scope.Close(v8::Handle<v8::Value>()); - v8::Local<v8::External> scp = v8::External::Cast(*info.Data()); - V8Scope* scope = (V8Scope*)(scp->Value()); val = scope->mongoToV8Element(elmt, holder->_readOnly); if (obj.objsize() > 128 || val->IsObject()) { @@ -115,59 +124,39 @@ namespace mongo { static v8::Handle<v8::Value> namedGetRO(v8::Local<v8::String> name, const v8::AccessorInfo &info) { return namedGet(name, info); - // Rest of function is unused but left in to ease backporting of SERVER-9267 - - v8::HandleScope handle_scope; - v8::Handle<v8::Value> val; - string key = toSTLString(name); - try { - BSONObj obj = unwrapBSONObj(info.Holder()); - BSONElement elmt = obj.getField(key.c_str()); - if (elmt.eoo()) - return handle_scope.Close(v8::Handle<v8::Value>()); - v8::Local<v8::External> scp = v8::External::Cast(*info.Data()); - V8Scope* scope = (V8Scope*)(scp->Value()); - val = scope->mongoToV8Element(elmt, true); - } - catch (const DBException &dbEx) { - return v8AssertionException(dbEx.toString()); - } - catch (...) { - return v8AssertionException(string("error getting read-only property ") + key); - } - return handle_scope.Close(val); } static v8::Handle<v8::Value> namedSet(v8::Local<v8::String> name, v8::Local<v8::Value> value_obj, const v8::AccessorInfo& info) { string key = toSTLString(name); - BSONHolder* holder = unwrapHolder(info.Holder()); + V8Scope* scope = getScope(info.GetIsolate()); + BSONHolder* holder = unwrapHolder(scope, info.Holder()); if (!holder) return v8::Handle<v8::Value>(); holder->_removed.erase(key); holder->_modified = true; - v8::Handle<v8::Object> realObject = unwrapObject(info.Holder()); + v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder()); + if (realObject.IsEmpty()) return v8::Handle<v8::Value>(); realObject->Set(name, value_obj); return value_obj; } static v8::Handle<v8::Array> namedEnumerator(const v8::AccessorInfo &info) { v8::HandleScope handle_scope; - BSONHolder* holder = unwrapHolder(info.Holder()); + V8Scope* scope = getScope(info.GetIsolate()); + BSONHolder* holder = unwrapHolder(scope, info.Holder()); if (!holder) return v8::Handle<v8::Array>(); BSONObj obj = holder->_obj; v8::Handle<v8::Array> out = v8::Array::New(); int outIndex = 0; - v8::Local<v8::External> scp = v8::External::Cast(*info.Data()); - V8Scope* scope = (V8Scope*)(scp->Value()); - unordered_set<string> added; + unordered_set<StringData, StringData::Hasher> added; // note here that if keys are parseable number, v8 will access them using index for (BSONObjIterator it(obj); it.more();) { const BSONElement& f = it.next(); - string sname = f.fieldName(); - if (holder->_removed.count(sname)) + StringData sname (f.fieldName(), f.fieldNameSize()-1); + if (holder->_removed.count(sname.toString())) continue; v8::Handle<v8::String> name = scope->v8StringData(sname); @@ -176,12 +165,13 @@ namespace mongo { } - v8::Handle<v8::Object> realObject = unwrapObject(info.Holder()); + v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder()); + if (realObject.IsEmpty()) return v8::Handle<v8::Array>(); v8::Handle<v8::Array> fields = realObject->GetOwnPropertyNames(); const int len = fields->Length(); for (int field=0; field < len; field++) { v8::Handle<v8::String> name = fields->Get(field).As<v8::String>(); - string sname = toSTLString(name); + V8String sname (name); if (added.count(sname)) continue; out->Set(outIndex++, name); @@ -192,12 +182,14 @@ namespace mongo { v8::Handle<v8::Boolean> namedDelete(v8::Local<v8::String> name, const v8::AccessorInfo& info) { v8::HandleScope handle_scope; string key = toSTLString(name); - BSONHolder* holder = unwrapHolder(info.Holder()); + V8Scope* scope = getScope(info.GetIsolate()); + BSONHolder* holder = unwrapHolder(scope, info.Holder()); if (!holder) return v8::Handle<v8::Boolean>(); holder->_removed.insert(key); holder->_modified = true; - v8::Handle<v8::Object> realObject = unwrapObject(info.Holder()); + v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder()); + if (realObject.IsEmpty()) return v8::Handle<v8::Boolean>(); realObject->Delete(name); return v8::True(); } @@ -206,16 +198,16 @@ namespace mongo { v8::HandleScope handle_scope; v8::Handle<v8::Value> val; try { - v8::Handle<v8::Object> realObject = unwrapObject(info.Holder()); + V8Scope* scope = getScope(info.GetIsolate()); + v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder()); + if (realObject.IsEmpty()) return v8::Handle<v8::Value>(); if (realObject->Has(index)) { // value already cached or added return handle_scope.Close(realObject->Get(index)); } string key = str::stream() << index; - v8::Local<v8::External> scp = v8::External::Cast(*info.Data()); - V8Scope* scope = (V8Scope*)(scp->Value()); - BSONHolder* holder = unwrapHolder(info.Holder()); + BSONHolder* holder = unwrapHolder(scope, info.Holder()); if (!holder) return v8::Handle<v8::Value>(); if (holder->_removed.count(key)) return handle_scope.Close(v8::Handle<v8::Value>()); @@ -245,56 +237,34 @@ namespace mongo { v8::Handle<v8::Boolean> indexedDelete(uint32_t index, const v8::AccessorInfo& info) { string key = str::stream() << index; - BSONHolder* holder = unwrapHolder(info.Holder()); + V8Scope* scope = getScope(info.GetIsolate()); + BSONHolder* holder = unwrapHolder(scope, info.Holder()); if (!holder) return v8::Handle<v8::Boolean>(); holder->_removed.insert(key); holder->_modified = true; // also delete in JS obj - v8::Handle<v8::Object> realObject = unwrapObject(info.Holder()); + v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder()); + if (realObject.IsEmpty()) return v8::Handle<v8::Boolean>(); realObject->Delete(index); return v8::True(); } static v8::Handle<v8::Value> indexedGetRO(uint32_t index, const v8::AccessorInfo &info) { return indexedGet(index, info); - // Rest of function is unused but left in-place to ease backporting of SERVER-9267 - - v8::HandleScope handle_scope; - v8::Handle<v8::Value> val; - try { - string key = str::stream() << index; - v8::Local<v8::External> scp = v8::External::Cast(*info.Data()); - V8Scope* scope = (V8Scope*)(scp->Value()); - - BSONObj obj = unwrapBSONObj(info.Holder()); - BSONElement elmt = obj.getField(key); - - if (elmt.eoo()) - return handle_scope.Close(v8::Handle<v8::Value>()); - - val = scope->mongoToV8Element(elmt, true); - } - catch (const DBException &dbEx) { - return v8AssertionException(dbEx.toString()); - } - catch (...) { - return v8AssertionException(str::stream() - << "error getting read-only indexed property " - << index); - } - return handle_scope.Close(val); } static v8::Handle<v8::Value> indexedSet(uint32_t index, v8::Local<v8::Value> value_obj, const v8::AccessorInfo& info) { string key = str::stream() << index; - BSONHolder* holder = unwrapHolder(info.Holder()); + V8Scope* scope = getScope(info.GetIsolate()); + BSONHolder* holder = unwrapHolder(scope, info.Holder()); if (!holder) return v8::Handle<v8::Value>(); holder->_removed.erase(key); holder->_modified = true; - v8::Handle<v8::Object> realObject = unwrapObject(info.Holder()); + v8::Handle<v8::Object> realObject = unwrapObject(scope, info.Holder()); + if (realObject.IsEmpty()) return v8::Handle<v8::Value>(); realObject->Set(index, value_obj); return value_obj; } @@ -302,15 +272,13 @@ namespace mongo { v8::Handle<v8::Value> NamedReadOnlySet(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { - string key = toSTLString(property); - cout << "cannot write property " << key << " to read-only object" << endl; + cout << "cannot write property " << V8String(property) << " to read-only object" << endl; return value; } v8::Handle<v8::Boolean> NamedReadOnlyDelete(v8::Local<v8::String> property, const v8::AccessorInfo& info) { - string key = toSTLString(property); - cout << "cannot delete property " << key << " from read-only object" << endl; + cout << "cannot delete property " << V8String(property) << " from read-only object" << endl; return v8::Boolean::New(false); } @@ -451,7 +419,8 @@ namespace mongo { void V8Scope::kill() { mongo::mutex::scoped_lock interruptLock(_interruptLock); if (!_inNativeExecution) { - // set the TERMINATE flag on the stack guard for this isolate + // Set the TERMINATE flag on the stack guard for this isolate. + // This won't happen between calls to nativePrologue and nativeEpilogue(). v8::V8::TerminateExecution(_isolate); LOG(1) << "killing v8 scope. isolate: " << _isolate << endl; } @@ -498,6 +467,8 @@ namespace mongo { _context = v8::Context::New(); v8::Context::Scope context_scope(_context); + _isolate->SetData(this); + // display heap statistics on MarkAndSweep GC run v8::V8::AddGCPrologueCallback(gcCallback<GCPrologueState>, v8::kGCTypeMarkSweepCompact); v8::V8::AddGCEpilogueCallback(gcCallback<GCEpilogueState>, v8::kGCTypeMarkSweepCompact); @@ -509,46 +480,33 @@ namespace mongo { // create a global (rooted) object _global = v8::Persistent<v8::Object>::New(_context->Global()); + // Grab the RegExp constructor before user code gets a chance to change it. This ensures + // we can always construct proper RegExps from C++. + v8::Handle<v8::Value> regexp = _global->Get(strLitToV8("RegExp")); + verify(regexp->IsFunction()); + _jsRegExpConstructor = v8::Persistent<v8::Function>::New(regexp.As<v8::Function>()); + // initialize lazy object template - lzObjectTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New()); - lzObjectTemplate->SetInternalFieldCount(2); - lzObjectTemplate->SetNamedPropertyHandler(namedGet, namedSet, 0, namedDelete, - namedEnumerator, v8::External::New(this)); - lzObjectTemplate->SetIndexedPropertyHandler(indexedGet, indexedSet, 0, indexedDelete, - namedEnumerator, v8::External::New(this)); - lzObjectTemplate->NewInstance()->GetPrototype()->ToObject()->ForceSet( - v8::String::New("_bson"), - v8::Boolean::New(true), - v8::DontEnum); - - roObjectTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New()); - roObjectTemplate->SetInternalFieldCount(2); - roObjectTemplate->SetNamedPropertyHandler(namedGetRO, NamedReadOnlySet, 0, - NamedReadOnlyDelete, namedEnumerator, - v8::External::New(this)); - roObjectTemplate->SetIndexedPropertyHandler(indexedGetRO, IndexedReadOnlySet, 0, - IndexedReadOnlyDelete, 0, - v8::External::New(this)); - roObjectTemplate->NewInstance()->GetPrototype()->ToObject()->ForceSet( - v8::String::New("_bson"), - v8::Boolean::New(true), - v8::DontEnum); - - // initialize lazy array template - // unfortunately it is not possible to create true v8 array from a template - // this means we use an object template and copy methods over - // this it creates issues when calling certain methods that check array type - lzArrayTemplate = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New()); - lzArrayTemplate->SetInternalFieldCount(1); - lzArrayTemplate->SetIndexedPropertyHandler(indexedGet, 0, 0, 0, 0, - v8::External::New(this)); - lzArrayTemplate->NewInstance()->GetPrototype()->ToObject()->ForceSet( - v8::String::New("_bson"), - v8::Boolean::New(true), - v8::DontEnum); - - internalFieldObjects = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New()); - internalFieldObjects->SetInternalFieldCount(1); + _LazyBsonFT = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); + LazyBsonFT()->InstanceTemplate()->SetInternalFieldCount(2); + LazyBsonFT()->InstanceTemplate()->SetNamedPropertyHandler( + namedGet, namedSet, NULL, namedDelete, namedEnumerator); + LazyBsonFT()->InstanceTemplate()->SetIndexedPropertyHandler( + indexedGet, indexedSet, NULL, indexedDelete, namedEnumerator); + LazyBsonFT()->PrototypeTemplate()->Set(strLitToV8("_bson"), + v8::Boolean::New(true), + v8::DontEnum); + + _ROBsonFT = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); + ROBsonFT()->Inherit(LazyBsonFT()); // This makes LazyBsonFT()->HasInstance() true + ROBsonFT()->InstanceTemplate()->SetInternalFieldCount(2); + ROBsonFT()->InstanceTemplate()->SetNamedPropertyHandler( + namedGetRO, NamedReadOnlySet, NULL, NamedReadOnlyDelete, namedEnumerator); + ROBsonFT()->InstanceTemplate()->SetIndexedPropertyHandler( + indexedGetRO, IndexedReadOnlySet, NULL, IndexedReadOnlyDelete, NULL); + ROBsonFT()->PrototypeTemplate()->Set(strLitToV8("_bson"), + v8::Boolean::New(true), + v8::DontEnum); injectV8Function("print", Print); injectV8Function("version", Version); // TODO: remove @@ -582,11 +540,9 @@ namespace mongo { _funcs[ i ].Dispose(); _funcs.clear(); _global.Dispose(); - lzObjectTemplate.Dispose(); - lzArrayTemplate.Dispose(); - roObjectTemplate.Dispose(); - internalFieldObjects.Dispose(); _context.Dispose(); + // Note: This block is unnecessary since we destroy the v8 Heap (Isolate) immediately + // after. Leaving in for now, but nothing new should be added. } _isolate->Dispose(); // set the isolate to NULL so ObjTracker destructors know that v8 is no longer reachable @@ -617,13 +573,13 @@ namespace mongo { v8::HandleScope handle_scope; try { v8::Local<v8::External> f = - v8::External::Cast(*args.Callee()->Get(v8::String::New("_native_function"))); + v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_native_function"))); NativeFunction function = (NativeFunction)(f->Value()); v8::Local<v8::External> data = - v8::External::Cast(*args.Callee()->Get(v8::String::New("_native_data"))); + v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_native_data"))); BSONObjBuilder b; for (int i = 0; i < args.Length(); ++i) - scope->v8ToMongoElement(b, str::stream() << i, args[i]); + scope->v8ToMongoElement(b, BSONObjBuilder::numStr(i), args[i]); BSONObj nativeArgs = b.obj(); ret = function(nativeArgs, data->Value()); } @@ -641,15 +597,14 @@ namespace mongo { v8::Handle<v8::Value> V8Scope::v8Callback(const v8::Arguments &args) { v8::HandleScope handle_scope; - v8::Local<v8::External> scp = v8::External::Cast(*args.Data()); - V8Scope* scope = (V8Scope*)(scp->Value()); + V8Scope* scope = getScope(args.GetIsolate()); if (!scope->nativePrologue()) // execution terminated return v8::Undefined(); v8::Local<v8::External> f = - v8::External::Cast(*args.Callee()->Get(v8::String::New("_v8_function"))); + v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_v8_function"))); v8Function function = (v8Function)(f->Value()); v8::Handle<v8::Value> ret; string exceptionText; @@ -784,38 +739,105 @@ namespace mongo { v8::Handle<v8::FunctionTemplate> getNumberLongFunctionTemplate(V8Scope* scope) { v8::Handle<v8::FunctionTemplate> numberLong = scope->createV8Function(numberLongInit); - v8::Local<v8::Template> proto = numberLong->PrototypeTemplate(); - scope->injectV8Function("valueOf", numberLongValueOf, proto); - scope->injectV8Function("toNumber", numberLongToNumber, proto); - scope->injectV8Function("toString", numberLongToString, proto); + v8::Handle<v8::ObjectTemplate> proto = numberLong->PrototypeTemplate(); + scope->injectV8Method("valueOf", numberLongValueOf, proto); + scope->injectV8Method("toNumber", numberLongToNumber, proto); + scope->injectV8Method("toString", numberLongToString, proto); return numberLong; } v8::Handle<v8::FunctionTemplate> getNumberIntFunctionTemplate(V8Scope* scope) { v8::Handle<v8::FunctionTemplate> numberInt = scope->createV8Function(numberIntInit); - v8::Local<v8::Template> proto = numberInt->PrototypeTemplate(); - scope->injectV8Function("valueOf", numberIntValueOf, proto); - scope->injectV8Function("toNumber", numberIntToNumber, proto); - scope->injectV8Function("toString", numberIntToString, proto); + v8::Handle<v8::ObjectTemplate> proto = numberInt->PrototypeTemplate(); + scope->injectV8Method("valueOf", numberIntValueOf, proto); + scope->injectV8Method("toNumber", numberIntToNumber, proto); + scope->injectV8Method("toString", numberIntToString, proto); return numberInt; } v8::Handle<v8::FunctionTemplate> getBinDataFunctionTemplate(V8Scope* scope) { v8::Handle<v8::FunctionTemplate> binData = scope->createV8Function(binDataInit); binData->InstanceTemplate()->SetInternalFieldCount(1); - v8::Local<v8::Template> proto = binData->PrototypeTemplate(); - scope->injectV8Function("toString", binDataToString, proto); - scope->injectV8Function("base64", binDataToBase64, proto); - scope->injectV8Function("hex", binDataToHex, proto); + v8::Handle<v8::ObjectTemplate> proto = binData->PrototypeTemplate(); + scope->injectV8Method("toString", binDataToString, proto); + scope->injectV8Method("base64", binDataToBase64, proto); + scope->injectV8Method("hex", binDataToHex, proto); return binData; } v8::Handle<v8::FunctionTemplate> getTimestampFunctionTemplate(V8Scope* scope) { v8::Handle<v8::FunctionTemplate> ts = scope->createV8Function(dbTimestampInit); - ts->InstanceTemplate()->SetInternalFieldCount(1); return ts; } + v8::Handle<v8::Value> minKeyToJson(V8Scope* scope, const v8::Arguments& args) { + // MinKey can't just be an object like {$minKey:1} since insert() checks for fields that + // start with $ and raises an error. See DBCollection.prototype._validateForStorage(). + return scope->strLitToV8("{ \"$minKey\" : 1 }"); + } + + v8::Handle<v8::Value> minKeyCall(const v8::Arguments& args) { + // The idea here is that MinKey and MaxKey are singleton callable objects + // that return the singleton when called. This enables all instances to + // compare == and === to MinKey even if created by "new MinKey()" in JS. + V8Scope* scope = getScope(args.GetIsolate()); + + v8::Handle<v8::Function> func = scope->MinKeyFT()->GetFunction(); + v8::Handle<v8::String> name = scope->strLitToV8("singleton"); + v8::Handle<v8::Value> singleton = func->GetHiddenValue(name); + if (!singleton.IsEmpty()) + return singleton; + + if (!args.IsConstructCall()) + return func->NewInstance(); + + verify(scope->MinKeyFT()->HasInstance(args.This())); + + func->SetHiddenValue(name, args.This()); + return v8::Undefined(); + } + + v8::Handle<v8::FunctionTemplate> getMinKeyFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> myTemplate = v8::FunctionTemplate::New(minKeyCall); + myTemplate->InstanceTemplate()->SetCallAsFunctionHandler(minKeyCall); + myTemplate->PrototypeTemplate()->Set( + "tojson", scope->createV8Function(minKeyToJson)->GetFunction()); + myTemplate->SetClassName(scope->strLitToV8("MinKey")); + return myTemplate; + } + + v8::Handle<v8::Value> maxKeyToJson(V8Scope* scope, const v8::Arguments& args) { + return scope->strLitToV8("{ \"$maxKey\" : 1 }"); + } + + v8::Handle<v8::Value> maxKeyCall(const v8::Arguments& args) { + // See comment in minKeyCall. + V8Scope* scope = getScope(args.GetIsolate()); + + v8::Handle<v8::Function> func = scope->MaxKeyFT()->GetFunction(); + v8::Handle<v8::String> name = scope->strLitToV8("singleton"); + v8::Handle<v8::Value> singleton = func->GetHiddenValue(name); + if (!singleton.IsEmpty()) + return singleton; + + if (!args.IsConstructCall()) + return func->NewInstance(); + + verify(scope->MaxKeyFT()->HasInstance(args.This())); + + func->SetHiddenValue(name, args.This()); + return v8::Undefined(); + } + + v8::Handle<v8::FunctionTemplate> getMaxKeyFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> myTemplate = v8::FunctionTemplate::New(maxKeyCall); + myTemplate->InstanceTemplate()->SetCallAsFunctionHandler(maxKeyCall); + myTemplate->PrototypeTemplate()->Set( + "tojson", scope->createV8Function(maxKeyToJson)->GetFunction()); + myTemplate->SetClassName(scope->strLitToV8("MaxKey")); + return myTemplate; + } + std::string V8Scope::v8ExceptionToSTLString(const v8::TryCatch* try_catch) { stringstream ss; v8::String::Utf8Value exceptionText(try_catch->Exception()); @@ -904,7 +926,7 @@ namespace mongo { // --- functions ----- - bool hasFunctionIdentifier(const string& code) { + bool hasFunctionIdentifier(const StringData& code) { if (code.size() < 9 || code.find("function") != 0 ) return false; @@ -973,20 +995,20 @@ namespace mongo { v8::Handle<v8::Value> funcValue = _funcs[func-1]; v8::TryCatch try_catch; v8::Local<v8::Value> result; - // TODO SERVER-8016: properly allocate handles on the stack - v8::Handle<v8::Value> args[24]; + // TODO SERVER-8016: properly allocate handles on the stack + static const int MAX_ARGS = 24; const int nargs = argsObject ? argsObject->nFields() : 0; + uassert(16862, "Too many arguments. Max is 24", + nargs <= MAX_ARGS); + + v8::Handle<v8::Value> args[MAX_ARGS]; if (nargs) { BSONObjIterator it(*argsObject); - for (int i=0; i<nargs && i<24; i++) { + for (int i=0; i<nargs; i++) { BSONElement next = it.next(); args[i] = mongoToV8Element(next, readOnlyArgs); } - setObject("args", *argsObject, readOnlyArgs); // for backwards compatibility - } - else { - _global->ForceSet(v8::String::New("args"), v8::Undefined()); } v8::Handle<v8::Object> v8recv; @@ -1024,14 +1046,14 @@ namespace mongo { v8::Handle<v8::Object> resultObject = result->ToObject(); // must validate the handle because TerminateExecution may have // been thrown after the above checks - if (!resultObject.IsEmpty() && resultObject->Has(v8StringData("_v8_function"))) { + if (!resultObject.IsEmpty() && resultObject->Has(strLitToV8("_v8_function"))) { log() << "storing native function as return value" << endl; _lastRetIsNativeCode = true; } else { _lastRetIsNativeCode = false; } - _global->ForceSet(v8::String::New("__returnValue"), result); + _global->ForceSet(strLitToV8("__returnValue"), result); } return 0; @@ -1080,11 +1102,11 @@ namespace mongo { if (checkV8ErrorState(result, try_catch, reportError, assertOnError)) return false; - _global->ForceSet(v8StringData("__lastres__"), result); + _global->ForceSet(strLitToV8("__lastres__"), result); if (printResult && !result->IsUndefined()) { // appears to only be used by shell - cout << toSTLString(result) << endl; + cout << V8String(result) << endl; } return true; @@ -1098,36 +1120,51 @@ namespace mongo { void V8Scope::injectNative(const char *field, NativeFunction func, v8::Handle<v8::Object>& obj, void* data) { v8::Handle<v8::FunctionTemplate> ft = createV8Function(nativeCallback); - ft->Set(v8::String::New("_native_function"), v8::External::New((void*)func)); - ft->Set(v8::String::New("_native_data"), v8::External::New(data)); - ft->SetClassName(v8StringData(field)); - obj->ForceSet(v8StringData(field), ft->GetFunction()); + ft->Set(strLitToV8("_native_function"), + v8::External::New((void*)func), + v8::PropertyAttribute(v8::DontEnum | v8::ReadOnly)); + ft->Set(strLitToV8("_native_data"), + v8::External::New(data), + v8::PropertyAttribute(v8::DontEnum | v8::ReadOnly)); + injectV8Function(field, ft, obj); } - void V8Scope::injectV8Function(const char *field, v8Function func) { - injectV8Function(field, func, _global); + v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Function(const char *field, v8Function func) { + return injectV8Function(field, func, _global); } - void V8Scope::injectV8Function(const char *field, v8Function func, - v8::Handle<v8::Object>& obj) { - v8::Handle<v8::FunctionTemplate> ft = createV8Function(func); - ft->SetClassName(v8StringData(field)); - v8::Handle<v8::Function> f = ft->GetFunction(); - obj->ForceSet(v8StringData(field), f); + v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Function(const char *field, + v8Function func, + v8::Handle<v8::Object>& obj) { + return injectV8Function(field, createV8Function(func), obj); + } + + v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Function(const char *fieldCStr, + v8::Handle<v8::FunctionTemplate> ft, + v8::Handle<v8::Object>& obj) { + v8::Handle<v8::String> field = v8StringData(fieldCStr); + ft->SetClassName(field); + v8::Handle<v8::Function> func = ft->GetFunction(); + func->SetName(field); + obj->ForceSet(field, func); + return ft; } - void V8Scope::injectV8Function(const char *field, v8Function func, - v8::Handle<v8::Template>& t) { + v8::Handle<v8::FunctionTemplate> V8Scope::injectV8Method( + const char *fieldCStr, + v8Function func, + v8::Handle<v8::ObjectTemplate>& proto) { + v8::Handle<v8::String> field = v8StringData(fieldCStr); v8::Handle<v8::FunctionTemplate> ft = createV8Function(func); - ft->SetClassName(v8StringData(field)); v8::Handle<v8::Function> f = ft->GetFunction(); - t->Set(v8StringData(field), f); + f->SetName(field); + proto->Set(field, f); + return ft; } v8::Handle<v8::FunctionTemplate> V8Scope::createV8Function(v8Function func) { - v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(v8Callback, - v8::External::New(this)); - ft->Set(v8::String::New("_v8_function"), v8::External::New(reinterpret_cast<void*>(func)), + v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(v8Callback); + ft->Set(strLitToV8("_v8_function"), v8::External::New(reinterpret_cast<void*>(func)), static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::ReadOnly)); return ft; } @@ -1140,6 +1177,7 @@ namespace mongo { } void V8Scope::localConnect(const char * dbName) { + typedef v8::Persistent<v8::FunctionTemplate> FTPtr; { V8_SIMPLE_HEADER if (_connectState == EXTERNAL) @@ -1162,8 +1200,8 @@ namespace mongo { injectV8Function("load", load); // install the Mongo function object and instantiate the 'db' global - _global->ForceSet(v8StringData("Mongo"), - getMongoFunctionTemplate(this, true)->GetFunction()); + _MongoFT = FTPtr::New(getMongoFunctionTemplate(this, true)); + injectV8Function("Mongo", MongoFT(), _global); execCoreFiles(); exec("_mongo = new Mongo();", "local connect 2", false, true, true, 0); exec((string)"db = _mongo.getDB(\"" + dbName + "\");", "local connect 3", @@ -1175,6 +1213,7 @@ namespace mongo { } void V8Scope::externalSetup() { + typedef v8::Persistent<v8::FunctionTemplate> FTPtr; V8_SIMPLE_HEADER if (_connectState == EXTERNAL) return; @@ -1191,56 +1230,61 @@ namespace mongo { injectV8Function("load", load); // install the Mongo function object - _global->ForceSet(v8StringData("Mongo"), - getMongoFunctionTemplate(this, false)->GetFunction()); + _MongoFT = FTPtr::New(getMongoFunctionTemplate(this, false)); + injectV8Function("Mongo", MongoFT(), _global); execCoreFiles(); _connectState = EXTERNAL; } void V8Scope::installDBAccess() { - v8::Handle<v8::FunctionTemplate> db = createV8Function(dbInit); - db->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, collectionSetter); - _global->ForceSet(v8StringData("DB"), db->GetFunction()); + typedef v8::Persistent<v8::FunctionTemplate> FTPtr; + _DBFT = FTPtr::New(createV8Function(dbInit)); + _DBQueryFT = FTPtr::New(createV8Function(dbQueryInit)); + _DBCollectionFT = FTPtr::New(createV8Function(collectionInit)); + + // These must be done before calling injectV8Function + DBFT()->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, collectionSetter); + DBQueryFT()->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess); + DBCollectionFT()->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, + collectionSetter); - v8::Handle<v8::FunctionTemplate> dbCollection = createV8Function(collectionInit); - dbCollection->InstanceTemplate()->SetNamedPropertyHandler(collectionGetter, - collectionSetter); - _global->ForceSet(v8StringData("DBCollection"), dbCollection->GetFunction()); + injectV8Function("DB", DBFT(), _global); + injectV8Function("DBQuery", DBQueryFT(), _global); + injectV8Function("DBCollection", DBCollectionFT(), _global); - v8::Handle<v8::FunctionTemplate> dbQuery = createV8Function(dbQueryInit); - dbQuery->InstanceTemplate()->SetIndexedPropertyHandler(dbQueryIndexAccess); - _global->ForceSet(v8StringData("DBQuery"), dbQuery->GetFunction()); + // The internal cursor type isn't exposed to the users at all + _InternalCursorFT = FTPtr::New(getInternalCursorFunctionTemplate(this)); } void V8Scope::installBSONTypes() { - injectV8Function("ObjectId", objectIdInit, _global); - injectV8Function("DBRef", dbRefInit, _global); - injectV8Function("DBPointer", dbPointerInit, _global); - - _global->ForceSet(v8StringData("BinData"), - getBinDataFunctionTemplate(this)->GetFunction()); - _global->ForceSet(v8StringData("UUID"), - createV8Function(uuidInit)->GetFunction()); - _global->ForceSet(v8StringData("MD5"), - createV8Function(md5Init)->GetFunction()); - _global->ForceSet(v8StringData("HexData"), - createV8Function(hexDataInit)->GetFunction()); - _global->ForceSet(v8StringData("NumberLong"), - getNumberLongFunctionTemplate(this)->GetFunction()); - _global->ForceSet(v8StringData("NumberInt"), - getNumberIntFunctionTemplate(this)->GetFunction()); - _global->ForceSet(v8StringData("Timestamp"), - getTimestampFunctionTemplate(this)->GetFunction()); - - BSONObjBuilder b; - b.appendMaxKey(""); - b.appendMinKey(""); - BSONObj o = b.obj(); - BSONObjIterator i(o); - _global->ForceSet(v8StringData("MaxKey"), mongoToV8Element(i.next()), v8::ReadOnly); - _global->ForceSet(v8StringData("MinKey"), mongoToV8Element(i.next()), v8::ReadOnly); - _global->Get(v8StringData("Object"))->ToObject()->ForceSet( - v8StringData("bsonsize"), + typedef v8::Persistent<v8::FunctionTemplate> FTPtr; + _ObjectIdFT = FTPtr::New(injectV8Function("ObjectId", objectIdInit)); + _DBRefFT = FTPtr::New(injectV8Function("DBRef", dbRefInit)); + _DBPointerFT = FTPtr::New(injectV8Function("DBPointer", dbPointerInit)); + + _BinDataFT = FTPtr::New(getBinDataFunctionTemplate(this)); + _NumberLongFT = FTPtr::New(getNumberLongFunctionTemplate(this)); + _NumberIntFT = FTPtr::New(getNumberIntFunctionTemplate(this)); + _TimestampFT = FTPtr::New(getTimestampFunctionTemplate(this)); + _MinKeyFT = FTPtr::New(getMinKeyFunctionTemplate(this)); + _MaxKeyFT = FTPtr::New(getMaxKeyFunctionTemplate(this)); + + injectV8Function("BinData", BinDataFT(), _global); + injectV8Function("NumberLong", NumberLongFT(), _global); + injectV8Function("NumberInt", NumberIntFT(), _global); + injectV8Function("Timestamp", TimestampFT(), _global); + + // These are instances created from the functions, not the functions themselves + _global->ForceSet(strLitToV8("MinKey"), MinKeyFT()->GetFunction()->NewInstance()); + _global->ForceSet(strLitToV8("MaxKey"), MaxKeyFT()->GetFunction()->NewInstance()); + + // These all create BinData objects so we don't need to hold on to them. + injectV8Function("UUID", uuidInit); + injectV8Function("MD5", md5Init); + injectV8Function("HexData", hexDataInit); + + _global->Get(strLitToV8("Object"))->ToObject()->ForceSet( + strLitToV8("bsonsize"), createV8Function(bsonsize)->GetFunction()); } @@ -1276,7 +1320,7 @@ namespace mongo { v8::Local<v8::Value> V8Scope::newId(const OID &id) { v8::HandleScope handle_scope; - v8::Function* idCons = this->getObjectIdCons(); + v8::Handle<v8::Function> idCons = ObjectIdFT()->GetFunction(); v8::Handle<v8::Value> argv[1]; argv[0] = v8::String::New(id.str().c_str()); return handle_scope.Close(idCons->NewInstance(1, argv)); @@ -1285,83 +1329,35 @@ namespace mongo { /** * converts a BSONObj to a Lazy V8 object */ - v8::Persistent<v8::Object> V8Scope::mongoToLZV8(const BSONObj& m, bool readOnly) { - v8::Local<v8::Object> o; - BSONHolder* own = new BSONHolder(m); - own->_readOnly = readOnly; - - if (readOnly) { - o = roObjectTemplate->NewInstance(); - massert(16497, str::stream() << "V8: NULL RO Object template instantiated. " - << (v8::V8::IsExecutionTerminating() ? - "v8 execution is terminating." : - "v8 still executing."), - *o != NULL); - } else { - o = lzObjectTemplate->NewInstance(); - massert(16496, str::stream() << "V8: NULL Object template instantiated. " - << (v8::V8::IsExecutionTerminating() ? - "v8 execution is terminating." : - "v8 still executing."), - *o != NULL); - static string ref = "$ref"; - if (ref == m.firstElement().fieldName()) { - const BSONElement& id = m["$id"]; - if (!id.eoo()) { - v8::Function* dbRef = getNamedCons("DBRef"); - o->SetPrototype(dbRef->NewInstance()->GetPrototype()); + v8::Handle<v8::Object> V8Scope::mongoToLZV8(const BSONObj& m, bool readOnly) { + if (m.firstElementType() == String && str::equals(m.firstElementFieldName(), "$ref")) { + BSONObjIterator it(m); + const BSONElement ref = it.next(); + const BSONElement id = it.next(); + if (id.ok() && str::equals(id.fieldName(), "$id")) { + v8::Handle<v8::Value> args[] = { + mongoToV8Element(ref, readOnly), + mongoToV8Element(id, readOnly) + }; + v8::Local<v8::Object> dbRef = DBRefFT()->GetFunction()->NewInstance(2, args); + while (it.more()) { + BSONElement elem = it.next(); + dbRef->Set(v8StringData(elem.fieldName()), mongoToV8Element(elem, readOnly)); } + return dbRef; } } - return wrapBSONObject(o, own); - - } + v8::Handle<v8::FunctionTemplate> templ = readOnly ? ROBsonFT() : LazyBsonFT(); + v8::Handle<v8::Object> o = templ->GetFunction()->NewInstance(); + massert(16496, str::stream() << "V8: NULL Object template instantiated. " + << (v8::V8::IsExecutionTerminating() ? + "v8 execution is terminating." : + "v8 still executing."), + *o != NULL); - v8::Handle<v8::Value> minKeyToJson(const v8::Arguments& args) { - return v8::String::New("{ \"$minKey\" : 1 }"); - } - - v8::Handle<v8::Value> minKeyToString(const v8::Arguments& args) { - return v8::String::New("[object MinKey]"); - } - - v8::Local<v8::Object> V8Scope::newMinKeyInstance() { - v8::Local<v8::ObjectTemplate> myTemplate = v8::Local<v8::ObjectTemplate>::New( - v8::ObjectTemplate::New()); - myTemplate->SetInternalFieldCount(1); - myTemplate->SetCallAsFunctionHandler(minKeyToJson); - - v8::Local<v8::Object> instance = myTemplate->NewInstance(); - instance->ForceSet(v8::String::New("tojson"), - v8::FunctionTemplate::New(minKeyToJson)->GetFunction(), v8::ReadOnly); - instance->ForceSet(v8::String::New("toString"), - v8::FunctionTemplate::New(minKeyToJson)->GetFunction(), v8::ReadOnly); - instance->SetInternalField(0, v8::Uint32::New( mongo::MinKey )); - return instance; - } - - v8::Handle<v8::Value> maxKeyToJson(const v8::Arguments& args) { - return v8::String::New("{ \"$maxKey\" : 1 }"); - } - - v8::Handle<v8::Value> maxKeyToString(const v8::Arguments& args) { - return v8::String::New("[object MaxKey]"); - } - - v8::Local<v8::Object> V8Scope::newMaxKeyInstance() { - v8::Local<v8::ObjectTemplate> myTemplate = v8::Local<v8::ObjectTemplate>::New( - v8::ObjectTemplate::New()); - myTemplate->SetInternalFieldCount(1); - myTemplate->SetCallAsFunctionHandler(maxKeyToJson); - - v8::Local<v8::Object> instance = myTemplate->NewInstance(); - instance->ForceSet(v8::String::New("tojson"), - v8::FunctionTemplate::New(maxKeyToJson)->GetFunction(), v8::ReadOnly); - instance->ForceSet(v8::String::New("toString"), - v8::FunctionTemplate::New(maxKeyToJson)->GetFunction(), v8::ReadOnly); - instance->SetInternalField(0, v8::Uint32::New( mongo::MaxKey )); - return instance; + wrapBSONObject(o, m, readOnly); + return o; } v8::Handle<v8::Value> V8Scope::mongoToV8Element(const BSONElement &elem, bool readOnly) { @@ -1410,10 +1406,24 @@ namespace mongo { case mongo::jstNULL: case mongo::Undefined: // duplicate sm behavior return v8::Null(); - case mongo::RegEx: - argv[0] = v8::String::New(elem.regex()); - argv[1] = v8::String::New(elem.regexFlags()); - return getNamedCons("RegExp")->NewInstance(2, argv); + case mongo::RegEx: { + // TODO parse into a custom type that can support any patterns and flags SERVER-9803 + v8::TryCatch tryCatch; + + v8::Handle<v8::Value> args[] = { + v8::String::New(elem.regex()), + v8::String::New(elem.regexFlags()) + }; + + v8::Handle<v8::Value> ret = _jsRegExpConstructor->NewInstance(2, args); + uassert(16863, str::stream() << "Error converting " << elem.toString(false) + << " in field " << elem.fieldName() + << " to a JS RegExp object: " + << toSTLString(tryCatch.Exception()), + !tryCatch.HasCaught()); + + return ret; + } case mongo::BinData: { int len; const char *data = elem.binData(len); @@ -1421,14 +1431,12 @@ namespace mongo { base64::encode(ss, data, len); argv[0] = v8::Number::New(elem.binDataType()); argv[1] = v8::String::New(ss.str().c_str()); - return getNamedCons("BinData")->NewInstance(2, argv); + return BinDataFT()->GetFunction()->NewInstance(2, argv); } case mongo::Timestamp: - instance = internalFieldObjects->NewInstance(); - instance->ForceSet(v8::String::New("t"), v8::Number::New(elem.timestampTime() / 1000 )); - instance->ForceSet(v8::String::New("i"), v8::Number::New(elem.timestampInc())); - instance->SetInternalField(0, v8::Uint32::New(elem.type())); - return instance; + argv[0] = v8::Number::New(elem.timestampTime() / 1000); + argv[1] = v8::Number::New(elem.timestampInc()); + return TimestampFT()->GetFunction()->NewInstance(2,argv); case mongo::NumberLong: nativeUnsignedLong = elem.numberLong(); // values above 2^53 are not accurately represented in JS @@ -1436,23 +1444,23 @@ namespace mongo { (long long)(double)(long long)(nativeUnsignedLong) && nativeUnsignedLong < 9007199254740992ULL) { argv[0] = v8::Number::New((double)(long long)(nativeUnsignedLong)); - return getNamedCons("NumberLong")->NewInstance(1, argv); + return NumberLongFT()->GetFunction()->NewInstance(1, argv); } else { argv[0] = v8::Number::New((double)(long long)(nativeUnsignedLong)); argv[1] = v8::Integer::New(nativeUnsignedLong >> 32); argv[2] = v8::Integer::New((unsigned long) (nativeUnsignedLong & 0x00000000ffffffff)); - return getNamedCons("NumberLong")->NewInstance(3, argv); + return NumberLongFT()->GetFunction()->NewInstance(3, argv); } case mongo::MinKey: - return newMinKeyInstance(); + return MinKeyFT()->GetFunction()->NewInstance(); case mongo::MaxKey: - return newMaxKeyInstance(); + return MaxKeyFT()->GetFunction()->NewInstance(); case mongo::DBRef: argv[0] = v8StringData(elem.dbrefNS()); argv[1] = newId(elem.dbrefOID()); - return getNamedCons("DBPointer")->NewInstance(2, argv); + return DBPointerFT()->GetFunction()->NewInstance(2, argv); default: massert(16661, str::stream() << "can't handle type: " << elem.type() << " " << elem.toString(), false); @@ -1462,13 +1470,14 @@ namespace mongo { } void V8Scope::v8ToMongoNumber(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Value> value, + const StringData& elementName, + v8::Handle<v8::Number> value, BSONObj* originalParent) { - double val = value->ToNumber()->Value(); + double val = value->Value(); // if previous type was integer, keep it int intval = static_cast<int>(val); if (val == intval && originalParent) { + // This makes copying an object of numbers O(n**2) :( BSONElement elmt = originalParent->getField(elementName); if (elmt.type() == mongo::NumberInt) { b.append(elementName, intval); @@ -1478,136 +1487,103 @@ namespace mongo { b.append(elementName, val); } - void V8Scope::v8ToMongoNumberLong(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> obj) { - // TODO might be nice to potentially speed this up with an indexed internal - // field, but I don't yet know how to use an ObjectTemplate with a - // constructor. - long long val; - if (!obj->Has(v8StringData("top"))) { - val = static_cast<int64_t>(obj->Get(v8StringData("floatApprox"))->NumberValue()); - } - else { - val = static_cast<int64_t>(( - static_cast<uint64_t>(obj->Get(v8StringData("top"))->ToInt32()->Value()) << 32) + - static_cast<uint32_t>(obj->Get(v8StringData("bottom"))->ToInt32()->Value())); - } - b.append(elementName, val); - } - - void V8Scope::v8ToMongoInternal(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> obj) { - uint32_t bsonType = obj->GetInternalField(0)->ToUint32()->Value(); - switch(bsonType) { - case Timestamp: - b.appendTimestamp(elementName, - Date_t(static_cast<uint64_t>( - obj->Get(v8::String::New("t"))->ToNumber()->Value() * 1000 )), - obj->Get(v8::String::New("i"))->ToInt32()->Value()); - return; - case MinKey: - b.appendMinKey(elementName); - return; - case MaxKey: - b.appendMaxKey(elementName); - return; - default: - massert(16665, "invalid internal field", false); - } - } - void V8Scope::v8ToMongoRegex(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> v8Regex) { - string regex = toSTLString(v8Regex); + const StringData& elementName, + v8::Handle<v8::RegExp> v8Regex) { + V8String v8RegexString (v8Regex); + StringData regex = v8RegexString; regex = regex.substr(1); - string r = regex.substr(0 ,regex.rfind("/")); - string o = regex.substr(regex.rfind("/") + 1); + StringData r = regex.substr(0 ,regex.rfind('/')); + StringData o = regex.substr(regex.rfind('/') + 1); b.appendRegex(elementName, r, o); } void V8Scope::v8ToMongoDBRef(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj) { - OID oid; - v8::Local<v8::Value> theid = obj->Get(v8StringData("id")); - oid.init(toSTLString(theid->ToObject()->Get(v8StringData("str")))); - string ns = toSTLString(obj->Get(v8StringData("ns"))); + verify(DBPointerFT()->HasInstance(obj)); + v8::Local<v8::Value> theid = obj->Get(strLitToV8("id")); + OID oid = v8ToMongoObjectID(theid->ToObject()); + string ns = toSTLString(obj->Get(strLitToV8("ns"))); b.appendDBRef(elementName, ns, oid); } void V8Scope::v8ToMongoBinData(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj) { - int len = obj->Get(v8StringData("len"))->ToInt32()->Value(); + + verify(BinDataFT()->HasInstance(obj)); + verify(obj->InternalFieldCount() == 1); + int len = obj->Get(strLitToV8("len"))->ToInt32()->Value(); b.appendBinData(elementName, len, - mongo::BinDataType(obj->Get(v8StringData("type"))->ToInt32()->Value()), + mongo::BinDataType(obj->Get(strLitToV8("type"))->ToInt32()->Value()), base64::decode(toSTLString(obj->GetInternalField(0))).c_str()); } - void V8Scope::v8ToMongoObjectID(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> obj) { - OID oid; - oid.init(toSTLString(obj->Get(v8StringData("str")))); - b.appendOID(elementName, &oid); + OID V8Scope::v8ToMongoObjectID(v8::Handle<v8::Object> obj) { + verify(ObjectIdFT()->HasInstance(obj)); + const string hexStr = toSTLString(obj->Get(strLitToV8("str"))); + + // OID parser doesn't have user-friendly error messages + uassert(16864, "ObjectID.str must be exactly 24 chars long", + hexStr.size() == 24); + uassert(16865, "ObjectID.str must only have hex characters [0-1a-fA-F]", + count_if(hexStr.begin(), hexStr.end(), ::isxdigit) == 24); + + return OID(hexStr); } void V8Scope::v8ToMongoObject(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Value> value, int depth, BSONObj* originalParent) { - // The user could potentially modify the fields of these special objects, - // wreaking havoc when we attempt to reinterpret them. Not doing any validation - // for now... - v8::Local<v8::Object> obj = value->ToObject(); - v8::Local<v8::Value> proto = obj->GetPrototype(); - - if (obj->InternalFieldCount() && obj->GetInternalField(0)->IsNumber()) { - v8ToMongoInternal(b, elementName, obj); - return; - } - - if (proto->IsRegExp()) - v8ToMongoRegex(b, elementName, obj); - else if (proto->IsObject() && - proto->ToObject()->HasRealNamedProperty(v8::String::New("isObjectId"))) - v8ToMongoObjectID(b, elementName, obj); - else if (!obj->GetHiddenValue(v8::String::New("__NumberLong")).IsEmpty()) - v8ToMongoNumberLong(b, elementName, obj); - else if (!obj->GetHiddenValue(v8::String::New("__NumberInt")).IsEmpty()) - b.append(elementName, - obj->GetHiddenValue(v8::String::New("__NumberInt"))->Int32Value()); - else if (!value->ToObject()->GetHiddenValue(v8::String::New("__DBPointer")).IsEmpty()) + verify(value->IsObject()); + v8::Handle<v8::Object> obj = value.As<v8::Object>(); + + if (value->IsRegExp()) { + v8ToMongoRegex(b, elementName, obj.As<v8::RegExp>()); + } else if (ObjectIdFT()->HasInstance(value)) { + b.append(elementName, v8ToMongoObjectID(obj)); + } else if (NumberLongFT()->HasInstance(value)) { + b.append(elementName, numberLongVal(this, obj)); + } else if (NumberIntFT()->HasInstance(value)) { + b.append(elementName, numberIntVal(this, obj)); + } else if (DBPointerFT()->HasInstance(value)) { v8ToMongoDBRef(b, elementName, obj); - else if (!value->ToObject()->GetHiddenValue(v8::String::New("__BinData")).IsEmpty()) + } else if (BinDataFT()->HasInstance(value)) { v8ToMongoBinData(b, elementName, obj); - else { + } else if (TimestampFT()->HasInstance(value)) { + OpTime ot (obj->Get(strLitToV8("t"))->Uint32Value(), + obj->Get(strLitToV8("i"))->Uint32Value()); + b.append(elementName, ot); + } else if (MinKeyFT()->HasInstance(value)) { + b.appendMinKey(elementName); + } else if (MaxKeyFT()->HasInstance(value)) { + b.appendMaxKey(elementName); + } else { // nested object or array BSONObj sub = v8ToMongo(obj, depth); b.append(elementName, sub); } } - void V8Scope::v8ToMongoElement(BSONObjBuilder & b, const string& sname, + void V8Scope::v8ToMongoElement(BSONObjBuilder & b, const StringData& sname, v8::Handle<v8::Value> value, int depth, BSONObj* originalParent) { if (value->IsString()) { - b.append(sname, toSTLString(value)); + b.append(sname, V8String(value)); return; } if (value->IsFunction()) { uassert(16716, "cannot convert native function to BSON", - !value->ToObject()->Has(v8StringData("_v8_function"))); - b.appendCode(sname, toSTLString(value)); + !value->ToObject()->Has(strLitToV8("_v8_function"))); + b.appendCode(sname, V8String(value)); return; } if (value->IsNumber()) { - v8ToMongoNumber(b, sname, value, originalParent); + v8ToMongoNumber(b, sname, value.As<v8::Number>(), originalParent); return; } if (value->IsArray()) { @@ -1651,9 +1627,9 @@ namespace mongo { BSONObj V8Scope::v8ToMongo(v8::Handle<v8::Object> o, int depth) { BSONObj originalBSON; - if (o->Has(v8::String::New("_bson"))) { - originalBSON = unwrapBSONObj(o); - BSONHolder* holder = unwrapHolder(o); + if (LazyBsonFT()->HasInstance(o)) { + originalBSON = unwrapBSONObj(this, o); + BSONHolder* holder = unwrapHolder(this, o); if (holder && !holder->_modified) { // object was not modified, use bson as is return originalBSON; @@ -1665,19 +1641,20 @@ namespace mongo { // We special case the _id field in top-level objects and move it to the front. // This matches other drivers behavior and makes finding the _id field quicker in BSON. if (depth == 0) { - if (o->HasOwnProperty(v8::String::New("_id"))) { - v8ToMongoElement(b, "_id", o->Get(v8::String::New("_id")), 0, &originalBSON); + if (o->HasOwnProperty(strLitToV8("_id"))) { + v8ToMongoElement(b, "_id", o->Get(strLitToV8("_id")), 0, &originalBSON); } } v8::Local<v8::Array> names = o->GetOwnPropertyNames(); for (unsigned int i=0; i<names->Length(); i++) { v8::Local<v8::String> name = names->Get(i)->ToString(); - v8::Local<v8::Value> value = o->Get(name); - const string sname = toSTLString(name); - if (depth == 0 && sname == "_id") + + if (depth == 0 && name->StrictEquals(strLitToV8("_id"))) continue; // already handled above + V8String sname(name); + v8::Local<v8::Value> value = o->Get(name); v8ToMongoElement(b, sname, value, depth + 1, &originalBSON); } return b.obj(); @@ -1685,14 +1662,6 @@ namespace mongo { // --- random utils ---- - v8::Function * V8Scope::getNamedCons(const char * name) { - return v8::Function::Cast(*(v8::Context::GetCurrent()->Global()->Get(v8StringData(name)))); - } - - v8::Function * V8Scope::getObjectIdCons() { - return getNamedCons("ObjectId"); - } - v8::Handle<v8::Value> V8Scope::Print(V8Scope* scope, const v8::Arguments& args) { stringstream ss; v8::HandleScope handle_scope; @@ -1703,7 +1672,7 @@ namespace mongo { else ss << " "; - if (!*args[i]) { + if (args[i].IsEmpty()) { // failed to get object to convert ss << "[unknown type]"; continue; diff --git a/src/mongo/scripting/engine_v8.h b/src/mongo/scripting/engine_v8.h index b86d063afae..448ab38af26 100644 --- a/src/mongo/scripting/engine_v8.h +++ b/src/mongo/scripting/engine_v8.h @@ -23,6 +23,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/client/dbclientinterface.h" #include "mongo/client/dbclientcursor.h" +#include "mongo/platform/unordered_map.h" #include "mongo/scripting/engine.h" #include "mongo/scripting/v8_deadline_monitor.h" #include "mongo/scripting/v8_profiler.h" @@ -201,9 +202,23 @@ namespace mongo { virtual void injectNative(const char* field, NativeFunction func, void* data = 0); void injectNative(const char* field, NativeFunction func, v8::Handle<v8::Object>& obj, void* data = 0); - void injectV8Function(const char* field, v8Function func); - void injectV8Function(const char* field, v8Function func, v8::Handle<v8::Object>& obj); - void injectV8Function(const char* field, v8Function func, v8::Handle<v8::Template>& t); + + // These functions inject a function (either an unwrapped function pointer or a pre-wrapped + // FunctionTemplate) into the provided object. If no object is provided, the function will + // be injected at global scope. These functions take care of setting the function and class + // name on the returned FunctionTemplate. + v8::Handle<v8::FunctionTemplate> injectV8Function(const char* name, v8Function func); + v8::Handle<v8::FunctionTemplate> injectV8Function(const char* name, + v8Function func, + v8::Handle<v8::Object>& obj); + v8::Handle<v8::FunctionTemplate> injectV8Function(const char* name, + v8::Handle<v8::FunctionTemplate> ft, + v8::Handle<v8::Object>& obj); + + // Injects a method into the provided prototype + v8::Handle<v8::FunctionTemplate> injectV8Method(const char* name, + v8Function func, + v8::Handle<v8::ObjectTemplate>& proto); v8::Handle<v8::FunctionTemplate> createV8Function(v8Function func); virtual ScriptingFunction _createFunction(const char* code, ScriptingFunction functionNumber = 0); @@ -213,7 +228,7 @@ namespace mongo { /** * Convert BSON types to v8 Javascript types */ - v8::Persistent<v8::Object> mongoToLZV8(const mongo::BSONObj& m, bool readOnly = false); + v8::Handle<v8::Object> mongoToLZV8(const mongo::BSONObj& m, bool readOnly = false); v8::Handle<v8::Value> mongoToV8Element(const BSONElement& f, bool readOnly = false); /** @@ -221,41 +236,29 @@ namespace mongo { */ mongo::BSONObj v8ToMongo(v8::Handle<v8::Object> obj, int depth = 0); void v8ToMongoElement(BSONObjBuilder& b, - const string& sname, + const StringData& sname, v8::Handle<v8::Value> value, int depth = 0, BSONObj* originalParent = 0); void v8ToMongoObject(BSONObjBuilder& b, - const string& sname, + const StringData& sname, v8::Handle<v8::Value> value, int depth, BSONObj* originalParent); void v8ToMongoNumber(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Value> value, + const StringData& elementName, + v8::Handle<v8::Number> value, BSONObj* originalParent); - void v8ToMongoNumberLong(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> obj); - void v8ToMongoInternal(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> obj); void v8ToMongoRegex(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> v8Regex); + const StringData& elementName, + v8::Handle<v8::RegExp> v8Regex); void v8ToMongoDBRef(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj); void v8ToMongoBinData(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj); - void v8ToMongoObjectID(BSONObjBuilder& b, - const string& elementName, - v8::Handle<v8::Object> obj); - - v8::Function* getNamedCons(const char* name); - - v8::Function* getObjectIdCons(); + OID v8ToMongoObjectID(v8::Handle<v8::Object> obj); v8::Local<v8::Value> newId(const OID& id); @@ -266,11 +269,6 @@ namespace mongo { std::string v8ExceptionToSTLString(const v8::TryCatch* try_catch); /** - * GC callback for weak references to BSON objects (via BSONHolder) - */ - v8::Persistent<v8::Object> wrapBSONObject(v8::Local<v8::Object> obj, BSONHolder* data); - - /** * Create a V8 string with a local handle */ static inline v8::Handle<v8::String> v8StringData(StringData str) { @@ -288,14 +286,64 @@ namespace mongo { */ v8::Persistent<v8::Context> getContext() { return _context; } + /** + * Get the global JS object + */ + v8::Persistent<v8::Object> getGlobal() { return _global; } + ObjTracker<BSONHolder> bsonHolderTracker; ObjTracker<DBClientWithCommands> dbClientWithCommandsTracker; ObjTracker<DBClientBase> dbClientBaseTracker; ObjTracker<DBClientCursor> dbClientCursorTracker; + // These are all named after the JS constructor name + FT + v8::Handle<v8::FunctionTemplate> ObjectIdFT() const { return _ObjectIdFT; } + v8::Handle<v8::FunctionTemplate> DBRefFT() const { return _DBRefFT; } + v8::Handle<v8::FunctionTemplate> DBPointerFT() const { return _DBPointerFT; } + v8::Handle<v8::FunctionTemplate> BinDataFT() const { return _BinDataFT; } + v8::Handle<v8::FunctionTemplate> NumberLongFT() const { return _NumberLongFT; } + v8::Handle<v8::FunctionTemplate> NumberIntFT() const { return _NumberIntFT; } + v8::Handle<v8::FunctionTemplate> TimestampFT() const { return _TimestampFT; } + v8::Handle<v8::FunctionTemplate> MinKeyFT() const { return _MinKeyFT; } + v8::Handle<v8::FunctionTemplate> MaxKeyFT() const { return _MaxKeyFT; } + v8::Handle<v8::FunctionTemplate> MongoFT() const { return _MongoFT; } + v8::Handle<v8::FunctionTemplate> DBFT() const { return _DBFT; } + v8::Handle<v8::FunctionTemplate> DBCollectionFT() const { return _DBCollectionFT; } + v8::Handle<v8::FunctionTemplate> DBQueryFT() const { return _DBQueryFT; } + v8::Handle<v8::FunctionTemplate> InternalCursorFT() const { return _InternalCursorFT; } + v8::Handle<v8::FunctionTemplate> LazyBsonFT() const { return _LazyBsonFT; } + v8::Handle<v8::FunctionTemplate> ROBsonFT() const { return _ROBsonFT; } + + template <size_t N> + v8::Handle<v8::String> strLitToV8(const char (&str)[N]) { + // Note that _strLitMap is keyed on string pointer not string + // value. This is OK because each string literal has a constant + // pointer for the program's lifetime. This works best if (but does + // not require) the linker interns all string literals giving + // identical strings used in different places the same pointer. + + StrLitMap::iterator it = _strLitMap.find(str); + if (it != _strLitMap.end()) + return it->second; + + StringData sd (str, StringData::LiteralTag()); + v8::Handle<v8::String> v8Str = v8StringData(sd); + + // We never need to Dispose since this should last as long as V8Scope exists + _strLitMap[str] = v8::Persistent<v8::String>::New(v8Str); + + return v8Str; + } + private: /** + * Attach data to obj such that the data has the same lifetime as the Object obj points to. + * obj must have been created by either LazyBsonFT or ROBsonFT. + */ + void wrapBSONObject(v8::Handle<v8::Object> obj, BSONObj data, bool readOnly); + + /** * Trampoline to call a c++ function with a specific signature (V8Scope*, v8::Arguments&). * Handles interruption, exceptions, etc. */ @@ -342,16 +390,6 @@ namespace mongo { void unregisterOpId(); /** - * Creates a new instance of the MinKey object - */ - v8::Local<v8::Object> newMinKeyInstance(); - - /** - * Creates a new instance of the MaxKey object - */ - v8::Local<v8::Object> newMaxKeyInstance(); - - /** * Create a new function; primarily used for BSON/V8 conversion. */ v8::Local<v8::Value> newFunction(const char *code); @@ -372,21 +410,44 @@ namespace mongo { enum ConnectState { NOT, LOCAL, EXTERNAL }; ConnectState _connectState; - v8::Persistent<v8::FunctionTemplate> lzFunctionTemplate; - v8::Persistent<v8::ObjectTemplate> lzObjectTemplate; - v8::Persistent<v8::ObjectTemplate> roObjectTemplate; - v8::Persistent<v8::ObjectTemplate> lzArrayTemplate; - v8::Persistent<v8::ObjectTemplate> internalFieldObjects; + // These are all named after the JS constructor name + FT + v8::Persistent<v8::FunctionTemplate> _ObjectIdFT; + v8::Persistent<v8::FunctionTemplate> _DBRefFT; + v8::Persistent<v8::FunctionTemplate> _DBPointerFT; + v8::Persistent<v8::FunctionTemplate> _BinDataFT; + v8::Persistent<v8::FunctionTemplate> _NumberLongFT; + v8::Persistent<v8::FunctionTemplate> _NumberIntFT; + v8::Persistent<v8::FunctionTemplate> _TimestampFT; + v8::Persistent<v8::FunctionTemplate> _MinKeyFT; + v8::Persistent<v8::FunctionTemplate> _MaxKeyFT; + v8::Persistent<v8::FunctionTemplate> _MongoFT; + v8::Persistent<v8::FunctionTemplate> _DBFT; + v8::Persistent<v8::FunctionTemplate> _DBCollectionFT; + v8::Persistent<v8::FunctionTemplate> _DBQueryFT; + v8::Persistent<v8::FunctionTemplate> _InternalCursorFT; + v8::Persistent<v8::FunctionTemplate> _LazyBsonFT; + v8::Persistent<v8::FunctionTemplate> _ROBsonFT; + + v8::Persistent<v8::Function> _jsRegExpConstructor; v8::Isolate* _isolate; V8CpuProfiler _cpuProfiler; + // See comments in strLitToV8 + typedef unordered_map<const char*, v8::Handle<v8::String> > StrLitMap; + StrLitMap _strLitMap; + mongo::mutex _interruptLock; // protects interruption-related flags bool _inNativeExecution; // protected by _interruptLock bool _pendingKill; // protected by _interruptLock int _opId; // op id for this scope }; + /// Helper to extract V8Scope for an Isolate + inline V8Scope* getScope(v8::Isolate* isolate) { + return static_cast<V8Scope*>(isolate->GetData()); + } + class V8ScriptEngine : public ScriptEngine { public: V8ScriptEngine(); diff --git a/src/mongo/scripting/sm_db.cpp b/src/mongo/scripting/sm_db.cpp index 3c53a85768a..f9505b77d60 100644 --- a/src/mongo/scripting/sm_db.cpp +++ b/src/mongo/scripting/sm_db.cpp @@ -1294,6 +1294,12 @@ zzz c.setProperty( obj, "i", c.toval( 0.0 ) ); } else { + smuassert( cx, + "Timestamp time must be a number", + JSVAL_IS_NUMBER( argv[ 0 ] ) ); + smuassert( cx, + "Timestamp increment must be a number", + JSVAL_IS_NUMBER( argv[ 1 ] ) ); long long t = parseLL(c.toString(argv[0]).c_str()); long long largestVal = ((2039LL-1970LL) *365*24*60*60); //seconds between 1970=2038 smuassert( cx, diff --git a/src/mongo/scripting/v8_db.cpp b/src/mongo/scripting/v8_db.cpp index 16b565c42a1..ea6cfe33591 100644 --- a/src/mongo/scripting/v8_db.cpp +++ b/src/mongo/scripting/v8_db.cpp @@ -33,9 +33,6 @@ using namespace std; -#define GETNS boost::scoped_array<char> ns(new char[args[0]->ToString()->Utf8Length()+1]); \ - args[0]->ToString()->WriteUtf8(ns.get()); - namespace mongo { namespace { @@ -61,18 +58,33 @@ namespace mongo { _mongoPrototypeManipulators.push_back(manipulator); } - static v8::Handle<v8::Value> newInstance(v8::Function* f, const v8::Arguments& args) { + static v8::Handle<v8::Value> newInstance(v8::Handle<v8::Function> f, const v8::Arguments& args) { // need to translate arguments into an array v8::HandleScope handle_scope; - int argc = args.Length(); + const int argc = args.Length(); + static const int MAX_ARGC = 24; + uassert(16858, "Too many arguments. Max is 24", + argc <= MAX_ARGC); + // TODO SERVER-8016: properly allocate handles on the stack - v8::Handle<v8::Value> argv[24]; - for (int i = 0; i < argc && i < 24; ++i) { + v8::Handle<v8::Value> argv[MAX_ARGC]; + for (int i = 0; i < argc; ++i) { argv[i] = args[i]; } return handle_scope.Close(f->NewInstance(argc, argv)); } + v8::Handle<v8::FunctionTemplate> getInternalCursorFunctionTemplate(V8Scope* scope) { + v8::Handle<v8::FunctionTemplate> ic = scope->createV8Function(internalCursorCons); + ic->InstanceTemplate()->SetInternalFieldCount(1); + v8::Handle<v8::ObjectTemplate> icproto = ic->PrototypeTemplate(); + scope->injectV8Method("next", internalCursorNext, icproto); + scope->injectV8Method("hasNext", internalCursorHasNext, icproto); + scope->injectV8Method("objsLeftInBatch", internalCursorObjsLeftInBatch, icproto); + scope->injectV8Method("readOnly", internalCursorReadOnly, icproto); + return ic; + } + v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate(V8Scope* scope, bool local) { v8::Handle<v8::FunctionTemplate> mongo; if (local) @@ -80,26 +92,18 @@ namespace mongo { else mongo = scope->createV8Function(mongoConsExternal); mongo->InstanceTemplate()->SetInternalFieldCount(1); - v8::Handle<v8::Template> proto = mongo->PrototypeTemplate(); - scope->injectV8Function("find", mongoFind, proto); - scope->injectV8Function("insert", mongoInsert, proto); - scope->injectV8Function("remove", mongoRemove, proto); - scope->injectV8Function("update", mongoUpdate, proto); - scope->injectV8Function("auth", mongoAuth, proto); - scope->injectV8Function("logout", mongoLogout, proto); + v8::Handle<v8::ObjectTemplate> proto = mongo->PrototypeTemplate(); + scope->injectV8Method("find", mongoFind, proto); + scope->injectV8Method("insert", mongoInsert, proto); + scope->injectV8Method("remove", mongoRemove, proto); + scope->injectV8Method("update", mongoUpdate, proto); + scope->injectV8Method("auth", mongoAuth, proto); + scope->injectV8Method("logout", mongoLogout, proto); fassert(16468, _mongoPrototypeManipulatorsFrozen); for (size_t i = 0; i < _mongoPrototypeManipulators.size(); ++i) _mongoPrototypeManipulators[i](scope, mongo); - v8::Handle<v8::FunctionTemplate> ic = scope->createV8Function(internalCursorCons); - ic->InstanceTemplate()->SetInternalFieldCount(1); - v8::Handle<v8::Template> icproto = ic->PrototypeTemplate(); - scope->injectV8Function("next", internalCursorNext, icproto); - scope->injectV8Function("hasNext", internalCursorHasNext, icproto); - scope->injectV8Function("objsLeftInBatch", internalCursorObjsLeftInBatch, icproto); - scope->injectV8Function("readOnly", internalCursorReadOnly, icproto); - proto->Set(scope->v8StringData("internalCursor"), ic); return mongo; } @@ -114,6 +118,11 @@ namespace mongo { strcpy(host, "127.0.0.1"); } + // only allow function template to be used by a constructor + uassert(16859, "Mongo function is only usable as a constructor", + args.IsConstructCall()); + verify(scope->MongoFT()->HasInstance(args.This())); + string errmsg; ConnectionString cs = ConnectionString::parse(host, errmsg); if (!cs.isValid()) { @@ -126,7 +135,7 @@ namespace mongo { return v8AssertionException(errmsg); } - v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.Holder()); + v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This()); scope->dbClientWithCommandsTracker.track(self, conn); ScriptEngine::runConnectCallback(*conn); @@ -141,6 +150,11 @@ namespace mongo { v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Arguments& args) { argumentCheck(args.Length() == 0, "local Mongo constructor takes no args") + // only allow function template to be used by a constructor + uassert(16860, "Mongo function is only usable as a constructor", + args.IsConstructCall()); + verify(scope->MongoFT()->HasInstance(args.This())); + DBClientBase* conn = createDirectClient(); v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This()); scope->dbClientBaseTracker.track(self, conn); @@ -152,7 +166,9 @@ namespace mongo { return v8::Undefined(); } - DBClientBase* getConnection(const v8::Arguments& args) { + DBClientBase* getConnection(V8Scope* scope, const v8::Arguments& args) { + verify(scope->MongoFT()->HasInstance(args.This())); + verify(args.This()->InternalFieldCount() == 1); v8::Local<v8::External> c = v8::External::Cast(*(args.This()->GetInternalField(0))); DBClientBase* conn = (DBClientBase*)(c->Value()); massert(16667, "Unable to get db client connection", conn); @@ -165,8 +181,8 @@ namespace mongo { v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& args) { argumentCheck(args.Length() == 7, "find needs 7 args") argumentCheck(args[1]->IsObject(), "needs to be an object") - DBClientBase * conn = getConnection(args); - GETNS; + DBClientBase * conn = getConnection(scope, args); + const string ns = toSTLString(args[0]); BSONObj fields; BSONObj q = scope->v8ToMongo(args[1]->ToObject()); bool haveFields = args[2]->IsObject() && @@ -174,24 +190,18 @@ namespace mongo { if (haveFields) fields = scope->v8ToMongo(args[2]->ToObject()); - v8::Local<v8::Object> mongo = args.This(); auto_ptr<mongo::DBClientCursor> cursor; - int nToReturn = (int)(args[3]->ToNumber()->Value()); - int nToSkip = (int)(args[4]->ToNumber()->Value()); - int batchSize = (int)(args[5]->ToNumber()->Value()); - int options = (int)(args[6]->ToNumber()->Value()); - cursor = conn->query(ns.get(), q, nToReturn, nToSkip, haveFields ? &fields : 0, + int nToReturn = args[3]->Int32Value(); + int nToSkip = args[4]->Int32Value(); + int batchSize = args[5]->Int32Value(); + int options = args[6]->Int32Value(); + cursor = conn->query(ns, q, nToReturn, nToSkip, haveFields ? &fields : NULL, options, batchSize); if (!cursor.get()) { return v8AssertionException("error doing query: failed"); } - v8::Function* cons = (v8::Function*)(*(mongo->Get(scope->v8StringData("internalCursor")))); - - if (!cons) { - return v8AssertionException("could not create a cursor"); - } - + v8::Handle<v8::Function> cons = scope->InternalCursorFT()->GetFunction(); v8::Persistent<v8::Object> c = v8::Persistent<v8::Object>::New(cons->NewInstance()); c->SetInternalField(0, v8::External::New(cursor.get())); scope->dbClientCursorTracker.track(c, cursor.release()); @@ -202,12 +212,14 @@ namespace mongo { argumentCheck(args.Length() == 3 ,"insert needs 3 args") argumentCheck(args[1]->IsObject() ,"attempted to insert a non-object") + verify(scope->MongoFT()->HasInstance(args.This())); + if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) { return v8AssertionException("js db in read only mode"); } - DBClientBase * conn = getConnection(args); - GETNS; + DBClientBase * conn = getConnection(scope, args); + const string ns = toSTLString(args[0]); v8::Handle<v8::Integer> flags = args[2]->ToInteger(); @@ -225,21 +237,21 @@ namespace mongo { if (!el->Has(scope->v8StringData("_id"))) { v8::Handle<v8::Value> argv[1]; el->ForceSet(scope->v8StringData("_id"), - scope->getObjectIdCons()->NewInstance(0, argv)); + scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv)); } bos.push_back(scope->v8ToMongo(el)); } - conn->insert(ns.get(), bos, flags->Int32Value()); + conn->insert(ns, bos, flags->Int32Value()); } else { v8::Handle<v8::Object> in = args[1]->ToObject(); if (!in->Has(scope->v8StringData("_id"))) { v8::Handle<v8::Value> argv[1]; in->ForceSet(scope->v8StringData("_id"), - scope->getObjectIdCons()->NewInstance(0, argv)); + scope->ObjectIdFT()->GetFunction()->NewInstance(0, argv)); } BSONObj o = scope->v8ToMongo(in); - conn->insert(ns.get(), o); + conn->insert(ns, o); } return v8::Undefined(); } @@ -248,12 +260,14 @@ namespace mongo { argumentCheck(args.Length() == 2 || args.Length() == 3, "remove needs 2 or 3 args") argumentCheck(args[1]->IsObject(), "attempted to remove a non-object") + verify(scope->MongoFT()->HasInstance(args.This())); + if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) { return v8AssertionException("js db in read only mode"); } - DBClientBase * conn = getConnection(args); - GETNS; + DBClientBase * conn = getConnection(scope, args); + const string ns = toSTLString(args[0]); v8::Handle<v8::Object> in = args[1]->ToObject(); BSONObj o = scope->v8ToMongo(in); @@ -263,7 +277,7 @@ namespace mongo { justOne = args[2]->BooleanValue(); } - conn->remove(ns.get(), o, justOne); + conn->remove(ns, o, justOne); return v8::Undefined(); } @@ -272,12 +286,14 @@ namespace mongo { argumentCheck(args[1]->IsObject(), "1st param to update has to be an object") argumentCheck(args[2]->IsObject(), "2nd param to update has to be an object") + verify(scope->MongoFT()->HasInstance(args.This())); + if (args.This()->Get(scope->v8StringData("readOnly"))->BooleanValue()) { return v8AssertionException("js db in read only mode"); } - DBClientBase * conn = getConnection(args); - GETNS; + DBClientBase * conn = getConnection(scope, args); + const string ns = toSTLString(args[0]); v8::Handle<v8::Object> q = args[1]->ToObject(); v8::Handle<v8::Object> o = args[2]->ToObject(); @@ -287,12 +303,12 @@ namespace mongo { BSONObj q1 = scope->v8ToMongo(q); BSONObj o1 = scope->v8ToMongo(o); - conn->update(ns.get(), q1, o1, upsert, multi); + conn->update(ns, q1, o1, upsert, multi); return v8::Undefined(); } v8::Handle<v8::Value> mongoAuth(V8Scope* scope, const v8::Arguments& args) { - DBClientWithCommands* conn = getConnection(args); + DBClientWithCommands* conn = getConnection(scope, args); if (NULL == conn) return v8AssertionException("no connection"); @@ -321,7 +337,7 @@ namespace mongo { v8::Handle<v8::Value> mongoLogout(V8Scope* scope, const v8::Arguments& args) { argumentCheck(args.Length() == 1, "logout needs 1 arg") - DBClientBase* conn = getConnection(args); + DBClientBase* conn = getConnection(scope, args); const string db = toSTLString(args[0]); BSONObj ret; conn->logout(db, ret); @@ -331,9 +347,11 @@ namespace mongo { /** * get cursor from v8 argument */ - mongo::DBClientCursor* getCursor(const v8::Arguments& args) { + mongo::DBClientCursor* getCursor(V8Scope* scope, const v8::Arguments& args) { + verify(scope->InternalCursorFT()->HasInstance(args.This())); + verify(args.This()->InternalFieldCount() == 1); v8::Local<v8::External> c = v8::External::Cast(*(args.This()->GetInternalField(0))); - mongo::DBClientCursor* cursor = (mongo::DBClientCursor*)(c->Value()); + mongo::DBClientCursor* cursor = static_cast<mongo::DBClientCursor*>(c->Value()); return cursor; } @@ -345,7 +363,7 @@ namespace mongo { * cursor.next() */ v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor* cursor = getCursor(args); + mongo::DBClientCursor* cursor = getCursor(scope, args); if (! cursor) return v8::Undefined(); BSONObj o = cursor->next(); @@ -359,7 +377,7 @@ namespace mongo { * cursor.hasNext() */ v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor* cursor = getCursor(args); + mongo::DBClientCursor* cursor = getCursor(scope, args); if (! cursor) return v8::Boolean::New(false); return v8::Boolean::New(cursor->more()); @@ -370,7 +388,7 @@ namespace mongo { */ v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, const v8::Arguments& args) { - mongo::DBClientCursor* cursor = getCursor(args); + mongo::DBClientCursor* cursor = getCursor(scope, args); if (! cursor) return v8::Number::New(0.0); return v8::Number::New(static_cast<double>(cursor->objsLeftInBatch())); @@ -380,12 +398,21 @@ namespace mongo { * cursor.readOnly() */ v8::Handle<v8::Value> internalCursorReadOnly(V8Scope* scope, const v8::Arguments& args) { + verify(scope->InternalCursorFT()->HasInstance(args.This())); + v8::Local<v8::Object> cursor = args.This(); cursor->ForceSet(v8::String::New("_ro"), v8::Boolean::New(true)); return cursor; } v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args) { + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->DBFT()->GetFunction(); + return newInstance(f, args); + } + + verify(scope->DBFT()->HasInstance(args.This())); + argumentCheck(args.Length() == 2, "db constructor requires 2 arguments") args.This()->ForceSet(scope->v8StringData("_mongo"), args[0]); @@ -404,8 +431,20 @@ namespace mongo { } v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Arguments& args) { + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->DBCollectionFT()->GetFunction(); + return newInstance(f, args); + } + + verify(scope->DBCollectionFT()->HasInstance(args.This())); + argumentCheck(args.Length() == 4, "collection constructor requires 4 arguments") + for (int i = 0; i < args.Length(); i++) { + argumentCheck(!args[i]->IsUndefined(), + "collection constructor called with undefined argument") + } + args.This()->ForceSet(scope->v8StringData("_mongo"), args[0]); args.This()->ForceSet(scope->v8StringData("_db"), args[1]); args.This()->ForceSet(scope->v8StringData("_shortName"), args[2]); @@ -415,14 +454,17 @@ namespace mongo { return v8AssertionException("can't use sharded collection from db.eval"); } - for (int i = 0; i < args.Length(); i++) { - argumentCheck(!args[i]->IsUndefined(), - "collection constructor called with undefined argument") - } return v8::Undefined(); } v8::Handle<v8::Value> dbQueryInit(V8Scope* scope, const v8::Arguments& args) { + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->DBQueryFT()->GetFunction(); + return newInstance(f, args); + } + + verify(scope->DBQueryFT()->HasInstance(args.This())); + argumentCheck(args.Length() >= 4, "dbQuery constructor requires at least 4 arguments") v8::Handle<v8::Object> t = args.This(); @@ -471,86 +513,126 @@ namespace mongo { v8::Handle<v8::Value> collectionSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { - // a collection name cannot be overwritten by a variable - string sname = toSTLString(name); - if (sname.length() == 0 || sname[0] == '_') { - // if starts with '_' we allow overwrite - return v8::Handle<v8::Value>(); + try { + V8Scope* scope = getScope(info.GetIsolate()); + + // Both DB and Collection objects use this setter + verify(scope->DBCollectionFT()->HasInstance(info.This()) + || scope->DBFT()->HasInstance(info.This())); + + // a collection name cannot be overwritten by a variable + string sname = toSTLString(name); + if (sname.length() == 0 || sname[0] == '_') { + // if starts with '_' we allow overwrite + return v8::Handle<v8::Value>(); + } + // dont set + return value; + } + catch (const DBException& dbEx) { + return v8AssertionException(dbEx.toString()); + } + catch (...) { + return v8AssertionException("unknown error in collationSetter"); } - // dont set - return value; } v8::Handle<v8::Value> collectionGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { - v8::TryCatch tryCatch; - - // first look in prototype, may be a function - v8::Handle<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get(name); - if (!real->IsUndefined()) - return real; - - // 2nd look into real values, may be cached collection object - string sname = toSTLString(name); - if (info.This()->HasRealNamedProperty(name)) { - v8::Local<v8::Value> prop = info.This()->GetRealNamedProperty(name); - if (prop->IsObject() && - prop->ToObject()->HasRealNamedProperty(v8::String::New("_fullName"))) { - // need to check every time that the collection did not get sharded - if (haveLocalShardingInfo(toSTLString( - prop->ToObject()->GetRealNamedProperty(v8::String::New("_fullName"))))) { - return v8AssertionException("can't use sharded collection from db.eval"); + try { + V8Scope* scope = getScope(info.GetIsolate()); + + // Both DB and Collection objects use this getter + verify(scope->DBCollectionFT()->HasInstance(info.This()) + || scope->DBFT()->HasInstance(info.This())); + + v8::TryCatch tryCatch; + + // first look in prototype, may be a function + v8::Handle<v8::Value> real = info.This()->GetPrototype()->ToObject()->Get(name); + if (!real->IsUndefined()) + return real; + + // 2nd look into real values, may be cached collection object + string sname = toSTLString(name); + if (info.This()->HasRealNamedProperty(name)) { + v8::Local<v8::Value> prop = info.This()->GetRealNamedProperty(name); + if (prop->IsObject() && + prop->ToObject()->HasRealNamedProperty(v8::String::New("_fullName"))) { + // need to check every time that the collection did not get sharded + if (haveLocalShardingInfo(toSTLString( + prop->ToObject()->GetRealNamedProperty(v8::String::New("_fullName"))))) { + return v8AssertionException("can't use sharded collection from db.eval"); + } } + return prop; + } + else if (sname.length() == 0 || sname[0] == '_') { + // if starts with '_' we dont return collection, one must use getCollection() + return v8::Handle<v8::Value>(); } - return prop; - } - else if (sname.length() == 0 || sname[0] == '_') { - // if starts with '_' we dont return collection, one must use getCollection() - return v8::Handle<v8::Value>(); - } - - // no hit, create new collection - v8::Handle<v8::Value> getCollection = info.This()->GetPrototype()->ToObject()->Get( - v8::String::New("getCollection")); - if (! getCollection->IsFunction()) { - return v8AssertionException("getCollection is not a function"); - } - v8::Function* f = (v8::Function*)(*getCollection); - v8::Handle<v8::Value> argv[1]; - argv[0] = name; - v8::Local<v8::Value> coll = f->Call(info.This(), 1, argv); - if (coll.IsEmpty()) { - if (tryCatch.HasCaught()) { - return v8::ThrowException(tryCatch.Exception()); + // no hit, create new collection + v8::Handle<v8::Value> getCollection = info.This()->GetPrototype()->ToObject()->Get( + v8::String::New("getCollection")); + if (! getCollection->IsFunction()) { + return v8AssertionException("getCollection is not a function"); } - return v8::Handle<v8::Value>(); - } - // cache collection for reuse, don't enumerate - info.This()->ForceSet(name, coll, v8::DontEnum); - return coll; + v8::Handle<v8::Function> f = getCollection.As<v8::Function>(); + v8::Handle<v8::Value> argv[1]; + argv[0] = name; + v8::Local<v8::Value> coll = f->Call(info.This(), 1, argv); + if (coll.IsEmpty()) + return tryCatch.ReThrow(); + + uassert(16861, "getCollection returned something other than a collection", + scope->DBCollectionFT()->HasInstance(coll)); + + // cache collection for reuse, don't enumerate + info.This()->ForceSet(name, coll, v8::DontEnum); + return coll; + } + catch (const DBException& dbEx) { + return v8AssertionException(dbEx.toString()); + } + catch (...) { + return v8AssertionException("unknown error in collectionGetter"); + } } v8::Handle<v8::Value> dbQueryIndexAccess(unsigned int index, const v8::AccessorInfo& info) { - v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get( - v8::String::New("arrayAccess")); - massert(16660, "arrayAccess is not a function", arrayAccess->IsFunction()); + try { + V8Scope* scope = getScope(info.GetIsolate()); + verify(scope->DBQueryFT()->HasInstance(info.This())); + + v8::Handle<v8::Value> arrayAccess = info.This()->GetPrototype()->ToObject()->Get( + v8::String::New("arrayAccess")); + massert(16660, "arrayAccess is not a function", arrayAccess->IsFunction()); - v8::Function* f = (v8::Function*)(*arrayAccess); - v8::Handle<v8::Value> argv[1]; - argv[0] = v8::Number::New(index); + v8::Handle<v8::Function> f = arrayAccess.As<v8::Function>(); + v8::Handle<v8::Value> argv[1]; + argv[0] = v8::Number::New(index); - return f->Call(info.This(), 1, argv); + return f->Call(info.This(), 1, argv); + } + catch (const DBException& dbEx) { + return v8AssertionException(dbEx.toString()); + } + catch (...) { + return v8AssertionException("unknown error in dbQueryIndexAccess"); + } } v8::Handle<v8::Value> objectIdInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getObjectIdCons(); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->ObjectIdFT()->GetFunction(); return newInstance(f, args); } + v8::Handle<v8::Object> it = args.This(); + verify(scope->ObjectIdFT()->HasInstance(it)); + OID oid; if (args.Length() == 0) { oid.init(); @@ -571,58 +653,65 @@ namespace mongo { } v8::Handle<v8::Value> dbRefInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("DBRef"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->DBRefFT()->GetFunction(); return newInstance(f, args); } - // We use a DBRef object to display DBRefs converted from BSON, so this clumsy hack - // lets the constructor serve two masters with different requirements. The downside - // is that this internal usage is available to direct calls from the shell, so DBRef() - // is accepted and produces DBRef(undefined, undefined). - // TODO(tad) fix this - if (args.Length() == 0) { - return it; - } + v8::Handle<v8::Object> it = args.This(); + verify(scope->DBRefFT()->HasInstance(it)); argumentCheck(args.Length() == 2, "DBRef needs 2 arguments") + argumentCheck(args[0]->IsString(), "DBRef 1st parameter must be a string") it->ForceSet(scope->v8StringData("$ref"), args[0]); it->ForceSet(scope->v8StringData("$id"), args[1]); return it; } v8::Handle<v8::Value> dbPointerInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("DBPointer"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->DBPointerFT()->GetFunction(); return newInstance(f, args); } + v8::Handle<v8::Object> it = args.This(); + verify(scope->DBPointerFT()->HasInstance(it)); + argumentCheck(args.Length() == 2, "DBPointer needs 2 arguments") + argumentCheck(args[0]->IsString(), "DBPointer 1st parameter must be a string") + argumentCheck(scope->ObjectIdFT()->HasInstance(args[1]), + "DBPointer 2nd parameter must be an ObjectId") + it->ForceSet(scope->v8StringData("ns"), args[0]); it->ForceSet(scope->v8StringData("id"), args[1]); - it->SetHiddenValue(scope->v8StringData("__DBPointer"), v8::Number::New(1)); return it; } v8::Handle<v8::Value> dbTimestampInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("Timestamp"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->TimestampFT()->GetFunction(); return newInstance(f, args); } + v8::Handle<v8::Object> it = args.This(); + verify(scope->TimestampFT()->HasInstance(it)); + if (args.Length() == 0) { it->ForceSet(scope->v8StringData("t"), v8::Number::New(0)); it->ForceSet(scope->v8StringData("i"), v8::Number::New(0)); } else if (args.Length() == 2) { + if (!args[0]->IsNumber()) { + return v8AssertionException("Timestamp time must be a number"); + } + if (!args[1]->IsNumber()) { + return v8AssertionException("Timestamp increment must be a number"); + } int64_t t = args[0]->IntegerValue(); int64_t largestVal = ((2039LL-1970LL) *365*24*60*60); //seconds between 1970-2038 if( t > largestVal ) return v8AssertionException( str::stream() - << "The first argument must be in seconds;" + << "The first argument must be in seconds; " << t << " is too large (max " << largestVal << ")"); it->ForceSet(scope->v8StringData("t"), args[0]); it->ForceSet(scope->v8StringData("i"), args[1]); @@ -631,29 +720,31 @@ namespace mongo { return v8AssertionException("Timestamp needs 0 or 2 arguments"); } - it->SetInternalField(0, v8::Uint32::New(Timestamp)); - return it; } v8::Handle<v8::Value> binDataInit(V8Scope* scope, const v8::Arguments& args) { - v8::Local<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("BinData"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); return newInstance(f, args); } - v8::Handle<v8::Value> type; + v8::Local<v8::Object> it = args.This(); + verify(scope->BinDataFT()->HasInstance(it)); + if (args.Length() == 2) { // 2 args: type, base64 string - type = args[0]; + v8::Handle<v8::Value> type = args[0]; + if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) { + return v8AssertionException( + "BinData subtype must be a Number between 0 and 255 inclusive)"); + } v8::String::Utf8Value utf(args[1]); // uassert if invalid base64 string string tmpBase64 = base64::decode(*utf); // length property stores the decoded length it->ForceSet(scope->v8StringData("len"), v8::Number::New(tmpBase64.length())); it->ForceSet(scope->v8StringData("type"), type); - it->SetHiddenValue(v8::String::New("__BinData"), v8::Number::New(1)); it->SetInternalField(0, args[1]); } else if (args.Length() != 0) { @@ -665,21 +756,27 @@ namespace mongo { v8::Handle<v8::Value> binDataToString(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); + verify(scope->BinDataFT()->HasInstance(it)); int type = it->Get(v8::String::New("type"))->Int32Value(); stringstream ss; + verify(it->InternalFieldCount() == 1); ss << "BinData(" << type << ",\"" << toSTLString(it->GetInternalField(0)) << "\")"; return v8::String::New(ss.str().c_str()); } v8::Handle<v8::Value> binDataToBase64(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); + verify(scope->BinDataFT()->HasInstance(it)); + verify(it->InternalFieldCount() == 1); return it->GetInternalField(0); } v8::Handle<v8::Value> binDataToHex(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); + verify(scope->BinDataFT()->HasInstance(it)); int len = v8::Handle<v8::Number>::Cast(it->Get(v8::String::New("len")))->Int32Value(); + verify(it->InternalFieldCount() == 1); string data = base64::decode(toSTLString(it->GetInternalField(0))); stringstream ss; ss.setf (ios_base::hex, ios_base::basefield); @@ -694,17 +791,18 @@ namespace mongo { static v8::Handle<v8::Value> hexToBinData(V8Scope* scope, v8::Local<v8::Object> it, int type, string hexstr) { + verify(scope->BinDataFT()->HasInstance(it)); + int len = hexstr.length() / 2; - scoped_array<char> data(new char[16]); + scoped_array<char> data(new char[len]); const char* src = hexstr.c_str(); - for(int i = 0; i < 16; i++) { + for(int i = 0; i < len; i++) { data[i] = fromHex(src + i * 2); } - string encoded = base64::encode(data.get(), 16); + string encoded = base64::encode(data.get(), len); it->ForceSet(v8::String::New("len"), v8::Number::New(len)); it->ForceSet(v8::String::New("type"), v8::Number::New(type)); - it->SetHiddenValue(v8::String::New("__BinData"), v8::Number::New(1)); it->SetInternalField(0, v8::String::New(encoded.c_str(), encoded.length())); return it; } @@ -714,7 +812,7 @@ namespace mongo { v8::String::Utf8Value utf(args[0]); argumentCheck(utf.length() == 32, "UUID string must have 32 characters") - v8::Function* f = scope->getNamedCons("BinData"); + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); v8::Local<v8::Object> it = f->NewInstance(); return hexToBinData(scope, it, bdtUUID, *utf); } @@ -724,7 +822,7 @@ namespace mongo { v8::String::Utf8Value utf(args[0]); argumentCheck(utf.length() == 32, "MD5 string must have 32 characters") - v8::Function* f = scope->getNamedCons("BinData"); + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); v8::Local<v8::Object> it = f->NewInstance(); return hexToBinData(scope, it, MD5Type, *utf); } @@ -732,21 +830,23 @@ namespace mongo { v8::Handle<v8::Value> hexDataInit(V8Scope* scope, const v8::Arguments& args) { argumentCheck(args.Length() == 2, "HexData needs 2 arguments") v8::String::Utf8Value utf(args[1]); - v8::Function* f = scope->getNamedCons("BinData"); + v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction(); v8::Local<v8::Object> it = f->NewInstance(); return hexToBinData(scope, it, args[0]->IntegerValue(), *utf); } v8::Handle<v8::Value> numberLongInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("NumberLong"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->NumberLongFT()->GetFunction(); return newInstance(f, args); } argumentCheck(args.Length() == 0 || args.Length() == 1 || args.Length() == 3, "NumberLong needs 0, 1 or 3 arguments") + v8::Handle<v8::Object> it = args.This(); + verify(scope->NumberLongFT()->HasInstance(it)); + if (args.Length() == 0) { it->ForceSet(scope->v8StringData("floatApprox"), v8::Number::New(0)); } @@ -784,15 +884,15 @@ namespace mongo { } } else { - it->ForceSet(scope->v8StringData("floatApprox"), args[0]); - it->ForceSet(scope->v8StringData("top"), args[1]); - it->ForceSet(scope->v8StringData("bottom"), args[2]); + it->ForceSet(scope->v8StringData("floatApprox"), args[0]->ToNumber()); + it->ForceSet(scope->v8StringData("top"), args[1]->ToUint32()); + it->ForceSet(scope->v8StringData("bottom"), args[2]->ToUint32()); } - it->SetHiddenValue(v8::String::New("__NumberLong"), v8::Number::New(1)); return it; } - long long numberLongVal(const v8::Handle<v8::Object>& it) { + long long numberLongVal(V8Scope* scope, const v8::Handle<v8::Object>& it) { + verify(scope->NumberLongFT()->HasInstance(it)); if (!it->Has(v8::String::New("top"))) return (long long)(it->Get(v8::String::New("floatApprox"))->NumberValue()); return @@ -803,7 +903,7 @@ namespace mongo { v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); - long long val = numberLongVal(it); + long long val = numberLongVal(scope, it); return v8::Number::New(double(val)); } @@ -815,7 +915,7 @@ namespace mongo { v8::Handle<v8::Object> it = args.This(); stringstream ss; - long long val = numberLongVal(it); + long long val = numberLongVal(scope, it); const long long limit = 2LL << 30; if (val <= -limit || limit <= val) @@ -828,12 +928,14 @@ namespace mongo { } v8::Handle<v8::Value> numberIntInit(V8Scope* scope, const v8::Arguments& args) { - v8::Handle<v8::Object> it = args.This(); - if (it->IsUndefined() || it == v8::Context::GetCurrent()->Global()) { - v8::Function* f = scope->getNamedCons("NumberInt"); + if (!args.IsConstructCall()) { + v8::Handle<v8::Function> f = scope->NumberIntFT()->GetFunction(); return newInstance(f, args); } + v8::Handle<v8::Object> it = args.This(); + verify(scope->NumberIntFT()->HasInstance(it)); + argumentCheck(args.Length() == 0 || args.Length() == 1, "NumberInt needs 0 or 1 arguments") if (args.Length() == 0) { it->SetHiddenValue(v8::String::New("__NumberInt"), v8::Number::New(0)); @@ -844,10 +946,16 @@ namespace mongo { return it; } + int numberIntVal(V8Scope* scope, const v8::Handle<v8::Object>& it) { + verify(scope->NumberIntFT()->HasInstance(it)); + v8::Handle<v8::Value> value = it->GetHiddenValue(v8::String::New("__NumberInt")); + verify(!value.IsEmpty()); + return value->Int32Value(); + } + v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); - int val = it->GetHiddenValue(v8::String::New("__NumberInt"))->Int32Value(); - return v8::Number::New(double(val)); + return v8::Integer::New(numberIntVal(scope, it)); } v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Arguments& args) { @@ -856,8 +964,7 @@ namespace mongo { v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Arguments& args) { v8::Handle<v8::Object> it = args.This(); - - int val = it->GetHiddenValue(v8::String::New("__NumberInt"))->Int32Value(); + int val = numberIntVal(scope, it); string ret = str::stream() << "NumberInt(" << val << ")"; return v8::String::New(ret.c_str()); } diff --git a/src/mongo/scripting/v8_db.h b/src/mongo/scripting/v8_db.h index 2f747fd9714..445ab783681 100644 --- a/src/mongo/scripting/v8_db.h +++ b/src/mongo/scripting/v8_db.h @@ -27,19 +27,12 @@ namespace mongo { class DBClientBase; /** - * install database access functions - */ - void installDBAccess(V8Scope* scope); - - /** - * install BSON types and helpers - */ - void installBSONTypes(V8Scope* scope); - - /** * get the DBClientBase connection from JS args */ - mongo::DBClientBase* getConnection(const v8::Arguments& args); + mongo::DBClientBase* getConnection(V8Scope* scope, const v8::Arguments& args); + + // Internal Cursor + v8::Handle<v8::FunctionTemplate> getInternalCursorFunctionTemplate(V8Scope* scope); // Mongo constructors v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Arguments& args); @@ -68,12 +61,14 @@ namespace mongo { v8::Handle<v8::Value> binDataToHex(V8Scope* scope, const v8::Arguments& args); // NumberLong object + long long numberLongVal(V8Scope* scope, const v8::Handle<v8::Object>& it); v8::Handle<v8::Value> numberLongInit(V8Scope* scope, const v8::Arguments& args); v8::Handle<v8::Value> numberLongToNumber(V8Scope* scope, const v8::Arguments& args); v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Arguments& args); v8::Handle<v8::Value> numberLongToString(V8Scope* scope, const v8::Arguments& args); - // Number object + // NumberInt object + int numberIntVal(V8Scope* scope, const v8::Handle<v8::Object>& it); v8::Handle<v8::Value> numberIntInit(V8Scope* scope, const v8::Arguments& args); v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Arguments& args); v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Arguments& args); diff --git a/src/mongo/scripting/v8_utils.cpp b/src/mongo/scripting/v8_utils.cpp index fa6099d61e1..ec6357e99a4 100644 --- a/src/mongo/scripting/v8_utils.cpp +++ b/src/mongo/scripting/v8_utils.cpp @@ -35,10 +35,7 @@ using namespace std; namespace mongo { std::string toSTLString(const v8::Handle<v8::Value>& o) { - v8::String::Utf8Value str(o); - massert(16686, "error converting js type to Utf8Value", *str); - std::string s(*str, str.length()); - return s; + return StringData(V8String(o)).toString(); } /** Get the properties of an object (and its prototype) as a comma-delimited string */ @@ -96,7 +93,7 @@ namespace mongo { // arguments need to be copied into the isolate, go through bson BSONObjBuilder b; for(int i = 0; i < args.Length(); ++i) { - scope->v8ToMongoElement(b, mongoutils::str::stream() << "arg" << i, args[i]); + scope->v8ToMongoElement(b, "arg" + BSONObjBuilder::numStr(i), args[i]); } _args = b.obj(); } diff --git a/src/mongo/scripting/v8_utils.h b/src/mongo/scripting/v8_utils.h index 7f753d36bd3..299e1f6eef9 100644 --- a/src/mongo/scripting/v8_utils.h +++ b/src/mongo/scripting/v8_utils.h @@ -23,6 +23,9 @@ #include <string> #include <v8.h> +#include <mongo/base/string_data.h> +#include <mongo/util/assert_util.h> + namespace mongo { #define jsassert(x,msg) uassert(16664, (msg), (x)) @@ -38,6 +41,45 @@ namespace mongo { /** Simple v8 object to string conversion helper */ std::string toSTLString(const v8::Handle<v8::Value>& o); + /** Like toSTLString but doesn't allocate a new std::string + * + * This owns the string's memory so you need to be careful not to let the + * converted StringDatas outlive the V8Scope object. These rules are the + * same as converting from a std::string into a StringData. + * + * Safe: + * void someFunction(StringData argument); + * v8::Handle<v8::String> aString; + * + * someFunction(V8String(aString)); // passing down stack as temporary + * + * V8String named (aString); + * someFunction(named); // passing up stack as named value + * + * StringData sd = named; // scope of sd is less than named + * + * Unsafe: + * StringData _member; + * + * StringData returningFunction() { + * StringData sd = V8String(aString); // sd outlives the temporary + * + * V8String named(aString) + * _member = named; // _member outlives named scope + * + * return V8String(aString); // passing up stack + * } + */ + class V8String { + public: + explicit V8String(const v8::Handle<v8::Value>& o) :_str(o) { + massert(16686, "error converting js type to Utf8Value", *_str); + } + operator StringData () const { return StringData(*_str, _str.length()); } + private: + v8::String::Utf8Value _str; + }; + /** Get the properties of an object (and it's prototype) as a comma-delimited string */ std::string v8ObjectToString(const v8::Handle<v8::Object>& o); diff --git a/src/mongo/shell/createCPPfromJavaScriptFiles.js b/src/mongo/shell/createCPPfromJavaScriptFiles.js index 32d2fbbd044..c77c56817e6 100644 --- a/src/mongo/shell/createCPPfromJavaScriptFiles.js +++ b/src/mongo/shell/createCPPfromJavaScriptFiles.js @@ -24,15 +24,7 @@ var whitespace = " \t"; function cppEscape( s ) { - for ( var i = 0, len = s.length; i < len; ++i ) { - if ( whitespace.indexOf( s.charAt( i ) ) === -1 ) { - s = s.substring( i ); - break; - } - } - if ( i == len ) - return ""; - for ( i = s.length - 1; i >= 0; --i ) { + for ( var i = s.length - 1; i >= 0; --i ) { if ( whitespace.indexOf( s.charAt( i ) ) === -1 ) { s = s.substr( 0, i + 1 ); break; diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js index c343f9b6615..a41172ecf1e 100644 --- a/src/mongo/shell/db.js +++ b/src/mongo/shell/db.js @@ -269,11 +269,15 @@ DB.prototype.auth = function() { */ DB.prototype.createCollection = function(name, opt) { var options = opt || {}; - var cmd = { create: name, capped: options.capped, size: options.size }; + var cmd = { create: name }; if (options.max != undefined) cmd.max = options.max; if (options.autoIndexId != undefined) cmd.autoIndexId = options.autoIndexId; + if (options.capped != undefined) + cmd.capped = options.capped; + if (options.size != undefined) + cmd.size = options.size; var res = this._dbCommand(cmd); return res; } diff --git a/src/mongo/shell/replsettest.js b/src/mongo/shell/replsettest.js index 1ca47130df7..a9c8c36b050 100644 --- a/src/mongo/shell/replsettest.js +++ b/src/mongo/shell/replsettest.js @@ -380,7 +380,7 @@ ReplSetTest.awaitRSClientHosts = function( conn, host, hostOk, rs ) { } ReplSetTest.prototype.awaitSecondaryNodes = function( timeout ) { - this.getMaster(); // Wait for a primary to be selected. + this.getMaster(timeout); // Wait for a primary to be selected. var tmo = timeout || 60000; var replTest = this; assert.soon( @@ -469,17 +469,17 @@ ReplSetTest.prototype.initiate = function( cfg , initCmd , timeout ) { var config = cfg || this.getReplSetConfig(); var cmd = {}; var cmdKey = initCmd || 'replSetInitiate'; - var timeout = timeout || 30000; + var tmo = timeout || 30000; cmd[cmdKey] = config; printjson(cmd); - jsTest.attempt({context:this, timeout: timeout, desc: "Initiate replica set"}, function() { + jsTest.attempt({context:this, timeout: tmo, desc: "Initiate replica set"}, function() { var result = master.runCommand(cmd); printjson(result); return result['ok'] == 1; }); - this.awaitSecondaryNodes(); + this.awaitSecondaryNodes(timeout); // Setup authentication if running test with authentication if (jsTestOptions().keyFile && !this.keyFile) { diff --git a/src/mongo/shell/shell_utils.cpp b/src/mongo/shell/shell_utils.cpp index dce193c1a44..9c48487d314 100644 --- a/src/mongo/shell/shell_utils.cpp +++ b/src/mongo/shell/shell_utils.cpp @@ -18,11 +18,13 @@ #include "pch.h" #include "mongo/shell/shell_utils.h" + +#include "mongo/client/dbclientinterface.h" +#include "mongo/scripting/engine.h" #include "mongo/shell/shell_utils_extended.h" #include "mongo/shell/shell_utils_launcher.h" #include "mongo/util/processinfo.h" -#include "mongo/client/dbclientinterface.h" -#include "mongo/scripting/engine.h" +#include "mongo/util/version.h" namespace mongo { @@ -124,6 +126,13 @@ namespace mongo { #endif } + BSONObj getBuildInfo(const BSONObj& a, void* data) { + uassert( 16822, "getBuildInfo accepts no arguments", a.nFields() == 0 ); + BSONObjBuilder b; + appendBuildInfo(b); + return BSON( "" << b.done() ); + } + BSONObj interpreterVersion(const BSONObj& a, void* data) { uassert( 16453, "interpreterVersion accepts no arguments", a.nFields() == 0 ); return BSON( "" << globalScriptEngine->getInterpreterVersionString() ); @@ -136,6 +145,7 @@ namespace mongo { scope.injectNative( "_rand" , JSRand ); scope.injectNative( "_isWindows" , isWindows ); scope.injectNative( "interpreterVersion", interpreterVersion ); + scope.injectNative( "getBuildInfo", getBuildInfo ); #ifndef MONGO_SAFE_SHELL //can't launch programs diff --git a/src/mongo/shell/shell_utils_launcher.cpp b/src/mongo/shell/shell_utils_launcher.cpp index cb940dce14e..625efa83e1c 100644 --- a/src/mongo/shell/shell_utils_launcher.cpp +++ b/src/mongo/shell/shell_utils_launcher.cpp @@ -299,8 +299,10 @@ namespace mongo { break; } if ( last != buf ) { - strcpy( temp, last ); - strcpy( buf, temp ); + strncpy( temp, last, bufSize ); + temp[ bufSize-1 ] = '\0'; + strncpy( buf, temp, bufSize ); + buf[ bufSize-1 ] = '\0'; } else { verify( strlen( buf ) < bufSize ); @@ -556,6 +558,14 @@ namespace mongo { return undefinedReturn; } + BSONObj PathExists( const BSONObj &a, void* data ) { + verify( a.nFields() == 1 ); + string path = a.firstElement().valuestrsafe(); + verify( !path.empty() ); + bool exists = boost::filesystem::exists(path); + return BSON( string( "" ) << exists ); + } + void copyDir( const boost::filesystem::path &from, const boost::filesystem::path &to ) { boost::filesystem::directory_iterator end; boost::filesystem::directory_iterator i( from ); @@ -773,6 +783,7 @@ namespace mongo { scope.injectNative( "waitProgram" , WaitProgram ); scope.injectNative( "checkProgram" , CheckProgram ); scope.injectNative( "resetDbpath", ResetDbpath ); + scope.injectNative( "pathExists", PathExists ); scope.injectNative( "copyDbpath", CopyDbpath ); } } diff --git a/src/mongo/shell/types.js b/src/mongo/shell/types.js index 3c5cfa8622b..9a63c05022b 100644 --- a/src/mongo/shell/types.js +++ b/src/mongo/shell/types.js @@ -68,9 +68,29 @@ ISODate = function(isoDateStr){ var date = parseInt(res[3],10) || 0; var hour = parseInt(res[5],10) || 0; var min = parseInt(res[7],10) || 0; - var sec = parseFloat(res[9]) || 0; - var ms = Math.round((sec%1) * 1000) - sec -= ms/1000 + var sec = parseInt((res[9] && res[9].substr(0,2)),10) || 0; + var ms = Math.round((parseFloat(res[10]) || 0) * 1000); + if (ms == 1000) { + ms = 0; + ++sec; + } + if (sec == 60) { + sec = 0; + ++min; + } + if (min == 60) { + min = 0; + ++hour; + } + if (hour == 24) { + hour = 0; // the day wrapped, let JavaScript figure out the rest + var tempTime = Date.UTC(year, month, date, hour, min, sec, ms); + tempTime += 24 * 60 * 60 * 1000; // milliseconds in a day + var tempDate = new Date(tempTime); + year = tempDate.getUTCFullYear(); + month = tempDate.getUTCMonth(); + date = tempDate.getUTCDate(); + } var time = Date.UTC(year, month, date, hour, min, sec, ms); @@ -539,7 +559,7 @@ tojson = function(x, indent, nolint){ case "object":{ var s = tojsonObject(x, indent, nolint); if ((nolint == null || nolint == true) && s.length < 80 && (indent == null || indent.length == 0)){ - s = s.replace(/[\s\r\n ]+/gm, " "); + s = s.replace(/[\t\r\n]+/gm, " "); } return s; } diff --git a/src/mongo/tools/restore.cpp b/src/mongo/tools/restore.cpp index e660bb541a4..abd7f868afe 100644 --- a/src/mongo/tools/restore.cpp +++ b/src/mongo/tools/restore.cpp @@ -485,24 +485,33 @@ private: } void createCollectionWithOptions(BSONObj cmdObj) { - if (!cmdObj.hasField("create") || cmdObj["create"].String() != _curcoll) { - BSONObjBuilder bo; - if (!cmdObj.hasField("create")) { + + // Create a new cmdObj to skip undefined fields and fix collection name + BSONObjBuilder bo; + + // Add a "create" field if it doesn't exist + if (!cmdObj.hasField("create")) { + bo.append("create", _curcoll); + } + + BSONObjIterator i(cmdObj); + while ( i.more() ) { + BSONElement e = i.next(); + + // Replace the "create" field with the name of the collection we are actually creating + if (strcmp(e.fieldName(), "create") == 0) { bo.append("create", _curcoll); } - - BSONObjIterator i(cmdObj); - while ( i.more() ) { - BSONElement e = i.next(); - if (strcmp(e.fieldName(), "create") == 0) { - bo.append("create", _curcoll); + else { + if (e.type() == Undefined) { + log() << _curns << ": skipping undefined field: " << e.fieldName() << endl; } else { bo.append(e); } } - cmdObj = bo.obj(); } + cmdObj = bo.obj(); BSONObj fields = BSON("options" << 1); scoped_ptr<DBClientCursor> cursor(conn().query(_curdb + ".system.namespaces", Query(BSON("name" << _curns)), 0, 0, &fields)); diff --git a/src/mongo/util/gsasl_session.cpp b/src/mongo/util/gsasl_session.cpp deleted file mode 100644 index de8be43ffb7..00000000000 --- a/src/mongo/util/gsasl_session.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2012 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 "mongo/util/gsasl_session.h" - -#include <cstdlib> - -#include "mongo/util/assert_util.h" - -namespace mongo { - - GsaslSession::GsaslSession() : _gsaslSession(NULL), _done(false) {} - - GsaslSession::~GsaslSession() { - if (_gsaslSession) - gsasl_finish(_gsaslSession); - } - - std::string GsaslSession::getMechanism() const { - return gsasl_mechanism_name(_gsaslSession); - } - - void GsaslSession::setProperty(Gsasl_property property, const StringData& value) { - gsasl_property_set_raw(_gsaslSession, property, value.rawData(), value.size()); - } - - const std::string GsaslSession::getProperty(Gsasl_property property) const { - const char* prop = gsasl_property_fast(_gsaslSession, property); - if (prop == NULL) { - return ""; - } - return prop; - } - - Status GsaslSession::initializeClientSession(Gsasl* gsasl, - const StringData& mechanism, - void* sessionHook) { - return _initializeSession(&gsasl_client_start, gsasl, mechanism, sessionHook); - } - - Status GsaslSession::initializeServerSession(Gsasl* gsasl, - const StringData& mechanism, - void* sessionHook) { - return _initializeSession(&gsasl_server_start, gsasl, mechanism, sessionHook); - } - - Status GsaslSession::_initializeSession( - GsaslSessionStartFn sessionStartFn, - Gsasl* gsasl, const StringData& mechanism, void* sessionHook) { - - if (_done || _gsaslSession) - return Status(ErrorCodes::CannotReuseObject, "Cannot reuse GsaslSession."); - - int rc = sessionStartFn(gsasl, mechanism.toString().c_str(), &_gsaslSession); - switch (rc) { - case GSASL_OK: - gsasl_session_hook_set(_gsaslSession, sessionHook); - return Status::OK(); - case GSASL_UNKNOWN_MECHANISM: - return Status(ErrorCodes::BadValue, gsasl_strerror(rc)); - default: - return Status(ErrorCodes::ProtocolError, gsasl_strerror(rc)); - } - } - - Status GsaslSession::step(const StringData& inputData, std::string* outputData) { - char* output; - size_t outputSize; - int rc = gsasl_step(_gsaslSession, - inputData.rawData(), inputData.size(), - &output, &outputSize); - - if (GSASL_OK == rc) - _done = true; - - switch (rc) { - case GSASL_OK: - case GSASL_NEEDS_MORE: - *outputData = std::string(output, output + outputSize); - free(output); - return Status::OK(); - case GSASL_AUTHENTICATION_ERROR: - return Status(ErrorCodes::AuthenticationFailed, gsasl_strerror(rc)); - default: - return Status(ErrorCodes::ProtocolError, gsasl_strerror(rc)); - } - } - -} // namespace mongo diff --git a/src/mongo/util/gsasl_session.h b/src/mongo/util/gsasl_session.h deleted file mode 100644 index cdaa9b58a5a..00000000000 --- a/src/mongo/util/gsasl_session.h +++ /dev/null @@ -1,147 +0,0 @@ -/* Copyright 2012 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. - */ - -#pragma once - -#include <string> - -#include "mongo/base/disallow_copying.h" -#include "mongo/base/status.h" -#include "mongo/base/string_data.h" -#include "mongo/platform/cstdint.h" // Must be included before <gsasl.h> because of SERVER-8086 - -#include <gsasl.h> // Must be included after "mongo/platform/cstdint.h" because of SERVER-8086. - -namespace mongo { - - /** - * C++ wrapper around Gsasl_session. - */ - class GsaslSession { - MONGO_DISALLOW_COPYING(GsaslSession); - public: - GsaslSession(); - ~GsaslSession(); - - /** - * Initializes "this" as a client sasl session. - * - * May only be called once on an instance of GsaslSession, and may not be called on an - * instance on which initializeServerSession has been called. - * - * "gsasl" is a pointer to a Gsasl library context that will exist for the rest of - * the lifetime of "this". - * - * "mechanism" is a SASL mechanism name. - * - * "sessionHook" is user-supplied data associated with this session. If is accessible in - * the gsasl callback set on "gsasl" using gsasl_session_hook_get(). May be NULL. Owned - * by caller, and must stay in scope as long as this object. - * - * Returns Status::OK() on success, some other status on errors. - */ - Status initializeClientSession(Gsasl* gsasl, - const StringData& mechanism, - void* sessionHook); - - /** - * Initializes "this" as a server sasl session. - * - * May only be called once on an instance of GsaslSession, and may not be called on an - * instance on which initializeClientSession has been called. - * - * "gsasl" is a pointer to a Gsasl library context that will exist for the rest of - * the lifetime of "this". - * - * "mechanism" is a SASL mechanism name. - * - * "sessionHook" is user-supplied data associated with this session. If is accessible in - * the gsasl callback set on "gsasl" using gsasl_session_hook_get(). May be NULL. Owned - * by caller, and must stay in scope as long as this object. - * - * Returns Status::OK() on success, some other status on errors. - */ - Status initializeServerSession(Gsasl* gsasl, - const StringData& mechanism, - void* sessionHook); - - /** - * Returns the string name of the SASL mechanism in use in this session. - * - * Not valid before initializeServerSession() or initializeClientSession(). - */ - std::string getMechanism() const; - - /** - * Sets a property on this session. - * - * Not valid before initializeServerSession() or initializeClientSession(). - */ - void setProperty(Gsasl_property property, const StringData& value); - - /** - * Gets a property on this session. Return an empty string if the property isn't set. - * - * Not valid before initializeServerSession() or initializeClientSession(). - */ - const std::string getProperty(Gsasl_property property) const; - - /** - * Performs one more step on this session. - * - * Receives "inputData" from the other side and produces "*outputData" to send. - * - * Both "inputData" and "*outputData" are logically strings of bytes, not characters. - * - * For the first step by the authentication initiator, "inputData" should have 0 length. - * - * Returns Status::OK() on success. In that case, isDone() can be queried to see if the - * session expects another call to step(). If isDone() is true, the authentication has - * completed successfully. - * - * Any return other than Status::OK() means that authentication has failed, but the specific - * code or reason message may provide insight as to why. - */ - Status step(const StringData& inputData, std::string* outputData); - - /** - * Returns true if this session has completed successfully. - * - * That is, returns true if the session expects no more calls to step(), and all previous - * calls to step() and initializeClientSession()/initializeServerSession() have returned - * Status::OK(). - */ - bool isDone() const { return _done; } - - private: - // Signature of gsas session start functions. - typedef int (*GsaslSessionStartFn)(Gsasl*, const char*, Gsasl_session**); - - /** - * Common helper code for initializing a session. - * - * Uses "sessionStartFn" to initialize the underlying Gsasl_session. - */ - Status _initializeSession(GsaslSessionStartFn sessionStartFn, - Gsasl* gsasl, const StringData& mechanism, void* sessionHook); - - /// Underlying C-library gsasl session object. - Gsasl_session* _gsaslSession; - - /// See isDone(), above. - bool _done; - }; - -} // namespace mongo diff --git a/src/mongo/util/processinfo_linux2.cpp b/src/mongo/util/processinfo_linux2.cpp index efbfdea8349..6b06ff6258b 100644 --- a/src/mongo/util/processinfo_linux2.cpp +++ b/src/mongo/util/processinfo_linux2.cpp @@ -49,7 +49,7 @@ namespace mongo { msgassertedNoTrace( 13538 , s.c_str() ); } int found = fscanf(f, - "%d %s %c " + "%d %127s %c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu " "%lu %lu %ld %ld " /* utime stime cutime cstime */ diff --git a/src/mongo/util/processinfo_sunos5.cpp b/src/mongo/util/processinfo_sunos5.cpp new file mode 100644 index 00000000000..089edc24eed --- /dev/null +++ b/src/mongo/util/processinfo_sunos5.cpp @@ -0,0 +1,68 @@ +// processinfo_none.cpp + +/* Copyright 2009 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 "processinfo.h" + +#include <iostream> +using namespace std; + +namespace mongo { + + ProcessInfo::ProcessInfo( pid_t pid ) { + } + + ProcessInfo::~ProcessInfo() { + } + + bool ProcessInfo::supported() { + return false; + } + + int ProcessInfo::getVirtualMemorySize() { + return -1; + } + + int ProcessInfo::getResidentSize() { + return -1; + } + + bool ProcessInfo::checkNumaEnabled() { + return false; + } + + bool ProcessInfo::blockCheckSupported() { + return false; + } + + void ProcessInfo::SystemInfo::collectSystemInfo() { + + } + + void ProcessInfo::getExtraInfo( BSONObjBuilder& info ) { + + } + + bool ProcessInfo::blockInMemory(const void* start) { + verify(0); + } + + bool ProcessInfo::pagesInMemory(const void* start, size_t numPages, vector<char>* out) { + verify(0); + } + +} diff --git a/src/mongo/util/processinfo_win32.cpp b/src/mongo/util/processinfo_win32.cpp index 83998831594..3f42eb12698 100644 --- a/src/mongo/util/processinfo_win32.cpp +++ b/src/mongo/util/processinfo_win32.cpp @@ -49,8 +49,10 @@ namespace mongo { } supported = false; } - } psapiGlobal; - + }; + + static PsApiInit* psapiGlobal = NULL; + int _wconvertmtos( SIZE_T s ) { return (int)( s / ( 1024 * 1024 ) ); } @@ -189,6 +191,9 @@ namespace mongo { osVersion = verstr.str(); hasNuma = checkNumaEnabled(); _extraStats = bExtra.obj(); + if (psapiGlobal == NULL) { + psapiGlobal = new PsApiInit(); + } } @@ -197,7 +202,7 @@ namespace mongo { } bool ProcessInfo::blockCheckSupported() { - return psapiGlobal.supported; + return psapiGlobal->supported; } bool ProcessInfo::blockInMemory(const void* start) { @@ -217,7 +222,7 @@ namespace mongo { #endif PSAPI_WORKING_SET_EX_INFORMATION wsinfo; wsinfo.VirtualAddress = const_cast<void*>(start); - BOOL result = psapiGlobal.QueryWSEx( GetCurrentProcess(), &wsinfo, sizeof(wsinfo) ); + BOOL result = psapiGlobal->QueryWSEx( GetCurrentProcess(), &wsinfo, sizeof(wsinfo) ); if ( result ) if ( wsinfo.VirtualAttributes.Valid ) return true; @@ -235,7 +240,7 @@ namespace mongo { reinterpret_cast<unsigned long long>(startOfFirstPage) + i * getPageSize()); } - BOOL result = psapiGlobal.QueryWSEx(GetCurrentProcess(), + BOOL result = psapiGlobal->QueryWSEx(GetCurrentProcess(), wsinfo.get(), sizeof(PSAPI_WORKING_SET_EX_INFORMATION) * numPages); diff --git a/src/mongo/util/sequence_util.h b/src/mongo/util/sequence_util.h new file mode 100644 index 00000000000..858f1de707e --- /dev/null +++ b/src/mongo/util/sequence_util.h @@ -0,0 +1,36 @@ +/* Copyright 2013 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. + */ + +/** + * This file declares utility methods for operating on sequence containers, such as vectors, lists + * and deques. + */ + +#pragma once + +#include <algorithm> + +namespace mongo { + +/* + * Returns true if "container" contains "value". + */ +template <typename C> +bool sequenceContains(const C& container, typename C::const_reference value) { + using std::find; + return find(container.begin(), container.end(), value) != container.end(); +} + +} // namespace mongo diff --git a/src/mongo/util/unordered_fast_key_table.h b/src/mongo/util/unordered_fast_key_table.h index 158003ed01f..2bc67e00aaf 100644 --- a/src/mongo/util/unordered_fast_key_table.h +++ b/src/mongo/util/unordered_fast_key_table.h @@ -62,7 +62,7 @@ namespace mongo { int find( const K_L& key, size_t hash, int* firstEmpty, const UnorderedFastKeyTable& sm ) const; - void transfer( Area* newArea, const UnorderedFastKeyTable& sm ) const; + bool transfer( Area* newArea, const UnorderedFastKeyTable& sm ) const; void swap( Area* other ) { using std::swap; diff --git a/src/mongo/util/unordered_fast_key_table_internal.h b/src/mongo/util/unordered_fast_key_table_internal.h index faef0473620..65421ba7625 100644 --- a/src/mongo/util/unordered_fast_key_table_internal.h +++ b/src/mongo/util/unordered_fast_key_table_internal.h @@ -77,7 +77,7 @@ namespace mongo { } template< typename K_L, typename K_S, typename V, typename H, typename E, typename C, typename C_LS > - inline void UnorderedFastKeyTable<K_L, K_S, V, H, E, C, C_LS>::Area::transfer( + inline bool UnorderedFastKeyTable<K_L, K_S, V, H, E, C, C_LS>::Area::transfer( Area* newArea, const UnorderedFastKeyTable& sm) const { for ( unsigned i = 0; i < _capacity; i++ ) { @@ -91,10 +91,13 @@ namespace mongo { sm ); verify( loc == -1 ); - verify( firstEmpty >= 0 ); + if ( firstEmpty < 0 ) { + return false; + } newArea->_entries[firstEmpty] = _entries[i]; } + return true; } template< typename K_L, typename K_S, typename V, typename H, typename E, typename C, typename C_LS > @@ -130,7 +133,7 @@ namespace mongo { const size_t hash = _hash( key ); - for ( int numGrowTries = 0; numGrowTries < 10; numGrowTries++ ) { + for ( int numGrowTries = 0; numGrowTries < 5; numGrowTries++ ) { int firstEmpty = -1; int pos = _area.find( key, hash, &firstEmpty, *this ); if ( pos >= 0 ) @@ -169,9 +172,19 @@ namespace mongo { template< typename K_L, typename K_S, typename V, typename H, typename E, typename C, typename C_LS > inline void UnorderedFastKeyTable<K_L, K_S, V, H, E, C, C_LS>::_grow() { - Area newArea( _area._capacity * 2, _maxProbeRatio ); - _area.transfer( &newArea, *this ); - _area.swap( &newArea ); + unsigned capacity = _area._capacity; + for ( int numGrowTries = 0; numGrowTries < 5; numGrowTries++ ) { + capacity *= 2; + Area newArea( capacity, _maxProbeRatio ); + bool success = _area.transfer( &newArea, *this ); + if ( !success ) { + continue; + } + _area.swap( &newArea ); + return; + } + msgasserted( 16845, + "UnorderedFastKeyTable::_grow couldn't add entry after growing many times" ); } template< typename K_L, typename K_S, typename V, typename H, typename E, typename C, typename C_LS > diff --git a/src/mongo/util/version.cpp b/src/mongo/util/version.cpp index 3ffc7eef508..0756bca0901 100644 --- a/src/mongo/util/version.cpp +++ b/src/mongo/util/version.cpp @@ -47,7 +47,7 @@ namespace mongo { * 1.2.3-rc4-pre- * If you really need to do something else you'll need to fix _versionArray() */ - const char versionString[] = "2.4.4-pre-"; + const char versionString[] = "2.4.5"; // See unit test for example outputs BSONArray toVersionArray(const char* version){ diff --git a/src/third_party/pcre-8.30/config.status b/src/third_party/pcre-8.30/config.status new file mode 100755 index 00000000000..b7e064fea3f --- /dev/null +++ b/src/third_party/pcre-8.30/config.status @@ -0,0 +1,2365 @@ +#! /bin/sh +# Generated by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by PCRE $as_me 8.30, which was +generated by GNU Autoconf 2.68. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +# Files that config.status was made for. +config_files=" Makefile libpcre.pc libpcre16.pc libpcreposix.pc libpcrecpp.pc pcre-config pcre.h pcre_stringpiece.h pcrecpparg.h" +config_headers=" config.h" +config_commands=" depfiles libtool script-chmod delete-old-chartables" + +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to the package provider." + +ac_cs_config="" +ac_cs_version="\ +PCRE config.status 8.30 +configured by ./configure, generated by GNU Autoconf 2.68, + with options \"$ac_cs_config\" + +Copyright (C) 2010 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='/media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30' +srcdir='.' +INSTALL='/usr/bin/install -c' +MKDIR_P='/bin/mkdir -p' +AWK='gawk' +test -n "$AWK" || AWK=awk +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +if $ac_cs_recheck; then + set X '/bin/sh' './configure' $ac_configure_extra_args --no-create --no-recursion + shift + $as_echo "running CONFIG_SHELL=/bin/sh $*" >&6 + CONFIG_SHELL='/bin/sh' + export CONFIG_SHELL + exec "$@" +fi + +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +# +# INIT-COMMANDS +# +AMDEP_TRUE="" ac_aux_dir="." + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' +double_quote_subst='s/\(["`\\]\)/\\\1/g' +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' +AS='as' +DLLTOOL='dlltool' +OBJDUMP='objdump' +macro_version='2.4' +macro_revision='1.3293' +enable_shared='yes' +enable_static='yes' +pic_mode='default' +enable_fast_install='yes' +SHELL='/bin/sh' +ECHO='printf %s\n' +host_alias='' +host='x86_64-unknown-linux-gnu' +host_os='linux-gnu' +build_alias='' +build='x86_64-unknown-linux-gnu' +build_os='linux-gnu' +SED='/bin/sed' +Xsed='/bin/sed -e 1s/^X//' +GREP='/bin/grep' +EGREP='/bin/grep -E' +FGREP='/bin/grep -F' +LD='/usr/bin/ld -m elf_x86_64' +NM='/usr/bin/nm -B' +LN_S='ln -s' +max_cmd_len='1572864' +ac_objext='o' +exeext='' +lt_unset='unset' +lt_SP2NL='tr \040 \012' +lt_NL2SP='tr \015\012 \040\040' +lt_cv_to_host_file_cmd='func_convert_file_noop' +lt_cv_to_tool_file_cmd='func_convert_file_noop' +reload_flag=' -r' +reload_cmds='$LD$reload_flag -o $output$reload_objs' +deplibs_check_method='pass_all' +file_magic_cmd='$MAGIC_CMD' +file_magic_glob='' +want_nocaseglob='no' +sharedlib_from_linklib_cmd='printf %s\n' +AR='ar' +AR_FLAGS='cru' +archiver_list_spec='@' +STRIP='strip' +RANLIB='ranlib' +old_postinstall_cmds='chmod 644 $oldlib~$RANLIB $oldlib' +old_postuninstall_cmds='' +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $oldlib' +lock_old_archive_extraction='no' +CC='gcc' +CFLAGS='-O2' +compiler='g++' +GCC='yes' +lt_cv_sys_global_symbol_pipe='sed -n -e '\''s/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$/\1 \2 \2/p'\'' | sed '\''/ __gnu_lto/d'\''' +lt_cv_sys_global_symbol_to_cdecl='sed -n -e '\''s/^T .* \(.*\)$/extern int \1();/p'\'' -e '\''s/^[ABCDGIRSTW]* .* \(.*\)$/extern char \1;/p'\''' +lt_cv_sys_global_symbol_to_c_name_address='sed -n -e '\''s/^: \([^ ]*\)[ ]*$/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"\2", (void *) \&\2},/p'\''' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='sed -n -e '\''s/^: \([^ ]*\)[ ]*$/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \(lib[^ ]*\)$/ {"\2", (void *) \&\2},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"lib\2", (void *) \&\2},/p'\''' +nm_file_list_spec='@' +lt_sysroot='' +objdir='.libs' +MAGIC_CMD='file' +lt_prog_compiler_no_builtin_flag=' -fno-builtin' +lt_prog_compiler_pic=' -fPIC -DPIC' +lt_prog_compiler_wl='-Wl,' +lt_prog_compiler_static='' +lt_cv_prog_compiler_c_o='yes' +need_locks='no' +MANIFEST_TOOL=':' +DSYMUTIL='' +NMEDIT='' +LIPO='' +OTOOL='' +OTOOL64='' +libext='a' +shrext_cmds='.so' +extract_expsyms_cmds='' +archive_cmds_need_lc='no' +enable_shared_with_static_runtimes='no' +export_dynamic_flag_spec='${wl}--export-dynamic' +whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' +compiler_needs_object='no' +old_archive_from_new_cmds='' +old_archive_from_expsyms_cmds='' +archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' +archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' +module_cmds='' +module_expsym_cmds='' +with_gnu_ld='yes' +allow_undefined_flag='' +no_undefined_flag='' +hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' +hardcode_libdir_flag_spec_ld='' +hardcode_libdir_separator='' +hardcode_direct='no' +hardcode_direct_absolute='no' +hardcode_minus_L='no' +hardcode_shlibpath_var='unsupported' +hardcode_automatic='no' +inherit_rpath='no' +link_all_deplibs='unknown' +always_export_symbols='no' +export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' +exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' +include_expsyms='' +prelink_cmds='' +postlink_cmds='' +file_list_spec='' +variables_saved_for_relink='PATH LD_LIBRARY_PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH' +need_lib_prefix='no' +need_version='no' +version_type='linux' +runpath_var='LD_RUN_PATH' +shlibpath_var='LD_LIBRARY_PATH' +shlibpath_overrides_runpath='no' +libname_spec='lib$name' +library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' +soname_spec='${libname}${release}${shared_ext}$major' +install_override_mode='' +postinstall_cmds='' +postuninstall_cmds='' +finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' +finish_eval='' +hardcode_into_libs='yes' +sys_lib_search_path_spec='/usr/lib/gcc/x86_64-redhat-linux/4.6.2 /usr/lib64 /lib64 ' +sys_lib_dlsearch_path_spec='/lib /usr/lib /usr/lib64/atlas /usr/lib64/llvm /usr/lib64/tracker-0.12 /usr/lib64/xulrunner-2 ' +hardcode_action='immediate' +enable_dlopen='unknown' +enable_dlopen_self='unknown' +enable_dlopen_self_static='unknown' +old_striplib='strip --strip-debug' +striplib='strip --strip-unneeded' +compiler_lib_search_dirs='' +predep_objects='' +postdep_objects='' +predeps='' +postdeps='' +compiler_lib_search_path='' +LD_CXX='/usr/bin/ld -m elf_x86_64' +reload_flag_CXX=' -r' +reload_cmds_CXX='$LD$reload_flag -o $output$reload_objs' +old_archive_cmds_CXX='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $oldlib' +compiler_CXX='g++' +GCC_CXX='yes' +lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' +lt_prog_compiler_pic_CXX=' -fPIC -DPIC' +lt_prog_compiler_wl_CXX='-Wl,' +lt_prog_compiler_static_CXX='' +lt_cv_prog_compiler_c_o_CXX='yes' +archive_cmds_need_lc_CXX='no' +enable_shared_with_static_runtimes_CXX='no' +export_dynamic_flag_spec_CXX='${wl}--export-dynamic' +whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' +compiler_needs_object_CXX='no' +old_archive_from_new_cmds_CXX='' +old_archive_from_expsyms_cmds_CXX='' +archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' +archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' +module_cmds_CXX='' +module_expsym_cmds_CXX='' +with_gnu_ld_CXX='yes' +allow_undefined_flag_CXX='' +no_undefined_flag_CXX='' +hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' +hardcode_libdir_flag_spec_ld_CXX='' +hardcode_libdir_separator_CXX='' +hardcode_direct_CXX='no' +hardcode_direct_absolute_CXX='no' +hardcode_minus_L_CXX='no' +hardcode_shlibpath_var_CXX='unsupported' +hardcode_automatic_CXX='no' +inherit_rpath_CXX='no' +link_all_deplibs_CXX='unknown' +always_export_symbols_CXX='no' +export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' +exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' +include_expsyms_CXX='' +prelink_cmds_CXX='' +postlink_cmds_CXX='' +file_list_spec_CXX='' +hardcode_action_CXX='immediate' +compiler_lib_search_dirs_CXX='/usr/lib/gcc/x86_64-redhat-linux/4.6.2 /usr/lib/gcc/x86_64-redhat-linux/4.6.2/../../../../lib64 /lib/../lib64 /usr/lib/../lib64 /usr/lib/gcc/x86_64-redhat-linux/4.6.2/../../..' +predep_objects_CXX='/usr/lib/gcc/x86_64-redhat-linux/4.6.2/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.6.2/crtbeginS.o' +postdep_objects_CXX='/usr/lib/gcc/x86_64-redhat-linux/4.6.2/crtendS.o /usr/lib/gcc/x86_64-redhat-linux/4.6.2/../../../../lib64/crtn.o' +predeps_CXX='' +postdeps_CXX='-lstdc++ -lm -lgcc_s -lc -lgcc_s' +compiler_lib_search_path_CXX='-L/usr/lib/gcc/x86_64-redhat-linux/4.6.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.6.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.6.2/../../..' + +LTCC='gcc' +LTCFLAGS='-O2' +compiler='gcc' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in AS DLLTOOL OBJDUMP SHELL ECHO SED GREP EGREP FGREP LD NM LN_S lt_SP2NL lt_NL2SP reload_flag deplibs_check_method file_magic_cmd file_magic_glob want_nocaseglob sharedlib_from_linklib_cmd AR AR_FLAGS archiver_list_spec STRIP RANLIB CC CFLAGS compiler lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl lt_cv_sys_global_symbol_to_c_name_address lt_cv_sys_global_symbol_to_c_name_address_lib_prefix nm_file_list_spec lt_prog_compiler_no_builtin_flag lt_prog_compiler_pic lt_prog_compiler_wl lt_prog_compiler_static lt_cv_prog_compiler_c_o need_locks MANIFEST_TOOL DSYMUTIL NMEDIT LIPO OTOOL OTOOL64 shrext_cmds export_dynamic_flag_spec whole_archive_flag_spec compiler_needs_object with_gnu_ld allow_undefined_flag no_undefined_flag hardcode_libdir_flag_spec hardcode_libdir_flag_spec_ld hardcode_libdir_separator exclude_expsyms include_expsyms file_list_spec variables_saved_for_relink libname_spec library_names_spec soname_spec install_override_mode finish_eval old_striplib striplib compiler_lib_search_dirs predep_objects postdep_objects predeps postdeps compiler_lib_search_path LD_CXX reload_flag_CXX compiler_CXX lt_prog_compiler_no_builtin_flag_CXX lt_prog_compiler_pic_CXX lt_prog_compiler_wl_CXX lt_prog_compiler_static_CXX lt_cv_prog_compiler_c_o_CXX export_dynamic_flag_spec_CXX whole_archive_flag_spec_CXX compiler_needs_object_CXX with_gnu_ld_CXX allow_undefined_flag_CXX no_undefined_flag_CXX hardcode_libdir_flag_spec_CXX hardcode_libdir_flag_spec_ld_CXX hardcode_libdir_separator_CXX exclude_expsyms_CXX include_expsyms_CXX file_list_spec_CXX compiler_lib_search_dirs_CXX predep_objects_CXX postdep_objects_CXX predeps_CXX postdeps_CXX compiler_lib_search_path_CXX; do + case `eval \\$ECHO \\""\\$$var"\\"` in + *[\\\`\"\$]*) + eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED \"\$sed_quote_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\$$var\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds old_postinstall_cmds old_postuninstall_cmds old_archive_cmds extract_expsyms_cmds old_archive_from_new_cmds old_archive_from_expsyms_cmds archive_cmds archive_expsym_cmds module_cmds module_expsym_cmds export_symbols_cmds prelink_cmds postlink_cmds postinstall_cmds postuninstall_cmds finish_cmds sys_lib_search_path_spec sys_lib_dlsearch_path_spec reload_cmds_CXX old_archive_cmds_CXX old_archive_from_new_cmds_CXX old_archive_from_expsyms_cmds_CXX archive_cmds_CXX archive_expsym_cmds_CXX module_cmds_CXX module_expsym_cmds_CXX export_symbols_cmds_CXX prelink_cmds_CXX postlink_cmds_CXX; do + case `eval \\$ECHO \\""\\$$var"\\"` in + *[\\\`\"\$]*) + eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + ;; + *) + eval "lt_$var=\\\"\$$var\\\"" + ;; + esac +done + +ac_aux_dir='.' +xsi_shell='yes' +lt_shell_append='yes' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='pcre' + VERSION='8.30' + TIMESTAMP='' + RM='rm -f' + ofile='libtool' + + + + + + + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "libpcre.pc") CONFIG_FILES="$CONFIG_FILES libpcre.pc" ;; + "libpcre16.pc") CONFIG_FILES="$CONFIG_FILES libpcre16.pc" ;; + "libpcreposix.pc") CONFIG_FILES="$CONFIG_FILES libpcreposix.pc" ;; + "libpcrecpp.pc") CONFIG_FILES="$CONFIG_FILES libpcrecpp.pc" ;; + "pcre-config") CONFIG_FILES="$CONFIG_FILES pcre-config" ;; + "pcre.h") CONFIG_FILES="$CONFIG_FILES pcre.h" ;; + "pcre_stringpiece.h") CONFIG_FILES="$CONFIG_FILES pcre_stringpiece.h" ;; + "pcrecpparg.h") CONFIG_FILES="$CONFIG_FILES pcrecpparg.h" ;; + "script-chmod") CONFIG_COMMANDS="$CONFIG_COMMANDS script-chmod" ;; + "delete-old-chartables") CONFIG_COMMANDS="$CONFIG_COMMANDS delete-old-chartables" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +cat >>"$ac_tmp/subs1.awk" <<\_ACAWK && +S["am__EXEEXT_FALSE"]="" +S["am__EXEEXT_TRUE"]="#" +S["LTLIBOBJS"]="" +S["LIBOBJS"]="" +S["LIBBZ2"]="" +S["LIBZ"]="" +S["DISTCHECK_CONFIGURE_FLAGS"]="CFLAGS='' CXXFLAGS='' --enable-pcre16 --enable-jit --enable-cpp --enable-unicode-properties" +S["EXTRA_LIBPCRECPP_LDFLAGS"]=" -version-info 0:0:0 " +S["EXTRA_LIBPCREPOSIX_LDFLAGS"]=" -version-info 0:0:0" +S["EXTRA_LIBPCRE16_LDFLAGS"]=" -version-info 0:0:0" +S["EXTRA_LIBPCRE_LDFLAGS"]=" -version-info 1:0:0" +S["PCRE_STATIC_CFLAG"]="" +S["LIBREADLINE"]="-lreadline" +S["WITH_UTF_FALSE"]="" +S["WITH_UTF_TRUE"]="#" +S["WITH_JIT_FALSE"]="" +S["WITH_JIT_TRUE"]="#" +S["WITH_REBUILD_CHARTABLES_FALSE"]="" +S["WITH_REBUILD_CHARTABLES_TRUE"]="#" +S["WITH_PCRE_CPP_FALSE"]="#" +S["WITH_PCRE_CPP_TRUE"]="" +S["WITH_PCRE16_FALSE"]="" +S["WITH_PCRE16_TRUE"]="#" +S["WITH_PCRE8_FALSE"]="#" +S["WITH_PCRE8_TRUE"]="" +S["pcre_have_bits_type_traits"]="0" +S["pcre_have_type_traits"]="0" +S["pcre_have_ulong_long"]="1" +S["pcre_have_long_long"]="1" +S["enable_cpp"]="yes" +S["enable_pcre16"]="no" +S["enable_pcre8"]="yes" +S["PCRE_DATE"]="2012-02-04" +S["PCRE_PRERELEASE"]="" +S["PCRE_MINOR"]="30" +S["PCRE_MAJOR"]="8" +S["CXXCPP"]="g++ -E" +S["OTOOL64"]="" +S["OTOOL"]="" +S["LIPO"]="" +S["NMEDIT"]="" +S["DSYMUTIL"]="" +S["MANIFEST_TOOL"]=":" +S["RANLIB"]="ranlib" +S["ac_ct_AR"]="ar" +S["AR"]="ar" +S["LN_S"]="ln -s" +S["NM"]="/usr/bin/nm -B" +S["ac_ct_DUMPBIN"]="" +S["DUMPBIN"]="" +S["LD"]="/usr/bin/ld -m elf_x86_64" +S["FGREP"]="/bin/grep -F" +S["SED"]="/bin/sed" +S["LIBTOOL"]="$(SHELL) $(top_builddir)/libtool" +S["OBJDUMP"]="objdump" +S["DLLTOOL"]="dlltool" +S["AS"]="as" +S["host_os"]="linux-gnu" +S["host_vendor"]="unknown" +S["host_cpu"]="x86_64" +S["host"]="x86_64-unknown-linux-gnu" +S["build_os"]="linux-gnu" +S["build_vendor"]="unknown" +S["build_cpu"]="x86_64" +S["build"]="x86_64-unknown-linux-gnu" +S["EGREP"]="/bin/grep -E" +S["GREP"]="/bin/grep" +S["CPP"]="gcc -E" +S["am__fastdepCXX_FALSE"]="#" +S["am__fastdepCXX_TRUE"]="" +S["CXXDEPMODE"]="depmode=gcc3" +S["ac_ct_CXX"]="g++" +S["CXXFLAGS"]="-O2" +S["CXX"]="g++" +S["am__fastdepCC_FALSE"]="#" +S["am__fastdepCC_TRUE"]="" +S["CCDEPMODE"]="depmode=gcc3" +S["AMDEPBACKSLASH"]="\\" +S["AMDEP_FALSE"]="#" +S["AMDEP_TRUE"]="" +S["am__quote"]="" +S["am__include"]="include" +S["DEPDIR"]=".deps" +S["OBJEXT"]="o" +S["EXEEXT"]="" +S["ac_ct_CC"]="gcc" +S["CPPFLAGS"]="" +S["LDFLAGS"]="" +S["CFLAGS"]="-O2" +S["CC"]="gcc" +S["AM_BACKSLASH"]="\\" +S["AM_DEFAULT_VERBOSITY"]="0" +S["am__untar"]="${AMTAR} xf -" +S["am__tar"]="${AMTAR} chof - \"$$tardir\"" +S["AMTAR"]="${SHELL} /media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30/missing --run tar" +S["am__leading_dot"]="." +S["SET_MAKE"]="" +S["AWK"]="gawk" +S["mkdir_p"]="/bin/mkdir -p" +S["MKDIR_P"]="/bin/mkdir -p" +S["INSTALL_STRIP_PROGRAM"]="$(install_sh) -c -s" +S["STRIP"]="strip" +S["install_sh"]="${SHELL} /media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30/install-sh" +S["MAKEINFO"]="${SHELL} /media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30/missing --run makeinfo" +S["AUTOHEADER"]="${SHELL} /media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30/missing --run autoheader" +S["AUTOMAKE"]="${SHELL} /media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30/missing --run automake-1.11" +S["AUTOCONF"]="${SHELL} /media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30/missing --run autoconf" +S["ACLOCAL"]="${SHELL} /media/DATA0/data/m/3827/mongo/src/third_party/pcre-8.30/missing --run aclocal-1.11" +S["VERSION"]="8.30" +S["PACKAGE"]="pcre" +S["CYGPATH_W"]="echo" +S["am__isrc"]="" +S["INSTALL_DATA"]="${INSTALL} -m 644" +S["INSTALL_SCRIPT"]="${INSTALL}" +S["INSTALL_PROGRAM"]="${INSTALL}" +S["target_alias"]="" +S["host_alias"]="" +S["build_alias"]="" +S["LIBS"]="" +S["ECHO_T"]="" +S["ECHO_N"]="-n" +S["ECHO_C"]="" +S["DEFS"]="-DHAVE_CONFIG_H" +S["mandir"]="${datarootdir}/man" +S["localedir"]="${datarootdir}/locale" +S["libdir"]="${exec_prefix}/lib" +S["psdir"]="${docdir}" +S["pdfdir"]="${docdir}" +S["dvidir"]="${docdir}" +S["htmldir"]="${docdir}/html" +S["infodir"]="${datarootdir}/info" +S["docdir"]="${datarootdir}/doc/${PACKAGE_TARNAME}" +S["oldincludedir"]="/usr/include" +S["includedir"]="${prefix}/include" +S["localstatedir"]="${prefix}/var" +S["sharedstatedir"]="${prefix}/com" +S["sysconfdir"]="${prefix}/etc" +S["datadir"]="${datarootdir}" +S["datarootdir"]="${prefix}/share" +S["libexecdir"]="${exec_prefix}/libexec" +S["sbindir"]="${exec_prefix}/sbin" +S["bindir"]="${exec_prefix}/bin" +S["program_transform_name"]="s,x,x," +S["prefix"]="/usr/local" +S["exec_prefix"]="${prefix}" +S["PACKAGE_URL"]="" +S["PACKAGE_BUGREPORT"]="" +S["PACKAGE_STRING"]="PCRE 8.30" +S["PACKAGE_VERSION"]="8.30" +S["PACKAGE_TARNAME"]="pcre" +S["PACKAGE_NAME"]="PCRE" +S["PATH_SEPARATOR"]=":" +S["SHELL"]="/bin/sh" +_ACAWK +cat >>"$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +D["PACKAGE_NAME"]=" \"PCRE\"" +D["PACKAGE_TARNAME"]=" \"pcre\"" +D["PACKAGE_VERSION"]=" \"8.30\"" +D["PACKAGE_STRING"]=" \"PCRE 8.30\"" +D["PACKAGE_BUGREPORT"]=" \"\"" +D["PACKAGE_URL"]=" \"\"" +D["PACKAGE"]=" \"pcre\"" +D["VERSION"]=" \"8.30\"" +D["STDC_HEADERS"]=" 1" +D["HAVE_SYS_TYPES_H"]=" 1" +D["HAVE_SYS_STAT_H"]=" 1" +D["HAVE_STDLIB_H"]=" 1" +D["HAVE_STRING_H"]=" 1" +D["HAVE_MEMORY_H"]=" 1" +D["HAVE_STRINGS_H"]=" 1" +D["HAVE_INTTYPES_H"]=" 1" +D["HAVE_STDINT_H"]=" 1" +D["HAVE_UNISTD_H"]=" 1" +D["HAVE_DLFCN_H"]=" 1" +D["LT_OBJDIR"]=" \".libs/\"" +D["STDC_HEADERS"]=" 1" +D["HAVE_LIMITS_H"]=" 1" +D["HAVE_SYS_TYPES_H"]=" 1" +D["HAVE_SYS_STAT_H"]=" 1" +D["HAVE_DIRENT_H"]=" 1" +D["HAVE_STRING"]=" 1" +D["HAVE_STRTOQ"]=" 1" +D["HAVE_LONG_LONG"]=" 1" +D["HAVE_UNSIGNED_LONG_LONG"]=" 1" +D["HAVE_BCOPY"]=" 1" +D["HAVE_MEMMOVE"]=" 1" +D["HAVE_STRERROR"]=" 1" +D["HAVE_ZLIB_H"]=" 1" +D["HAVE_BZLIB_H"]=" 1" +D["HAVE_READLINE_READLINE_H"]=" 1" +D["HAVE_READLINE_HISTORY_H"]=" 1" +D["SUPPORT_PCRE8"]=" /**/" +D["PCREGREP_BUFSIZE"]=" 20480" +D["NEWLINE"]=" 10" +D["LINK_SIZE"]=" 2" +D["POSIX_MALLOC_THRESHOLD"]=" 10" +D["MATCH_LIMIT"]=" 10000000" +D["MATCH_LIMIT_RECURSION"]=" MATCH_LIMIT" +D["MAX_NAME_SIZE"]=" 32" +D["MAX_NAME_COUNT"]=" 10000" + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+[_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ][_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]*([\t (]|$)/ { + line = $ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + ac_datarootdir_hack=' + s&@datadir@&${datarootdir}&g + s&@docdir@&${datarootdir}/doc/${PACKAGE_TARNAME}&g + s&@infodir@&${datarootdir}/info&g + s&@localedir@&${datarootdir}/locale&g + s&@mandir@&${datarootdir}/man&g + s&\${datarootdir}&${prefix}/share&g' ;; +esac +ac_sed_extra="/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +} + +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010 Free Software Foundation, +# Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="CXX " + +# ### BEGIN LIBTOOL CONFIG + +# Assembler program. +AS=$lt_AS + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Object dumper program. +OBJDUMP=$lt_OBJDUMP + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot=$lt_sysroot + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + if test x"$xsi_shell" = xyes; then + sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ +func_dirname ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_basename ()$/,/^} # func_basename /c\ +func_basename ()\ +{\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ +func_dirname_and_basename ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ +func_stripname ()\ +{\ +\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ +\ # positional parameters, so assign one to ordinary parameter first.\ +\ func_stripname_result=${3}\ +\ func_stripname_result=${func_stripname_result#"${1}"}\ +\ func_stripname_result=${func_stripname_result%"${2}"}\ +} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ +func_split_long_opt ()\ +{\ +\ func_split_long_opt_name=${1%%=*}\ +\ func_split_long_opt_arg=${1#*=}\ +} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ +func_split_short_opt ()\ +{\ +\ func_split_short_opt_arg=${1#??}\ +\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ +} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ +func_lo2o ()\ +{\ +\ case ${1} in\ +\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ +\ *) func_lo2o_result=${1} ;;\ +\ esac\ +} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_xform ()$/,/^} # func_xform /c\ +func_xform ()\ +{\ + func_xform_result=${1%.*}.lo\ +} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_arith ()$/,/^} # func_arith /c\ +func_arith ()\ +{\ + func_arith_result=$(( $* ))\ +} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_len ()$/,/^} # func_len /c\ +func_len ()\ +{\ + func_len_result=${#1}\ +} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + +fi + +if test x"$lt_shell_append" = xyes; then + sed -e '/^func_append ()$/,/^} # func_append /c\ +func_append ()\ +{\ + eval "${1}+=\\${2}"\ +} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ +func_append_quoted ()\ +{\ +\ func_quote_for_eval "${2}"\ +\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ +} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 +$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} +fi + + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + "script-chmod":C) chmod a+x pcre-config ;; + "delete-old-chartables":C) rm -f pcre_chartables.c ;; + + esac +done # for ac_tag + + +as_fn_exit 0 |
