summaryrefslogtreecommitdiff
path: root/cpp/src
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src')
-rw-r--r--cpp/src/CMakeLists.txt3
-rw-r--r--cpp/src/posix/QpiddBroker.cpp5
-rw-r--r--cpp/src/qpidd.cpp5
-rw-r--r--cpp/src/qpidd.h3
-rw-r--r--cpp/src/windows/QpiddBroker.cpp290
-rw-r--r--cpp/src/windows/SCM.cpp332
-rw-r--r--cpp/src/windows/SCM.h109
-rw-r--r--cpp/src/windows/Service.cpp1050
-rw-r--r--cpp/src/windows/Service.h200
9 files changed, 621 insertions, 1376 deletions
diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index 9dd81e0e31..60f505a10e 100644
--- a/cpp/src/CMakeLists.txt
+++ b/cpp/src/CMakeLists.txt
@@ -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
diff --git a/cpp/src/posix/QpiddBroker.cpp b/cpp/src/posix/QpiddBroker.cpp
index 879935462e..86504ba7fc 100644
--- a/cpp/src/posix/QpiddBroker.cpp
+++ b/cpp/src/posix/QpiddBroker.cpp
@@ -188,3 +188,8 @@ int QpiddBroker::execute (QpiddOptions *options) {
}
return 0;
}
+
+int main(int argc, char* argv[])
+{
+ return run_broker(argc, argv);
+}
diff --git a/cpp/src/qpidd.cpp b/cpp/src/qpidd.cpp
index a7c1dbe8a6..a0e329ca9d 100644
--- a/cpp/src/qpidd.cpp
+++ b/cpp/src/qpidd.cpp
@@ -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.
diff --git a/cpp/src/qpidd.h b/cpp/src/qpidd.h
index c702270e80..a3150a2737 100644
--- a/cpp/src/qpidd.h
+++ b/cpp/src/qpidd.h
@@ -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*/
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;
}
diff --git a/cpp/src/windows/SCM.cpp b/cpp/src/windows/SCM.cpp
new file mode 100644
index 0000000000..232bb04c17
--- /dev/null
+++ b/cpp/src/windows/SCM.cpp
@@ -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
diff --git a/cpp/src/windows/SCM.h b/cpp/src/windows/SCM.h
new file mode 100644
index 0000000000..bdc73bc210
--- /dev/null
+++ b/cpp/src/windows/SCM.h
@@ -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 */
diff --git a/cpp/src/windows/Service.cpp b/cpp/src/windows/Service.cpp
deleted file mode 100644
index 9ca3fb381b..0000000000
--- a/cpp/src/windows/Service.cpp
+++ /dev/null
@@ -1,1050 +0,0 @@
-/*
- *
- * 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 <boost/format.hpp>
-#include <sstream>
-#include <iostream>
-using std::ostringstream;
-#include "qpid/log/Statement.h"
-#include "Service.h"
-
-#pragma comment(lib, "advapi32.lib")
-
-namespace {
-//
-// Purpose:
-// Called by SCM whenever a control code is sent to the service
-// using the ControlService function.
-//
-// Parameters:
-// dwCtrl - control code
-//
-// Return value:
-// None
-//
-VOID WINAPI SvcCtrlHandler(DWORD dwCtrl)
-{
- qpid::windows::Service * pWinService = qpid::windows::Service::getInstance();
- if (pWinService)
- pWinService->svcCtrlHandler(dwCtrl);
-}
-
-/**
- * Entrypoint from the system to run the service.
- */
-VOID WINAPI SvcMain(DWORD dwArgc, char *lpszArgv[])
-{
- qpid::windows::Service* pService = qpid::windows::Service::getInstance();
- if (pService)
- pService->svcMain(dwArgc, lpszArgv);
-}
-
-} // namespace
-
-namespace qpid {
-namespace windows {
-
-/**
- * Ye service instance
- */
-Service* Service::s_pInstance = 0;
-
-/**
- * Run the service
- *
- * @return false if the service could not be started
- */
-bool Service::run(tServiceMainProc main)
-{
- // Register main
- if (!main)
- return false;
- m_main = main;
-
- // TO_DO: Add any additional services for the process to this table.
- SERVICE_TABLE_ENTRY DispatchTable[] =
- {
- { const_cast<LPSTR>(m_serviceName.c_str()), (LPSERVICE_MAIN_FUNCTION) SvcMain },
- { NULL, NULL }
- };
-
- // This call returns when the service has stopped.
- // The process should simply terminate when the call returns.
-
- if (!StartServiceCtrlDispatcher(DispatchTable)) {
- reportApiErrorEvent("StartServiceCtrlDispatcher");
- return false;
- }
- return true;
-}
-
-/**
- * Build a command argc/argv set from a string
- */
-void WinService::commandLineFromString( const string & args, vector<string> & argsv, int * pargc, char ** pargv[] )
-{
- // Build the substring vector
- size_t s = 0;
- while( true )
- {
- // Skip leading space
- size_t p = args.find_first_not_of( " ", s );
- if( p != args.npos )
- s = p;
-
- p = args.find_first_of( " \"", s ); // Look for next quote or space
-
- if( p == args.npos ) // didn't find it ?
- {
- if( s < args.size() - 1 ) // If anything left
- argsv.push_back( args.substr( s ) ); // substring = rest of string
- break; // we're done breaking up
- }
-
- if( args[p] == '\"' ) // found an open quote ?
- {
- size_t p2 = args.find_first_of( "\"", p + 1 ); // search for close quote
- if( p2 == args.npos ) // didn't find it ?
- {
- argsv.push_back( args.substr( p + 1 ) ); // substring = rest of string
- break; // we're done breaking up
- }
- if( p2 - p > 0 ) // If something in between the quotes
- argsv.push_back( args.substr( p + 1, ( p2 - p ) - 1 ) ); // use it
- s = p2 + 1; // resume past close quote
- }
-
- else // otherwise found a space
- {
- argsv.push_back( args.substr( s, p - s ) ); // use it
- s = p + 1; // resume past space
- }
- }
-
- // Build the argv list
- if( pargc )
- * pargc = argsv.size();
- if( pargv )
- {
- char ** ppc = * pargv = new char*[ argsv.size() ];
- for( size_t i = 0; i < argsv.size(); i++ )
- ppc[ i ] = const_cast<char*>( argsv[i].c_str());
- }
-}
-
-/**
- *
- */
-Service::Service(const string& serviceName)
- : ghSvcStopEvent(NULL),
- m_main(0),
- m_shutdownProc(0),
- m_serviceName(serviceName),
- m_pShutdownContext(0)
-{
- s_pInstance = this;
-}
-
-/**
- *
- */
-SC_HANDLE Service::openSvcManager()
-{
- SC_HANDLE schSCManager = ::OpenSCManager(NULL, // local computer
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // Rights
- if (NULL == schSCManager) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("OpenSCManager failed: %1%") % GetLastError()).str());
- //printf("OpenSCManager failed (%d)\n", GetLastError());
- return 0;
- }
-
- return schSCManager;
-}
-
-/**
- *
- */
-SC_HANDLE Service::openService(SC_HANDLE hSvcManager,
- const string& serviceName,
- DWORD rights)
-{
- // Get a handle to the service.
- SC_HANDLE schService = ::OpenService(hSvcManager,
- serviceName.c_str(),
- rights);
- if (schService == NULL) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("OpenService failed: %1%") % GetLastError()).str());
- //printf("OpenService failed (%d)\n", GetLastError());
- ::CloseServiceHandle(hSvcManager);
- return false;
- }
-
- return schService;
-}
-
-/**
- * Install this executable as a service
- */
-bool Service::install(const string& serviceName,
- const string& args,
- DWORD startType,
- const string& account,
- const string& password,
- const string& depends)
-{
- SC_HANDLE schSCManager;
- SC_HANDLE schService;
- char szPath[MAX_PATH];
-
- // Handle dependent service name list
- char * pDepends = 0;
- 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');
- pDepends = const_cast<char*>(depends2.c_str()); // win doesn't modify, so can use in this case
- }
-
- // 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);
-
- // Get fully qualified .exe name
- if (!::GetModuleFileName(NULL, szPath, MAX_PATH)) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("Cannot install service: %1%") % GetLastError()).str());
- //printf("Cannot install service (%d)\n", GetLastError());
- return false;
- }
-
- string imagePath = szPath;
- if (!args.empty())
- imagePath += " " + args;
-
- // Get a handle to the SCM database.
- if (!(schSCManager = openSvcManager()))
- return false;
-
- // Create the service
- schService = ::CreateService(schSCManager, // SCM database
- serviceName.c_str(), // name of service
- serviceName.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
- pDepends, // Dependant svc list
- account.empty() ? NULL : account.c_str(), // account name, or NULL for LocalSystem
- password.empty() ? NULL : password.c_str()); // password, or NULL for none
- if (schService == NULL) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("CreateService failed: %1%") % GetLastError()).str());
- //printf("CreateService failed (%d)\n", GetLastError());
- ::CloseServiceHandle(schSCManager);
- return false;
- }
- //Note [ds, 27.09.2010]
- QPID_LOG(info, "Service installed successfully");
- //printf("Service installed successfully\n");
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
-
- return true;
-}
-
-/**
- *
- */
-bool Service::start(const string& serviceName)
-{
- SC_HANDLE schSCManager;
- SC_HANDLE schService;
-
- SERVICE_STATUS_PROCESS ssStatus;
- DWORD dwOldCheckPoint;
- DWORD dwStartTickCount;
- DWORD dwWaitTime;
- DWORD dwBytesNeeded;
-
- // Get a handle to the SCM database.
- if (!(schSCManager = openSvcManager()))
- return false;
-
- // Get a handle to the service.
- if (!(schService = openService(schSCManager, serviceName, SERVICE_ALL_ACCESS)))
- return false;
-
- // Check the status in case the service is not stopped.
- if (!::QueryServiceStatusEx(schService, // handle to service
- SC_STATUS_PROCESS_INFO, // information level
- (LPBYTE) &ssStatus, // address of structure
- sizeof(SERVICE_STATUS_PROCESS), // size of structure
- &dwBytesNeeded)) { // size needed if buffer is too small
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("QueryServiceStatusEx failed: %1%") % GetLastError()).str());
- //printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
- }
-
- // Check if the service is already running. It would be possible
- // to stop the service here, but for simplicity this example just returns.
- if (ssStatus.dwCurrentState != SERVICE_STOPPED &&
- ssStatus.dwCurrentState != SERVICE_STOP_PENDING) {
- //Note [ds, 27.09.2010]
- QPID_LOG(warning, "Cannot start the service because it is already running");
- //printf("Cannot start the service because it is already running\n");
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
- }
-
- // Save the tick count and initial checkpoint.
- dwStartTickCount = ::GetTickCount();
- dwOldCheckPoint = ssStatus.dwCheckPoint;
-
- // Wait for the service to stop before attempting to start it.
- while (ssStatus.dwCurrentState == SERVICE_STOP_PENDING) {
- // 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.
- dwWaitTime = ssStatus.dwWaitHint / 10;
- if (dwWaitTime < 1000)
- dwWaitTime = 1000;
- else if (dwWaitTime > 10000)
- dwWaitTime = 10000;
-
- ::Sleep(dwWaitTime);
-
- // Check the status until the service is no longer stop pending.
- if (!::QueryServiceStatusEx(
- schService, // handle to service
- SC_STATUS_PROCESS_INFO, // information level
- (LPBYTE) &ssStatus, // address of structure
- sizeof(SERVICE_STATUS_PROCESS), // size of structure
- &dwBytesNeeded)) { // size needed if buffer is too small
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("QueryServiceStatusEx failed: %1%") % GetLastError()).str());
- //printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
- }
-
- if (ssStatus.dwCheckPoint > dwOldCheckPoint) {
- // Continue to wait and check.
- dwStartTickCount = GetTickCount();
- dwOldCheckPoint = ssStatus.dwCheckPoint;
- } else {
- if ((::GetTickCount() - dwStartTickCount) > ssStatus.dwWaitHint) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, "Service was in SERVICE_STOP_PENDING state, timeout waiting for service to stop");
- //printf("Timeout waiting for service to stop\n");
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
- }
- }
- }
-
- // Attempt to start the service.
- if (!::StartService(schService, // handle to service
- 0, // number of arguments
- NULL)) { // no arguments
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("StartService failed: %1%") % GetLastError()).str());
- //printf("StartService failed (%d)\n", GetLastError());
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
- }
-
- //Note [ds, 27.09.2010]
- QPID_LOG(info, "Service start pending...");
- //printf("Service start pending...\n");
-
- // Check the status until the service is no longer start pending.
- if (!::QueryServiceStatusEx(
- schService, // handle to service
- SC_STATUS_PROCESS_INFO, // info level
- (LPBYTE) &ssStatus, // address of structure
- sizeof(SERVICE_STATUS_PROCESS), // size of structure
- &dwBytesNeeded)) { // if buffer too small
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("QueryServiceStatusEx failed: %1%") % GetLastError()).str());
- //printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
- }
-
- // Save the tick count and initial checkpoint.
- dwStartTickCount = ::GetTickCount();
- dwOldCheckPoint = ssStatus.dwCheckPoint;
-
- while (ssStatus.dwCurrentState == SERVICE_START_PENDING) {
- // Do not wait longer than the wait hint. A good interval is
- // one-tenth the wait hint, but no less than 1 second and no
- // more than 10 seconds.
-
- dwWaitTime = ssStatus.dwWaitHint / 10;
-
- if (dwWaitTime < 1000)
- dwWaitTime = 1000;
- else if (dwWaitTime > 10000)
- dwWaitTime = 10000;
- ::Sleep(dwWaitTime);
-
- // Check the status again.
- if (!::QueryServiceStatusEx(schService, // handle to svc
- SC_STATUS_PROCESS_INFO, // info level
- (LPBYTE) &ssStatus, // address of structure
- sizeof(SERVICE_STATUS_PROCESS), // size of structure
- &dwBytesNeeded)) { // if buff too small
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("QueryServiceStatusEx failed: %1%") % GetLastError()).str());
- //printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
- break;
- }
-
- if (ssStatus.dwCheckPoint > dwOldCheckPoint) {
- // Continue to wait and check.
- dwStartTickCount = GetTickCount();
- dwOldCheckPoint = ssStatus.dwCheckPoint;
- }
- else {
- if ((::GetTickCount() - dwStartTickCount) > ssStatus.dwWaitHint) {
- // No progress made within the wait hint.
- break;
- }
- }
- }
-
- // Determine whether the service is running.
- if (ssStatus.dwCurrentState == SERVICE_RUNNING) {
- //Note [ds, 27.09.2010]
- QPID_LOG(info, "Service started successfully");
- //printf("Service started successfully.\n");
-
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return true;
- }
-
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("Service not started: %1% ") % GetLastError()).str());
- //printf("Service not started. \n");
- QPID_LOG(error, (boost::format("Current State: %1% ") % ssStatus.dwCurrentState).str());
- //printf(" Current State: %d\n", ssStatus.dwCurrentState);
- QPID_LOG(error, (boost::format("Exit Code: %1% ") % ssStatus.dwWin32ExitCode).str());
- //printf(" Exit Code: %d\n", ssStatus.dwWin32ExitCode);
- QPID_LOG(error, (boost::format("Check Point: %1% ") % ssStatus.dwCheckPoint).str());
- //printf(" Check Point: %d\n", ssStatus.dwCheckPoint);
- QPID_LOG(error, (boost::format("Wait Hint: %1% ") % ssStatus.dwWaitHint).str());
- //printf(" Wait Hint: %d\n", ssStatus.dwWaitHint);
-
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
-}
-
-/**
- *
- */
-bool Service::stop(const string& serviceName)
-{
- SC_HANDLE schSCManager;
- SC_HANDLE schService;
-
- SERVICE_STATUS_PROCESS ssp;
- DWORD dwStartTime = GetTickCount();
- DWORD dwBytesNeeded;
- DWORD dwTimeout = 30000; // 30-second time-out
- DWORD dwWaitTime;
-
- // Get a handle to the SCM database.
- if (!(schSCManager = openSvcManager()))
- return false;
-
- // Get a handle to the service.
- if (!(schService = openService(schSCManager,
- serviceName,
- SERVICE_STOP | SERVICE_QUERY_STATUS |
- SERVICE_ENUMERATE_DEPENDENTS)))
- return false;
-
- // Make sure the service is not already stopped.
- if (!::QueryServiceStatusEx(schService,
- SC_STATUS_PROCESS_INFO,
- (LPBYTE)&ssp,
- sizeof(SERVICE_STATUS_PROCESS),
- &dwBytesNeeded)) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("QueryServiceStatusEx failed: %1%") % GetLastError()).str());
- //printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
- goto stop_cleanup;
- }
-
- if (ssp.dwCurrentState == SERVICE_STOPPED) {
- //Note [ds, 27.09.2010]
- QPID_LOG(info, "Service is already stopped");
- //printf("Service is already stopped.\n");
- goto stop_cleanup;
- }
-
- // If a stop is pending, wait for it.
- while (ssp.dwCurrentState == SERVICE_STOP_PENDING) {
- //Note [ds, 27.09.2010]
- QPID_LOG(info, "Service stop pending...");
- //printf("Service stop pending...\n");
-
- // 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.
- dwWaitTime = ssp.dwWaitHint / 10;
- if (dwWaitTime < 1000)
- dwWaitTime = 1000;
- else if (dwWaitTime > 10000)
- dwWaitTime = 10000;
- ::Sleep(dwWaitTime);
-
- if (!::QueryServiceStatusEx(schService,
- SC_STATUS_PROCESS_INFO,
- (LPBYTE)&ssp,
- sizeof(SERVICE_STATUS_PROCESS),
- &dwBytesNeeded)) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("QueryServiceStatusEx failed: %1%") % GetLastError()).str());
- //printf("QueryServiceStatusEx failed (%d)\n", GetLastError());
- goto stop_cleanup;
- }
-
- if (ssp.dwCurrentState == SERVICE_STOPPED) {
- //Note [ds, 27.09.2010]
- QPID_LOG(info, "Service stopped successfully.");
- //printf("Service stopped successfully.\n");
- goto stop_cleanup;
- }
-
- if ((::GetTickCount() - dwStartTime) > dwTimeout) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, "Service stop timed out.");
- //printf("Service stop timed out.\n");
- goto stop_cleanup;
- }
- }
-
- // If the service is running, dependencies must be stopped first.
- StopDependentServices(schSCManager, schService);
-
- // Send a stop code to the service.
- if (!::ControlService(schService,
- SERVICE_CONTROL_STOP,
- (LPSERVICE_STATUS)&ssp)) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("ControlService failed: %1%") % GetLastError()).str());
- //printf( "ControlService failed (%d)\n", GetLastError() );
- goto stop_cleanup;
- }
-
- // Wait for the service to stop.
- while (ssp.dwCurrentState != SERVICE_STOPPED) {
- ::Sleep(ssp.dwWaitHint);
- if (!::QueryServiceStatusEx(schService,
- SC_STATUS_PROCESS_INFO,
- (LPBYTE)&ssp,
- sizeof(SERVICE_STATUS_PROCESS),
- &dwBytesNeeded)) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("QueryServiceStatusEx failed: %1%") % GetLastError()).str());
- //printf( "QueryServiceStatusEx failed (%d)\n", GetLastError() );
- goto stop_cleanup;
- }
-
- if (ssp.dwCurrentState == SERVICE_STOPPED)
- break;
-
- if ((::GetTickCount() - dwStartTime) > dwTimeout) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, "Wait timed out.");
- //printf( "Wait timed out\n" );
- goto stop_cleanup;
- }
- }
- //Note [ds, 27.09.2010]
- QPID_LOG(info,"Service stopped successfully.");
- //printf("Service stopped successfully\n");
-
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return true;
-
-stop_cleanup:
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
-}
-
-/**
- *
- */
-BOOL Service::StopDependentServices(SC_HANDLE schSCManager,
- SC_HANDLE schService)
-{
- DWORD i;
- DWORD dwBytesNeeded;
- DWORD dwCount;
-
- LPENUM_SERVICE_STATUS lpDependencies = NULL;
- ENUM_SERVICE_STATUS ess;
- SC_HANDLE hDepService;
- SERVICE_STATUS_PROCESS ssp;
-
- DWORD dwStartTime = ::GetTickCount();
- DWORD dwTimeout = 30000; // 30-second time-out
-
- // Pass a zero-length buffer to get the required buffer size.
- if (::EnumDependentServices(schService,
- SERVICE_ACTIVE,
- lpDependencies,
- 0,
- &dwBytesNeeded,
- &dwCount)) {
- // If the Enum call succeeds, then there are no dependent
- // services, so do nothing.
- return TRUE;
- }
- else {
- if (::GetLastError() != ERROR_MORE_DATA)
- return FALSE; // Unexpected error
-
- // Allocate a buffer for the dependencies.
- lpDependencies = (LPENUM_SERVICE_STATUS) HeapAlloc(::GetProcessHeap(),
- HEAP_ZERO_MEMORY,
- dwBytesNeeded);
- if (!lpDependencies)
- return FALSE;
-
- __try {
- // Enumerate the dependencies.
- if (!::EnumDependentServices(schService,
- SERVICE_ACTIVE,
- lpDependencies,
- dwBytesNeeded,
- &dwBytesNeeded,
- &dwCount))
- return FALSE;
-
- for (i = 0; i < dwCount; i++) {
- ess = *(lpDependencies + i);
- // Open the service.
- hDepService = ::OpenService(schSCManager,
- ess.lpServiceName,
- SERVICE_STOP | SERVICE_QUERY_STATUS);
- if (!hDepService)
- return FALSE;
-
- __try {
- // Send a stop code.
- if (!::ControlService(hDepService,
- SERVICE_CONTROL_STOP,
- (LPSERVICE_STATUS) &ssp))
- return FALSE;
-
- // Wait for the service to stop.
- while (ssp.dwCurrentState != SERVICE_STOPPED) {
- ::Sleep(ssp.dwWaitHint);
- if (!::QueryServiceStatusEx(hDepService,
- SC_STATUS_PROCESS_INFO,
- (LPBYTE)&ssp,
- sizeof(ssp),
- &dwBytesNeeded))
- return FALSE;
-
- if (ssp.dwCurrentState == SERVICE_STOPPED)
- break;
-
- if ((::GetTickCount() - dwStartTime) > dwTimeout)
- return FALSE;
- }
- }
- __finally {
- // Always release the service handle.
- ::CloseServiceHandle(hDepService);
- }
- }
- }
- __finally {
- // Always free the enumeration buffer.
- ::HeapFree(GetProcessHeap(), 0, lpDependencies);
- }
- }
- return TRUE;
-}
-
-/**
- *
- */
-bool Service::uninstall(const string& serviceName)
-{
- SC_HANDLE schSCManager;
- SC_HANDLE schService;
-
- // Get a handle to the SCM database.
- if (!(schSCManager = openSvcManager()))
- return false;
-
- // Get a handle to the service.
- if (!(schService = openService(schSCManager, serviceName, DELETE)))
- return false;
-
- // Delete the service.
- if (!::DeleteService(schService)) {
- //Note [ds, 27.09.2010]
- QPID_LOG(error, (boost::format("DeleteService failed: %1%") % GetLastError()).str());
- //printf("DeleteService failed (%d)\n", GetLastError());
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return false;
- }
-
- //Note [ds, 27.09.2010]
- QPID_LOG(info, "Service deleted successfully.");
- //printf("Service deleted successfully\n");
- ::CloseServiceHandle(schService);
- ::CloseServiceHandle(schSCManager);
- return true;
-}
-
-/**
- *
- */
-bool Service::getServiceImagePathArgs(string& args)
-{
- // Get service ImagePath string from registry
- string subkey = "SYSTEM\\CurrentControlSet\\Services\\" + m_serviceName;
- HKEY hKey;
- LONG lResult;
- if ((lResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
- subkey.c_str(),
- 0,
- KEY_READ,
- &hKey)) != ERROR_SUCCESS) {
- reportApiErrorEvent("RegOpenKeyEx");
- return false;
- }
-
- DWORD type, len;
- char buf[1024];
- len = sizeof(buf) - 1;
- lResult = ::RegQueryValueEx(hKey,
- "ImagePath",
- 0,
- &type,
- reinterpret_cast<LPBYTE>(buf),
- &len);
- ::RegCloseKey(hKey);
- if (lResult != ERROR_SUCCESS) {
- reportApiErrorEvent( "RegQueryValueEx" );
- return false;
- }
- buf[len] = 0; // Guarantee string ends with null pad (required - Windows 'quirk')
- string ip(buf); // The service ImagePath string
-
- // Strip service path from start
- char szPath[MAX_PATH];
- if (!::GetModuleFileName(NULL, szPath, MAX_PATH)) {
- reportApiErrorEvent( "GetModuleFileName");
- return false;
- }
- string sp(szPath); // The service path string
- string ipa;
- if (sp.length() < ip.length())
- ipa = ip.substr(sp.length() + 1);
- args = ipa;
- return true;
-}
-
-/**
- *
- */
-void WINAPI Service::svcMain(DWORD dwArgc, char *lpszArgv[])
-{
- // Use service name passed in
- m_serviceName = lpszArgv[0];
-
- // Register the handler function for the service
- if (!(gSvcStatusHandle = ::RegisterServiceCtrlHandler(m_serviceName.c_str(),
- SvcCtrlHandler))) {
- reportApiErrorEvent( TEXT("RegisterServiceCtrlHandler") );
- return;
- }
-
- // Initialize SERVICE_STATUS members
- gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- gSvcStatus.dwServiceSpecificExitCode = 0;
-
- // Report initial status to the SCM
- ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
-
- // Only got 1 arg? Try to obtain auto-start arg list
- // For now, always use it...
- string ipa;
- if (!getServiceImagePathArgs(ipa))
- return;
- Service::reportInfoEvent("ipa = " + ipa);
-
- vector<string> argsv;
- int pargc;
- char ** pargv = 0;
- commandLineFromString( ipa, argsv, &pargc, &pargv );
-
- // Assemble command line
- string r_args = "rargs:";
- for( DWORD i=0; i<dwArgc; i++ ) {
- if (i)
- r_args += " ";
- r_args += lpszArgv[i];
- }
- Service::reportInfoEvent(r_args);
- Service::reportInfoEvent(string("Hello world!"));
-
- // Report running status when initialization is complete.
- ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
-
- // Create stop event
- ghSvcStopEvent = ::CreateEvent(NULL, // default security attributes
- TRUE, // manual reset event
- FALSE, // not signaled
- NULL); // no name
- if (ghSvcStopEvent == NULL) {
- ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
- return;
- }
-
- // Pass control to main or init function
- //if( m_main )
- // m_main( dwArgc, lpszArgv );
- //else
- // svcInit( dwArgc, lpszArgv );
-
- if (m_main)
- m_main(pargc, pargv);
- else
- svcInit(pargc, pargv);
-
- if (pargv)
- delete[] pargv;
-
- // Report stop as our last action...
- ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
-}
-
-//
-// Purpose:
-// The service code
-//
-// Parameters:
-// dwArgc - Number of arguments in the lpszArgv array
-// lpszArgv - Array of strings. The first string is the name of
-// the service and subsequent strings are passed by the process
-// that called the StartService function to start the service.
-//
-// Return value:
-// None
-//
-void Service::svcInit(DWORD dwArgc, char *lpszArgv[])
-{
- // TO_DO: Declare and set any required variables.
- // Be sure to periodically call ReportSvcStatus() with
- // SERVICE_START_PENDING. If initialization fails, call
- // ReportSvcStatus with SERVICE_STOPPED.
-
- // Create an event. The control handler function, SvcCtrlHandler,
- // signals this event when it receives the stop control code.
-
-
-
- // TO_DO: Perform work until service stops.
-
- while (1) {
- // Check whether to stop the service.
- ::WaitForSingleObject(ghSvcStopEvent, INFINITE);
- ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
- return;
- }
-}
-
-//
-// Purpose:
-// Sets the current service status and reports it to the SCM.
-//
-// Parameters:
-// dwCurrentState - The current state (see SERVICE_STATUS)
-// dwWin32ExitCode - The system error code
-// dwWaitHint - Estimated time for pending operation,
-// in milliseconds
-//
-// Return value:
-// None
-//
-void Service::ReportSvcStatus(DWORD dwCurrentState,
- DWORD dwWin32ExitCode,
- DWORD dwWaitHint)
-{
- static DWORD dwCheckPoint = 1;
-
- // Fill in the SERVICE_STATUS structure.
- gSvcStatus.dwCurrentState = dwCurrentState;
- gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
- gSvcStatus.dwWaitHint = dwWaitHint;
-
- if (dwCurrentState == SERVICE_START_PENDING)
- gSvcStatus.dwControlsAccepted = 0;
- else
- gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
-
- if ((dwCurrentState == SERVICE_RUNNING) ||
- (dwCurrentState == SERVICE_STOPPED) )
- gSvcStatus.dwCheckPoint = 0;
- else
- gSvcStatus.dwCheckPoint = dwCheckPoint++;
-
- // Report the status of the service to the SCM.
- ::SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
-}
-
-
-void WINAPI Service::svcCtrlHandler(DWORD dwCtrl)
-{
- // Handle the requested control code.
- switch(dwCtrl) {
- case SERVICE_CONTROL_STOP:
- ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
- // Signal the service to stop.
- // SetEvent(ghSvcStopEvent);
- if (m_shutdownProc)
- m_shutdownProc(m_pShutdownContext);
-
- ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
- break;
-
- case SERVICE_CONTROL_INTERROGATE:
- break;
-
- default:
- break;
- }
-}
-
-//
-// Purpose:
-// Logs messages to the event log
-//
-// Parameters:
-// szFunction - name of function that failed
-//
-// Return value:
-// None
-//
-// Remarks:
-// The service must have an entry in the Application event log.
-//
-void Service::reportApiErrorEvent(char * szFunction)
-{
- HANDLE hEventSource;
- LPCTSTR lpszStrings[2];
- // char Buffer[80];
-
- hEventSource = ::RegisterEventSource(NULL, m_serviceName.c_str());
- if (NULL != hEventSource) {
- lpszStrings[0] = m_serviceName.c_str();
-
- // sprintf( Buffer, "%s failed with %d", szFunction, GetLastError());
- // lpszStrings[1] = Buffer;
- ostringstream buf;
- buf << szFunction << " failed with " << GetLastError();
- lpszStrings[1] = buf.str().c_str();
-
-#define SVC_ERROR ((DWORD)0xC0020001L)
-
- ::ReportEvent(hEventSource, // event log handle
- EVENTLOG_ERROR_TYPE, // event type
- 0, // event category
- SVC_ERROR, // event identifier
- NULL, // no security identifier
- 2, // size of lpszStrings array
- 0, // no binary data
- lpszStrings, // array of strings
- NULL); // no binary data
-
- ::DeregisterEventSource(hEventSource);
- }
-}
-
-/**
- *
- */
-void Service::reportInfoEvent(const string & message)
-{
- HANDLE hEventSource = ::RegisterEventSource(NULL, m_serviceName.c_str());
- if (hEventSource) {
-// #define SVC_ERROR ((DWORD)0xC0020001L)
-#define SVC_INFO ((DWORD)0x40020001L)
-
- LPCTSTR lpszStrings[2];
- lpszStrings[0] = m_serviceName.c_str();
- lpszStrings[1] = message.c_str();
-
- ::ReportEvent(hEventSource, // event log handle
- EVENTLOG_INFORMATION_TYPE, // event type
- 0, // event category
- SVC_INFO, // event identifier
- NULL, // no security identifier
- 2, // size of lpszStrings array
- 0, // no binary data
- lpszStrings, // array of strings
- NULL); // no binary data
-
- ::DeregisterEventSource( hEventSource );
- }
-}
diff --git a/cpp/src/windows/Service.h b/cpp/src/windows/Service.h
deleted file mode 100644
index df0c4a67a6..0000000000
--- a/cpp/src/windows/Service.h
+++ /dev/null
@@ -1,200 +0,0 @@
-#ifndef WINDOWS_SERVICE_H
-#define WINDOWS_SERVICE_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 <string>
-#include <vector>
-using std::string;
-using std::vector;
-
-#ifdef UNICODE
-#undef UNICODE
-#endif
-
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-
-#include <windows.h>
-
-namespace qpid {
-namespace windows {
-
-class Service
-{
-public:
-
- /**
- * @param serviceName the name to register the service as
- */
- Service(const string& serviceName);
-
- /**
- * Install this executable as a service
- *
- * @param serviceName The name of the service
- * @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
- * @return false on error, which will be sent to stdout
- */
- static bool install(const string& serviceName,
- const string& args,
- DWORD startType = SERVICE_DEMAND_START,
- const string& account = "",
- const string& password = "",
- const string& depends = "");
-
- /**
- * Uninstall this executable as a service
- *
- * @param serviceName the name of the service
- * @return false on error, which will be sent to stdout
- */
- static bool uninstall(const string& serviceName);
-
- /**
- * Start the specified service
- *
- * @param serviceName the name of the service
- * @return false on error, which will be sent to stdout
- */
- static bool start(const string& serviceName);
-
- /**
- * Stop the specified service
- *
- * @param serviceName the name of the service
- * @return false on error, which will be sent to stdout
- */
- static bool stop(const string &serviceName);
-
- typedef VOID (WINAPI *tServiceMainProc)(DWORD dwNumServicesArgs,
- LPSTR *lpServiceArgVectors);
-
- /**
- * Run the service
- *
- * @return false if the service could not be started
- */
- bool run(tServiceMainProc main);
-
- typedef void (WINAPI *tShutdownProc)(void* pContext);
-
- /**
- * Set the shutdown proc
- */
- void setShutdownProc(tShutdownProc shutdownProc,
- void * pContext)
- { m_shutdownProc = shutdownProc; m_pShutdownContext = pContext; }
-
- /**
- *
- */
- void reportApiErrorEvent(char* szFunction);
-
- /**
- *
- */
- void WINAPI svcMain(DWORD dwArgc, char *lpszArgv[]);
-
- /**
- *
- */
- void WINAPI svcCtrlHandler(DWORD dwCtrl);
-
- /**
- *
- */
- void svcInit(DWORD dwArgc, char *lpszArgv[]);
-
- /**
- *
- */
- static Service* getInstance() { return s_pInstance; }
-
- void reportInfoEvent(const string& message);
-
-protected:
-
- static SC_HANDLE openSvcManager();
-
- static SC_HANDLE openService(SC_HANDLE hSvcManager,
- const string& serviceName,
- DWORD rights);
-
- bool getServiceImagePathArgs(string& args);
-
- /**
- * Build a command argc/argv set from a string
- *
- * Note that by convention, the argv list does not OWN the actual substring
- * pointers. To maintain that convention, this function also requires a
- * temporary array of strings to be maintained, which will own the substring
- * memory. Note that the substring pointers are only valid while this
- * temporary array exists. While technically unnecessary (the argv list
- * COULD own the substrings), it simplifies substring management, as it is
- * trivial to place the substring vector on the stack and thereby guarantee
- * it is freed automatically.
- *
- * @param args the string to parse
- * @param argsv the vector to build to hold the substrings
- * @param pargc pointer to the int to return the number of substrings in
- * @param pargv pointer to the character pointer array to allocate to hold
- * substring pointers
- */
- // REPLACE THIS WITH boost split_winmain static void commandLineFromString( const string & args, vector<string> & argsv, int * pargc, char ** pargv[] );
-
- /**
- *
- */
- void ReportSvcStatus(DWORD dwCurrentState,
- DWORD dwWin32ExitCode,
- DWORD dwWaitHint);
-
- /**
- *
- */
- static BOOL StopDependentServices(SC_HANDLE schSCManager,
- SC_HANDLE schService);
-
- static Service * s_pInstance; ///< Singleton
-
- SERVICE_STATUS gSvcStatus; ///<
- SERVICE_STATUS_HANDLE gSvcStatusHandle; ///<
- HANDLE ghSvcStopEvent; ///<
- string m_serviceName; ///< Specified service name
- tServiceMainProc m_main; ///< Registered app entry point
- tShutdownProc m_shutdownProc; ///< Registered shutdown function pointer
- void* m_pShutdownContext; ///< Context pointer supplied to shutdown proc
-};
-
-}} // namespace qpid::windows
-
-#endif /* #ifndef WINDOWS_SERVICE_H */