diff options
| author | Stephen D. Huston <shuston@apache.org> | 2011-03-08 01:41:53 +0000 |
|---|---|---|
| committer | Stephen D. Huston <shuston@apache.org> | 2011-03-08 01:41:53 +0000 |
| commit | 718ff5b34dd1e87eb79fa4c61fec668d1dc33103 (patch) | |
| tree | dcfb94e75656c6c239fc3dcb754cd2015126424d /cpp/src/windows/QpiddBroker.cpp | |
| parent | d07e9a0a24eeab9b16706c8a001864af1f181182 (diff) | |
| download | qpid-python-718ff5b34dd1e87eb79fa4c61fec668d1dc33103.tar.gz | |
Changes applied to be able to install, uninstall, start, stop, run service. Has trouble running as LocalService (SCM reports timeout after 1 second)
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/QPID-2519@1079078 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp/src/windows/QpiddBroker.cpp')
| -rw-r--r-- | cpp/src/windows/QpiddBroker.cpp | 290 |
1 files changed, 167 insertions, 123 deletions
diff --git a/cpp/src/windows/QpiddBroker.cpp b/cpp/src/windows/QpiddBroker.cpp index 9432cbc077..884cdc366b 100644 --- a/cpp/src/windows/QpiddBroker.cpp +++ b/cpp/src/windows/QpiddBroker.cpp @@ -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 qpid::Options { } }; -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 *options) { 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 *options) { ::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; } |
