Skip to content

Commit

Permalink
Refactor byte parsing and property testing
Browse files Browse the repository at this point in the history
  • Loading branch information
RandomNoun7 committed Mar 31, 2020
1 parent a8ef6ac commit 18bca55
Showing 1 changed file with 93 additions and 108 deletions.
201 changes: 93 additions & 108 deletions source/DSCResources/DSC_xServiceResource/DSC_xServiceResource.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,20 @@ function Get-TargetResource
$serviceFailureActions = Get-ServiceFailureActions -Service $service.Name

$serviceResource = @{
Name = $Name
Ensure = 'Present'
Path = $serviceCimInstance.PathName
StartupType = $startupType
BuiltInAccount = $builtInAccount
State = $service.Status.ToString()
DisplayName = $service.DisplayName
Description = $serviceCimInstance.Description
DesktopInteract = $serviceCimInstance.DesktopInteract
Dependencies = $dependencies
ResetPeriodSeconds = $serviceFailureActions.resetPeriodSeconds
FailureCommand = $serviceFailureActions.failureCommand
RebootMessage = $serviceFailureActions.rebootMessage
failure1Action = $serviceFailureActions.failureAction1.actionType
failure1Delay = $serviceFailureActions.failureAction1.delay
failure2Action = $serviceFailureActions.failureAction2.actionType
failure2Delay = $serviceFailureActions.failureAction2.delay
failure3Action = $serviceFailureActions.failureAction3.actionType
failure3Delay = $serviceFailureActions.failureAction3.delay
Name = $Name
Ensure = 'Present'
Path = $serviceCimInstance.PathName
StartupType = $startupType
BuiltInAccount = $builtInAccount
State = $service.Status.ToString()
DisplayName = $service.DisplayName
Description = $serviceCimInstance.Description
DesktopInteract = $serviceCimInstance.DesktopInteract
Dependencies = $dependencies
ResetPeriodSeconds = $serviceFailureActions.resetPeriodSeconds
FailureCommand = $serviceFailureActions.failureCommand
RebootMessage = $serviceFailureActions.rebootMessage
FailureActionsCollection = $serviceFailureActions.ActionsCollection
}
}
else
Expand Down Expand Up @@ -529,28 +524,8 @@ function Test-TargetResource
$FailureCommand,

[Parameter()]
[ACTION_TYPE]
$Failure1Action,

[Parameter()]
[System.UInt32]
$Failure1Delay,

[Parameter()]
[ACTION_TYPE]
$Failure2Action,

[Parameter()]
[System.UInt32]
$Failure2Delay,

[Parameter()]
[ACTION_TYPE]
$Failure3Action,

[Parameter()]
[System.UInt32]
$Failure3Delay,
[System.Object[]]
$FailureActionsCollection,

[Parameter()]
[ValidateNotNull()]
Expand Down Expand Up @@ -723,47 +698,34 @@ function Test-TargetResource
return $false
}

# Check the failure 1 action
if($PSBoundParameters.ContainsKey('Failure1Action') -and $Failure1Action -ine $serviceResource.failure1Action)
{
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure1Action', $Name, $Failure1Action, $serviceResource.failure1Action)
return $false
}
# Check the failure actions collection
if($PSBoundParameters.ContainsKey('FailureActionsCollection')) {
$inSync = $true
if($FailureActionsCollection.count -ne $serviceResource.FailureActionsCollection.count) {
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'FailureActionsCollection.count', $Name, $FailureActionsCollection.count, $serviceResource.FailureActionsCollection.count)
return $false
}

# Check failure 1 delay
if($PSBoundParameters.ContainsKey('Failure1Delay') -and $Failure1Delay -ine $serviceResource.failure1Delay)
{
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure1Delay', $Name, $Failure1Delay, $serviceResource.failure1Delay)
return $false
}
foreach ($actionIndex in (0..($FailureActionsCollection.count - 1))) {
$parameterAction = $FailureActionsCollection[$actionIndex]
$serviceResourceAction = $serviceResource.FailureActionsCollection[$actionIndex]

# Check the failure 2 action
if($PSBoundParameters.ContainsKey('Failure2Action') -and $Failure2Action -ine $serviceResource.failure2Action)
{
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure2Action', $Name, $Failure2Action, $serviceResource.failure2Action)
return $false
}
if($parameterAction.type -ne $serviceResourceAction.type) {
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f "FailureActionsCollection Action $actionIndex type", $Name, $parameterAction.type, $serviceResourceAction.type)
$inSync = $false
}

# Check failure 2 delay
if($PSBoundParameters.ContainsKey('Failure2Delay') -and $Failure2Delay -ine $serviceResource.failure2Delay)
{
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure2Delay', $Name, $Failure2Delay, $serviceResource.failure2Delay)
return $false
}
if($parameterAction.delay -ne $serviceResourceAction.delay) {
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f "FailureActionsCollection Action $actionIndex delay", $Name, $parameterAction.delay, $serviceResourceAction.delay)
$inSync = $false
}
}

# Check the failure 3 action
if($PSBoundParameters.ContainsKey('Failure3Action') -and $Failure3Action -ine $serviceResource.failure3Action)
{
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure3Action', $Name, $Failure3Action, $serviceResource.failure3Action)
return $false
if(-not $inSync) {
return $inSync
}
}

