summaryrefslogtreecommitdiff
path: root/cpp/src/windows/QpiddBroker.cpp
diff options
context:
space:
mode:
authorStephen D. Huston <shuston@apache.org>2011-03-08 01:41:53 +0000
committerStephen D. Huston <shuston@apache.org>2011-03-08 01:41:53 +0000
commit718ff5b34dd1e87eb79fa4c61fec668d1dc33103 (patch)
treedcfb94e75656c6c239fc3dcb754cd2015126424d /cpp/src/windows/QpiddBroker.cpp
parentd07e9a0a24eeab9b16706c8a001864af1f181182 (diff)
downloadqpid-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.cpp290
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;
}