summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Schwerin <schwerin@10gen.com>2012-10-16 12:24:39 -0400
committerAndy Schwerin <schwerin@10gen.com>2012-10-23 10:38:44 -0400
commitf5c70af4dca92270a00786963bccbf3fc359f5e3 (patch)
tree3b927b90125cbf71594e0021cbd623856eb5e90c
parentbf452ce511554505ee36648524be968a73735dde (diff)
downloadmongo-f5c70af4dca92270a00786963bccbf3fc359f5e3.tar.gz
Refactor the ntservice module, to separate parameter processing from service startup.
SERVER-7332
-rw-r--r--src/mongo/db/db.cpp29
-rw-r--r--src/mongo/s/server.cpp27
-rw-r--r--src/mongo/util/ntservice.cpp180
-rw-r--r--src/mongo/util/ntservice.h85
4 files changed, 169 insertions, 152 deletions
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 47f6128ac4d..fbab1d4d818 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -52,6 +52,7 @@
#include "mongo/util/concurrency/task.h"
#include "mongo/util/file_allocator.h"
#include "mongo/util/net/message_server.h"
+#include "mongo/util/ntservice.h"
#include "mongo/util/ramlog.h"
#include "mongo/util/stacktrace.h"
#include "mongo/util/startup_test.h"
@@ -59,7 +60,6 @@
#include "mongo/util/version.h"
#if defined(_WIN32)
-# include "mongo/util/ntservice.h"
# include <DbgHelp.h>
#else
# include <sys/file.h>
@@ -85,7 +85,7 @@ namespace mongo {
void exitCleanly( ExitCode code );
#ifdef _WIN32
- ntServiceDefaultStrings defaultServiceStrings = {
+ ntservice::NtServiceDefaultStrings defaultServiceStrings = {
L"MongoDB",
L"Mongo DB",
L"Mongo DB Server"
@@ -594,11 +594,10 @@ namespace mongo {
}
#if defined(_WIN32)
- bool initService() {
- ServiceController::reportStatus( SERVICE_RUNNING );
+ void initService() {
+ ntservice::reportStatus( SERVICE_RUNNING );
log() << "Service running" << endl;
initAndListen( cmdLine.port );
- return true;
}
#endif
@@ -1078,6 +1077,14 @@ static int mongoDbMain(int argc, char* argv[], char **envp) {
if( cmdLine.pretouch )
log() << "--pretouch " << cmdLine.pretouch << endl;
+#ifdef _WIN32
+ ntservice::configureService(initService,
+ params,
+ defaultServiceStrings,
+ std::vector<std::string>(),
+ std::vector<std::string>(argv, argv + argc));
+#endif // _WIN32
+
#ifdef __linux__
if (params.count("shutdown")){
bool failed = false;
@@ -1133,14 +1140,10 @@ static int mongoDbMain(int argc, char* argv[], char **envp) {
dataFileSync.go();
#if defined(_WIN32)
- vector<string> disallowedOptions;
- if (serviceParamsCheck( params, dbpath, defaultServiceStrings, disallowedOptions, argc, argv )) {
- return 0; // this means that we are running as a service, and we won't
- // reach this statement until initService() has run and returned,
- // but it usually exits directly so we never actually get here
- }
- // if we reach here, then we are not running as a service. service installation
- // exits directly and so never reaches here either.
+ if (ntservice::shouldStartService()) {
+ ntservice::startService();
+ // exits directly and so never reaches here either.
+ }
#endif
if (sizeof(void*) == 4 && !journalExplicit){
diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp
index 4df083ba87b..0a351bd34ee 100644
--- a/src/mongo/s/server.cpp
+++ b/src/mongo/s/server.cpp
@@ -56,11 +56,12 @@
namespace mongo {
#if defined(_WIN32)
- ntServiceDefaultStrings defaultServiceStrings = {
+ ntservice::NtServiceDefaultStrings defaultServiceStrings = {
L"MongoS",
L"Mongo DB Router",
L"Mongo DB Sharding Router"
};
+ static void initService();
#endif
CmdLine cmdLine;
@@ -459,13 +460,16 @@ int _main(int argc, char* argv[]) {
#if defined(_WIN32)
vector<string> disallowedOptions;
disallowedOptions.push_back( "upgrade" );
- if ( serviceParamsCheck( params, "", defaultServiceStrings, disallowedOptions, argc, argv ) ) {
- return 0; // this means that we are running as a service, and we won't
- // reach this statement until initService() has run and returned,
- // but it usually exits directly so we never actually get here
+ ntservice::configureService(initService,
+ params,
+ defaultServiceStrings,
+ disallowedOptions,
+ std::vector<std::string>(argv, argv + argc));
+ if (ntservice::shouldStartService()) {
+ ntservice::startService();
+ // if we reach here, then we are not running as a service. service installation
+ // exits directly and so never reaches here either.
}
- // if we reach here, then we are not running as a service. service installation
- // exits directly and so never reaches here either.
#endif
runMongosServer( params.count( "upgrade" ) > 0 );
@@ -474,15 +478,12 @@ int _main(int argc, char* argv[]) {
#if defined(_WIN32)
namespace mongo {
-
- bool initService() {
- ServiceController::reportStatus( SERVICE_RUNNING );
+ static void initService() {
+ ntservice::reportStatus( SERVICE_RUNNING );
log() << "Service running" << endl;
runMongosServer( false );
- return true;
}
-
-} // namespace mongo
+} // namespace mongo
#endif
int main(int argc, char* argv[], char** envp) {
diff --git a/src/mongo/util/ntservice.cpp b/src/mongo/util/ntservice.cpp
index 1eed0b7459b..9362b6a7f2a 100644
--- a/src/mongo/util/ntservice.cpp
+++ b/src/mongo/util/ntservice.cpp
@@ -15,51 +15,68 @@
* limitations under the License.
*/
-#include "pch.h"
-#include "ntservice.h"
-#include "../db/client.h"
-#include "../db/instance.h"
-#include "winutil.h"
-#include "text.h"
-
#if defined(_WIN32)
+#include "mongo/pch.h"
+
+#include "mongo/util/ntservice.h"
+
+#include "mongo/db/client.h"
+#include "mongo/db/instance.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/text.h"
+#include "mongo/util/winutil.h"
+
using std::wstring;
namespace mongo {
- SERVICE_STATUS_HANDLE ServiceController::_statusHandle = NULL;
- wstring ServiceController::_serviceName;
- ServiceCallback ServiceController::_serviceCallback = NULL;
+namespace ntservice {
+namespace {
+ bool _startService = false;
+ SERVICE_STATUS_HANDLE _statusHandle = NULL;
+ wstring _serviceName;
+ ServiceCallback _serviceCallback = NULL;
+} // namespace
+
+ static void installServiceOrDie(
+ const wstring& serviceName,
+ const wstring& displayName,
+ const wstring& serviceDesc,
+ const wstring& serviceUser,
+ const wstring& servicePassword,
+ const std::vector<std::string>& argv);
+
+ static void removeServiceOrDie(const wstring& serviceName);
- ServiceController::ServiceController() {}
+ bool shouldStartService() {
+ return _startService;
+ }
- // defined in db/db.cpp for mongod.exe and in s/server.cpp for mongos.exe
- extern bool initService();
+ static void WINAPI serviceCtrl(DWORD ctrlCode);
- // returns true if the service is started.
- bool serviceParamsCheck(
- boost::program_options::variables_map& params,
- const std::string& dbpath,
- const ntServiceDefaultStrings& defaultStrings,
- const vector<string>& disallowedOptions,
- int argc,
- char* argv[]
+ void configureService(
+ ServiceCallback serviceCallback,
+ const boost::program_options::variables_map& params,
+ const NtServiceDefaultStrings& defaultStrings,
+ const std::vector<std::string>& disallowedOptions,
+ const std::vector<std::string>& argv
) {
bool installService = false;
bool removeService = false;
bool reinstallService = false;
- bool startService = false;
+
+ _serviceCallback = serviceCallback;
int badOption = -1;
- for ( int i = 0, disallowedListLength = disallowedOptions.size(); i < disallowedListLength; ++i ) {
- if ( params.count( disallowedOptions[i] ) > 0 ) {
+ for (size_t i = 0; i < disallowedOptions.size(); ++i) {
+ if (params.count(disallowedOptions[i]) > 0) {
badOption = i;
break;
}
}
- wstring windowsServiceName( defaultStrings.serviceName );
+ _serviceName = defaultStrings.serviceName;
wstring windowsServiceDisplayName( defaultStrings.displayName );
wstring windowsServiceDescription( defaultStrings.serviceDescription );
wstring windowsServiceUser;
@@ -99,7 +116,7 @@ namespace mongo {
log() << "--service cannot be used with --" << disallowedOptions[badOption] << endl;
::_exit( EXIT_BADOPTIONS );
}
- startService = true;
+ _startService = true;
}
if (params.count("serviceName")) {
@@ -107,7 +124,7 @@ namespace mongo {
log() << "--serviceName cannot be used with --" << disallowedOptions[badOption] << endl;
::_exit( EXIT_BADOPTIONS );
}
- windowsServiceName = toWideString( params[ "serviceName" ].as<string>().c_str() );
+ _serviceName = toWideString( params[ "serviceName" ].as<string>().c_str() );
}
if (params.count("serviceDisplayName")) {
if ( badOption != -1 ) {
@@ -138,48 +155,32 @@ namespace mongo {
windowsServicePassword = toWideString( params[ "servicePassword" ].as<string>().c_str() );
}
- if ( reinstallService ) {
- ServiceController::removeService( windowsServiceName );
- }
if ( installService || reinstallService ) {
- if ( !ServiceController::installService(
- windowsServiceName,
- windowsServiceDisplayName,
- windowsServiceDescription,
- windowsServiceUser,
- windowsServicePassword,
- dbpath,
- argc,
- argv )
- ) {
- ::_exit( EXIT_NTSERVICE_ERROR );
+ if ( reinstallService ) {
+ removeServiceOrDie(_serviceName);
}
- ::_exit( EXIT_CLEAN );
+ installServiceOrDie(
+ _serviceName,
+ windowsServiceDisplayName,
+ windowsServiceDescription,
+ windowsServiceUser,
+ windowsServicePassword,
+ argv);
+ ::_exit(EXIT_CLEAN);
}
else if ( removeService ) {
- if ( !ServiceController::removeService( windowsServiceName ) ) {
- ::_exit( EXIT_NTSERVICE_ERROR );
- }
+ removeServiceOrDie(_serviceName);
::_exit( EXIT_CLEAN );
}
- else if ( startService ) {
- if ( !ServiceController::startService( windowsServiceName , mongo::initService ) ) {
- ::_exit( EXIT_NTSERVICE_ERROR );
- }
- return true;
- }
- return false;
}
- bool ServiceController::installService(
+ void installServiceOrDie(
const wstring& serviceName,
const wstring& displayName,
const wstring& serviceDesc,
const wstring& serviceUser,
const wstring& servicePassword,
- const std::string& dbpath,
- int argc,
- char* argv[]
+ const std::vector<std::string>& argv
) {
log() << "Trying to install Windows service '" << toUtf8String(serviceName) << "'" << endl;
@@ -194,14 +195,15 @@ namespace mongo {
// likewise for all options. this means that when parsing option-by-option as
// we do here, we need to handle both "-" and "--" prefixes.
- for ( int i = 1; i < argc; i++ ) {
- std::string arg( argv[ i ] );
+ const size_t argc = argv.size();
+ for ( size_t i = 1; i < argc; i++ ) {
+ std::string arg(argv[i]);
// replace install command to indicate process is being started as a service
if ( arg == "-install" || arg == "--install" || arg == "-reinstall" || arg == "--reinstall" ) {
arg = "--service";
}
else if ( (arg == "-dbpath" || arg == "--dbpath") && i + 1 < argc ) {
- commandLine << arg << " \"" << dbpath << "\" ";
+ commandLine << arg << " \"" << argv[i+1] << "\" ";
i++;
continue;
}
@@ -252,7 +254,7 @@ namespace mongo {
if ( schSCManager == NULL ) {
DWORD err = ::GetLastError();
log() << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
- return false;
+ ::_exit(EXIT_NTSERVICE_ERROR);
}
// Make sure service doesn't already exist.
@@ -262,7 +264,7 @@ namespace mongo {
log() << "There is already a service named '" << toUtf8String(serviceName) << "', aborting" << endl;
::CloseServiceHandle( schService );
::CloseServiceHandle( schSCManager );
- return false;
+ ::_exit(EXIT_NTSERVICE_ERROR);
}
std::basic_ostringstream< TCHAR > commandLineWide;
commandLineWide << commandLine.str().c_str();
@@ -286,7 +288,7 @@ namespace mongo {
DWORD err = ::GetLastError();
log() << "Error creating service: " << GetWinErrMsg(err) << endl;
::CloseServiceHandle( schSCManager );
- return false;
+ ::_exit( EXIT_NTSERVICE_ERROR );
}
log() << "Service '" << toUtf8String(serviceName) << "' (" << toUtf8String(displayName) <<
@@ -363,24 +365,25 @@ namespace mongo {
::CloseServiceHandle( schService );
::CloseServiceHandle( schSCManager );
- return serviceInstalled;
+ if (!serviceInstalled)
+ ::_exit( EXIT_NTSERVICE_ERROR );
}
- bool ServiceController::removeService( const wstring& serviceName ) {
+ void removeServiceOrDie(const wstring& serviceName) {
log() << "Trying to remove Windows service '" << toUtf8String(serviceName) << "'" << endl;
SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if ( schSCManager == NULL ) {
DWORD err = ::GetLastError();
log() << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
- return false;
+ ::_exit(EXIT_NTSERVICE_ERROR);
}
SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
if ( schService == NULL ) {
log() << "Could not find a service named '" << toUtf8String(serviceName) << "' to remove" << endl;
::CloseServiceHandle( schSCManager );
- return false;
+ ::_exit(EXIT_NTSERVICE_ERROR);
}
SERVICE_STATUS serviceStatus;
@@ -409,23 +412,11 @@ namespace mongo {
log() << "Failed to remove service '" << toUtf8String(serviceName) << "'" << endl;
}
- return serviceRemoved;
- }
-
- bool ServiceController::startService( const wstring& serviceName, ServiceCallback startService ) {
- _serviceName = serviceName;
- _serviceCallback = startService;
-
- SERVICE_TABLE_ENTRY dispTable[] = {
- { (LPTSTR)serviceName.c_str(), (LPSERVICE_MAIN_FUNCTION)ServiceController::initService },
- { NULL, NULL }
- };
-
- log() << "Trying to start Windows service '" << toUtf8String(serviceName) << "'" << endl;
- return StartServiceCtrlDispatcher( dispTable );
+ if (!serviceRemoved)
+ ::_exit(EXIT_NTSERVICE_ERROR);
}
- bool ServiceController::reportStatus( DWORD reportState, DWORD waitHint ) {
+ bool reportStatus(DWORD reportState, DWORD waitHint) {
if ( _statusHandle == NULL )
return false;
@@ -456,7 +447,7 @@ namespace mongo {
return SetServiceStatus( _statusHandle, &ssStatus );
}
- void WINAPI ServiceController::initService( DWORD argc, LPTSTR *argv ) {
+ static void WINAPI initService( DWORD argc, LPTSTR *argv ) {
_statusHandle = RegisterServiceCtrlHandler( _serviceName.c_str(), serviceCtrl );
if ( !_statusHandle )
return;
@@ -472,16 +463,16 @@ namespace mongo {
Client::initThread( "serviceShutdown" );
log() << "got " << controlCodeName << " request from Windows Service Control Manager, " <<
( inShutdown() ? "already in shutdown" : "will terminate after current cmd ends" ) << endl;
- ServiceController::reportStatus( SERVICE_STOP_PENDING );
+ reportStatus( SERVICE_STOP_PENDING );
if ( ! inShutdown() ) {
// TODO: SERVER-5703, separate the "cleanup for shutdown" functionality from
// the "terminate process" functionality in exitCleanly.
exitCleanly( EXIT_WINDOWS_SERVICE_STOP );
- ServiceController::reportStatus( SERVICE_STOPPED );
+ reportStatus( SERVICE_STOPPED );
}
}
- void WINAPI ServiceController::serviceCtrl( DWORD ctrlCode ) {
+ static void WINAPI serviceCtrl( DWORD ctrlCode ) {
switch ( ctrlCode ) {
case SERVICE_CONTROL_STOP:
serviceShutdown( "SERVICE_CONTROL_STOP" );
@@ -492,6 +483,25 @@ namespace mongo {
}
}
+ void startService() {
+
+ fassert(0, _startService);
+
+ SERVICE_TABLE_ENTRYW dispTable[] = {
+ { const_cast<LPWSTR>(_serviceName.c_str()), (LPSERVICE_MAIN_FUNCTION)initService },
+ { NULL, NULL }
+ };
+
+ log() << "Trying to start Windows service '" << toUtf8String(_serviceName) << "'" << endl;
+ if (StartServiceCtrlDispatcherW(dispTable)) {
+ ::_exit(EXIT_CLEAN);
+ }
+ else {
+ ::exit(EXIT_NTSERVICE_ERROR);
+ }
+ }
+
+} // namspace ntservice
} // namespace mongo
#endif
diff --git a/src/mongo/util/ntservice.h b/src/mongo/util/ntservice.h
index 8ce43feeed3..5a0200ac8ed 100644
--- a/src/mongo/util/ntservice.h
+++ b/src/mongo/util/ntservice.h
@@ -1,5 +1,3 @@
-// ntservice.h
-
/* Copyright 2009 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,58 +13,63 @@
* limitations under the License.
*/
+/**
+ * The ntservice namespace provides minimal support for running mongo servers as NT services.
+ *
+ * TODO: ntservice should only provide implementation for a more general server process
+ * startup/shutdown/management interface.
+ */
+
#pragma once
-#if defined(_WIN32)
-#include <windows.h>
-#include "boost/program_options.hpp"
+#ifdef _WIN32
+
+#include <boost/program_options.hpp>
+#include <string>
+#include <vector>
+
+#include "mongo/platform/compiler.h"
namespace mongo {
- struct ntServiceDefaultStrings {
+namespace ntservice {
+ struct NtServiceDefaultStrings {
const wchar_t* serviceName;
const wchar_t* displayName;
const wchar_t* serviceDescription;
};
- typedef bool ( *ServiceCallback )( void );
- bool serviceParamsCheck(
- boost::program_options::variables_map& params,
- const std::string& dbpath,
- const ntServiceDefaultStrings& defaultStrings,
- const vector<string>& disallowedOptions,
- int argc,
- char* argv[]
- );
+ typedef void (*ServiceCallback)(void);
- class ServiceController {
- public:
- ServiceController();
- virtual ~ServiceController() {}
+ /**
+ * Configure the service.
+ *
+ * Also performs service installation and removal.
+ *
+ * This function calls _exit() with an error if bad parameters are passed in. If
+ * the parameters specify that the service should be installed, removed, etc, performs that
+ * operation and exits.
+ *
+ * If this function returns to the caller, the caller should either call startService, or run
+ * the service as a regular process, depending on the return value of shouldStartService().
+ */
+ void configureService(
+ ServiceCallback serviceCallback,
+ const boost::program_options::variables_map& params,
+ const NtServiceDefaultStrings& defaultStrings,
+ const std::vector<std::string>& disallowedOptions,
+ const std::vector<std::string>& argv);
- static bool installService(
- const std::wstring& serviceName,
- const std::wstring& displayName,
- const std::wstring& serviceDesc,
- const std::wstring& serviceUser,
- const std::wstring& servicePassword,
- const std::string& dbpath,
- int argc,
- char* argv[]
- );
- static bool removeService( const std::wstring& serviceName );
- static bool startService( const std::wstring& serviceName, ServiceCallback startService );
- static bool reportStatus( DWORD reportState, DWORD waitHint = 0 );
+ bool shouldStartService();
- static void WINAPI initService( DWORD argc, LPTSTR *argv );
- static void WINAPI serviceCtrl( DWORD ctrlCode );
+ /**
+ * Start the service. Never returns.
+ */
+ MONGO_COMPILER_NORETURN void startService();
- protected:
- static std::wstring _serviceName;
- static SERVICE_STATUS_HANDLE _statusHandle;
- static ServiceCallback _serviceCallback;
- };
+ bool reportStatus(DWORD reportState, DWORD waitHint = 0);
-} // namespace mongo
+} // namespace ntservice
+} // namespace mongo
-#endif
+#endif // defined(_WIN32)