875 lines
23 KiB
C++
875 lines
23 KiB
C++
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
CfgAPI2.cxx
|
||
|
||
Abstract:
|
||
|
||
This file contains the Service Controller's extended Config API.
|
||
RChangeServiceConfig2W
|
||
RQueryServiceConfig2W
|
||
COutBuf
|
||
CUpdateOptionalString::Update
|
||
CUpdateOptionalString::~CUpdateOptionalString
|
||
PrintConfig2Parms
|
||
|
||
|
||
Author:
|
||
|
||
Anirudh Sahni (AnirudhS) 11-Oct-96
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
11-Oct-1996 AnirudhS
|
||
Created.
|
||
|
||
--*/
|
||
|
||
|
||
//
|
||
// INCLUDES
|
||
//
|
||
|
||
#include "precomp.hxx"
|
||
#include <tstr.h> // Unicode string macros
|
||
#include <align.h> // COUNT_IS_ALIGNED
|
||
#include <valid.h> // ACTION_TYPE_INVALID
|
||
#include <sclib.h> // ScImagePathsMatch
|
||
#include <scwow.h> // 32/64-bit interop structures
|
||
#include "scconfig.h" // ScOpenServiceConfigKey, etc.
|
||
#include "scsec.h" // ScPrivilegeCheckAndAudit
|
||
#include "smartp.h" // CHeapPtr
|
||
|
||
|
||
#if DBG == 1
|
||
VOID
|
||
PrintConfig2Parms(
|
||
IN SC_RPC_HANDLE hService,
|
||
IN SC_RPC_CONFIG_INFOW Info
|
||
);
|
||
#endif
|
||
|
||
//
|
||
// Class definitions
|
||
//
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Class: COutBuf
|
||
//
|
||
// Purpose: Abstraction of an output buffer that is written sequentially
|
||
//
|
||
// History: 22-Nov-96 AnirudhS Created.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
class COutBuf
|
||
{
|
||
public:
|
||
COutBuf(LPBYTE lpBuffer) :
|
||
_Start(lpBuffer),
|
||
_Used(0)
|
||
{ }
|
||
|
||
LPBYTE Next() const { return (_Start + _Used); }
|
||
DWORD OffsetNext() const { return _Used; }
|
||
void AddUsed(DWORD Bytes) { _Used += Bytes; }
|
||
|
||
void AppendBytes(void * Source, DWORD Bytes)
|
||
{
|
||
RtlCopyMemory(Next(), Source, Bytes);
|
||
AddUsed(Bytes);
|
||
}
|
||
private:
|
||
LPBYTE _Start;
|
||
DWORD _Used;
|
||
};
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Class: CUpdateOptionalString
|
||
//
|
||
// Purpose: An object of this class represents an update of an optional
|
||
// string value in the registry. The update takes place when
|
||
// the Update() method is called. When the object is destroyed
|
||
// the operation is undone, unless the Commit() method has been
|
||
// called.
|
||
//
|
||
// This class simplifies the writing of APIs like
|
||
// ChangeServiceConfig2.
|
||
//
|
||
// History: 27-Nov-96 AnirudhS Created.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
class CUpdateOptionalString
|
||
{
|
||
public:
|
||
CUpdateOptionalString (HKEY Key, LPCWSTR ValueName) :
|
||
_Key(Key),
|
||
_ValueName(ValueName),
|
||
_UndoNeeded(FALSE)
|
||
{ }
|
||
~CUpdateOptionalString();
|
||
DWORD Update (LPCWSTR NewValue);
|
||
void Commit ()
|
||
{ _UndoNeeded = FALSE; }
|
||
|
||
private:
|
||
HKEY _Key;
|
||
LPCWSTR _ValueName;
|
||
CHeapPtr< LPWSTR > _OldValue;
|
||
BOOL _UndoNeeded;
|
||
};
|
||
|
||
|
||
|
||
DWORD
|
||
CUpdateOptionalString::Update(
|
||
IN LPCWSTR NewValue
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
See class definition.
|
||
|
||
--*/
|
||
{
|
||
// This method should be called only once in the object's lifetime
|
||
SC_ASSERT(_UndoNeeded == FALSE && _OldValue == NULL);
|
||
|
||
//
|
||
// Read the old value.
|
||
//
|
||
DWORD Error = ScReadOptionalString(_Key, _ValueName, &_OldValue);
|
||
if (Error != ERROR_SUCCESS)
|
||
{
|
||
return Error;
|
||
}
|
||
|
||
//
|
||
// Write the new value. Note that NULL means no change.
|
||
//
|
||
Error = ScWriteOptionalString(_Key, _ValueName, NewValue);
|
||
|
||
//
|
||
// Remember whether the change needs to be undone.
|
||
//
|
||
if (Error == ERROR_SUCCESS && NewValue != NULL)
|
||
{
|
||
_UndoNeeded = TRUE;
|
||
}
|
||
|
||
return Error;
|
||
}
|
||
|
||
|
||
|
||
|
||
CUpdateOptionalString::~CUpdateOptionalString(
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
See class definition.
|
||
|
||
--*/
|
||
{
|
||
if (_UndoNeeded)
|
||
{
|
||
DWORD Error = ScWriteOptionalString(
|
||
_Key,
|
||
_ValueName,
|
||
_OldValue ? _OldValue : L""
|
||
);
|
||
|
||
if (Error != ERROR_SUCCESS)
|
||
{
|
||
// Nothing we can do about it
|
||
SC_LOG3(ERROR, "Couldn't roll back update to %ws value, error %lu."
|
||
" Old value was \"%ws\".\n",
|
||
_ValueName, Error, _OldValue);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
RChangeServiceConfig2W(
|
||
IN SC_RPC_HANDLE hService,
|
||
IN SC_RPC_CONFIG_INFOW Info
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
SC_LOG(CONFIG_API, "In RChangeServiceConfig2W for service handle %#lx\n", hService);
|
||
|
||
#if DBG == 1
|
||
PrintConfig2Parms(hService, Info);
|
||
#endif // DBG == 1
|
||
|
||
if (ScShutdownInProgress)
|
||
{
|
||
return ERROR_SHUTDOWN_IN_PROGRESS;
|
||
}
|
||
|
||
//
|
||
// Check the handle.
|
||
//
|
||
if (!ScIsValidServiceHandle(hService))
|
||
{
|
||
return ERROR_INVALID_HANDLE;
|
||
}
|
||
|
||
//
|
||
// Do we have permission to do this?
|
||
//
|
||
if (!RtlAreAllAccessesGranted(
|
||
((LPSC_HANDLE_STRUCT) hService)->AccessGranted,
|
||
SERVICE_CHANGE_CONFIG
|
||
))
|
||
{
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Lock database, as we want to add stuff without other threads tripping
|
||
// on our feet.
|
||
//
|
||
CServiceRecordExclusiveLock RLock;
|
||
|
||
//
|
||
// Find the service record for this handle.
|
||
//
|
||
LPSERVICE_RECORD serviceRecord =
|
||
((LPSC_HANDLE_STRUCT) hService)->Type.ScServiceObject.ServiceRecord;
|
||
SC_ASSERT(serviceRecord != NULL);
|
||
SC_ASSERT(serviceRecord->Signature == SERVICE_SIGNATURE);
|
||
|
||
//
|
||
// Disallow this call if record is marked for delete.
|
||
//
|
||
if (DELETE_FLAG_IS_SET(serviceRecord))
|
||
{
|
||
return ERROR_SERVICE_MARKED_FOR_DELETE;
|
||
}
|
||
|
||
//-----------------------------
|
||
//
|
||
// Begin Updating the Registry
|
||
//
|
||
//-----------------------------
|
||
HKEY ServiceNameKey = NULL;
|
||
DWORD ApiStatus = ScOpenServiceConfigKey(
|
||
serviceRecord->ServiceName,
|
||
KEY_WRITE | KEY_READ,
|
||
FALSE, // don't create if missing
|
||
&ServiceNameKey
|
||
);
|
||
if (ApiStatus != NO_ERROR)
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
switch (Info.dwInfoLevel)
|
||
{
|
||
//-----------------------------
|
||
//
|
||
// Service Description
|
||
//
|
||
//-----------------------------
|
||
case SERVICE_CONFIG_DESCRIPTION:
|
||
|
||
//
|
||
// NULL means no change
|
||
//
|
||
if (Info.psd == NULL)
|
||
{
|
||
ApiStatus = NO_ERROR;
|
||
break;
|
||
}
|
||
|
||
ApiStatus = ScWriteDescription(ServiceNameKey, Info.psd->lpDescription);
|
||
break;
|
||
|
||
//-----------------------------
|
||
//
|
||
// Service Failure Actions
|
||
//
|
||
//-----------------------------
|
||
case SERVICE_CONFIG_FAILURE_ACTIONS:
|
||
{
|
||
LPSERVICE_FAILURE_ACTIONSW psfa = Info.psfa;
|
||
|
||
//
|
||
// NULL means no change
|
||
//
|
||
if (psfa == NULL)
|
||
{
|
||
ApiStatus = NO_ERROR;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Validate the structure and permissions
|
||
//
|
||
if (psfa->lpsaActions != NULL &&
|
||
psfa->cActions != 0)
|
||
{
|
||
//
|
||
// These settings are only valid for Win32 services
|
||
//
|
||
if (! (serviceRecord->ServiceStatus.dwServiceType & SERVICE_WIN32))
|
||
{
|
||
ApiStatus = ERROR_CANNOT_DETECT_DRIVER_FAILURE;
|
||
break;
|
||
}
|
||
|
||
BOOL RebootRequested = FALSE;
|
||
BOOL RestartRequested = FALSE;
|
||
for (DWORD i = 0; i < psfa->cActions; i++)
|
||
{
|
||
SC_ACTION_TYPE Type = psfa->lpsaActions[i].Type;
|
||
if (Type == SC_ACTION_RESTART)
|
||
{
|
||
RestartRequested = TRUE;
|
||
}
|
||
else if (Type == SC_ACTION_REBOOT)
|
||
{
|
||
RebootRequested = TRUE;
|
||
}
|
||
else if (ACTION_TYPE_INVALID(Type))
|
||
{
|
||
SC_LOG(ERROR, "RChangeServiceConfig2W: invalid action type %#lx\n", Type);
|
||
ApiStatus = ERROR_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
if (RestartRequested)
|
||
{
|
||
if (!RtlAreAllAccessesGranted(
|
||
((LPSC_HANDLE_STRUCT) hService)->AccessGranted,
|
||
SERVICE_START
|
||
))
|
||
{
|
||
SC_LOG0(ERROR, "Service handle lacks start access\n");
|
||
ApiStatus = ERROR_ACCESS_DENIED;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (RebootRequested)
|
||
{
|
||
NTSTATUS Status = ScPrivilegeCheckAndAudit(
|
||
SE_SHUTDOWN_PRIVILEGE,
|
||
hService,
|
||
SERVICE_CHANGE_CONFIG
|
||
);
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
SC_LOG0(ERROR, "Caller lacks shutdown privilege\n");
|
||
ApiStatus = ERROR_ACCESS_DENIED;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the service's image path
|
||
//
|
||
CHeapPtr<LPWSTR> ImageName;
|
||
ApiStatus = ScGetImageFileName(serviceRecord->ServiceName, &ImageName);
|
||
if (ApiStatus != NO_ERROR)
|
||
{
|
||
SC_LOG(ERROR,"RChangeServiceConfig2W: GetImageFileName failed %lu\n", ApiStatus);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the service runs in services.exe, we certainly won't
|
||
// detect if the service process dies, so don't pretend we will
|
||
//
|
||
if (ScImagePathsMatch(ImageName, ScGlobalThisExePath))
|
||
{
|
||
ApiStatus = ERROR_CANNOT_DETECT_PROCESS_ABORT;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Write the string values, followed by the non-string values.
|
||
// If anything fails, the values written up to that point will be
|
||
// backed out.
|
||
// (Backing out the update of the non-string values is a little
|
||
// more complicated than backing out the string updates. So we
|
||
// do the non-string update last, to avoid having to back it out.)
|
||
//
|
||
CUpdateOptionalString UpdateRebootMessage
|
||
(ServiceNameKey, REBOOTMESSAGE_VALUENAME_W);
|
||
CUpdateOptionalString UpdateFailureCommand
|
||
(ServiceNameKey, FAILURECOMMAND_VALUENAME_W);
|
||
|
||
if ((ApiStatus = UpdateRebootMessage.Update(psfa->lpRebootMsg)) == ERROR_SUCCESS &&
|
||
(ApiStatus = UpdateFailureCommand.Update(psfa->lpCommand)) == ERROR_SUCCESS &&
|
||
(ApiStatus = ScWriteFailureActions(ServiceNameKey, psfa)) == ERROR_SUCCESS)
|
||
{
|
||
UpdateRebootMessage.Commit();
|
||
UpdateFailureCommand.Commit();
|
||
}
|
||
}
|
||
break;
|
||
|
||
//-----------------------------
|
||
//
|
||
// Other (invalid)
|
||
//
|
||
//-----------------------------
|
||
default:
|
||
ApiStatus = ERROR_INVALID_LEVEL;
|
||
break;
|
||
}
|
||
|
||
Cleanup:
|
||
|
||
if (ServiceNameKey != NULL)
|
||
{
|
||
ScRegFlushKey(ServiceNameKey);
|
||
ScRegCloseKey(ServiceNameKey);
|
||
}
|
||
|
||
SC_LOG1(CONFIG_API, "RChangeServiceConfig2W returning %lu\n", ApiStatus);
|
||
|
||
return ApiStatus;
|
||
}
|
||
|
||
|
||
|
||
DWORD
|
||
RQueryServiceConfig2W(
|
||
IN SC_RPC_HANDLE hService,
|
||
IN DWORD dwInfoLevel,
|
||
OUT LPBYTE lpBuffer,
|
||
IN DWORD cbBufSize,
|
||
OUT LPDWORD pcbBytesNeeded
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
SC_LOG(CONFIG_API, "In RQueryServiceConfig2W for service handle %#lx\n", hService);
|
||
|
||
if (ScShutdownInProgress)
|
||
{
|
||
return ERROR_SHUTDOWN_IN_PROGRESS;
|
||
}
|
||
|
||
//
|
||
// Check the handle.
|
||
//
|
||
if (!ScIsValidServiceHandle(hService))
|
||
{
|
||
return ERROR_INVALID_HANDLE;
|
||
}
|
||
|
||
//
|
||
// MIDL doesn't support optional [out] parameters efficiently, so
|
||
// we require these parameters.
|
||
//
|
||
if (lpBuffer == NULL || pcbBytesNeeded == NULL)
|
||
{
|
||
SC_ASSERT(!"RPC passed NULL for [out] pointers");
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Do we have permission to do this?
|
||
//
|
||
if (!RtlAreAllAccessesGranted(
|
||
((LPSC_HANDLE_STRUCT) hService)->AccessGranted,
|
||
SERVICE_QUERY_CONFIG
|
||
))
|
||
{
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Initialize *pcbBytesNeeded. It is incremented below.
|
||
// (For consistency with QueryServiceConfig, it is set even on a success
|
||
// return.)
|
||
//
|
||
*pcbBytesNeeded = 0;
|
||
|
||
//
|
||
// Lock database, as we want to look at stuff without other threads changing
|
||
// fields at the same time.
|
||
//
|
||
CServiceRecordSharedLock RLock;
|
||
|
||
LPSERVICE_RECORD serviceRecord =
|
||
((LPSC_HANDLE_STRUCT) hService)->Type.ScServiceObject.ServiceRecord;
|
||
SC_ASSERT(serviceRecord != NULL);
|
||
|
||
|
||
//
|
||
// Open the service name key.
|
||
//
|
||
HKEY ServiceNameKey = NULL;
|
||
DWORD ApiStatus = ScOpenServiceConfigKey(
|
||
serviceRecord->ServiceName,
|
||
KEY_READ,
|
||
FALSE, // Create if missing
|
||
&ServiceNameKey
|
||
);
|
||
|
||
if (ApiStatus != NO_ERROR)
|
||
{
|
||
return ApiStatus;
|
||
}
|
||
|
||
|
||
switch (dwInfoLevel)
|
||
{
|
||
//-----------------------------
|
||
//
|
||
// Service Description
|
||
//
|
||
//-----------------------------
|
||
case SERVICE_CONFIG_DESCRIPTION:
|
||
{
|
||
*pcbBytesNeeded = sizeof(SERVICE_DESCRIPTION_WOW64);
|
||
|
||
//
|
||
// Read the string from the registry
|
||
//
|
||
CHeapPtr< LPWSTR > pszDescription;
|
||
ApiStatus = ScReadDescription(
|
||
ServiceNameKey,
|
||
&pszDescription,
|
||
pcbBytesNeeded
|
||
);
|
||
|
||
SC_ASSERT(ApiStatus != ERROR_INSUFFICIENT_BUFFER);
|
||
|
||
if (ApiStatus != NO_ERROR)
|
||
{
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Check for sufficient buffer space
|
||
//
|
||
if (cbBufSize < *pcbBytesNeeded)
|
||
{
|
||
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Copy the information to the output buffer
|
||
// The format is:
|
||
// SERVICE_DESCRIPTION_WOW64 struct
|
||
// description string
|
||
// Pointers in the struct are replaced with offsets based at the
|
||
// start of the buffer. NULL pointers remain NULL.
|
||
//
|
||
COutBuf OutBuf(lpBuffer);
|
||
|
||
LPSERVICE_DESCRIPTION_WOW64 psdOut =
|
||
(LPSERVICE_DESCRIPTION_WOW64) OutBuf.Next();
|
||
OutBuf.AddUsed(sizeof(SERVICE_DESCRIPTION_WOW64));
|
||
SC_ASSERT(COUNT_IS_ALIGNED(OutBuf.OffsetNext(), sizeof(WCHAR)));
|
||
|
||
if (pszDescription != NULL)
|
||
{
|
||
psdOut->dwDescriptionOffset = OutBuf.OffsetNext();
|
||
OutBuf.AppendBytes(pszDescription, (DWORD) WCSSIZE(pszDescription));
|
||
}
|
||
else
|
||
{
|
||
psdOut->dwDescriptionOffset = 0;
|
||
}
|
||
}
|
||
break;
|
||
|
||
//-----------------------------
|
||
//
|
||
// Service Failure Actions
|
||
//
|
||
//-----------------------------
|
||
case SERVICE_CONFIG_FAILURE_ACTIONS:
|
||
{
|
||
//
|
||
// Read the non-string info
|
||
//
|
||
CHeapPtr< LPSERVICE_FAILURE_ACTIONS_WOW64 > psfa;
|
||
|
||
ApiStatus = ScReadFailureActions(ServiceNameKey, &psfa, pcbBytesNeeded);
|
||
if (ApiStatus != NO_ERROR)
|
||
{
|
||
break;
|
||
}
|
||
if (psfa == NULL)
|
||
{
|
||
SC_ASSERT(*pcbBytesNeeded == 0);
|
||
*pcbBytesNeeded = sizeof(SERVICE_FAILURE_ACTIONS_WOW64);
|
||
}
|
||
SC_ASSERT(COUNT_IS_ALIGNED(*pcbBytesNeeded, sizeof(WCHAR)));
|
||
|
||
//
|
||
// Read the string values
|
||
//
|
||
CHeapPtr< LPWSTR > RebootMessage;
|
||
|
||
ApiStatus = ScReadRebootMessage(
|
||
ServiceNameKey,
|
||
&RebootMessage,
|
||
pcbBytesNeeded
|
||
);
|
||
if (ApiStatus != NO_ERROR)
|
||
{
|
||
break;
|
||
}
|
||
SC_ASSERT(COUNT_IS_ALIGNED(*pcbBytesNeeded, sizeof(WCHAR)));
|
||
|
||
CHeapPtr< LPWSTR > FailureCommand;
|
||
|
||
ApiStatus = ScReadFailureCommand(
|
||
ServiceNameKey,
|
||
&FailureCommand,
|
||
pcbBytesNeeded
|
||
);
|
||
if (ApiStatus != NO_ERROR)
|
||
{
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Check for sufficient buffer space
|
||
//
|
||
if (cbBufSize < *pcbBytesNeeded)
|
||
{
|
||
ApiStatus = ERROR_INSUFFICIENT_BUFFER;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Copy the information to the output buffer
|
||
// The format is:
|
||
// SERVICE_FAILURE_ACTIONS_WOW64 struct
|
||
// SC_ACTIONS array
|
||
// strings
|
||
// Pointers in the struct are replaced with offsets based at the
|
||
// start of the buffer. NULL pointers remain NULL.
|
||
//
|
||
COutBuf OutBuf(lpBuffer);
|
||
|
||
LPSERVICE_FAILURE_ACTIONS_WOW64 psfaOut =
|
||
(LPSERVICE_FAILURE_ACTIONS_WOW64) OutBuf.Next();
|
||
|
||
if (psfa != NULL)
|
||
{
|
||
psfaOut->dwResetPeriod = ((LPSERVICE_FAILURE_ACTIONS_WOW64) psfa)->dwResetPeriod;
|
||
psfaOut->cActions = ((LPSERVICE_FAILURE_ACTIONS_WOW64) psfa)->cActions;
|
||
}
|
||
else
|
||
{
|
||
psfaOut->dwResetPeriod = 0;
|
||
psfaOut->cActions = 0;
|
||
}
|
||
OutBuf.AddUsed(sizeof(SERVICE_FAILURE_ACTIONS_WOW64));
|
||
|
||
if (psfaOut->cActions != 0)
|
||
{
|
||
psfaOut->dwsaActionsOffset = OutBuf.OffsetNext();
|
||
|
||
OutBuf.AppendBytes(psfa + 1,
|
||
psfaOut->cActions * sizeof(SC_ACTION));
|
||
}
|
||
else
|
||
{
|
||
psfaOut->dwsaActionsOffset = 0;
|
||
}
|
||
SC_ASSERT(COUNT_IS_ALIGNED(OutBuf.OffsetNext(), sizeof(WCHAR)));
|
||
|
||
if (RebootMessage != NULL)
|
||
{
|
||
psfaOut->dwRebootMsgOffset = OutBuf.OffsetNext();
|
||
OutBuf.AppendBytes(RebootMessage, (DWORD) WCSSIZE(RebootMessage));
|
||
}
|
||
else
|
||
{
|
||
psfaOut->dwRebootMsgOffset = 0;
|
||
}
|
||
|
||
if (FailureCommand != NULL)
|
||
{
|
||
psfaOut->dwCommandOffset = OutBuf.OffsetNext();
|
||
OutBuf.AppendBytes(FailureCommand, (DWORD) WCSSIZE(FailureCommand));
|
||
}
|
||
else
|
||
{
|
||
psfaOut->dwCommandOffset = 0;
|
||
}
|
||
}
|
||
break;
|
||
|
||
//-----------------------------
|
||
//
|
||
// Other (invalid)
|
||
//
|
||
//-----------------------------
|
||
default:
|
||
ApiStatus = ERROR_INVALID_LEVEL;
|
||
break;
|
||
}
|
||
|
||
|
||
ScRegCloseKey(ServiceNameKey);
|
||
|
||
SC_LOG1(CONFIG_API, "RQueryServiceConfig2W returning %lu\n", ApiStatus);
|
||
|
||
return ApiStatus;
|
||
}
|
||
|
||
|
||
|
||
#if DBG == 1
|
||
|
||
VOID
|
||
PrintConfig2Parms(
|
||
IN SC_RPC_HANDLE hService,
|
||
IN SC_RPC_CONFIG_INFOW Info
|
||
)
|
||
{
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
"Parameters to RChangeServiceConfig2W:\n"));
|
||
|
||
LPSTR psz;
|
||
switch (Info.dwInfoLevel)
|
||
{
|
||
case SERVICE_CONFIG_DESCRIPTION:
|
||
psz = "SERVICE_CONFIG_DESCRIPTION";
|
||
break;
|
||
case SERVICE_CONFIG_FAILURE_ACTIONS:
|
||
psz = "SERVICE_CONFIG_FAILURE_ACTIONS";
|
||
break;
|
||
default:
|
||
psz = "invalid";
|
||
break;
|
||
}
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" dwInfoLevel = %ld (%s)\n", Info.dwInfoLevel,
|
||
psz));
|
||
|
||
switch (Info.dwInfoLevel)
|
||
{
|
||
case SERVICE_CONFIG_DESCRIPTION:
|
||
|
||
if (Info.psd == NULL)
|
||
{
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" NULL information pointer -- no action requested\n\n"));
|
||
|
||
break;
|
||
}
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" pszDescription = \"%ws\"\n",
|
||
Info.psd->lpDescription));
|
||
|
||
break;
|
||
|
||
case SERVICE_CONFIG_FAILURE_ACTIONS:
|
||
if (Info.psfa == NULL)
|
||
{
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" NULL information pointer -- no action requested\n\n"));
|
||
|
||
break;
|
||
}
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" dwResetPeriod = %ld\n",
|
||
Info.psfa->dwResetPeriod));
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" lpRebootMsg = \"%ws\"\n",
|
||
Info.psfa->lpRebootMsg));
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" lpCommand = \"%ws\"\n",
|
||
Info.psfa->lpCommand));
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" %ld failure %s\n",
|
||
Info.psfa->cActions,
|
||
Info.psfa->cActions == 0 ? "actions." :
|
||
Info.psfa->cActions == 1 ? "action:" : "actions:"));
|
||
|
||
if (Info.psfa->lpsaActions == NULL)
|
||
{
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" NULL array pointer -- no change in fixed info\n\n"));
|
||
}
|
||
else
|
||
{
|
||
for (DWORD i = 0; i < Info.psfa->cActions; i++)
|
||
{
|
||
SC_ACTION& sa = Info.psfa->lpsaActions[i];
|
||
KdPrintEx((DPFLTR_SCSERVER_ID,
|
||
DEBUG_CONFIG_API,
|
||
" %ld: Action %ld, Delay %ld\n",
|
||
i,
|
||
sa.Type,
|
||
sa.Delay));
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
KdPrintEx((DPFLTR_SCSERVER_ID, DEBUG_CONFIG_API, "\n"));
|
||
}
|
||
|
||
#endif // DBG == 1
|