windows-nt/Source/XPSP1/NT/net/config/common/ncbase/ncsvc.cpp
2020-09-26 16:20:57 +08:00

1260 lines
36 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: N C S V C . C P P
//
// Contents: Implementation of non-inline CService and CServiceManager
// methods.
//
// Notes:
//
// Author: mikemi 6 Mar 1997
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include "ncstring.h"
#include "ncsvc.h"
#include "ncmisc.h"
#include "ncperms.h"
struct CSCTX
{
SC_HANDLE hScm;
const CSFLAGS* pFlags;
DWORD dwErr;
// This just allows us to save some stack space otherwise wasted by
// recursion.
//
SERVICE_STATUS status;
};
VOID
SvcControlServicesAndWait (
CSCTX* pCtx,
UINT cServices,
const PCWSTR* apszServices);
VOID
StopDependentServices (
SC_HANDLE hSvc,
PCWSTR pszService,
CSCTX* pCtx)
{
// Try a first guess of 256 bytes for the buffer needed to hold the
// dependent information. If that fails, retry with the buffer size
// returned from EnumDependentServices.
//
DWORD cbBuf = 256;
ENUM_SERVICE_STATUS* aess = NULL;
DWORD cess = 0;
DWORD dwErr = ERROR_SUCCESS;
INT cLoop = 0;
const INT cLoopMax = 2;
do
{
// Allocate the needed space if we know it.
//
if (cbBuf)
{
MemFree (aess);
aess = reinterpret_cast<ENUM_SERVICE_STATUS*>(MemAlloc (cbBuf));
if (!aess)
{
dwErr = ERROR_OUTOFMEMORY;
break;
}
}
dwErr = ERROR_SUCCESS;
if (!EnumDependentServices (hSvc, SERVICE_ACTIVE, aess, cbBuf,
&cbBuf, &cess))
{
dwErr = GetLastError ();
}
}
while ((ERROR_MORE_DATA == dwErr) && (++cLoop < cLoopMax));
// If we have some services to stop, stop them and wait.
//
if ((ERROR_SUCCESS == dwErr) && cess)
{
// The array of ENUM_SERVICE_STATUS has the service names but not
// in a form that can be passed directly to
// SvcControlServicesAndWait, so we must transform the data into
// an array of string pointers.
//
PCWSTR* apszServices = reinterpret_cast<PCWSTR*>(
PvAllocOnStack (cess * sizeof(PCWSTR)));
for (UINT i = 0; i < cess; i++)
{
apszServices[i] = aess[i].lpServiceName;
}
Assert (SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl);
TraceTag (ttidSvcCtl, "Stopping dependents of %S...", pszService);
SvcControlServicesAndWait (pCtx, cess, apszServices);
}
// Otherwise, if we've had an error, but there is no context error yet,
// propagate our error to the context error for the caller.
//
else if ((ERROR_SUCCESS != dwErr) && (ERROR_SUCCESS == pCtx->dwErr))
{
pCtx->dwErr = dwErr;
}
MemFree (aess);
}
VOID
SvcControlServicesAndWait (
CSCTX* pCtx,
UINT cServices,
const PCWSTR* apszServices)
{
BOOL fr = TRUE;
DWORD dwErr;
// We only set this to TRUE if we successfuly open and control
// at least one service in the first phase.
//
BOOL fWaitIfNeeded = FALSE;
// Allocate a buffer (on the stack) to place the opened service
// handles in and zero it.
//
size_t cb = cServices * sizeof(SC_HANDLE);
SC_HANDLE* ahSvc = reinterpret_cast<SC_HANDLE*>
(PvAllocOnStack (cb));
ZeroMemory (ahSvc, cb);
// For each service, open it and apply the requested control
// (if requested). If the control succeeds, add the handle to
// our array for later use.
//
for (UINT i = 0; i < cServices; i++)
{
// Open the service.
//
SC_HANDLE hSvc = OpenService (pCtx->hScm,
apszServices[i],
SERVICE_QUERY_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_START | SERVICE_STOP |
SERVICE_USER_DEFINED_CONTROL);
if (hSvc)
{
// If we're to ignore demand-start and disabled services,
// check for it now and skip if needed. Remember to close
// the service handle because we're going to open the next if
// we skip this one.
//
if (pCtx->pFlags->fIgnoreDisabledAndDemandStart)
{
BOOL fSkip = FALSE;
LPQUERY_SERVICE_CONFIG pConfig;
if (SUCCEEDED(HrQueryServiceConfigWithAlloc (hSvc, &pConfig)))
{
if ((pConfig->dwStartType == SERVICE_DEMAND_START) ||
(pConfig->dwStartType == SERVICE_DISABLED))
{
fSkip = TRUE;
TraceTag (ttidSvcCtl, "Skipping %S because its start "
"type is %d.",
apszServices[i],
pConfig->dwStartType);
}
// Free our memory before we continue.
//
MemFree (pConfig);
if (fSkip)
{
CloseServiceHandle (hSvc);
continue;
}
}
}
// Initialize fr and dwErr assuming that something goes wrong.
// fr and dwErr should always be set to something in the following
// if,else statement.
//
fr = FALSE;
dwErr = ERROR_INVALID_DATA;
// Start or Control the service if requested. (Or do nothing
// if we just want to wait.
//
if (pCtx->pFlags->fStart)
{
TraceTag (ttidSvcCtl, "Starting %S", apszServices[i]);
fr = StartService (hSvc, 0, NULL);
if (!fr)
{
dwErr = GetLastError ();
}
}
else if (pCtx->pFlags->dwControl)
{
// Stop dependent services if we're stopping the service.
//
if (SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl)
{
// We don't need to worry about the success or failure
// of this call here. It simply recurses into this
// function so pCtx->dwErr will be set however we set
// it in this function on the next recursion.
//
StopDependentServices (hSvc, apszServices[i], pCtx);
//
// Now handle any special cases
//
if (0 == _wcsicmp(L"Netbios", apszServices[i]))
{
TraceTag (ttidSvcCtl, "Running special-case code to stop NetBIOS");
ScStopNetbios();
}
TraceTag (ttidSvcCtl, "Stopping %S", apszServices[i]);
}
fr = ControlService (hSvc, pCtx->pFlags->dwControl,
&pCtx->status);
if (!fr)
{
dwErr = GetLastError ();
}
TraceTag(ttidSvcCtl,
"Just issued control (0x%x) to %S. ret=%u (dwErr=%u), status.dwCurrentState=0x%x",
pCtx->pFlags->dwControl,
apszServices[i],
fr,
(!fr) ? dwErr : ERROR_SUCCESS,
pCtx->status.dwCurrentState);
if (!fr)
{
if ((SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl) &&
((ERROR_INVALID_SERVICE_CONTROL == dwErr) ||
(ERROR_SERVICE_CANNOT_ACCEPT_CTRL == dwErr)))
{
if (SERVICE_STOP_PENDING == pCtx->status.dwCurrentState)
{
TraceTag(ttidSvcCtl,
"Issued stop to service %S which is pending stop",
apszServices[i]);
// This is an okay condition. We want to wait on
// this service below.
//
fr = TRUE;
dwErr = ERROR_SUCCESS;
}
}
}
}
if (fr)
{
// We have at least one handle, indicate we may
// need to wait below and save the handle so the
// the wait code will use it.
//
fWaitIfNeeded = TRUE;
ahSvc[i] = hSvc;
}
else
{
Assert (!ahSvc[i]); // don't want to wait on this index
Assert (ERROR_SUCCESS != dwErr); // obtained above
if (SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl)
{
// We can ignore service not running errors.
//
// the first part of the OR is for the service case,
// the 2nd handles the driver and service cases respectively.
//
if ((ERROR_SERVICE_NOT_ACTIVE == dwErr) ||
(((ERROR_INVALID_SERVICE_CONTROL == dwErr) ||
(ERROR_SERVICE_CANNOT_ACCEPT_CTRL == dwErr)) &&
(SERVICE_STOPPED == pCtx->status.dwCurrentState)))
{
TraceTag(ttidSvcCtl,
"Issued stop to service %S which is already stopped",
apszServices[i]);
dwErr = ERROR_SUCCESS;
}
}
else if (pCtx->pFlags->fStart)
{
// We can ignore service already running errors.
//
if (ERROR_SERVICE_ALREADY_RUNNING == dwErr)
{
TraceTag(ttidSvcCtl,
"Issued start to service %S which is already running",
apszServices[i]);
dwErr = ERROR_SUCCESS;
}
}
// If we still have an error, time to remember it and move on.
//
if (ERROR_SUCCESS != dwErr)
{
// Keep going, but note that we have an error.
//
pCtx->dwErr = dwErr;
TraceHr (ttidError, FAL,
HRESULT_FROM_WIN32 (dwErr), FALSE,
"SvcControlServicesAndWait: %s (%S)",
(pCtx->pFlags->fStart) ?
"StartService" : "ControlService",
apszServices[i]);
}
CloseServiceHandle (hSvc);
}
}
#ifdef ENABLETRACE
else
{
TraceHr (ttidError, FAL, HrFromLastWin32Error (), FALSE,
"SvcControlServicesAndWait: OpenService (%S)",
apszServices[i]);
}
#endif
}
// For each service, wait for it to enter the requested state
// (if requested).
//
if (fWaitIfNeeded &&
pCtx->pFlags->dwMaxWaitMilliseconds && pCtx->pFlags->dwStateToWaitFor)
{
// We wait in increments of 100 milliseconds. Therefore, the
// total number of checks to perform is dwMaxWaitMilliseconds
// divided by 100 with a minimum of one check.
//
const UINT cmsWait = 100;
UINT cLoop = pCtx->pFlags->dwMaxWaitMilliseconds / cmsWait;
if (0 == cLoop)
{
cLoop = 1;
}
// Wait the request number of times...
// (Assume we timeout)
//
dwErr = ERROR_TIMEOUT;
for (UINT nLoop = 0; nLoop < cLoop; nLoop++, Sleep (cmsWait))
{
// Querying the state of the service to see if its entered
// the requested state. We can quit the outer loop early
// if all services have entered the requested state.
//
BOOL fAllDone = TRUE;
for (i = 0; i < cServices; i++)
{
// Skip services that have already entered the state or
// that we never opened.
//
if (!ahSvc[i])
{
continue;
}
fr = QueryServiceStatus (ahSvc[i], &pCtx->status);
if (fr)
{
if (pCtx->status.dwCurrentState !=
pCtx->pFlags->dwStateToWaitFor)
{
// Not there yet. We'll need to check this
// again and we now know we're definately not
// all done.
//
fAllDone = FALSE;
}
else
{
// No need to check this service anymore,
// its in the right state.
//
CloseServiceHandle (ahSvc[i]);
ahSvc[i] = NULL;
}
}
#ifdef ENABLETRACE
else
{
TraceHr (ttidError, FAL, HrFromLastWin32Error (), FALSE,
"SvcControlServicesAndWait: QueryServiceStatus (%S)",
apszServices[i]);
}
#endif
}
if (fAllDone)
{
dwErr = ERROR_SUCCESS;
break;
}
}
// If we had an error in the above wait (like a timeout), and
// we haven't had any prior errors, remember this new one for the
// caller.
//
if ((ERROR_SUCCESS != dwErr) && (ERROR_SUCCESS == pCtx->dwErr))
{
pCtx->dwErr = dwErr;
}
}
// Close the remaining open service handles.
//
for (i = 0; i < cServices; i++)
{
if (ahSvc[i])
{
CloseServiceHandle (ahSvc[i]);
#ifdef ENABLETRACE
if (fWaitIfNeeded &&
pCtx->pFlags->dwMaxWaitMilliseconds &&
pCtx->pFlags->dwStateToWaitFor)
{
TraceTag (ttidSvcCtl, "%S did not %s within %i milliseconds",
apszServices[i],
(SERVICE_RUNNING == pCtx->pFlags->dwStateToWaitFor)
? "start" : "stop",
pCtx->pFlags->dwMaxWaitMilliseconds);
}
#endif
}
}
}
HRESULT
HrQueryServiceConfigWithAlloc (
SC_HANDLE hService,
LPQUERY_SERVICE_CONFIG* ppConfig)
{
// Initial guess for the buffer size is the structure size plus
// room for 5 strings of 32 characters each. (since there are
// 5 strings in the structure.)
//
static DWORD cbBufGuess = sizeof (QUERY_SERVICE_CONFIG) +
5 * (32 * sizeof(WCHAR));
DWORD cbBuf = cbBufGuess;
LPQUERY_SERVICE_CONFIG pConfig = NULL;
DWORD dwErr = ERROR_SUCCESS;
INT cLoop = 0;
const INT cLoopMax = 2;
do
{
// If we require more room, allocate the needed space.
//
MemFree (pConfig);
pConfig = (LPQUERY_SERVICE_CONFIG)MemAlloc (cbBuf);
if (!pConfig)
{
dwErr = ERROR_OUTOFMEMORY;
break;
}
BOOL fr = QueryServiceConfig (hService, pConfig, cbBuf, &cbBuf);
if (fr)
{
dwErr = ERROR_SUCCESS;
// Update our guess for next time to be what QueryServiceConfig
// says we needed. But only do so if we needed more than our
// guess.
//
if (cbBuf > cbBufGuess)
{
cbBufGuess = cbBuf;
}
}
else
{
dwErr = GetLastError ();
#ifdef ENABLETRACE
if (ERROR_INSUFFICIENT_BUFFER == dwErr)
{
TraceTag (ttidSvcCtl,
"Perf: Guessed buffer size incorrectly calling "
"QueryServiceConfig.\nNeeded %d bytes. "
"(Guessed %d bytes.)",
cbBuf,
cbBufGuess);
}
#endif
}
}
while ((ERROR_INSUFFICIENT_BUFFER == dwErr) && (++cLoop < cLoopMax));
AssertSz (cLoop < cLoopMax, "Why can we never allocate a buffer big "
"enough for QueryServiceConfig when its telling us how big "
"the buffer should be?");
HRESULT hr = HRESULT_FROM_WIN32 (dwErr);
if (S_OK == hr)
{
*ppConfig = pConfig;
}
else
{
MemFree (pConfig);
*ppConfig = NULL;
}
TraceError ("HrQueryServiceConfigWithAlloc", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrChangeServiceStartType
//
// Purpose: Changes the start type of the given service to the given type.
//
// Arguments:
// pszServiceName [in] Name of service to change.
// dwStartType [in] New start type for service. See the Win32
// documentation on ChangeServiceConfig for the valid
// service start type values.
//
// Returns: S_OK if succeeded, HRESULT_FROM_WIN32 error code otherwise.
//
// Author: danielwe 25 Feb 1997
//
// Notes: Don't call this function too many times. It is fairly
// inefficient.
//
HRESULT
HrChangeServiceStartType (
IN PCWSTR pszServiceName,
IN DWORD dwStartType)
{
CServiceManager scm;
CService svc;
HRESULT hr = scm.HrOpenService (&svc, pszServiceName, WITH_LOCK);
if (S_OK == hr)
{
hr = svc.HrSetStartType(dwStartType);
}
TraceHr (ttidError, FAL, hr,
HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr,
"HrChangeServiceStartType");
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrChangeServiceStartTypeOptional
//
// Purpose: Changes the start type of the given service to the given type.
//
// Arguments:
// pszServiceName [in] Name of service to change.
// dwStartType [in] New start type for service. See the Win32
// documentation on ChangeServiceConfig for the valid
// service start type values.
//
// Returns: S_OK if succeeded, NETCFG_E_SVC_* error otherwise.
//
// Author: danielwe 25 Feb 1997
//
// Notes: If the service does not exist, nothing is done.
//
HRESULT
HrChangeServiceStartTypeOptional (
IN PCWSTR pszServiceName,
IN DWORD dwStartType)
{
HRESULT hr = HrChangeServiceStartType (pszServiceName, dwStartType);
if (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr)
{
hr = S_OK;
}
TraceError ("HrChangeServiceStartTypeOptional", hr);
return hr;
}
HRESULT
HrSvcQueryStatus (
IN PCWSTR pszService,
OUT DWORD* pdwState)
{
Assert (pszService);
Assert (pdwState);
*pdwState = 0;
CServiceManager scm;
CService svc;
HRESULT hr = scm.HrOpenService (&svc, pszService, NO_LOCK,
SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS);
if (S_OK == hr)
{
hr = svc.HrQueryState (pdwState);
}
TraceHr (ttidError, FAL, hr,
HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr,
"HrSvcQueryStatus");
return hr;
}
VOID
CService::Close ()
{
if (_schandle)
{
BOOL fr = ::CloseServiceHandle( _schandle );
AssertSz(fr, "CloseServiceHandle failed!");
_schandle = NULL;
}
}
HRESULT
CService::HrControl (
IN DWORD dwControl)
{
Assert (_schandle);
HRESULT hr = S_OK;
SERVICE_STATUS status;
if (!::ControlService (_schandle, dwControl, &status))
{
hr = HrFromLastWin32Error ();
}
TraceError ("CService::HrControl", hr);
return hr;
}
HRESULT
CService::HrRequestStop ()
{
Assert (_schandle);
HRESULT hr = S_OK;
SERVICE_STATUS status;
if (!::ControlService (_schandle, SERVICE_CONTROL_STOP, &status))
{
hr = HrFromLastWin32Error ();
// Don't consider it an error if the service is not running.
//
if (HRESULT_FROM_WIN32 (ERROR_SERVICE_NOT_ACTIVE) == hr)
{
hr = S_OK;
}
// (driver case) ERROR_INVALID_SERVICE_CONTROL is returned if the service
// is not running - which may mean pending_stop.
// (non-driver case) ERROR_SERVICE_CANNOT_ACCEPT_CTRL is returned if the
// service is either stop_pending or stopped.
// ... so in either case we need to query for the state.
//
if (((HRESULT_FROM_WIN32 (ERROR_INVALID_SERVICE_CONTROL) == hr) ||
(HRESULT_FROM_WIN32 (ERROR_SERVICE_CANNOT_ACCEPT_CTRL) == hr)) &&
(SERVICE_STOPPED == status.dwCurrentState))
{
hr = S_OK;
}
}
TraceError ("CService::HrRequestStop", hr);
return hr;
}
HRESULT
CService::HrQueryState (
OUT DWORD* pdwState)
{
Assert (pdwState);
Assert (_schandle);
SERVICE_STATUS sStatus;
if (!::QueryServiceStatus( _schandle, &sStatus ))
{
*pdwState = 0;
return ::HrFromLastWin32Error();
}
*pdwState = sStatus.dwCurrentState;
return S_OK;
}
HRESULT
CService::HrQueryStartType (
OUT DWORD* pdwStartType)
{
Assert (pdwStartType);
*pdwStartType = 0;
LPQUERY_SERVICE_CONFIG pConfig;
HRESULT hr = HrQueryServiceConfig (&pConfig);
if (S_OK == hr)
{
*pdwStartType = pConfig->dwStartType;
MemFree (pConfig);
}
TraceError ("CService::HrQueryStartType", hr);
return hr;
}
HRESULT
CService::HrSetServiceRestartRecoveryOption(
IN SERVICE_FAILURE_ACTIONS *psfa
)
{
HRESULT hr = S_OK;
if (!ChangeServiceConfig2(_schandle,
SERVICE_CONFIG_FAILURE_ACTIONS,
(LPVOID)psfa))
{
hr = HrFromLastWin32Error();
}
TraceError("CService::HrSetServiceRestartRecoveryOption", hr);
return hr;
}
CServiceManager::~CServiceManager ()
{
if (_sclock)
{
Unlock();
}
if (_schandle)
{
Close();
}
}
VOID
CServiceManager::Close ()
{
Assert (_schandle);
BOOL fr = ::CloseServiceHandle (_schandle);
AssertSz (fr, "CloseServiceHandle failed!");
_schandle = NULL;
}
HRESULT
CServiceManager::HrControlServicesAndWait (
IN UINT cServices,
IN const PCWSTR* apszServices,
IN const CSFLAGS* pFlags)
{
Assert (cServices);
Assert (apszServices);
Assert (pFlags);
// Make sure we have something to do before wasting time.
//
Assert ( (pFlags->fStart || pFlags->dwControl)
|| (pFlags->dwMaxWaitMilliseconds && pFlags->dwStateToWaitFor));
HRESULT hr = S_OK;
if (!_schandle)
{
hr = HrOpen (NO_LOCK, SC_MANAGER_CONNECT);
}
if (S_OK == hr)
{
Assert (_schandle);
// Setup the context structure and call the internal routine (which
// may recurse which is why we use the context structure).
//
CSCTX ctx;
ZeroMemory (&ctx, sizeof(ctx));
ctx.hScm = _schandle;
ctx.pFlags = pFlags;
SvcControlServicesAndWait (&ctx, cServices, apszServices);
hr = HRESULT_FROM_WIN32 (ctx.dwErr);
}
TraceError ("CServiceManager::HrControlServicesAndWait", hr);
return hr;
}
HRESULT
CServiceManager::HrStartServicesNoWait (
IN UINT cServices,
IN const PCWSTR* apszServices)
{
CSFLAGS flags =
{ TRUE, 0, 0, SERVICE_RUNNING, FALSE };
HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
TraceError ("CServiceManager::HrStartServicesNoWait", hr);
return hr;
}
HRESULT
CServiceManager::HrStartServicesAndWait (
IN UINT cServices,
IN const PCWSTR* apszServices,
IN DWORD dwWaitMilliseconds /*= 15000*/)
{
CSFLAGS flags =
{ TRUE, 0, dwWaitMilliseconds, SERVICE_RUNNING, FALSE };
HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
TraceError ("CServiceManager::HrStartServicesAndWait", hr);
return hr;
}
HRESULT
CServiceManager::HrStopServicesNoWait (
IN UINT cServices,
IN const PCWSTR* apszServices)
{
CSFLAGS flags =
{ FALSE, SERVICE_CONTROL_STOP, 0, SERVICE_STOPPED, FALSE };
HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
TraceError ("CServiceManager::HrStopServicesNoWait", hr);
return hr;
}
HRESULT
CServiceManager::HrStopServicesAndWait (
IN UINT cServices,
IN const PCWSTR* apszServices,
IN DWORD dwWaitMilliseconds /*= 15000*/)
{
CSFLAGS flags =
{ FALSE, SERVICE_CONTROL_STOP, dwWaitMilliseconds, SERVICE_STOPPED, FALSE };
HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
TraceError ("CServiceManager::HrStopServicesAndWait", hr);
return hr;
}
HRESULT
CServiceManager::HrCreateService (
IN CService* pcsService,
IN PCWSTR pszServiceName,
IN PCWSTR pszDisplayName,
IN DWORD dwServiceType,
IN DWORD dwStartType,
IN DWORD dwErrorControl,
IN PCWSTR pszBinaryPathName,
IN PCWSTR pslzDependencies,
IN PCWSTR pszLoadOrderGroup,
IN PDWORD pdwTagId,
IN DWORD dwDesiredAccess,
IN PCWSTR pszServiceStartName,
IN PCWSTR pszPassword,
IN PCWSTR pszDescription)
{
HRESULT hr = S_OK;
// Open the service control manager if needed.
//
if (!_schandle)
{
hr = HrOpen ();
}
if (S_OK == hr)
{
// make sure the service is not in use
//
if (pcsService->_schandle)
{
pcsService->Close();
}
pcsService->_schandle = ::CreateService (_schandle,
pszServiceName,
pszDisplayName,
dwDesiredAccess,
dwServiceType,
dwStartType,
dwErrorControl,
pszBinaryPathName,
pszLoadOrderGroup,
pdwTagId,
pslzDependencies,
pszServiceStartName,
pszPassword );
if (!pcsService->_schandle)
{
hr = HrFromLastWin32Error ();
}
else
{
// Set the description is one is supplied
//
if (pszDescription)
{
SERVICE_DESCRIPTION sd = {0};
sd.lpDescription = (PWSTR)pszDescription;
(VOID)ChangeServiceConfig2(pcsService->_schandle,
SERVICE_CONFIG_DESCRIPTION, &sd);
}
}
}
TraceError ("CServiceManager::HrCreateService", hr);
return hr;
}
HRESULT
CServiceManager::HrQueryLocked (
OUT BOOL* pfLocked)
{
LPQUERY_SERVICE_LOCK_STATUS pqslStatus = NULL;
DWORD cbNeeded = sizeof( QUERY_SERVICE_LOCK_STATUS );
DWORD cbSize;
BOOL frt;
Assert(_schandle != NULL );
Assert(pfLocked != NULL);
*pfLocked = FALSE;
// loop, allocating the needed size
do
{
pqslStatus = (LPQUERY_SERVICE_LOCK_STATUS) MemAlloc (cbNeeded);
if (pqslStatus == NULL)
{
return E_OUTOFMEMORY;
}
cbSize = cbNeeded;
frt = ::QueryServiceLockStatus( _schandle,
pqslStatus,
cbSize,
&cbNeeded );
*pfLocked = pqslStatus->fIsLocked;
MemFree (pqslStatus);
pqslStatus = NULL;
if (!frt && (cbNeeded == cbSize))
{
// if an error, treat this as a lock
return ::HrFromLastWin32Error();
}
} while (!frt && (cbNeeded != cbSize));
return S_OK;
}
HRESULT
CServiceManager::HrLock ()
{
INT cRetries = 30;
const INT c_msecWait = 1000;
Assert (_schandle != NULL);
Assert (_sclock == NULL);
while (cRetries--)
{
_sclock = ::LockServiceDatabase( _schandle );
if (_sclock)
{
return S_OK;
}
else
{
HRESULT hr = HrFromLastWin32Error();
if ((HRESULT_FROM_WIN32(ERROR_SERVICE_DATABASE_LOCKED) != hr) ||
(0 == cRetries))
{
return hr;
}
TraceTag(ttidSvcCtl, "SCM is locked, waiting for %d "
"seconds before retrying...", c_msecWait / 1000);
// wait for a bit to see if the database unlocks in that
// time.
Sleep (c_msecWait);
}
}
AssertSz (FALSE, "Lock me Amadeus! I'm not supposed to get here!");
return S_OK;
}
HRESULT
CServiceManager::HrOpen (
CSLOCK eLock, // = NO_LOCK
DWORD dwDesiredAccess, // = SC_MANAGER_ALL_ACCESS
PCWSTR pszMachineName, // = NULL
PCWSTR pszDatabaseName // = NULL
)
{
HRESULT hr = S_OK;
if (_schandle)
{
Close();
}
_schandle = ::OpenSCManager (pszMachineName, pszDatabaseName,
dwDesiredAccess );
if (_schandle)
{
if (WITH_LOCK == eLock)
{
hr = HrLock ();
}
}
else
{
hr = ::HrFromLastWin32Error();
}
TraceHr (ttidError, FAL, hr, FALSE,
"CServiceManager::HrOpen failed. eLock=%d dwDesiredAccess=0x%08x",
eLock, dwDesiredAccess);
return hr;
}
HRESULT
CServiceManager::HrOpenService (
CService* pcsService,
PCWSTR pszServiceName,
CSLOCK eLock, // = NO_LOCK
DWORD dwScmAccess, // = SC_MANAGER_ALL_ACCESS
DWORD dwSvcAccess // = SERVICE_ALL_ACCESS
)
{
HRESULT hr = S_OK;
// Open the service control manager if needed.
//
if (!_schandle)
{
hr = HrOpen (eLock, dwScmAccess);
}
if (S_OK == hr)
{
// make sure the service is not in use
//
if (pcsService->_schandle)
{
pcsService->Close();
}
pcsService->_schandle = ::OpenService (_schandle,
pszServiceName,
dwSvcAccess);
if (!pcsService->_schandle)
{
hr = HrFromLastWin32Error();
}
}
TraceHr (ttidError, FAL, hr,
(HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr),
"CServiceManager::HrOpenService failed opening '%S'", pszServiceName);
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CServiceManager::HrAddRemoveServiceDependency
//
// Purpose: Add/remove dependency to a service
//
// Arguments:
// pszService [in] Name of service
// pszDependency [in] Dependency to add
// enumFlag [in] Indicates add or remove
//
// Returns: S_OK if success, Win32 HRESULT otherwise.
//
// Author: tongl 17 Jun 1997
//
// Notes: this function is not for adding/removing group dependency
//
HRESULT
CServiceManager::HrAddRemoveServiceDependency (
PCWSTR pszServiceName,
PCWSTR pszDependency,
DEPENDENCY_ADDREMOVE enumFlag)
{
HRESULT hr = S_OK;
Assert(pszServiceName);
Assert(pszDependency);
Assert((enumFlag == DEPENDENCY_ADD) || (enumFlag == DEPENDENCY_REMOVE));
// If either string is empty, do nothing
if (*pszServiceName && *pszDependency)
{
hr = HrLock();
if (S_OK == hr)
{
PCWSTR pszSrv = pszDependency;
CService svc;
// Check if the dependency service exists
hr = HrOpenService(&svc, pszDependency);
if (S_OK == hr)
{
// Open the service we are changing dependency on
pszSrv = pszServiceName;
hr = HrOpenService(&svc, pszServiceName);
if (S_OK == hr)
{
LPQUERY_SERVICE_CONFIG pConfig;
hr = svc.HrQueryServiceConfig (&pConfig);
if (S_OK == hr)
{
BOOL fChanged = FALSE;
if (enumFlag == DEPENDENCY_ADD)
{
PWSTR pmszNewDependencies;
hr = HrAddSzToMultiSz(
pszDependency,
pConfig->lpDependencies,
STRING_FLAG_DONT_MODIFY_IF_PRESENT |
STRING_FLAG_ENSURE_AT_END, 0,
&pmszNewDependencies,
&fChanged);
if ((S_OK == hr) && fChanged)
{
Assert (pmszNewDependencies);
hr = svc.HrSetDependencies (pmszNewDependencies);
MemFree (pmszNewDependencies);
}
}
else if (enumFlag == DEPENDENCY_REMOVE)
{
RemoveSzFromMultiSz(
pszDependency,
pConfig->lpDependencies,
STRING_FLAG_REMOVE_ALL,
&fChanged);
if (fChanged)
{
hr = svc.HrSetDependencies (pConfig->lpDependencies);
}
}
MemFree (pConfig);
}
}
}
if (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr) // If either services do not exist
{
TraceTag(ttidSvcCtl, "CServiceManager::HrAddServiceDependency, Service %s does not exist.", pszSrv);
hr = S_OK;
}
Unlock();
}
} // if szDependency is not empty string
TraceError("CServiceManager::HrAddRemoveServiceDependency", hr);
return hr;
}
VOID
CServiceManager::Unlock ()
{
Assert (_schandle);
Assert (_sclock);
BOOL fr = ::UnlockServiceDatabase (_sclock);
AssertSz (fr, "UnlockServiceDatabase failed!");
_sclock = NULL;
}
//+---------------------------------------------------------------------------
//
// Function: AllocateAndInitializeAcl
//
// Purpose: Combine the common operation of allocation and initialization
// of an ACL. Similiar to AllocateAndInitializeSid.
//
// Arguments:
// cbAcl [in] size in bytes of ACL
// dwAclRevision [in] ACL_REVISION
// ppAcl [out] the returned ACL
//
// Returns: TRUE if successful, FALSE if not.
//
// Author: shaunco 4 Sep 1997
//
// Notes:
//
BOOL
AllocateAndInitializeAcl (
DWORD cbAcl,
DWORD dwAclRevision,
PACL* ppAcl)
{
Assert (ppAcl);
*ppAcl = reinterpret_cast<PACL>(LocalAlloc (LPTR,
static_cast<UINT>(cbAcl)));
if (*ppAcl)
{
return InitializeAcl (*ppAcl, cbAcl, dwAclRevision);
}
return FALSE;
}