Author: shuston
Date: Tue Mar  8 01:41:53 2011
New Revision: 1079078

URL: http://svn.apache.org/viewvc?rev=1079078&view=rev
Log:
Changes applied to be able to install, uninstall, start, stop, run service. Has 
trouble running as LocalService (SCM reports timeout after 1 second)

Added:
    qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp
    qpid/branches/QPID-2519/cpp/src/windows/SCM.h
Removed:
    qpid/branches/QPID-2519/cpp/src/windows/Service.cpp
    qpid/branches/QPID-2519/cpp/src/windows/Service.h
Modified:
    qpid/branches/QPID-2519/cpp/src/CMakeLists.txt
    qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp
    qpid/branches/QPID-2519/cpp/src/qpidd.cpp
    qpid/branches/QPID-2519/cpp/src/qpidd.h
    qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp

Modified: qpid/branches/QPID-2519/cpp/src/CMakeLists.txt
URL: 
http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/CMakeLists.txt?rev=1079078&r1=1079077&r2=1079078&view=diff
==============================================================================
--- qpid/branches/QPID-2519/cpp/src/CMakeLists.txt (original)
+++ qpid/branches/QPID-2519/cpp/src/CMakeLists.txt Tue Mar  8 01:41:53 2011
@@ -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

Modified: qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp
URL: 
http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp?rev=1079078&r1=1079077&r2=1079078&view=diff
==============================================================================
--- qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp (original)
+++ qpid/branches/QPID-2519/cpp/src/posix/QpiddBroker.cpp Tue Mar  8 01:41:53 
2011
@@ -188,3 +188,8 @@ int QpiddBroker::execute (QpiddOptions *
     }
     return 0;
 }
+
+int main(int argc, char* argv[])
+{
+    return run_broker(argc, argv);
+}

Modified: qpid/branches/QPID-2519/cpp/src/qpidd.cpp
URL: 
http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/qpidd.cpp?rev=1079078&r1=1079077&r2=1079078&view=diff
==============================================================================
--- qpid/branches/QPID-2519/cpp/src/qpidd.cpp (original)
+++ qpid/branches/QPID-2519/cpp/src/qpidd.cpp Tue Mar  8 01:41:53 2011
@@ -31,7 +31,8 @@ using namespace std;
 
 auto_ptr<QpiddOptions> options;
 
-int main(int argc, char* argv[])
+// Broker real entry; various system-invoked entrypoints call here.
+int run_broker(int argc, char *argv[], bool hidden)
 {
     try
     {
@@ -43,6 +44,8 @@ int main(int argc, char* argv[])
         // module-supplied options.
         try {
             bootOptions.parse (argc, argv, bootOptions.common.config, true);
+            if (hidden)
+                bootOptions.log.sinkOptions->detached();
             qpid::log::Logger::instance().configure(bootOptions.log);
         } catch (const std::exception& e) {
             // Couldn't configure logging so write the message direct to 
stderr.

Modified: qpid/branches/QPID-2519/cpp/src/qpidd.h
URL: 
http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/qpidd.h?rev=1079078&r1=1079077&r2=1079078&view=diff
==============================================================================
--- qpid/branches/QPID-2519/cpp/src/qpidd.h (original)
+++ qpid/branches/QPID-2519/cpp/src/qpidd.h Tue Mar  8 01:41:53 2011
@@ -67,4 +67,7 @@ public:
     int execute (QpiddOptions *options);
 };
 
+// Broker real entry; various system-invoked entrypoints call here.
+int run_broker(int argc, char *argv[], bool hidden = false);
+
 #endif  /*!QPID_H*/

Modified: qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp
URL: 
http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp?rev=1079078&r1=1079077&r2=1079078&view=diff
==============================================================================
--- qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp (original)
+++ qpid/branches/QPID-2519/cpp/src/windows/QpiddBroker.cpp Tue Mar  8 01:41:53 
2011
@@ -19,17 +19,9 @@
  *
  */
 
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#else
-// These need to be made something sensible, like reading a value from
-// the registry. But for now, get things going with a local definition.
-namespace {
-const char *QPIDD_CONF_FILE = "qpid_broker.conf";
-const char *QPIDD_MODULE_DIR = ".";
-}
-#endif
+#include "config.h"
 #include "qpidd.h"
+#include "SCM.h"
 #include "qpid/Exception.h"
 #include "qpid/Options.h"
 #include "qpid/Plugin.h"
@@ -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 qp
     }
 };
 
-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 *
 
     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 *
     ::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;
 }

Added: qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp
URL: 
http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp?rev=1079078&view=auto
==============================================================================
--- qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp (added)
+++ qpid/branches/QPID-2519/cpp/src/windows/SCM.cpp Tue Mar  8 01:41:53 2011
@@ -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

Added: qpid/branches/QPID-2519/cpp/src/windows/SCM.h
URL: 
http://svn.apache.org/viewvc/qpid/branches/QPID-2519/cpp/src/windows/SCM.h?rev=1079078&view=auto
==============================================================================
--- qpid/branches/QPID-2519/cpp/src/windows/SCM.h (added)
+++ qpid/branches/QPID-2519/cpp/src/windows/SCM.h Tue Mar  8 01:41:53 2011
@@ -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 */



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscr...@qpid.apache.org

Reply via email to