Author: shuston Date: Tue Mar 8 01:41:53 2011 New Revision: 1079078 URL: http://svn.apache.org/viewvc?rev=1079078&view=rev Log: Changes applied to be able to install, uninstall, start, stop, run service. Has trouble running as LocalService (SCM reports timeout after 1 second)
Added: qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp qpid/branches/QPID-2519/cpp/src/windows/SCM.h Removed: qpid/branches/QPID-2519/cpp/src/windows/Service.cpp qpid/branches/QPID-2519/cpp/src/windows/Service.h Modified: qpid/branches/QPID-2519/cpp/src/CMakeLists.txt qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp qpid/branches/QPID-2519/cpp/src/qpidd.cpp qpid/branches/QPID-2519/cpp/src/qpidd.h qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp Modified: qpid/branches/QPID-2519/cpp/src/CMakeLists.txt URL: http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/CMakeLists.txt?rev=1079078&r1=1079077&r2=1079078&view=diff ============================================================================== --- qpid/branches/QPID-2519/cpp/src/CMakeLists.txt (original) +++ qpid/branches/QPID-2519/cpp/src/CMakeLists.txt Tue Mar 8 01:41:53 2011 @@ -660,8 +660,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows) set (qpidd_platform_SOURCES windows/QpiddBroker.cpp - windows/Service.h - windows/Service.cpp + windows/SCM.cpp ) set (qpidmessaging_platform_SOURCES Modified: qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp URL: http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp?rev=1079078&r1=1079077&r2=1079078&view=diff ============================================================================== --- qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp (original) +++ qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp Tue Mar 8 01:41:53 2011 @@ -188,3 +188,8 @@ int QpiddBroker::execute (QpiddOptions * } return 0; } + +int main(int argc, char* argv[]) +{ + return run_broker(argc, argv); +} Modified: qpid/branches/QPID-2519/cpp/src/qpidd.cpp URL: http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/qpidd.cpp?rev=1079078&r1=1079077&r2=1079078&view=diff ============================================================================== --- qpid/branches/QPID-2519/cpp/src/qpidd.cpp (original) +++ qpid/branches/QPID-2519/cpp/src/qpidd.cpp Tue Mar 8 01:41:53 2011 @@ -31,7 +31,8 @@ using namespace std; auto_ptr<QpiddOptions> options; -int main(int argc, char* argv[]) +// Broker real entry; various system-invoked entrypoints call here. +int run_broker(int argc, char *argv[], bool hidden) { try { @@ -43,6 +44,8 @@ int main(int argc, char* argv[]) // module-supplied options. try { bootOptions.parse (argc, argv, bootOptions.common.config, true); + if (hidden) + bootOptions.log.sinkOptions->detached(); qpid::log::Logger::instance().configure(bootOptions.log); } catch (const std::exception& e) { // Couldn't configure logging so write the message direct to stderr. Modified: qpid/branches/QPID-2519/cpp/src/qpidd.h URL: http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/qpidd.h?rev=1079078&r1=1079077&r2=1079078&view=diff ============================================================================== --- qpid/branches/QPID-2519/cpp/src/qpidd.h (original) +++ qpid/branches/QPID-2519/cpp/src/qpidd.h Tue Mar 8 01:41:53 2011 @@ -67,4 +67,7 @@ public: int execute (QpiddOptions *options); }; +// Broker real entry; various system-invoked entrypoints call here. +int run_broker(int argc, char *argv[], bool hidden = false); + #endif /*!QPID_H*/ Modified: qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp URL: http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp?rev=1079078&r1=1079077&r2=1079078&view=diff ============================================================================== --- qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp (original) +++ qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp Tue Mar 8 01:41:53 2011 @@ -19,17 +19,9 @@ * */ -#ifdef HAVE_CONFIG_H -# include "config.h" -#else -// These need to be made something sensible, like reading a value from -// the registry. But for now, get things going with a local definition. -namespace { -const char *QPIDD_CONF_FILE = "qpid_broker.conf"; -const char *QPIDD_MODULE_DIR = "."; -} -#endif +#include "config.h" #include "qpidd.h" +#include "SCM.h" #include "qpid/Exception.h" #include "qpid/Options.h" #include "qpid/Plugin.h" @@ -42,11 +34,6 @@ const char *QPIDD_MODULE_DIR = "."; using namespace qpid::broker; -// Service support -#include "Service.h" -Service s_service( "qpidd" ); - - BootstrapOptions::BootstrapOptions(const char* argv0) : qpid::Options("Options"), common("", QPIDD_CONF_FILE), @@ -210,8 +197,56 @@ struct BrokerInfo { DWORD pid; }; +// Service-related items. Only involved when running the broker as a Windows +// service. + +const std::string svcName = "qpidd"; +SERVICE_STATUS svcStatus; +SERVICE_STATUS_HANDLE svcStatusHandle = 0; + +// This function is only called when the broker is run as a Windows +// service. It receives control requests from Windows. +VOID WINAPI SvcCtrlHandler(DWORD control) +{ + switch(control) { + case SERVICE_CONTROL_STOP: + svcStatus.dwCurrentState = SERVICE_STOP_PENDING; + svcStatus.dwControlsAccepted = 0; + svcStatus.dwCheckPoint = 1; + svcStatus.dwWaitHint = 5000; // 5 secs. + ::SetServiceStatus(svcStatusHandle, &svcStatus); + CtrlHandler(CTRL_C_EVENT); + break; + + case SERVICE_CONTROL_INTERROGATE: + break; + + default: + break; + } } +VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) +{ + ::memset(&svcStatus, 0, sizeof(svcStatus)); + svcStatusHandle = ::RegisterServiceCtrlHandler(svcName.c_str(), + SvcCtrlHandler); + svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + svcStatus.dwCheckPoint = 1; + svcStatus.dwWaitHint = 10000; // 10 secs. + svcStatus.dwCurrentState = SERVICE_START_PENDING; + ::SetServiceStatus(svcStatusHandle, &svcStatus); + // QpiddBroker class resets state to running. + svcStatus.dwWin32ExitCode = run_broker(argc, argv, true); + svcStatus.dwCurrentState = SERVICE_STOPPED; + svcStatus.dwCheckPoint = 0; + svcStatus.dwWaitHint = 0; + ::SetServiceStatus(svcStatusHandle, &svcStatus); +} + +} // namespace + + struct ProcessControlOptions : public qpid::Options { bool quit; bool check; @@ -230,39 +265,49 @@ struct ProcessControlOptions : public qp } }; -struct DaemonOptions : public qpid::Options { - bool install; - bool start; - bool stop; - bool uninstall; - bool daemon; - string startType; - string account; - string password; - string depends; - - DaemonOptions() - : qpid::Options("Service options"), install(false), start(false), stop(false), uninstall(false), daemon(false), - startType("demand") - { +struct ServiceOptions : public qpid::Options { + bool install; + bool start; + bool stop; + bool uninstall; + bool daemon; + std::string startType; + std::string startArgs; + std::string account; + std::string password; + std::string depends; + + ServiceOptions() + : qpid::Options("Service options"), + install(false), + start(false), + stop(false), + uninstall(false), + daemon(false), + startType("demand"), + startArgs(""), + account("NT AUTHORITY\\LocalService"), + password(""), + depends("") + { addOptions() - ("install", qpid::optValue(install), "Install as service") - ("start-type", qpid::optValue(startType,"auto|demand|disabled"), "Service start type\nApplied at install time only.") - ("account", qpid::optValue(account,"(LocalService)"), "Account to run as, default is LocalService\nApplied at install time only.") - ("password", qpid::optValue(password,"PASSWORD"), "Account password, if needed\nApplied at install time only.") - ("depends", qpid::optValue(depends,"(comma delimited list)"), "Names of services that must start before this service\nApplied at install time only.") + ("install", qpid::optValue(install), "Install as service") + ("start-type", qpid::optValue(startType, "auto|demand|disabled"), "Service start type\nApplied at install time only.") + ("arguments", qpid::optValue(startArgs, "COMMAND LINE ARGS"), "Arguments to pass when service auto-starts") + ("account", qpid::optValue(account, "(LocalService)"), "Account to run as, default is LocalService\nApplied at install time only.") + ("password", qpid::optValue(password, "PASSWORD"), "Account password, if needed\nApplied at install time only.") + ("depends", qpid::optValue(depends, "(comma delimited list)"), "Names of services that must start before this service\nApplied at install time only.") ("start", qpid::optValue(start), "Start the service.") ("stop", qpid::optValue(stop), "Stop the service.") - ("uninstall", qpid::optValue(uninstall), "Uninstall the service.") - ("daemon", qpid::optValue(daemon), "Run as a daemon service (internal use only"); - } + ("uninstall", qpid::optValue(uninstall), "Uninstall the service."); + } }; struct QpiddWindowsOptions : public QpiddOptionsPrivate { ProcessControlOptions control; - DaemonOptions daemon; + ServiceOptions service; QpiddWindowsOptions(QpiddOptions *parent) : QpiddOptionsPrivate(parent) { - parent->add(daemon); + parent->add(service); parent->add(control); } }; @@ -287,88 +332,63 @@ void QpiddOptions::usage() const { << *this << std::endl; } -void WINAPI ShutdownProc( void *pContext ) -{ - if( pContext ) - reinterpret_cast<Broker*>(pContext)->shutdown(); -} - -int __cdecl main(int argc, char* argv[]); +int QpiddBroker::execute (QpiddOptions *options) { -void WINAPI main2( DWORD argc, char* argv[] ) -{ - (void)main( argc, argv ); -} + // If running as a service, bump the status checkpoint to let SCM know + // we're still making progress. + if (svcStatusHandle != 0) { + svcStatus.dwCheckPoint++; + ::SetServiceStatus(svcStatusHandle, &svcStatus); + } -int QpiddBroker::execute (QpiddOptions *options) { // Options that affect a running daemon. QpiddWindowsOptions *myOptions = - reinterpret_cast<QpiddWindowsOptions *>(options->platform.get()); + reinterpret_cast<QpiddWindowsOptions *>(options->platform.get()); if (myOptions == 0) throw qpid::Exception("Internal error obtaining platform options"); - if( myOptions->daemon.install ) - { - size_t p; - - // Handle start type - DWORD startType; - if( myOptions->daemon.startType.compare( "demand" ) == 0 ) - startType = SERVICE_DEMAND_START; - else if( myOptions->daemon.startType.compare( "auto" ) == 0 ) - startType = SERVICE_AUTO_START; - else if( myOptions->daemon.startType.compare( "disabled" ) == 0 ) - startType = SERVICE_DISABLED; - else if( ! myOptions->daemon.startType.empty() ) - throw qpid::Exception( "Invalid service start type: " + myOptions->daemon.startType ); - - // Get original command line arguments and substitute daemon for install... - string args( ::GetCommandLineA() ); - if( args[0] == '\"' ) // if OS prepended w/ fully qualified path - { - if( ( p = args.find_first_of( "\"", 1 ) ) != args.npos ) - args = args.substr( p + 2 ); // trim .exe - } - else - { - if( ( p = args.find_first_of( " ", 1 ) ) != args.npos ) - args = args.substr( p + 1 ); // trim .exe - } - if( ( p = args.find( "install" ) ) == args.npos ) - throw qpid::Exception("Internal error relocating install argument for service"); - string args2 = args.substr( 0, p ); - args2 += "daemon"; - args2 += args.substr( p + 7 ); - - // Install service and exit - WinService::install( "qpidd", args2, startType, myOptions->daemon.account, myOptions->daemon.password, myOptions->daemon.depends ); - return 0; - } - - if( myOptions->daemon.start ) - { - WinService::start( "qpidd" ); - return 0; - } - - else if( myOptions->daemon.stop ) - { - WinService::stop( "qpidd" ); - return 0; - } - - else if( myOptions->daemon.uninstall ) - { - WinService::uninstall( "qpidd" ); - return 0; - } - - // Detect daemon special argument - else if( myOptions->daemon.daemon ) - { - WinService::getInstance()->run( main2 ); - return 1; - } + if (myOptions->service.install) { + // Handle start type + DWORD startType; + if (myOptions->service.startType.compare("demand") == 0) + startType = SERVICE_DEMAND_START; + else if (myOptions->service.startType.compare("auto") == 0) + startType = SERVICE_AUTO_START; + else if (myOptions->service.startType.compare("disabled") == 0) + startType = SERVICE_DISABLED; + else if (!myOptions->service.startType.empty()) + throw qpid::Exception("Invalid service start type: " + + myOptions->service.startType); + + // Install service and exit + qpid::windows::SCM manager; + manager.install(svcName, + "Apache Qpid Message Broker", + myOptions->service.startArgs, + startType, + myOptions->service.account, + myOptions->service.password, + myOptions->service.depends); + return 0; + } + + if (myOptions->service.start) { + qpid::windows::SCM manager; + manager.start(svcName); + return 0; + } + + if (myOptions->service.stop) { + qpid::windows::SCM manager; + manager.stop(svcName); + return 0; + } + + if (myOptions->service.uninstall) { + qpid::windows::SCM manager; + manager.uninstall(svcName); + return 0; + } if (myOptions->control.check || myOptions->control.quit) { // Relies on port number being set via --port or QPID_PORT env variable. @@ -392,9 +412,6 @@ int QpiddBroker::execute (QpiddOptions * boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->broker)); - // Enable shutdown - s_service.setShutdownProc( ShutdownProc, brokerPtr.get() ); - // Need the correct port number to use in the pid file name. if (options->broker.port == 0) options->broker.port = brokerPtr->getPort(myOptions->control.transport); @@ -415,14 +432,41 @@ int QpiddBroker::execute (QpiddOptions * ::SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); brokerPtr->accept(); std::cout << options->broker.port << std::endl; - brokerPtr->run(); - // Now disable shutdown - s_service.setShutdownProc(0,0); + // If running as a service, tell SCM we're up. There's still a chance + // that store recovery will drag out the time before the broker actually + // responds to requests, but integrating that mechanism with the SCM + // updating is probably more work than it's worth. + if (svcStatusHandle != 0) { + svcStatus.dwCheckPoint = 0; + svcStatus.dwWaitHint = 0; + svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + svcStatus.dwCurrentState = SERVICE_RUNNING; + ::SetServiceStatus(svcStatusHandle, &svcStatus); + } - waitShut.signal(); // In case we shut down some other way + brokerPtr->run(); + waitShut.signal(); // In case we shut down some other way waitThr.join(); + return 0; +} + - // CloseHandle(h); +int main(int argc, char* argv[]) +{ + // If started as a service, notify the SCM we're up. Else just run. + // If as a service, StartServiceControlDispatcher doesn't return until + // the service is stopped. + SERVICE_TABLE_ENTRY dispatchTable[] = + { + { "", (LPSERVICE_MAIN_FUNCTION)ServiceMain }, + { NULL, NULL } + }; + if (!StartServiceCtrlDispatcher(dispatchTable)) { + DWORD err = ::GetLastError(); + if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) // Run as console + return run_broker(argc, argv); + throw QPID_WINDOWS_ERROR(err); + } return 0; } Added: qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp URL: http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp?rev=1079078&view=auto ============================================================================== --- qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp (added) +++ qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp Tue Mar 8 01:41:53 2011 @@ -0,0 +1,332 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "qpid/log/Statement.h" +#include "qpid/sys/windows/check.h" +#include "SCM.h" + +#pragma comment(lib, "advapi32.lib") + +namespace { + +// Container that will close a SC_HANDLE upon destruction. +class AutoServiceHandle { +public: + AutoServiceHandle(SC_HANDLE h_ = NULL) : h(h_) {} + ~AutoServiceHandle() { if (h != NULL) ::CloseServiceHandle(h); } + void release() { h = NULL; } + void reset(SC_HANDLE newHandle) + { + if (h != NULL) + ::CloseServiceHandle(h); + h = newHandle; + } + operator SC_HANDLE() const { return h; } + +private: + SC_HANDLE h; +}; + +} + +namespace qpid { +namespace windows { + +SCM::SCM() : scmHandle(NULL) +{ +} + +SCM::~SCM() +{ + if (NULL != scmHandle) + ::CloseServiceHandle(scmHandle); +} + +/** + * Install this executable as a service + */ +void SCM::install(const string& serviceName, + const string& serviceDesc, + const string& args, + DWORD startType, + const string& account, + const string& password, + const string& depends) +{ + // Handle dependent service name list; Windows wants a set of nul-separated + // names ending with a double nul. + string depends2 = depends; + if (!depends2.empty()) { + // CDL to null delimiter w/ trailing double null + size_t p = 0; + while ((p = depends2.find_first_of( ',', p)) != string::npos) + depends2.replace(p, 1, 1, '\0'); + depends2.push_back('\0'); + depends2.push_back('\0'); + } + +#if 0 + // I'm nervous about adding a user/password check here. Is this a + // potential attack vector, letting users check passwords without + // control? -Steve Huston, Feb 24, 2011 + + // Validate account, password + HANDLE hToken = NULL; + bool logStatus = false; + if (!account.empty() && !password.empty() && + !(logStatus = ::LogonUserA(account.c_str(), + "", + password.c_str(), + LOGON32_LOGON_NETWORK, + LOGON32_PROVIDER_DEFAULT, + &hToken ) != 0)) + std::cout << "warning: supplied account & password failed with LogonUser." << std::endl; + if (logStatus) + ::CloseHandle(hToken); +#endif + + // Get fully qualified .exe name + char myPath[MAX_PATH]; + DWORD myPathLength = ::GetModuleFileName(NULL, myPath, MAX_PATH); + QPID_WINDOWS_CHECK_NOT(myPathLength, 0); + string imagePath(myPath, myPathLength); + if (!args.empty()) + imagePath += " " + args; + + // Ensure there's a handle to the SCM database. + openSvcManager(); + + // Create the service + SC_HANDLE svcHandle; + svcHandle = ::CreateService(scmHandle, // SCM database + serviceName.c_str(), // name of service + serviceDesc.c_str(), // name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + startType, // start type + SERVICE_ERROR_NORMAL, // error cntrl type + imagePath.c_str(), // path to service's binary w/ optional arguments + NULL, // no load ordering group + NULL, // no tag identifier + depends2.empty() ? NULL : depends2.c_str(), + account.empty() ? NULL : account.c_str(), // account name, or NULL for LocalSystem + password.empty() ? NULL : password.c_str()); // password, or NULL for none + QPID_WINDOWS_CHECK_NULL(svcHandle); + ::CloseServiceHandle(svcHandle); + QPID_LOG(info, "Service installed successfully"); +} + +/** + * + */ +void SCM::uninstall(const string& serviceName) +{ + // Ensure there's a handle to the SCM database. + openSvcManager(); + AutoServiceHandle svc(::OpenService(scmHandle, + serviceName.c_str(), + DELETE)); + QPID_WINDOWS_CHECK_NULL((SC_HANDLE)svc); + QPID_WINDOWS_CHECK_NOT(::DeleteService(svc), 0); + QPID_LOG(info, "Service deleted successfully."); +} + +/** + * Attempt to start the service. + */ +void SCM::start(const string& serviceName) +{ + // Ensure we have a handle to the SCM database. + openSvcManager(); + + // Get a handle to the service. + AutoServiceHandle svc(::OpenService(scmHandle, + serviceName.c_str(), + SERVICE_ALL_ACCESS)); + QPID_WINDOWS_CHECK_NULL(svc); + + // Check the status in case the service is not stopped. + DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING); + if (state == SERVICE_STOP_PENDING) + throw qpid::Exception("Timed out waiting for running service to stop."); + + // Attempt to start the service. + QPID_WINDOWS_CHECK_NOT(::StartService(svc, 0, NULL), 0); + + QPID_LOG(info, "Service start pending..."); + + // Check the status until the service is no longer start pending. + state = waitForStateChangeFrom(svc, SERVICE_START_PENDING); + // Determine whether the service is running. + if (state == SERVICE_RUNNING) { + QPID_LOG(info, "Service started successfully"); + } + else { + throw qpid::Exception(QPID_MSG("Service not yet running; state now " << state)); + } +} + +/** + * + */ +void SCM::stop(const string& serviceName) +{ + // Ensure a handle to the SCM database. + openSvcManager(); + + // Get a handle to the service. + AutoServiceHandle svc(::OpenService(scmHandle, + serviceName.c_str(), + SERVICE_STOP | SERVICE_QUERY_STATUS | + SERVICE_ENUMERATE_DEPENDENTS)); + QPID_WINDOWS_CHECK_NULL(svc); + + // Make sure the service is not already stopped; if it's stop-pending, + // wait for it to finalize. + DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING); + if (state == SERVICE_STOPPED) { + QPID_LOG(info, "Service is already stopped"); + return; + } + + // If the service is running, dependencies must be stopped first. + std::auto_ptr<ENUM_SERVICE_STATUS> deps; + DWORD numDeps = getDependentServices(svc, deps); + for (DWORD i = 0; i < numDeps; i++) + stop(deps.get()[i].lpServiceName); + + // Dependents stopped; send a stop code to the service. + SERVICE_STATUS_PROCESS ssp; + if (!::ControlService(svc, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp)) + throw qpid::Exception(QPID_MSG("Stopping " << serviceName << ": " << + qpid::sys::strError(::GetLastError()))); + + // Wait for the service to stop. + state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING); + if (state == SERVICE_STOPPED) + QPID_LOG(info, QPID_MSG("Service " << serviceName << + " stopped successfully.")); +} + +/** + * + */ +void SCM::openSvcManager() +{ + if (NULL != scmHandle) + return; + + scmHandle = ::OpenSCManager(NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // Rights + QPID_WINDOWS_CHECK_NULL(scmHandle); +} + +DWORD SCM::waitForStateChangeFrom(SC_HANDLE svc, DWORD originalState) +{ + SERVICE_STATUS_PROCESS ssStatus; + DWORD bytesNeeded; + DWORD waitTime; + if (!::QueryServiceStatusEx(svc, // handle to service + SC_STATUS_PROCESS_INFO, // information level + (LPBYTE)&ssStatus, // address of structure + sizeof(ssStatus), // size of structure + &bytesNeeded)) // size needed if buffer is too small + throw QPID_WINDOWS_ERROR(::GetLastError()); + + // Save the tick count and initial checkpoint. + DWORD startTickCount = ::GetTickCount(); + DWORD oldCheckPoint = ssStatus.dwCheckPoint; + + // Wait for the service to change out of the noted state. + while (ssStatus.dwCurrentState == originalState) { + // Do not wait longer than the wait hint. A good interval is + // one-tenth of the wait hint but not less than 1 second + // and not more than 10 seconds. + waitTime = ssStatus.dwWaitHint / 10; + if (waitTime < 1000) + waitTime = 1000; + else if (waitTime > 10000) + waitTime = 10000; + + ::Sleep(waitTime); + + // Check the status until the service is no longer stop pending. + if (!::QueryServiceStatusEx(svc, + SC_STATUS_PROCESS_INFO, + (LPBYTE) &ssStatus, + sizeof(ssStatus), + &bytesNeeded)) + throw QPID_WINDOWS_ERROR(::GetLastError()); + + if (ssStatus.dwCheckPoint > oldCheckPoint) { + // Continue to wait and check. + startTickCount = ::GetTickCount(); + oldCheckPoint = ssStatus.dwCheckPoint; + } else { + if ((::GetTickCount() - startTickCount) > ssStatus.dwWaitHint) + break; + } + } + return ssStatus.dwCurrentState; +} + +/** + * Get the services that depend on @arg svc. All dependent service info + * is returned in an array of ENUM_SERVICE_STATUS structures via @arg deps. + * + * @retval The number of dependent services. + */ +DWORD SCM::getDependentServices(SC_HANDLE svc, + std::auto_ptr<ENUM_SERVICE_STATUS>& deps) +{ + DWORD bytesNeeded; + DWORD numEntries; + + // Pass a zero-length buffer to get the required buffer size. + if (::EnumDependentServices(svc, + SERVICE_ACTIVE, + 0, + 0, + &bytesNeeded, + &numEntries)) { + // If the Enum call succeeds, then there are no dependent + // services, so do nothing. + return 0; + } + + if (::GetLastError() != ERROR_MORE_DATA) + throw QPID_WINDOWS_ERROR((::GetLastError())); + + // Allocate a buffer for the dependencies. + deps.reset((LPENUM_SERVICE_STATUS)(new char[bytesNeeded])); + // Enumerate the dependencies. + if (!::EnumDependentServices(svc, + SERVICE_ACTIVE, + deps.get(), + bytesNeeded, + &bytesNeeded, + &numEntries)) + throw QPID_WINDOWS_ERROR((::GetLastError())); + return numEntries; +} + +} } // namespace qpid::windows Added: qpid/branches/QPID-2519/cpp/src/windows/SCM.h URL: http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/windows/SCM.h?rev=1079078&view=auto ============================================================================== --- qpid/branches/QPID-2519/cpp/src/windows/SCM.h (added) +++ qpid/branches/QPID-2519/cpp/src/windows/SCM.h Tue Mar 8 01:41:53 2011 @@ -0,0 +1,109 @@ +#ifndef WINDOWS_SCM_H +#define WINDOWS_SCM_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <memory> +#include <string> +using std::string; + +#ifdef UNICODE +#undef UNICODE +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include <windows.h> + +namespace qpid { +namespace windows { + +/** + * @class SCM + * + * Access the Windows Service Control Manager. + */ +class SCM +{ +public: + SCM(); + ~SCM(); + + /** + * Install this executable as a service + * + * @param serviceName The name of the service + * @param serviceDesc Description of the service's purpose + * @param args The argument list to pass into the service + * @param startType The start type: SERVICE_DEMAND_START, + * SERVICE_AUTO_START, SERVICE_DISABLED + * @param account If not empty, the account name to install this + * service under + * @param password If not empty, the account password to install this + * service with + * @param depends If not empty, a comma delimited list of services + * that must start before this one + */ + void install(const string& serviceName, + const string& serviceDesc, + const string& args, + DWORD startType = SERVICE_DEMAND_START, + const string& account = "NT AUTHORITY\\LocalSystem", + const string& password = "", + const string& depends = ""); + + /** + * Uninstall this executable as a service + * + * @param serviceName the name of the service + */ + void uninstall(const string& serviceName); + + /** + * Start the specified service + * + * @param serviceName the name of the service + */ + void start(const string& serviceName); + + /** + * Stop the specified service + * + * @param serviceName the name of the service + */ + void stop(const string &serviceName); + +private: + SC_HANDLE scmHandle; + + void openSvcManager(); + DWORD waitForStateChangeFrom(SC_HANDLE svc, DWORD originalState); + DWORD getDependentServices(SC_HANDLE svc, + std::auto_ptr<ENUM_SERVICE_STATUS>& deps); + +}; + +}} // namespace qpid::windows + +#endif /* #ifndef WINDOWS_SCM_H */ --------------------------------------------------------------------- Apache Qpid - AMQP Messaging Implementation Project: http://qpid.apache.org Use/Interact: mailto:commits-subscr...@qpid.apache.org