mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
365 lines
14 KiB
C++
365 lines
14 KiB
C++
// ntservice.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 "ntservice.h"
|
|
#include "winutil.h"
|
|
#include "text.h"
|
|
#include <direct.h>
|
|
|
|
#if defined(_WIN32)
|
|
|
|
namespace mongo {
|
|
|
|
void shutdownServer();
|
|
|
|
SERVICE_STATUS_HANDLE ServiceController::_statusHandle = NULL;
|
|
std::wstring ServiceController::_serviceName;
|
|
ServiceCallback ServiceController::_serviceCallback = NULL;
|
|
|
|
ServiceController::ServiceController() {}
|
|
|
|
bool initService();
|
|
|
|
// returns true if the service is started.
|
|
bool serviceParamsCheck( program_options::variables_map& params, const std::string dbpath, int argc, char* argv[] ) {
|
|
bool installService = false;
|
|
bool removeService = false;
|
|
bool reinstallService = false;
|
|
bool startService = false;
|
|
|
|
std::wstring windowsServiceName = L"MongoDB";
|
|
std::wstring windowsServiceDisplayName = L"Mongo DB";
|
|
std::wstring windowsServiceDescription = L"Mongo DB Server";
|
|
std::wstring windowsServiceUser = L"";
|
|
std::wstring windowsServicePassword = L"";
|
|
|
|
if (params.count("install")) {
|
|
if ( ! params.count( "logpath" ) ) {
|
|
cerr << "--install has to be used with --logpath" << endl;
|
|
::exit(-1);
|
|
}
|
|
installService = true;
|
|
}
|
|
if (params.count("reinstall")) {
|
|
if ( ! params.count( "logpath" ) ) {
|
|
cerr << "--reinstall has to be used with --logpath" << endl;
|
|
::exit(-1);
|
|
}
|
|
reinstallService = true;
|
|
}
|
|
if (params.count("remove")) {
|
|
removeService = true;
|
|
}
|
|
if (params.count("service")) {
|
|
startService = true;
|
|
}
|
|
|
|
if (params.count("serviceName")) {
|
|
string x = params["serviceName"].as<string>();
|
|
windowsServiceName = wstring(x.size(),L' ');
|
|
for ( size_t i=0; i<x.size(); i++) {
|
|
windowsServiceName[i] = x[i];
|
|
}
|
|
}
|
|
if (params.count("serviceDisplayName")) {
|
|
string x = params["serviceDisplayName"].as<string>();
|
|
windowsServiceDisplayName = wstring(x.size(),L' ');
|
|
for ( size_t i=0; i<x.size(); i++) {
|
|
windowsServiceDisplayName[i] = x[i];
|
|
}
|
|
}
|
|
if (params.count("serviceDescription")) {
|
|
string x = params["serviceDescription"].as<string>();
|
|
windowsServiceDescription = wstring(x.size(),L' ');
|
|
for ( size_t i=0; i<x.size(); i++) {
|
|
windowsServiceDescription[i] = x[i];
|
|
}
|
|
}
|
|
if (params.count("serviceUser")) {
|
|
string x = params["serviceUser"].as<string>();
|
|
windowsServiceUser = wstring(x.size(),L' ');
|
|
for ( size_t i=0; i<x.size(); i++) {
|
|
windowsServiceUser[i] = x[i];
|
|
}
|
|
}
|
|
if (params.count("servicePassword")) {
|
|
string x = params["servicePassword"].as<string>();
|
|
windowsServicePassword = wstring(x.size(),L' ');
|
|
for ( size_t i=0; i<x.size(); i++) {
|
|
windowsServicePassword[i] = x[i];
|
|
}
|
|
}
|
|
|
|
if ( reinstallService ) {
|
|
ServiceController::removeService( windowsServiceName );
|
|
}
|
|
if ( installService || reinstallService ) {
|
|
if ( !ServiceController::installService( windowsServiceName , windowsServiceDisplayName, windowsServiceDescription, windowsServiceUser, windowsServicePassword, dbpath, argc, argv ) )
|
|
dbexit( EXIT_NTSERVICE_ERROR );
|
|
dbexit( EXIT_CLEAN );
|
|
}
|
|
else if ( removeService ) {
|
|
if ( !ServiceController::removeService( windowsServiceName ) )
|
|
dbexit( EXIT_NTSERVICE_ERROR );
|
|
dbexit( EXIT_CLEAN );
|
|
}
|
|
else if ( startService ) {
|
|
if ( !ServiceController::startService( windowsServiceName , mongo::initService ) )
|
|
dbexit( EXIT_NTSERVICE_ERROR );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ServiceController::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[] ) {
|
|
assert(argc >= 1);
|
|
|
|
stringstream commandLine;
|
|
|
|
if ( strchr(argv[0], ':') ) { // a crude test for fully qualified path
|
|
commandLine << '"' << argv[0] << "\" ";
|
|
}
|
|
else {
|
|
char buffer[256];
|
|
assert( _getcwd(buffer, 256) );
|
|
commandLine << '"' << buffer << '\\' << argv[0] << "\" ";
|
|
}
|
|
|
|
for ( int 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 == "--reinstall" ) {
|
|
arg = "--service";
|
|
}
|
|
else if ( arg == "--dbpath" && i + 1 < argc ) {
|
|
commandLine << arg << " \"" << dbpath << "\" ";
|
|
i++;
|
|
continue;
|
|
}
|
|
else if ( arg == "--logpath" && i + 1 < argc ) {
|
|
commandLine << arg << " \"" << argv[i+1] << "\" ";
|
|
i++;
|
|
continue;
|
|
}
|
|
else if ( arg.length() > 9 && arg.substr(0, 9) == "--service" ) {
|
|
// Strip off --service(Name|User|Password) arguments
|
|
i++;
|
|
continue;
|
|
}
|
|
commandLine << arg << " ";
|
|
}
|
|
|
|
SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
|
|
if ( schSCManager == NULL ) {
|
|
DWORD err = ::GetLastError();
|
|
cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
|
|
return false;
|
|
}
|
|
|
|
// Make sure servise doesn't already exist.
|
|
// TODO: Check to see if service is in "Deleting" status, suggest the user close down Services MMC snap-ins.
|
|
SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
|
|
if ( schService != NULL ) {
|
|
cerr << "There is already a service named " << toUtf8String(serviceName) << ". Aborting" << endl;
|
|
::CloseServiceHandle( schService );
|
|
::CloseServiceHandle( schSCManager );
|
|
return false;
|
|
}
|
|
std::basic_ostringstream< TCHAR > commandLineWide;
|
|
commandLineWide << commandLine.str().c_str();
|
|
|
|
cerr << "Creating service " << toUtf8String(serviceName) << "." << endl;
|
|
|
|
// create new service
|
|
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 ) {
|
|
DWORD err = ::GetLastError();
|
|
cerr << "Error creating service: " << GetWinErrMsg(err) << endl;
|
|
::CloseServiceHandle( schSCManager );
|
|
return false;
|
|
}
|
|
|
|
cerr << "Service creation successful." << endl;
|
|
cerr << "Service can be started from the command line via 'net start \"" << toUtf8String(serviceName) << "\"'." << endl;
|
|
|
|
bool serviceInstalled;
|
|
|
|
// TODO: If neccessary grant user "Login as a Service" permission.
|
|
if ( !serviceUser.empty() ) {
|
|
std::wstring actualServiceUser;
|
|
if ( serviceUser.find(L"\\") == string::npos ) {
|
|
actualServiceUser = L".\\" + serviceUser;
|
|
}
|
|
else {
|
|
actualServiceUser = serviceUser;
|
|
}
|
|
|
|
cerr << "Setting service login credentials. User: " << toUtf8String(actualServiceUser) << endl;
|
|
serviceInstalled = ::ChangeServiceConfig( schService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, actualServiceUser.c_str(), servicePassword.c_str(), NULL );
|
|
if ( !serviceInstalled ) {
|
|
cerr << "Setting service login failed. Service has 'LocalService' permissions." << endl;
|
|
}
|
|
}
|
|
|
|
// set the service description
|
|
SERVICE_DESCRIPTION serviceDescription;
|
|
serviceDescription.lpDescription = (LPTSTR)serviceDesc.c_str();
|
|
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 );
|
|
|
|
}
|
|
else {
|
|
cerr << "Could not set service description. Check the event log for more details." << endl;
|
|
}
|
|
|
|
::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 ) {
|
|
DWORD err = ::GetLastError();
|
|
cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
|
|
return false;
|
|
}
|
|
|
|
SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
|
|
if ( schService == NULL ) {
|
|
cerr << "Could not find a service named " << toUtf8String(serviceName) << " to uninstall." << endl;
|
|
::CloseServiceHandle( schSCManager );
|
|
return false;
|
|
}
|
|
|
|
SERVICE_STATUS serviceStatus;
|
|
|
|
// stop service if its running
|
|
if ( ::ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus ) ) {
|
|
cerr << "Service " << toUtf8String(serviceName) << " is currently running. Stopping service." << endl;
|
|
while ( ::QueryServiceStatus( schService, &serviceStatus ) ) {
|
|
if ( serviceStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
|
|
Sleep( 1000 );
|
|
}
|
|
else { break; }
|
|
}
|
|
cerr << "Service stopped." << endl;
|
|
}
|
|
|
|
cerr << "Deleting service " << toUtf8String(serviceName) << "." << endl;
|
|
bool serviceRemoved = ::DeleteService( schService );
|
|
|
|
::CloseServiceHandle( schService );
|
|
::CloseServiceHandle( schSCManager );
|
|
|
|
if (serviceRemoved) {
|
|
cerr << "Service deleted successfully." << endl;
|
|
}
|
|
else {
|
|
cerr << "Failed to delete service." << endl;
|
|
}
|
|
|
|
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;
|
|
|
|
DWORD dwControlsAccepted;
|
|
switch ( reportState ) {
|
|
case SERVICE_START_PENDING:
|
|
case SERVICE_STOP_PENDING:
|
|
case SERVICE_STOPPED:
|
|
dwControlsAccepted = 0;
|
|
break;
|
|
default:
|
|
dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
break;
|
|
}
|
|
|
|
ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
ssStatus.dwServiceSpecificExitCode = 0;
|
|
ssStatus.dwControlsAccepted = dwControlsAccepted;
|
|
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();
|
|
dbexit( EXIT_CLEAN );
|
|
|
|
reportStatus( SERVICE_STOPPED );
|
|
}
|
|
|
|
void WINAPI ServiceController::serviceCtrl( DWORD ctrlCode ) {
|
|
switch ( ctrlCode ) {
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
reportStatus( SERVICE_STOP_PENDING );
|
|
shutdownServer();
|
|
reportStatus( SERVICE_STOPPED );
|
|
return;
|
|
}
|
|
}
|
|
|
|
} // namespace mongo
|
|
|
|
#endif
|