diff options
author | alanw <alanwright.atex@googlemail.com> | 2009-06-04 11:26:15 +0100 |
---|---|---|
committer | alanw <alanwright.atex@googlemail.com> | 2009-06-04 11:26:15 +0100 |
commit | ad297c79c7e6fadd96729fa682834ce876295220 (patch) | |
tree | 5cd31162a41d8d8200ca9bfad3c0719265ad363a | |
parent | 7d9befe0e06a373a3212496993fa54f52948b4f1 (diff) | |
download | mongo-ad297c79c7e6fadd96729fa682834ce876295220.tar.gz |
Added support for running mongod as a Windows NT Service
-rw-r--r-- | db/db.cpp | 48 | ||||
-rw-r--r-- | db/instance.cpp | 13 | ||||
-rw-r--r-- | msvc/server_only/server_only.vcproj | 8 | ||||
-rw-r--r-- | util/ntservice.cpp | 177 | ||||
-rw-r--r-- | util/ntservice.h | 48 |
5 files changed, 287 insertions, 7 deletions
diff --git a/db/db.cpp b/db/db.cpp index dae6aab4722..5d957731a02 100644 --- a/db/db.cpp +++ b/db/db.cpp @@ -31,6 +31,10 @@ #include <sys/file.h> #endif +#if defined(_WIN32) +#include "../util/ntservice.h" +#endif + #include "../scripting/engine.h" namespace mongo { @@ -47,6 +51,7 @@ namespace mongo { extern int port; extern string bind_ip; + extern char *appsrvPath; extern int curOp; extern bool autoresync; extern string dashDashSource; @@ -387,12 +392,20 @@ namespace mongo { } } + #if defined(_WIN32) + bool initService() { + ServiceController::reportStatus( SERVICE_RUNNING ); + initAndListen( port, appsrvPath ); + return true; + } + #endif + } // namespace mongo using namespace mongo; -int q; +bool mongo::initService(); int main(int argc, char* argv[], char *envp[] ) { @@ -446,7 +459,9 @@ int main(int argc, char* argv[], char *envp[] ) * slightly different mode where "run" is assumed and we can set values */ - char *appsrvPath = null; + bool installService = false; + bool removeService = false; + bool startService = false; for (int i = 1; i < argc; i++) { @@ -508,7 +523,13 @@ int main(int argc, char* argv[], char *envp[] ) useHints = false; else if ( s == "--nohttpinterface" ) noHttpInterface = true; - else if ( s == "--cacheSize" ) { + else if ( s == "--install" ) + installService = true; + else if ( s == "--remove" ) + removeService = true; + else if ( s == "--service" ) + startService = true; + else if ( s == "--cacheSize" ) { long x = strtol( argv[ ++i ], 0, 10 ); uassert("bad --cacheSize arg", x > 0); setRecCacheSize(x); @@ -541,7 +562,22 @@ int main(int argc, char* argv[], char *envp[] ) } } - initAndListen(port, appsrvPath); + #if defined(_WIN32) + if ( installService ) { + if ( !ServiceController::installService( L"MongoDB", L"Mongo DB", L"Mongo DB Server", argc, argv ) ) + dbexit( 1 ); + } + else if ( removeService ) { + if ( !ServiceController::removeService( L"MongoDB" ) ) + dbexit( 1 ); + } + else if ( startService ) { + if ( !ServiceController::startService( L"MongoDB", mongo::initService ) ) + dbexit( 1 ); + } + else + #endif + initAndListen( port, appsrvPath ); dbexit(0); } @@ -571,6 +607,10 @@ usage: out() << " --oplog<n> 0=off 1=W 2=R 3=both 7=W+some reads" << endl; out() << " --sysinfo print some diagnostic system information\n"; out() << " --deDupMem <size_Bytes> custom memory limit for query de-duping\n"; + #if defined(_WIN32) + out() << " --install install mongo db service\n"; + out() << " --remove remove mongo db service\n"; + #endif out() << "\nReplication:" << endl; out() << " --master\n"; out() << " --slave" << endl; diff --git a/db/instance.cpp b/db/instance.cpp index 96bf5d8b7b4..90264303846 100644 --- a/db/instance.cpp +++ b/db/instance.cpp @@ -58,6 +58,7 @@ namespace mongo { 7 = log a few reads, and all writes. */ int opLogging = 0; + char *appsrvPath = null; int getOpLogging() { return opLogging; @@ -640,6 +641,7 @@ namespace mongo { boost::mutex &exitMutex( *( new boost::mutex ) ); bool firstExit = true; + void shutdown(); /* not using log() herein in case we are already locked */ void dbexit(int rc, const char *why) { @@ -658,6 +660,14 @@ namespace mongo { ss << "dbexit: " << why << endl; rawOut( ss.str() ); + shutdown(); // gracefully shutdown instance + + rawOut( "dbexit: really exiting now\n" ); + ::exit(rc); + } + + void shutdown() { + #ifndef _WIN32 { // close listener sockets @@ -691,9 +701,6 @@ namespace mongo { #if !defined(_WIN32) && !defined(__sunos__) flock( lockFile, LOCK_UN ); #endif - - rawOut( "dbexit: really exiting now\n" ); - ::exit(rc); } void acquirePathLock() { diff --git a/msvc/server_only/server_only.vcproj b/msvc/server_only/server_only.vcproj index 3f45dee5e6c..cbafeff1b0f 100644 --- a/msvc/server_only/server_only.vcproj +++ b/msvc/server_only/server_only.vcproj @@ -335,6 +335,14 @@ RelativePath="..\..\util\miniwebserver.h"
>
</File>
+ <File
+ RelativePath="..\..\util\ntservice.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\util\ntservice.h"
+ >
+ </File>
</Filter>
<Filter
Name="s"
diff --git a/util/ntservice.cpp b/util/ntservice.cpp new file mode 100644 index 00000000000..f4338fbb621 --- /dev/null +++ b/util/ntservice.cpp @@ -0,0 +1,177 @@ +// ntservice.cpp + +/** +* Copyright (C) 2008 10gen 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 <http://www.gnu.org/licenses/>. +*/ + + +#include "stdafx.h" +#include "ntservice.h" + +#if defined(_WIN32) + +namespace mongo { + + void shutdown(); + + SERVICE_STATUS_HANDLE ServiceController::_statusHandle = null; + std::wstring ServiceController::_serviceName; + ServiceCallback ServiceController::_serviceCallback = null; + + ServiceController::ServiceController() { + } + + bool ServiceController::installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, int argc, char* argv[] ) { + + std::string commandLine; + + for ( int i = 0; i < argc; i++ ) { + std::string arg( argv[ i ] ); + + // replace install command to indicate process is being started as a service + if ( arg == "--install" ) + arg = "--service"; + + commandLine += arg + " "; + } + + SC_HANDLE schSCManager = ::OpenSCManager( null, null, SC_MANAGER_ALL_ACCESS ); + if ( schSCManager == null ) + return false; + + std::basic_ostringstream< TCHAR > commandLineWide; + commandLineWide << commandLine.c_str(); + + // create new service + SC_HANDLE schService = ::CreateService( schSCManager, serviceName.c_str(), displayName.c_str(), + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, + commandLineWide.str().c_str(), null, null, L"\0\0", null, null ); + + if ( schService == null ) { + ::CloseServiceHandle( schSCManager ); + return false; + } + + SERVICE_DESCRIPTION serviceDescription; + serviceDescription.lpDescription = (LPTSTR)serviceDesc.c_str(); + + // set new service description + bool serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_DESCRIPTION, &serviceDescription ); + + if ( serviceInstalled ) { + SC_ACTION aActions[ 3 ] = { { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 } }; + + SERVICE_FAILURE_ACTIONS serviceFailure; + ZeroMemory( &serviceFailure, sizeof( SERVICE_FAILURE_ACTIONS ) ); + serviceFailure.cActions = 3; + serviceFailure.lpsaActions = aActions; + + // set service recovery options + serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_FAILURE_ACTIONS, &serviceFailure ); + } + + ::CloseServiceHandle( schService ); + ::CloseServiceHandle( schSCManager ); + + return serviceInstalled; + } + + bool ServiceController::removeService( const std::wstring& serviceName ) { + SC_HANDLE schSCManager = ::OpenSCManager( null, null, SC_MANAGER_ALL_ACCESS ); + if ( schSCManager == null ) + return false; + + SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS ); + + if ( schService == null ) { + ::CloseServiceHandle( schSCManager ); + return false; + } + + SERVICE_STATUS serviceStatus; + + // stop service if running + if ( ::ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus ) ) { + while ( ::QueryServiceStatus( schService, &serviceStatus ) ) { + if ( serviceStatus.dwCurrentState == SERVICE_STOP_PENDING ) + Sleep( 1000 ); + } + } + + bool serviceRemoved = ::DeleteService( schService ); + + ::CloseServiceHandle( schService ); + ::CloseServiceHandle( schSCManager ); + + return serviceRemoved; + } + + bool ServiceController::startService( const std::wstring& serviceName, ServiceCallback startService ) { + _serviceName = serviceName; + _serviceCallback = startService; + + SERVICE_TABLE_ENTRY dispTable[] = { + { (LPTSTR)serviceName.c_str(), (LPSERVICE_MAIN_FUNCTION)ServiceController::initService }, + { null, null } + }; + + return StartServiceCtrlDispatcher( dispTable ); + } + + bool ServiceController::reportStatus( DWORD reportState, DWORD waitHint ) { + if ( _statusHandle == null ) + return false; + + static DWORD checkPoint = 1; + + SERVICE_STATUS ssStatus; + + ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ssStatus.dwServiceSpecificExitCode = 0; + ssStatus.dwControlsAccepted = reportState == SERVICE_START_PENDING ? 0 : SERVICE_ACCEPT_STOP; + ssStatus.dwCurrentState = reportState; + ssStatus.dwWin32ExitCode = NO_ERROR; + ssStatus.dwWaitHint = waitHint; + ssStatus.dwCheckPoint = ( reportState == SERVICE_RUNNING || reportState == SERVICE_STOPPED ) ? 0 : checkPoint++; + + return SetServiceStatus( _statusHandle, &ssStatus ); + } + + void WINAPI ServiceController::initService( DWORD argc, LPTSTR *argv ) { + _statusHandle = RegisterServiceCtrlHandler( _serviceName.c_str(), serviceCtrl ); + if ( !_statusHandle ) + return; + + reportStatus( SERVICE_START_PENDING, 1000 ); + + _serviceCallback(); + + reportStatus( SERVICE_STOPPED ); + } + + void WINAPI ServiceController::serviceCtrl( DWORD ctrlCode ) { + switch ( ctrlCode ) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + shutdown(); + reportStatus( SERVICE_STOPPED ); + return; + } + } + +} // namespace mongo + +#endif diff --git a/util/ntservice.h b/util/ntservice.h new file mode 100644 index 00000000000..b9670aa69f7 --- /dev/null +++ b/util/ntservice.h @@ -0,0 +1,48 @@ +// ntservice.h + +/** +* Copyright (C) 2008 10gen 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 <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#if defined(_WIN32) + +namespace mongo { + + typedef bool ( *ServiceCallback )( void ); + + class ServiceController { + public: + ServiceController(); + virtual ~ServiceController() {} + + static bool installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, 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 ); + + static void WINAPI initService( DWORD argc, LPTSTR *argv ); + static void WINAPI serviceCtrl( DWORD ctrlCode ); + + protected: + static std::wstring _serviceName; + static SERVICE_STATUS_HANDLE _statusHandle; + static ServiceCallback _serviceCallback; + }; + +} // namespace mongo + +#endif |