diff options
Diffstat (limited to 'src/mongo/util/ntservice.cpp')
-rw-r--r-- | src/mongo/util/ntservice.cpp | 907 |
1 files changed, 445 insertions, 462 deletions
diff --git a/src/mongo/util/ntservice.cpp b/src/mongo/util/ntservice.cpp index 41dde9d15c3..a92483be232 100644 --- a/src/mongo/util/ntservice.cpp +++ b/src/mongo/util/ntservice.cpp @@ -55,436 +55,423 @@ namespace mongo { namespace ntservice { namespace { - bool _startService = false; - SERVICE_STATUS_HANDLE _statusHandle = NULL; - wstring _serviceName; - ServiceCallback _serviceCallback = NULL; +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, - const bool reinstall); - - static void removeServiceOrDie(const wstring& serviceName); - - bool shouldStartService() { - return _startService; - } - - static void WINAPI serviceCtrl(DWORD ctrlCode); - - void configureService( - ServiceCallback serviceCallback, - const moe::Environment& 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; - - _serviceCallback = serviceCallback; - - int badOption = -1; - for (size_t i = 0; i < disallowedOptions.size(); ++i) { - if (params.count(disallowedOptions[i])) { - badOption = i; - break; - } +static void installServiceOrDie(const wstring& serviceName, + const wstring& displayName, + const wstring& serviceDesc, + const wstring& serviceUser, + const wstring& servicePassword, + const std::vector<std::string>& argv, + const bool reinstall); + +static void removeServiceOrDie(const wstring& serviceName); + +bool shouldStartService() { + return _startService; +} + +static void WINAPI serviceCtrl(DWORD ctrlCode); + +void configureService(ServiceCallback serviceCallback, + const moe::Environment& 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; + + _serviceCallback = serviceCallback; + + int badOption = -1; + for (size_t i = 0; i < disallowedOptions.size(); ++i) { + if (params.count(disallowedOptions[i])) { + badOption = i; + break; } + } - _serviceName = defaultStrings.serviceName; - wstring windowsServiceDisplayName( defaultStrings.displayName ); - wstring windowsServiceDescription( defaultStrings.serviceDescription ); - wstring windowsServiceUser; - wstring windowsServicePassword; + _serviceName = defaultStrings.serviceName; + wstring windowsServiceDisplayName(defaultStrings.displayName); + wstring windowsServiceDescription(defaultStrings.serviceDescription); + wstring windowsServiceUser; + wstring windowsServicePassword; - if (params.count("install")) { - if ( badOption != -1 ) { - log() << "--install cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - if ( !params.count("systemLog.destination") || - params["systemLog.destination"].as<std::string>() != "file" ) { - log() << "--install has to be used with a log file for server output"; - quickExit( EXIT_BADOPTIONS ); - } - installService = true; + if (params.count("install")) { + if (badOption != -1) { + log() << "--install cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } - if (params.count("reinstall")) { - if ( badOption != -1 ) { - log() << "--reinstall cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - if ( !params.count("systemLog.destination") || - params["systemLog.destination"].as<std::string>() != "file" ) { - log() << "--reinstall has to be used with a log file for server output"; - quickExit( EXIT_BADOPTIONS ); - } - reinstallService = true; + if (!params.count("systemLog.destination") || + params["systemLog.destination"].as<std::string>() != "file") { + log() << "--install has to be used with a log file for server output"; + quickExit(EXIT_BADOPTIONS); } - if (params.count("remove")) { - if ( badOption != -1 ) { - log() << "--remove cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - removeService = true; + installService = true; + } + if (params.count("reinstall")) { + if (badOption != -1) { + log() << "--reinstall cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } - if (params.count("service")) { - if ( badOption != -1 ) { - log() << "--service cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - _startService = true; + if (!params.count("systemLog.destination") || + params["systemLog.destination"].as<std::string>() != "file") { + log() << "--reinstall has to be used with a log file for server output"; + quickExit(EXIT_BADOPTIONS); + } + reinstallService = true; + } + if (params.count("remove")) { + if (badOption != -1) { + log() << "--remove cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); + } + removeService = true; + } + if (params.count("service")) { + if (badOption != -1) { + log() << "--service cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } + _startService = true; + } - if (params.count("processManagement.windowsService.serviceName")) { - if ( badOption != -1 ) { - log() << "--serviceName cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - _serviceName = toWideString( - params[ "processManagement.windowsService.serviceName" ].as<string>().c_str() ); + if (params.count("processManagement.windowsService.serviceName")) { + if (badOption != -1) { + log() << "--serviceName cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } - if (params.count("processManagement.windowsService.displayName")) { - if ( badOption != -1 ) { - log() << "--serviceDisplayName cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - windowsServiceDisplayName = toWideString( - params[ "processManagement.windowsService.displayName" ].as<string>().c_str() ); + _serviceName = toWideString( + params["processManagement.windowsService.serviceName"].as<string>().c_str()); + } + if (params.count("processManagement.windowsService.displayName")) { + if (badOption != -1) { + log() << "--serviceDisplayName cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } - if (params.count("processManagement.windowsService.description")) { - if ( badOption != -1 ) { - log() << "--serviceDescription cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - windowsServiceDescription = toWideString( - params[ "processManagement.windowsService.description" ].as<string>().c_str() ); + windowsServiceDisplayName = toWideString( + params["processManagement.windowsService.displayName"].as<string>().c_str()); + } + if (params.count("processManagement.windowsService.description")) { + if (badOption != -1) { + log() << "--serviceDescription cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } - if (params.count("processManagement.windowsService.serviceUser")) { - if ( badOption != -1 ) { - log() << "--serviceUser cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - windowsServiceUser = toWideString( - params[ "processManagement.windowsService.serviceUser" ].as<string>().c_str() ); + windowsServiceDescription = toWideString( + params["processManagement.windowsService.description"].as<string>().c_str()); + } + if (params.count("processManagement.windowsService.serviceUser")) { + if (badOption != -1) { + log() << "--serviceUser cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } - if (params.count("processManagement.windowsService.servicePassword")) { - if ( badOption != -1 ) { - log() << "--servicePassword cannot be used with --" << disallowedOptions[badOption]; - quickExit( EXIT_BADOPTIONS ); - } - windowsServicePassword = toWideString( - params[ "processManagement.windowsService.servicePassword" - ].as<string>().c_str() ); + windowsServiceUser = toWideString( + params["processManagement.windowsService.serviceUser"].as<string>().c_str()); + } + if (params.count("processManagement.windowsService.servicePassword")) { + if (badOption != -1) { + log() << "--servicePassword cannot be used with --" << disallowedOptions[badOption]; + quickExit(EXIT_BADOPTIONS); } + windowsServicePassword = toWideString( + params["processManagement.windowsService.servicePassword"].as<string>().c_str()); + } - if ( installService || reinstallService ) { - if ( reinstallService ) { - removeServiceOrDie(_serviceName); - } - - installServiceOrDie( - _serviceName, - windowsServiceDisplayName, - windowsServiceDescription, - windowsServiceUser, - windowsServicePassword, - argv, - reinstallService); - quickExit(EXIT_CLEAN); - } - else if ( removeService ) { + if (installService || reinstallService) { + if (reinstallService) { removeServiceOrDie(_serviceName); - quickExit( EXIT_CLEAN ); } - } - // This implementation assumes that inputArgv was a valid argv to mongod. That is, it assumes - // that options that take arguments received them, and options that do not take arguments did - // not. - std::vector<std::string> constructServiceArgv(const std::vector<std::string>& inputArgv) { - - static const char*const optionsWithoutArgumentsToStrip[] = { - "-install", "--install", - "-reinstall", "--reinstall", - "-service", "--service" - }; - - // Pointer to just past the end of optionsWithoutArgumentsToStrip, for use as an "end" - // iterator. - static const char*const *const optionsWithoutArgumentsToStripEnd = - optionsWithoutArgumentsToStrip + boost::size(optionsWithoutArgumentsToStrip); - - static const char*const optionsWithArgumentsToStrip[] = { - "-serviceName", "--serviceName", - "-serviceUser", "--serviceUser", - "-servicePassword", "--servicePassword", - "-serviceDescription", "--serviceDescription", - "-serviceDisplayName", "--serviceDisplayName" - }; - - // Pointer to just past the end of optionsWithArgumentsToStrip, for use as an "end" - // iterator. - static const char*const *const optionsWithArgumentsToStripEnd = - optionsWithArgumentsToStrip + boost::size(optionsWithArgumentsToStrip); - - std::vector<std::string> result; - for (std::vector<std::string>::const_iterator iter = inputArgv.begin(), - end = inputArgv.end(); iter != end; ++iter) { - - if (optionsWithoutArgumentsToStripEnd != std::find(optionsWithoutArgumentsToStrip, - optionsWithoutArgumentsToStripEnd, - *iter)) { - // The current element of inputArgv is an option that we wish to strip, that takes - // no arguments. Skip adding it to "result". - continue; - } + installServiceOrDie(_serviceName, + windowsServiceDisplayName, + windowsServiceDescription, + windowsServiceUser, + windowsServicePassword, + argv, + reinstallService); + quickExit(EXIT_CLEAN); + } else if (removeService) { + removeServiceOrDie(_serviceName); + quickExit(EXIT_CLEAN); + } +} + +// This implementation assumes that inputArgv was a valid argv to mongod. That is, it assumes +// that options that take arguments received them, and options that do not take arguments did +// not. +std::vector<std::string> constructServiceArgv(const std::vector<std::string>& inputArgv) { + static const char* const optionsWithoutArgumentsToStrip[] = { + "-install", "--install", "-reinstall", "--reinstall", "-service", "--service"}; + + // Pointer to just past the end of optionsWithoutArgumentsToStrip, for use as an "end" + // iterator. + static const char* const* const optionsWithoutArgumentsToStripEnd = + optionsWithoutArgumentsToStrip + boost::size(optionsWithoutArgumentsToStrip); + + static const char* const optionsWithArgumentsToStrip[] = {"-serviceName", + "--serviceName", + "-serviceUser", + "--serviceUser", + "-servicePassword", + "--servicePassword", + "-serviceDescription", + "--serviceDescription", + "-serviceDisplayName", + "--serviceDisplayName"}; + + // Pointer to just past the end of optionsWithArgumentsToStrip, for use as an "end" + // iterator. + static const char* const* const optionsWithArgumentsToStripEnd = + optionsWithArgumentsToStrip + boost::size(optionsWithArgumentsToStrip); + + std::vector<std::string> result; + for (std::vector<std::string>::const_iterator iter = inputArgv.begin(), end = inputArgv.end(); + iter != end; + ++iter) { + if (optionsWithoutArgumentsToStripEnd != + std::find(optionsWithoutArgumentsToStrip, optionsWithoutArgumentsToStripEnd, *iter)) { + // The current element of inputArgv is an option that we wish to strip, that takes + // no arguments. Skip adding it to "result". + continue; + } - std::string name; - std::string value; - bool foundEqualSign = mongoutils::str::splitOn(*iter, '=', name, value); - if (!foundEqualSign) - name = *iter; - if (optionsWithArgumentsToStripEnd != std::find(optionsWithArgumentsToStrip, - optionsWithArgumentsToStripEnd, - name)) { - // The current element, and maybe the next one, form an option and its argument. - // Skip adding them to "result". - if (!foundEqualSign) { - // The next argv value must be the argument to the parameter, so strip it. - ++iter; - } - continue; + std::string name; + std::string value; + bool foundEqualSign = mongoutils::str::splitOn(*iter, '=', name, value); + if (!foundEqualSign) + name = *iter; + if (optionsWithArgumentsToStripEnd != + std::find(optionsWithArgumentsToStrip, optionsWithArgumentsToStripEnd, name)) { + // The current element, and maybe the next one, form an option and its argument. + // Skip adding them to "result". + if (!foundEqualSign) { + // The next argv value must be the argument to the parameter, so strip it. + ++iter; } - - result.push_back(*iter); + continue; } - result.push_back("--service"); // Service command lines all contain "--service". - return result; + result.push_back(*iter); } - void installServiceOrDie( - const wstring& serviceName, - const wstring& displayName, - const wstring& serviceDesc, - const wstring& serviceUser, - const wstring& servicePassword, - const std::vector<std::string>& argv, - const bool reinstall - ) { - log() << "Trying to install Windows service '" << toUtf8String(serviceName) << "'"; - - std::vector<std::string> serviceArgv = constructServiceArgv(argv); - - char exePath[1024]; - GetModuleFileNameA( NULL, exePath, sizeof exePath ); - serviceArgv.at(0) = exePath; - - std::string commandLine = constructUtf8WindowsCommandLine(serviceArgv); - - 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); - quickExit(EXIT_NTSERVICE_ERROR); - } + result.push_back("--service"); // Service command lines all contain "--service". + return result; +} - SC_HANDLE schService = NULL; - int retryCount = 10; +void installServiceOrDie(const wstring& serviceName, + const wstring& displayName, + const wstring& serviceDesc, + const wstring& serviceUser, + const wstring& servicePassword, + const std::vector<std::string>& argv, + const bool reinstall) { + log() << "Trying to install Windows service '" << toUtf8String(serviceName) << "'"; - while (true) { + std::vector<std::string> serviceArgv = constructServiceArgv(argv); - // Make sure service doesn't already exist. - // TODO: Check to see if service is in "Deleting" status, suggest the user close down Services MMC snap-ins. - schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS ); - if ( schService != NULL) { - log() << "There is already a service named '" << toUtf8String(serviceName) << - (retryCount > 0 ? "', sleeping and retrying" : "', aborting"); - ::CloseServiceHandle( schService ); + char exePath[1024]; + GetModuleFileNameA(NULL, exePath, sizeof exePath); + serviceArgv.at(0) = exePath; - // If we are reinstalling the service, but SCM thinks it is installed, then wait - // and try again - if(--retryCount > 0 && reinstall) { - sleepmillis(500); - continue; - } + std::string commandLine = constructUtf8WindowsCommandLine(serviceArgv); - ::CloseServiceHandle( schSCManager ); - quickExit(EXIT_NTSERVICE_ERROR); - } - else { - break; + 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); + quickExit(EXIT_NTSERVICE_ERROR); + } + + SC_HANDLE schService = NULL; + int retryCount = 10; + + while (true) { + // Make sure service doesn't already exist. + // TODO: Check to see if service is in "Deleting" status, suggest the user close down Services MMC snap-ins. + schService = ::OpenService(schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS); + if (schService != NULL) { + log() << "There is already a service named '" << toUtf8String(serviceName) + << (retryCount > 0 ? "', sleeping and retrying" : "', aborting"); + ::CloseServiceHandle(schService); + + // If we are reinstalling the service, but SCM thinks it is installed, then wait + // and try again + if (--retryCount > 0 && reinstall) { + sleepmillis(500); + continue; } - } - std::wstring commandLineWide = toWideString(commandLine.c_str()); - - // create new service - schService = ::CreateServiceW( - schSCManager, // Service Control Manager handle - serviceName.c_str(), // service name - displayName.c_str(), // service display name - SERVICE_ALL_ACCESS, // desired access - SERVICE_WIN32_OWN_PROCESS, // service type - SERVICE_AUTO_START, // start type - SERVICE_ERROR_NORMAL, // error control - commandLineWide.c_str(), // command line - NULL, // load order group - NULL, // tag id - L"\0\0", // dependencies - NULL, // user account - NULL ); // user account password - if ( schService == NULL ) { - DWORD err = ::GetLastError(); - log() << "Error creating service: " << GetWinErrMsg(err); - ::CloseServiceHandle( schSCManager ); - quickExit( EXIT_NTSERVICE_ERROR ); + ::CloseServiceHandle(schSCManager); + quickExit(EXIT_NTSERVICE_ERROR); + } else { + break; } + } - log() << "Service '" << toUtf8String(serviceName) << "' (" << toUtf8String(displayName) << - ") installed with command line '" << commandLine << "'"; - string typeableName( ( serviceName.find(L' ') != wstring::npos ) ? - "\"" + toUtf8String(serviceName) + "\"" : - toUtf8String(serviceName) ); - log() << "Service can be started from the command line with 'net start " << typeableName << "'"; - - bool serviceInstalled; + std::wstring commandLineWide = toWideString(commandLine.c_str()); + + // create new service + schService = ::CreateServiceW(schSCManager, // Service Control Manager handle + serviceName.c_str(), // service name + displayName.c_str(), // service display name + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_AUTO_START, // start type + SERVICE_ERROR_NORMAL, // error control + commandLineWide.c_str(), // command line + NULL, // load order group + NULL, // tag id + L"\0\0", // dependencies + NULL, // user account + NULL); // user account password + if (schService == NULL) { + DWORD err = ::GetLastError(); + log() << "Error creating service: " << GetWinErrMsg(err); + ::CloseServiceHandle(schSCManager); + quickExit(EXIT_NTSERVICE_ERROR); + } - // TODO: If necessary grant user "Login as a Service" permission. - if ( !serviceUser.empty() ) { - wstring actualServiceUser; - if ( serviceUser.find(L"\\") == string::npos ) { - actualServiceUser = L".\\" + serviceUser; - } - else { - actualServiceUser = serviceUser; - } + log() << "Service '" << toUtf8String(serviceName) << "' (" << toUtf8String(displayName) + << ") installed with command line '" << commandLine << "'"; + string typeableName((serviceName.find(L' ') != wstring::npos) + ? "\"" + toUtf8String(serviceName) + "\"" + : toUtf8String(serviceName)); + log() << "Service can be started from the command line with 'net start " << typeableName << "'"; + + bool serviceInstalled; + + // TODO: If necessary grant user "Login as a Service" permission. + if (!serviceUser.empty()) { + wstring actualServiceUser; + if (serviceUser.find(L"\\") == string::npos) { + actualServiceUser = L".\\" + serviceUser; + } else { + actualServiceUser = serviceUser; + } - log() << "Setting service login credentials for user: " << toUtf8String(actualServiceUser); - serviceInstalled = ::ChangeServiceConfig( - schService, // service handle - SERVICE_NO_CHANGE, // service type - SERVICE_NO_CHANGE, // start type - SERVICE_NO_CHANGE, // error control - NULL, // path - NULL, // load order group - NULL, // tag id - NULL, // dependencies - actualServiceUser.c_str(), // user account - servicePassword.c_str(), // user account password - NULL ); // service display name - if ( !serviceInstalled ) { - log() << "Setting service login failed, service has 'LocalService' permissions"; - } + log() << "Setting service login credentials for user: " << toUtf8String(actualServiceUser); + serviceInstalled = ::ChangeServiceConfig(schService, // service handle + SERVICE_NO_CHANGE, // service type + SERVICE_NO_CHANGE, // start type + SERVICE_NO_CHANGE, // error control + NULL, // path + NULL, // load order group + NULL, // tag id + NULL, // dependencies + actualServiceUser.c_str(), // user account + servicePassword.c_str(), // user account password + NULL); // service display name + if (!serviceInstalled) { + log() << "Setting service login failed, service has 'LocalService' permissions"; } + } - // set the service description - SERVICE_DESCRIPTION serviceDescription; - serviceDescription.lpDescription = (LPTSTR)serviceDesc.c_str(); - serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_DESCRIPTION, &serviceDescription ); + // set the service description + SERVICE_DESCRIPTION serviceDescription; + serviceDescription.lpDescription = (LPTSTR)serviceDesc.c_str(); + serviceInstalled = + ::ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &serviceDescription); #if 1 - if ( ! serviceInstalled ) { + if (!serviceInstalled) { #else - // This code sets the mongod service to auto-restart, forever. - // This might be a fine thing to do except that when mongod or Windows has a crash, the mongo.lock - // file is still around, so any attempt at a restart will immediately fail. With auto-restart, we - // go into a loop, crashing and restarting, crashing and restarting, until someone comes in and - // disables the service or deletes the mongod.lock file. - // - // I'm leaving the old code here for now in case we solve this and are able to turn SC_ACTION_RESTART - // back on. - // - 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 { + // This code sets the mongod service to auto-restart, forever. + // This might be a fine thing to do except that when mongod or Windows has a crash, the mongo.lock + // file is still around, so any attempt at a restart will immediately fail. With auto-restart, we + // go into a loop, crashing and restarting, crashing and restarting, until someone comes in and + // disables the service or deletes the mongod.lock file. + // + // I'm leaving the old code here for now in case we solve this and are able to turn SC_ACTION_RESTART + // back on. + // + 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 { #endif - log() << "Could not set service description. Check the Windows Event Log for more details."; - } + log() << "Could not set service description. Check the Windows Event Log for more details."; + } - ::CloseServiceHandle( schService ); - ::CloseServiceHandle( schSCManager ); + ::CloseServiceHandle(schService); + ::CloseServiceHandle(schSCManager); - if (!serviceInstalled) - quickExit( EXIT_NTSERVICE_ERROR ); - } + if (!serviceInstalled) + quickExit(EXIT_NTSERVICE_ERROR); +} - void removeServiceOrDie(const wstring& serviceName) { - log() << "Trying to remove Windows service '" << toUtf8String(serviceName) << "'"; +void removeServiceOrDie(const wstring& serviceName) { + log() << "Trying to remove Windows service '" << toUtf8String(serviceName) << "'"; - 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); - quickExit(EXIT_NTSERVICE_ERROR); - } + 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); + quickExit(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"; - ::CloseServiceHandle( schSCManager ); - quickExit(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"; + ::CloseServiceHandle(schSCManager); + quickExit(EXIT_NTSERVICE_ERROR); + } - SERVICE_STATUS serviceStatus; + SERVICE_STATUS serviceStatus; - // stop service if its running - if ( ::ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus ) ) { - log() << "Service " << toUtf8String(serviceName) << " is currently running, stopping service"; - while ( ::QueryServiceStatus( schService, &serviceStatus ) ) { - if ( serviceStatus.dwCurrentState == SERVICE_STOP_PENDING ) { - Sleep( 1000 ); - } - else { break; } + // stop service if its running + if (::ControlService(schService, SERVICE_CONTROL_STOP, &serviceStatus)) { + log() << "Service " << toUtf8String(serviceName) + << " is currently running, stopping service"; + while (::QueryServiceStatus(schService, &serviceStatus)) { + if (serviceStatus.dwCurrentState == SERVICE_STOP_PENDING) { + Sleep(1000); + } else { + break; } - log() << "Service '" << toUtf8String(serviceName) << "' stopped"; } + log() << "Service '" << toUtf8String(serviceName) << "' stopped"; + } - bool serviceRemoved = ::DeleteService( schService ); - - ::CloseServiceHandle( schService ); - ::CloseServiceHandle( schSCManager ); + bool serviceRemoved = ::DeleteService(schService); - if (serviceRemoved) { - log() << "Service '" << toUtf8String(serviceName) << "' removed"; - } - else { - log() << "Failed to remove service '" << toUtf8String(serviceName) << "'"; - } + ::CloseServiceHandle(schService); + ::CloseServiceHandle(schSCManager); - if (!serviceRemoved) - quickExit(EXIT_NTSERVICE_ERROR); + if (serviceRemoved) { + log() << "Service '" << toUtf8String(serviceName) << "' removed"; + } else { + log() << "Failed to remove service '" << toUtf8String(serviceName) << "'"; } - bool reportStatus(DWORD reportState, DWORD waitHint, DWORD exitCode) { - if ( _statusHandle == NULL ) - return false; + if (!serviceRemoved) + quickExit(EXIT_NTSERVICE_ERROR); +} + +bool reportStatus(DWORD reportState, DWORD waitHint, DWORD exitCode) { + if (_statusHandle == NULL) + return false; - static DWORD checkPoint = 1; + static DWORD checkPoint = 1; - SERVICE_STATUS ssStatus; + SERVICE_STATUS ssStatus; - DWORD dwControlsAccepted; - switch ( reportState ) { + DWORD dwControlsAccepted; + switch (reportState) { case SERVICE_START_PENDING: case SERVICE_STOP_PENDING: case SERVICE_STOPPED: @@ -493,122 +480,118 @@ namespace { default: dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; break; - } - - ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - ssStatus.dwServiceSpecificExitCode = exitCode; - ssStatus.dwControlsAccepted = dwControlsAccepted; - ssStatus.dwCurrentState = reportState; - - // Only report ERROR_SERVICE_SPECIFIC_ERROR when the exit is not clean - if (reportState == SERVICE_STOPPED && exitCode != EXIT_CLEAN) - ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - else - ssStatus.dwWin32ExitCode = NO_ERROR; - - ssStatus.dwWaitHint = waitHint; - ssStatus.dwCheckPoint = - ( reportState == SERVICE_RUNNING || reportState == SERVICE_STOPPED ) ? - 0 : checkPoint++; - - return SetServiceStatus( _statusHandle, &ssStatus ); } - static void serviceStopWorker() { - Client::initThread("serviceStopWorker"); - - // Stop the process - // TODO: SERVER-5703, separate the "cleanup for shutdown" functionality from - // the "terminate process" functionality in exitCleanly. - exitCleanly(EXIT_WINDOWS_SERVICE_STOP); + ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ssStatus.dwServiceSpecificExitCode = exitCode; + ssStatus.dwControlsAccepted = dwControlsAccepted; + ssStatus.dwCurrentState = reportState; + + // Only report ERROR_SERVICE_SPECIFIC_ERROR when the exit is not clean + if (reportState == SERVICE_STOPPED && exitCode != EXIT_CLEAN) + ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + else + ssStatus.dwWin32ExitCode = NO_ERROR; + + ssStatus.dwWaitHint = waitHint; + ssStatus.dwCheckPoint = + (reportState == SERVICE_RUNNING || reportState == SERVICE_STOPPED) ? 0 : checkPoint++; + + return SetServiceStatus(_statusHandle, &ssStatus); +} + +static void serviceStopWorker() { + Client::initThread("serviceStopWorker"); + + // Stop the process + // TODO: SERVER-5703, separate the "cleanup for shutdown" functionality from + // the "terminate process" functionality in exitCleanly. + exitCleanly(EXIT_WINDOWS_SERVICE_STOP); +} + +// Minimum of time we tell Windows to wait before we are guilty of a hung shutdown +const int kStopWaitHintMillis = 30000; + +// Run exitCleanly on a separate thread so we can report progress to Windows +// Note: Windows may still kill us for taking too long, +// On client OSes, SERVICE_CONTROL_SHUTDOWN has a 5 second timeout configured in +// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control +static void serviceStop() { + stdx::thread serviceWorkerThread(serviceStopWorker); + + // We periodically check if we are done exiting by polling at half of each wait interval + // + while ( + !serviceWorkerThread.try_join_for(boost::chrono::milliseconds(kStopWaitHintMillis / 2))) { + reportStatus(SERVICE_STOP_PENDING, kStopWaitHintMillis); + log() << "Service Stop is waiting for storage engine to finish shutdown"; } +} - // Minimum of time we tell Windows to wait before we are guilty of a hung shutdown - const int kStopWaitHintMillis = 30000; - - // Run exitCleanly on a separate thread so we can report progress to Windows - // Note: Windows may still kill us for taking too long, - // On client OSes, SERVICE_CONTROL_SHUTDOWN has a 5 second timeout configured in - // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control - static void serviceStop() { - stdx::thread serviceWorkerThread(serviceStopWorker); - - // We periodically check if we are done exiting by polling at half of each wait interval - // - while (!serviceWorkerThread.try_join_for( - boost::chrono::milliseconds(kStopWaitHintMillis / 2))) { - reportStatus(SERVICE_STOP_PENDING, kStopWaitHintMillis); - log() << "Service Stop is waiting for storage engine to finish shutdown"; - } - } +static void WINAPI initService(DWORD argc, LPTSTR* argv) { + _statusHandle = RegisterServiceCtrlHandler(_serviceName.c_str(), serviceCtrl); + if (!_statusHandle) + return; - static void WINAPI initService( DWORD argc, LPTSTR *argv ) { - _statusHandle = RegisterServiceCtrlHandler( _serviceName.c_str(), serviceCtrl ); - if ( !_statusHandle ) - return; + reportStatus(SERVICE_START_PENDING, 1000); - reportStatus( SERVICE_START_PENDING, 1000 ); + ExitCode exitCode = _serviceCallback(); - ExitCode exitCode = _serviceCallback(); + // During clean shutdown, ie NT SCM signals us, _serviceCallback returns here + // as part of the listener loop terminating. + // exitCleanly is supposed to return. If it blocks, some other thread must be exiting. + // + serviceStop(); - // During clean shutdown, ie NT SCM signals us, _serviceCallback returns here - // as part of the listener loop terminating. - // exitCleanly is supposed to return. If it blocks, some other thread must be exiting. - // - serviceStop(); + reportStatus(SERVICE_STOPPED, 0, exitCode); +} - reportStatus(SERVICE_STOPPED, 0, exitCode); - } +static void serviceShutdown(const char* controlCodeName) { + Client::initThread("serviceShutdown"); - static void serviceShutdown( const char* controlCodeName ) { - Client::initThread( "serviceShutdown" ); + log() << "got " << controlCodeName << " request from Windows Service Control Manager, " + << (inShutdown() ? "already in shutdown" : "will terminate after current cmd ends"); - log() << "got " << controlCodeName << " request from Windows Service Control Manager, " << - ( inShutdown() ? "already in shutdown" : "will terminate after current cmd ends" ); + reportStatus(SERVICE_STOP_PENDING, kStopWaitHintMillis); - reportStatus(SERVICE_STOP_PENDING, kStopWaitHintMillis); + // Note: This triggers _serviceCallback, ie ServiceMain, + // to stop by setting inShutdown() == true + signalShutdown(); - // Note: This triggers _serviceCallback, ie ServiceMain, - // to stop by setting inShutdown() == true - signalShutdown(); + // Note: we will report exit status in initService +} - // Note: we will report exit status in initService - } - - static void WINAPI serviceCtrl( DWORD ctrlCode ) { - switch ( ctrlCode ) { +static void WINAPI serviceCtrl(DWORD ctrlCode) { + switch (ctrlCode) { case SERVICE_CONTROL_STOP: - serviceShutdown( "SERVICE_CONTROL_STOP" ); + serviceShutdown("SERVICE_CONTROL_STOP"); break; case SERVICE_CONTROL_SHUTDOWN: - serviceShutdown( "SERVICE_CONTROL_SHUTDOWN" ); + serviceShutdown("SERVICE_CONTROL_SHUTDOWN"); break; - } } +} - void startService() { - - fassert(16454, _startService); +void startService() { + fassert(16454, _startService); - // Remove the Control-C handler so that we properly process SERVICE_CONTROL_SHUTDOWN - // via the service handler instead of CTRL_SHUTDOWN_EVENT via the Control-C Handler - removeControlCHandler(); + // Remove the Control-C handler so that we properly process SERVICE_CONTROL_SHUTDOWN + // via the service handler instead of CTRL_SHUTDOWN_EVENT via the Control-C Handler + removeControlCHandler(); - SERVICE_TABLE_ENTRYW dispTable[] = { - { const_cast<LPWSTR>(_serviceName.c_str()), (LPSERVICE_MAIN_FUNCTION)initService }, - { NULL, NULL } - }; + SERVICE_TABLE_ENTRYW dispTable[] = { + {const_cast<LPWSTR>(_serviceName.c_str()), (LPSERVICE_MAIN_FUNCTION)initService}, + {NULL, NULL}}; - log() << "Trying to start Windows service '" << toUtf8String(_serviceName) << "'"; - if (StartServiceCtrlDispatcherW(dispTable)) { - quickExit(EXIT_CLEAN); - } - else { - ::exit(EXIT_NTSERVICE_ERROR); - } + log() << "Trying to start Windows service '" << toUtf8String(_serviceName) << "'"; + if (StartServiceCtrlDispatcherW(dispTable)) { + quickExit(EXIT_CLEAN); + } else { + ::exit(EXIT_NTSERVICE_ERROR); } +} } // namspace ntservice -} // namespace mongo +} // namespace mongo #endif |