mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
874 lines
23 KiB
874 lines
23 KiB
/*++
|
|
|
|
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
|