summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralanw <alanwright.atex@googlemail.com>2009-06-04 11:26:15 +0100
committeralanw <alanwright.atex@googlemail.com>2009-06-04 11:26:15 +0100
commitad297c79c7e6fadd96729fa682834ce876295220 (patch)
tree5cd31162a41d8d8200ca9bfad3c0719265ad363a
parent7d9befe0e06a373a3212496993fa54f52948b4f1 (diff)
downloadmongo-ad297c79c7e6fadd96729fa682834ce876295220.tar.gz
Added support for running mongod as a Windows NT Service
-rw-r--r--db/db.cpp48
-rw-r--r--db/instance.cpp13
-rw-r--r--msvc/server_only/server_only.vcproj8
-rw-r--r--util/ntservice.cpp177
-rw-r--r--util/ntservice.h48
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