# Check failure 3 delay
if($PSBoundParameters.ContainsKey('Failure3Delay') -and $Failure3Delay -ine $serviceResource.failure3Delay)
{
Write-Verbose -Message ($script:localizedData.ServicePropertyDoesNotMatch -f 'Failure3Delay', $Name, $Failure3Delay, $serviceResource.failure3Delay)
return $false
}
}

return $true
Expand Down Expand Up @@ -2029,9 +1991,7 @@ function Get-ServiceFailureActions {
failureActionCount = $null
failureCommand = $null
rebootMessage = $null
failureAction1 = @{actionType = $null; delay = $null}
failureAction2 = @{actionType = $null; delay = $null}
failureAction3 = @{actionType = $null; delay = $null}
ActionsCollection = $null
}

if($registryData.GetvalueNames() -match 'FailureCommand') {
Expand All @@ -2048,62 +2008,87 @@ function Get-ServiceFailureActions {

# The first four bytes represent the Reset Period. The bytes are little endian
# so they are reversed, converted to hex, and then cast to an integer.
$failureActions.resetPeriodSeconds = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 0
$failureActions.resetPeriodSeconds = Get-FailureActionsProperty -PropertyName ResetPeriodSeconds -Bytes $failureActionsBinaryData

# Next found bytes indicate the presence of a reboot message in case one of the chosen failure actions is
# SC_ACTION_REBOOT. The actual value of the message is stored in the 'RebootMessage' property
$failureActions.hasRebootMessage = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 1
$failureActions.hasRebootMessage = Get-FailureActionsProperty -PropertyName HasRebootMsg -Bytes $failureActionsBinaryData

# The next four bytes indicate whether a failure action run command exists. This command
# would be run in the case one of the failure actions chosen is SC_ACTION_RUN_COMMAND
# If this value is true then the actual command string is stored in a different value.
$failureActions.hasFailureCommand = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 2
$failureActions.hasFailureCommand = Get-FailureActionsProperty -PropertyName HasFailureCommand -Bytes $failureActionsBinaryData

# These four bytes give the count of how many reboot failure actions have been defined.
# Up to three actions may be defined.
$failureActions.failureActionCount = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -Offset 3
$failureActions.failureActionCount = Get-FailureActionsProperty -PropertyName FailureActionCount -Bytes $failureActionsBinaryData

if($failureActions.failureActionCount -gt 0) {
foreach ($item in 1..$failureActions.failureActionCount) {

# Manually counting the array offset is easier than implementing some weird array arithmetic.
$offset = switch ($item) {
1 { 5 }
2 { 7 }
3 { 9 }
Default {-1}
}
# Occasionaly a service will store an array acount greater than 3. As far as I can tell
# A count greater than 3 has no meaning, so we only support 3.
if($offset -lt 0) { break }
$failureActions."failureAction$item".actionType = [ACTION_TYPE](Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -offset $offset)
$failureActions."failureAction$item".delay = Convert-RegistryBinaryValueToInt -Bytes $failureActionsBinaryData -offset ($offset + 1)
}
$failureActions.ActionsCollection = Get-FailureActionCollection -Bytes $failureActionsBinaryData -ActionsCount $failureActions.failureActionCount
}
}

$failureActions
}
}

function Convert-RegistryBinaryValueToInt
function Get-FailureActionsProperty
{
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[ValidateSet('ResetPeriodSeconds','HasRebootMsg','HasFailureCommand','FailureActionCount')]
[System.String]
$PropertyName,
[Parameter(Mandatory)]
[System.Byte[]]
$Bytes
)

process {
$byteRange = switch ($PropertyName) {
ResetPeriodSeconds { 0..3 }
HasRebootMsg { 4..7 }
HasFailureCommand { 8..11 }
FailureActionCount { 12..15 }
Default {}
}

[System.BitConverter]::ToInt32($Bytes[$byteRange],0)
}
}

function Get-FailureActionCollection
{
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[byte[]]
[System.Byte[]]
$Bytes,
[Parameter(Mandatory)]
[int]
$Offset,
[int]
$Size = 4
[System.UInt32]
$ActionsCount
)

process {
$lastByte = (($offset + 1) * 4) - 1
$firstByte = $lastbyte - ($size - 1)
# Bytes in the registry are little endian, so we reverse them before conversion.
[int]"0x$(($bytes[$lastByte..$firstByte] | %{"{0:x}" -f $_}) -join '' | out-string)"
$actionsCollection = New-Object System.Collections.Generic.List[PSObject]

foreach($action in 0..($ActionsCount - 1)) {

# The structure of the _SERVICE_FAILURE_ACTIONS struct encoded in this registry key
# dictates that the array of _SC_ACTION structs will always start at byte 20.
# The 8 is because each _SC_ACTION is a 8 byte struct.
$actionTypeByteRange = (20 + (8 * $action))..(20 + (8 * $action) + 3)
$actionDelayByteRange = (20 + (8 * $action) + 4)..(20 + (8 * $action) + 7)

$currentAction = [PSCustomObject]@{
type = [ACTION_TYPE]([System.BitConverter]::ToInt32($Bytes[$actionTypeByteRange],0))
delay = [System.BitConverter]::ToInt32($Bytes[$actionDelayByteRange],0)
}

$actionsCollection.Add($currentAction) | Out-Null
}

$actionsCollection
}
}

0 comments on commit 18bca55

Please sign in to comment.