Author: shuston Date: Fri Oct 21 18:55:57 2011 New Revision: 1187499 URL: http://svn.apache.org/viewvc?rev=1187499&view=rev Log: Add ability to run broker as a Windows service; resolves QPID-2519.
Added: qpid/trunk/qpid/cpp/src/windows/SCM.cpp - copied unchanged from r1079078, qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp qpid/trunk/qpid/cpp/src/windows/SCM.h - copied unchanged from r1079078, qpid/branches/QPID-2519/cpp/src/windows/SCM.h Modified: qpid/trunk/qpid/cpp/src/ (props changed) qpid/trunk/qpid/cpp/src/CMakeLists.txt (contents, props changed) qpid/trunk/qpid/cpp/src/posix/QpiddBroker.cpp qpid/trunk/qpid/cpp/src/qpidd.cpp qpid/trunk/qpid/cpp/src/qpidd.h qpid/trunk/qpid/cpp/src/windows/QpiddBroker.cpp Propchange: qpid/trunk/qpid/cpp/src/ ------------------------------------------------------------------------------ --- svn:mergeinfo (added) +++ svn:mergeinfo Fri Oct 21 18:55:57 2011 @@ -0,0 +1,7 @@ +/qpid/branches/0.5.x-dev/qpid/cpp/src:892761,894875 +/qpid/branches/0.6-release-windows-installer/cpp/src:926803 +/qpid/branches/0.6-release-windows-installer/qpid/cpp/src:926803,927233 +/qpid/branches/QPID-2519/cpp/src:1072051-1079078 +/qpid/branches/java-network-refactor/qpid/cpp/src:805429-825319 +/qpid/branches/qpid-2935/qpid/cpp/src:1061302-1072333 +/qpid/branches/qpid-3346/qpid/cpp/src:1144319-1179855 Modified: qpid/trunk/qpid/cpp/src/CMakeLists.txt URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/CMakeLists.txt?rev=1187499&r1=1187498&r2=1187499&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/CMakeLists.txt (original) +++ qpid/trunk/qpid/cpp/src/CMakeLists.txt Fri Oct 21 18:55:57 2011 @@ -665,6 +665,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows) set (qpidd_platform_SOURCES windows/QpiddBroker.cpp + windows/SCM.cpp ) set (qpidmessaging_platform_SOURCES Propchange: qpid/trunk/qpid/cpp/src/CMakeLists.txt ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Fri Oct 21 18:55:57 2011 @@ -1,6 +1,7 @@ /qpid/branches/0.5.x-dev/qpid/cpp/src/CMakeLists.txt:892761,894875 /qpid/branches/0.6-release-windows-installer/cpp/src/CMakeLists.txt:926803 /qpid/branches/0.6-release-windows-installer/qpid/cpp/src/CMakeLists.txt:926803,927233,932132 +/qpid/branches/QPID-2519/cpp/src/CMakeLists.txt:1072051-1079078 /qpid/branches/java-network-refactor/qpid/cpp/src/CMakeLists.txt:805429-825319 /qpid/branches/qpid-2935/qpid/cpp/src/CMakeLists.txt:1061302-1072333 /qpid/branches/qpid-3346/qpid/cpp/src/CMakeLists.txt:1144319-1179855 Modified: qpid/trunk/qpid/cpp/src/posix/QpiddBroker.cpp URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/posix/QpiddBroker.cpp?rev=1187499&r1=1187498&r2=1187499&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/posix/QpiddBroker.cpp (original) +++ qpid/trunk/qpid/cpp/src/posix/QpiddBroker.cpp Fri Oct 21 18:55:57 2011 @@ -196,3 +196,8 @@ int QpiddBroker::execute (QpiddOptions * } return 0; } + +int main(int argc, char* argv[]) +{ + return run_broker(argc, argv); +} Modified: qpid/trunk/qpid/cpp/src/qpidd.cpp URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpidd.cpp?rev=1187499&r1=1187498&r2=1187499&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpidd.cpp (original) +++ qpid/trunk/qpid/cpp/src/qpidd.cpp Fri Oct 21 18:55:57 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/trunk/qpid/cpp/src/qpidd.h URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpidd.h?rev=1187499&r1=1187498&r2=1187499&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/qpidd.h (original) +++ qpid/trunk/qpid/cpp/src/qpidd.h Fri Oct 21 18:55:57 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/trunk/qpid/cpp/src/windows/QpiddBroker.cpp URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/windows/QpiddBroker.cpp?rev=1187499&r1=1187498&r2=1187499&view=diff ============================================================================== --- qpid/trunk/qpid/cpp/src/windows/QpiddBroker.cpp (original) +++ qpid/trunk/qpid/cpp/src/windows/QpiddBroker.cpp Fri Oct 21 18:55:57 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" @@ -205,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; @@ -225,9 +265,49 @@ struct ProcessControlOptions : public qp } }; +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.") + ("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."); + } +}; + struct QpiddWindowsOptions : public QpiddOptionsPrivate { ProcessControlOptions control; + ServiceOptions service; QpiddWindowsOptions(QpiddOptions *parent) : QpiddOptionsPrivate(parent) { + parent->add(service); parent->add(control); } }; @@ -253,12 +333,63 @@ void QpiddOptions::usage() const { } int QpiddBroker::execute (QpiddOptions *options) { + + // 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); + } + // 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->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. NamedSharedMemory<BrokerInfo> info(brokerInfoName(options->broker.port)); @@ -301,10 +432,41 @@ int QpiddBroker::execute (QpiddOptions * ::SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); brokerPtr->accept(); std::cout << options->broker.port << std::endl; + + // 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); + } + 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; } --------------------------------------------------------------------- Apache Qpid - AMQP Messaging Implementation Project: http://qpid.apache.org Use/Interact: mailto:commits-subscr...@qpid.apache.org