/** * Copyright (C) 2018 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage #include "mongo/platform/basic.h" #include "mongo/client/embedded/embedded.h" #include "mongo/base/checked_cast.h" #include "mongo/base/initializer.h" #include "mongo/client/embedded/replication_coordinator_embedded.h" #include "mongo/client/embedded/service_context_embedded.h" #include "mongo/client/embedded/service_entry_point_embedded.h" #include "mongo/config.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog/health_log.h" #include "mongo/db/catalog/uuid_catalog.h" #include "mongo/db/client.h" #include "mongo/db/commands/feature_compatibility_version.h" #include "mongo/db/commands/fsync_locked.h" #include "mongo/db/concurrency/lock_state.h" #include "mongo/db/dbdirectclient.h" #include "mongo/db/global_settings.h" #include "mongo/db/index_rebuilder.h" #include "mongo/db/kill_sessions_local.h" #include "mongo/db/op_observer_impl.h" #include "mongo/db/op_observer_registry.h" #include "mongo/db/repair_database_and_check_version.h" #include "mongo/db/repl/storage_interface_impl.h" #include "mongo/db/service_context_registrar.h" #include "mongo/db/session_catalog.h" #include "mongo/db/session_killer.h" #include "mongo/db/storage/encryption_hooks.h" #include "mongo/db/ttl.h" #include "mongo/logger/log_component.h" #include "mongo/scripting/dbdirectclient_factory.h" #include "mongo/util/background.h" #include "mongo/util/exit.h" #include "mongo/util/log.h" #include "mongo/util/periodic_runner_factory.h" #include "mongo/util/quick_exit.h" #include "mongo/util/time_support.h" #include namespace mongo { namespace embedded { namespace { void initWireSpec() { WireSpec& spec = WireSpec::instance(); // The featureCompatibilityVersion behavior defaults to the downgrade behavior while the // in-memory version is unset. spec.incomingInternalClient.minWireVersion = RELEASE_2_4_AND_BEFORE; spec.incomingInternalClient.maxWireVersion = LATEST_WIRE_VERSION; spec.outgoing.minWireVersion = RELEASE_2_4_AND_BEFORE; spec.outgoing.maxWireVersion = LATEST_WIRE_VERSION; spec.isInternalClient = true; } // Noop, to fulfull dependencies for other initializers MONGO_INITIALIZER_GENERAL(ForkServer, ("EndStartupOptionHandling"), ("default")) (InitializerContext* context) { return Status::OK(); } // Create a minimalistic replication coordinator to provide a limited interface for users. Not // functional to provide any replication logic. GlobalInitializerRegisterer replicationManagerInitializer( "CreateReplicationManager", {"SSLManager", "default"}, [](InitializerContext* context) { auto serviceContext = context->serviceContext(); repl::StorageInterface::set(serviceContext, std::make_unique()); auto replCoord = std::make_unique(serviceContext); repl::ReplicationCoordinator::set(serviceContext, std::move(replCoord)); repl::setOplogCollectionName(serviceContext); return Status::OK(); }, [](DeinitializerContext* context) { auto serviceContext = context->serviceContext(); repl::ReplicationCoordinator::set(serviceContext, nullptr); repl::StorageInterface::set(serviceContext, nullptr); return Status::OK(); }); MONGO_INITIALIZER(fsyncLockedForWriting)(InitializerContext* context) { setLockedForWritingImpl([]() { return false; }); return Status::OK(); } } // namespace using logger::LogComponent; using std::endl; void shutdown(ServiceContext* srvContext) { Client::initThreadIfNotAlready(); auto const client = Client::getCurrent(); auto const serviceContext = client->getServiceContext(); invariant(srvContext == serviceContext); serviceContext->setKillAllOperations(); // We should always be able to acquire the global lock at shutdown. // Close all open databases, shutdown storage engine and run all deinitializers. auto shutdownOpCtx = serviceContext->makeOperationContext(client); { Lock::GlobalLock lk(shutdownOpCtx.get(), MODE_X, Date_t::max()); dbHolder().closeAll(shutdownOpCtx.get(), "shutdown"); // Shut down the background periodic task runner if (auto runner = serviceContext->getPeriodicRunner()) { runner->shutdown(); } // Global storage engine may not be started in all cases before we exit if (serviceContext->getGlobalStorageEngine()) { serviceContext->shutdownGlobalStorageEngineCleanly(); } Status status = mongo::runGlobalDeinitializers(serviceContext); uassertStatusOKWithContext(status, "Global deinitilization failed"); } shutdownOpCtx.reset(); if (Client::getCurrent()) Client::destroy(); setGlobalServiceContext(nullptr); log(LogComponent::kControl) << "now exiting"; } ServiceContext* initialize(int argc, char* argv[], char** envp) { srand(static_cast(curTimeMicros64())); setGlobalServiceContext(createServiceContext()); Status status = mongo::runGlobalInitializers(argc, argv, envp, getGlobalServiceContext()); uassertStatusOKWithContext(status, "Global initilization failed"); Client::initThread("initandlisten"); initWireSpec(); auto serviceContext = checked_cast(getGlobalServiceContext()); auto opObserverRegistry = std::make_unique(); opObserverRegistry->addObserver(std::make_unique()); opObserverRegistry->addObserver(std::make_unique()); serviceContext->setOpObserver(std::move(opObserverRegistry)); DBDirectClientFactory::get(serviceContext).registerImplementation([](OperationContext* opCtx) { return std::unique_ptr(new DBDirectClient(opCtx)); }); { ProcessId pid = ProcessId::getCurrent(); LogstreamBuilder l = log(LogComponent::kControl); l << "MongoDB starting : pid=" << pid << " port=" << serverGlobalParams.port << " dbpath=" << storageGlobalParams.dbpath; const bool is32bit = sizeof(int*) == 4; l << (is32bit ? " 32" : " 64") << "-bit" << endl; } DEV log(LogComponent::kControl) << "DEBUG build (which is slower)" << endl; serviceContext->createLockFile(); serviceContext->setServiceEntryPoint( std::make_unique(serviceContext)); serviceContext->initializeGlobalStorageEngine(); // Warn if we detect configurations for multiple registered storage engines in the same // configuration file/environment. if (serverGlobalParams.parsedOpts.hasField("storage")) { BSONElement storageElement = serverGlobalParams.parsedOpts.getField("storage"); invariant(storageElement.isABSONObj()); for (auto&& e : storageElement.Obj()) { // Ignore if field name under "storage" matches current storage engine. if (storageGlobalParams.engine == e.fieldName()) { continue; } // Warn if field name matches non-active registered storage engine. if (serviceContext->isRegisteredStorageEngine(e.fieldName())) { warning() << "Detected configuration for non-active storage engine " << e.fieldName() << " when current storage engine is " << storageGlobalParams.engine; } } } { std::stringstream ss; ss << endl; ss << "*********************************************************************" << endl; ss << " ERROR: dbpath (" << storageGlobalParams.dbpath << ") does not exist." << endl; ss << " Create this directory or give existing directory in --dbpath." << endl; ss << " See http://dochub.mongodb.org/core/startingandstoppingmongo" << endl; ss << "*********************************************************************" << endl; uassert(50677, ss.str().c_str(), boost::filesystem::exists(storageGlobalParams.dbpath)); } { std::stringstream ss; ss << "repairpath (" << storageGlobalParams.repairpath << ") does not exist"; uassert(50678, ss.str().c_str(), boost::filesystem::exists(storageGlobalParams.repairpath)); } if (!storageGlobalParams.readOnly) { boost::filesystem::remove_all(storageGlobalParams.dbpath + "/_tmp/"); } auto startupOpCtx = serviceContext->makeOperationContext(&cc()); bool canCallFCVSetIfCleanStartup = !storageGlobalParams.readOnly && !(storageGlobalParams.engine == "devnull"); if (canCallFCVSetIfCleanStartup) { Lock::GlobalWrite lk(startupOpCtx.get()); FeatureCompatibilityVersion::setIfCleanStartup(startupOpCtx.get(), repl::StorageInterface::get(serviceContext)); } auto swNonLocalDatabases = repairDatabasesAndCheckVersion(startupOpCtx.get()); if (!swNonLocalDatabases.isOK()) { // SERVER-31611 introduced a return value to `repairDatabasesAndCheckVersion`. Previously, // a failing condition would fassert. SERVER-31611 covers a case where the binary (3.6) is // refusing to start up because it refuses acknowledgement of FCV 3.2 and requires the // user to start up with an older binary. Thus shutting down the server must leave the // datafiles in a state that the older binary can start up. This requires going through a // clean shutdown. // // The invariant is *not* a statement that `repairDatabasesAndCheckVersion` must return // `MustDowngrade`. Instead, it is meant as a guardrail to protect future developers from // accidentally buying into this behavior. New errors that are returned from the method // may or may not want to go through a clean shutdown, and they likely won't want the // program to return an exit code of `EXIT_NEED_DOWNGRADE`. severe(LogComponent::kControl) << "** IMPORTANT: " << swNonLocalDatabases.getStatus().reason(); invariant(swNonLocalDatabases == ErrorCodes::MustDowngrade); quickExit(EXIT_NEED_DOWNGRADE); } // Assert that the in-memory featureCompatibilityVersion parameter has been explicitly set. If // we are part of a replica set and are started up with no data files, we do not set the // featureCompatibilityVersion until a primary is chosen. For this case, we expect the in-memory // featureCompatibilityVersion parameter to still be uninitialized until after startup. if (canCallFCVSetIfCleanStartup) { invariant(serverGlobalParams.featureCompatibility.isVersionInitialized()); } if (storageGlobalParams.upgrade) { log() << "finished checking dbs"; exitCleanly(EXIT_CLEAN); } // This is for security on certain platforms (nonce generation) srand((unsigned)(curTimeMicros64()) ^ (unsigned(uintptr_t(&startupOpCtx)))); if (!storageGlobalParams.readOnly) { restartInProgressIndexesFromLastShutdown(startupOpCtx.get()); } // MessageServer::run will return when exit code closes its socket and we don't need the // operation context anymore startupOpCtx.reset(); // Make sure current thread have no client set in thread_local Client::releaseCurrent(); serviceContext->notifyStartupComplete(); return serviceContext; } } // namespace embedded } // namespace mongo