diff options
Diffstat (limited to 'cpp/src')
-rw-r--r-- | cpp/src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | cpp/src/posix/QpiddBroker.cpp | 5 | ||||
-rw-r--r-- | cpp/src/qpidd.cpp | 5 | ||||
-rw-r--r-- | cpp/src/qpidd.h | 3 | ||||
-rw-r--r-- | cpp/src/windows/QpiddBroker.cpp | 290 | ||||
-rw-r--r-- | cpp/src/windows/SCM.cpp | 332 | ||||
-rw-r--r-- | cpp/src/windows/SCM.h | 109 | ||||
-rw-r--r-- | cpp/src/windows/Service.cpp | 1050 | ||||
-rw-r--r-- | cpp/src/windows/Service.h | 200 |
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 */ |