win: improve and unify service start/stop logic

This commit introduces a more structured approach to starting, stopping,
and managing Windows services. By abstracting service control operations
into dedicated functions (`StopService`, `StartService`, etc.), it
improves code readability and facilitates future maintenance.

The modifications include:

- Creation of files (`%APPDIR%`\privacy.sexy-<serviceName>`) for
  managing service restart states. This approach simplifies the process
  of determining whether a service was running before the script
  executed and should therefore be restarted afterward.
- Using `DeleteFiles` and `ClearDirectoryContents` functions to safely
  remove files without affecting service operations. This is enabled by
  using shared funtions for service operations.
This commit is contained in:
undergroundwires
2024-03-14 07:17:11 +01:00
parent 4ac1425f76
commit adc2089887

View File

@@ -1174,7 +1174,7 @@ actions:
parameters: parameters:
fileGlob: '%SYSTEMROOT%\Logs\DISM\DISM.log' fileGlob: '%SYSTEMROOT%\Logs\DISM\DISM.log'
- -
name: Clear Windows update files # Marked: stop-service-do-stuff-restart-service name: Clear Windows update files
docs: |- docs: |-
This script clears the contents of the `%SYSTEMROOT%\SoftwareDistribution\` directory. This script clears the contents of the `%SYSTEMROOT%\SoftwareDistribution\` directory.
This action is sometimes called *resetting the Windows Update Agent* or *resetting Windows Update components* by Microsoft [1]. This action is sometimes called *resetting the Windows Update Agent* or *resetting Windows Update components* by Microsoft [1].
@@ -1203,18 +1203,22 @@ actions:
[8]: https://web.archive.org/web/20231027190213/https://support.microsoft.com/en-us/windows/troubleshoot-problems-updating-windows-188c2b0f-10a7-d72f-65b8-32d177eb136c#WindowsVersion=Windows_11 "Troubleshoot problems updating Windows - Microsoft Support | support.microsoft.com" [8]: https://web.archive.org/web/20231027190213/https://support.microsoft.com/en-us/windows/troubleshoot-problems-updating-windows-188c2b0f-10a7-d72f-65b8-32d177eb136c#WindowsVersion=Windows_11 "Troubleshoot problems updating Windows - Microsoft Support | support.microsoft.com"
[9]: https://web.archive.org/web/20231027190503/https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/update-management/troubleshoot-software-update-scan-failures "Troubleshoot software update scan failures - Configuration Manager | Microsoft Learn | learn.microsoft.com" [9]: https://web.archive.org/web/20231027190503/https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/update-management/troubleshoot-software-update-scan-failures "Troubleshoot software update scan failures - Configuration Manager | Microsoft Learn | learn.microsoft.com"
[10]: https://web.archive.org/web/20231029172022/https://support.microsoft.com/en-us/topic/you-receive-an-administrators-only-error-message-in-windows-xp-when-you-try-to-visit-the-windows-update-web-site-or-the-microsoft-update-web-site-d2c732b6-21e0-a2ce-8d18-303ed71736c9 'You receive an "Administrators only" error message in Windows XP when you try to visit the Windows Update Web site or the Microsoft Update Web site - Microsoft Support | support.microsoft.com' [10]: https://web.archive.org/web/20231029172022/https://support.microsoft.com/en-us/topic/you-receive-an-administrators-only-error-message-in-windows-xp-when-you-try-to-visit-the-windows-update-web-site-or-the-microsoft-update-web-site-d2c732b6-21e0-a2ce-8d18-303ed71736c9 'You receive an "Administrators only" error message in Windows XP when you try to visit the Windows Update Web site or the Microsoft Update Web site - Microsoft Support | support.microsoft.com'
code: |- # `sc queryex` output is the same in every OS language call:
setlocal EnableDelayedExpansion -
SET /A wuau_service_running=0 function: StopService
SC queryex "wuauserv"|Find "STATE"|Find /v "RUNNING">Nul||( parameters:
SET /A wuau_service_running=1 serviceName: wuauserv
net stop wuauserv waitUntilStopped: true
) serviceRestartStateFile: '%APPDATA%\privacy.sexy-wuauserv' # Marked: refactor-with-variables (app dir should be unified, not using %TEMP% as it can be cleaned during operation)
del /q /s /f "%SYSTEMROOT%\SoftwareDistribution\*" -
IF !wuau_service_running! == 1 ( function: ClearDirectoryContents
net start wuauserv parameters:
) directoryGlob: '%SYSTEMROOT%\SoftwareDistribution'
endlocal -
function: StartService
parameters:
serviceName: wuauserv
serviceRestartStateFile: '%APPDATA%\privacy.sexy-wuauserv' # Marked: refactor-with-variables (app dir should be unified, not using %TEMP% as it can be cleaned during operation)
- -
name: Clear Common Language Runtime system logs name: Clear Common Language Runtime system logs
recommend: standard recommend: standard
@@ -1251,7 +1255,7 @@ actions:
parameters: parameters:
directoryGlob: '%SYSTEMROOT%\System32\LogFiles\setupcln' directoryGlob: '%SYSTEMROOT%\System32\LogFiles\setupcln'
- -
name: Clear diagnostics tracking logs # Marked: stop-service-do-stuff-restart-service ("DiagTrack") name: Clear diagnostics tracking logs
recommend: standard recommend: standard
docs: |- docs: |-
This script deletes primary telemetry files in Windows. This script deletes primary telemetry files in Windows.
@@ -1286,6 +1290,12 @@ actions:
[6]: https://web.archive.org/web/20231027164510/https://learn.microsoft.com/en-us/windows/win32/etw/configuring-and-starting-an-autologger-session "Configuring and Starting an AutoLogger Session - Win32 apps | Microsoft Learn | learn.microsoft.com" [6]: https://web.archive.org/web/20231027164510/https://learn.microsoft.com/en-us/windows/win32/etw/configuring-and-starting-an-autologger-session "Configuring and Starting an AutoLogger Session - Win32 apps | Microsoft Learn | learn.microsoft.com"
[7]: https://web.archive.org/web/20240217185108/https://learn.microsoft.com/en-us/windows/privacy/configure-windows-diagnostic-data-in-your-organization "Configure Windows diagnostic data in your organization (Windows 10 and Windows 11) - Windows Privacy | Microsoft Learn | learn.microsoft.com" [7]: https://web.archive.org/web/20240217185108/https://learn.microsoft.com/en-us/windows/privacy/configure-windows-diagnostic-data-in-your-organization "Configure Windows diagnostic data in your organization (Windows 10 and Windows 11) - Windows Privacy | Microsoft Learn | learn.microsoft.com"
call: call:
-
function: StopService
parameters:
serviceName: DiagTrack
waitUntilStopped: true
serviceRestartStateFile: '%APPDATA%\privacy.sexy-DiagTrack' # Marked: refactor-with-variables (app dir should be unified, not using %TEMP% as it can be cleaned during operation)
- -
function: DeleteFiles function: DeleteFiles
parameters: parameters:
@@ -1296,6 +1306,11 @@ actions:
parameters: parameters:
fileGlob: '%PROGRAMDATA%\Microsoft\Diagnosis\ETLLogs\ShutdownLogger\AutoLogger-Diagtrack-Listener.etl' fileGlob: '%PROGRAMDATA%\Microsoft\Diagnosis\ETLLogs\ShutdownLogger\AutoLogger-Diagtrack-Listener.etl'
grantPermissions: true grantPermissions: true
-
function: StartService
parameters:
serviceName: DiagTrack
serviceRestartStateFile: '%APPDATA%\privacy.sexy-DiagTrack' # Marked: refactor-with-variables (app dir should be unified, not using %TEMP% as it can be cleaned during operation)
- -
name: Clear event logs in Event Viewer application name: Clear event logs in Event Viewer application
docs: https://serverfault.com/questions/407838/do-windows-events-from-the-windows-event-log-have-sensitive-information docs: https://serverfault.com/questions/407838/do-windows-events-from-the-windows-event-log-have-sensitive-information
@@ -1452,7 +1467,7 @@ actions:
recommend: standard recommend: standard
code: dism /online /Remove-DefaultAppAssociations code: dism /online /Remove-DefaultAppAssociations
- -
name: Clear System Resource Usage Monitor (SRUM) data # Marked: stop-service-do-stuff-restart-service name: Clear System Resource Usage Monitor (SRUM) data
recommend: standard recommend: standard
docs: |- docs: |-
This script deletes the Windows System Resource Usage Monitor (SRUM) database file. This script deletes the Windows System Resource Usage Monitor (SRUM) database file.
@@ -1472,47 +1487,25 @@ actions:
[5]: https://web.archive.org/web/20231008135321/https://devblogs.microsoft.com/sustainable-software/measuring-your-application-power-and-carbon-impact-part-1/ "Measuring Your Application Power and Carbon Impact (Part 1) - Sustainable Software | devblogs.microsoft.com" [5]: https://web.archive.org/web/20231008135321/https://devblogs.microsoft.com/sustainable-software/measuring-your-application-power-and-carbon-impact-part-1/ "Measuring Your Application Power and Carbon Impact (Part 1) - Sustainable Software | devblogs.microsoft.com"
[6]: https://web.archive.org/web/20231008135333/https://www.sciencedirect.com/science/article/abs/pii/S1742287615000031 "Forensic implications of System Resource Usage Monitor (SRUM) data in Windows 8 | Yogesh Khatri | sciencedirect.com" [6]: https://web.archive.org/web/20231008135333/https://www.sciencedirect.com/science/article/abs/pii/S1742287615000031 "Forensic implications of System Resource Usage Monitor (SRUM) data in Windows 8 | Yogesh Khatri | sciencedirect.com"
call: call:
function: RunPowerShell -
parameters:
# If the service is not stopped, following error is thrown: # If the service is not stopped, following error is thrown:
# Failed to delete SRUM database file at: "C:\Windows\System32\sru\SRUDB.dat". Error Details: The process cannot access # Failed to delete SRUM database file at: "C:\Windows\System32\sru\SRUDB.dat". Error Details: The process cannot access
# the file 'C:\Windows\System32\sru\SRUDB.dat' because it is being used by another process. # the file 'C:\Windows\System32\sru\SRUDB.dat' because it is being used by another proces
code: |- function: StopService
$srumDatabaseFilePath = "$env:WINDIR\System32\sru\SRUDB.dat" parameters:
if (!(Test-Path -Path $srumDatabaseFilePath)) { serviceName: DPS
Write-Output "Skipping, SRUM database file not found at `"$srumDatabaseFilePath`". No actions are required." waitUntilStopped: true
exit 0 serviceRestartStateFile: '%APPDATA%\privacy.sexy-DPS' # Marked: refactor-with-variables (app dir should be unified, not using %TEMP% as it can be cleaned during operation)
} -
$dps = Get-Service -Name 'DPS' -ErrorAction Ignore function: DeleteFiles
$isDpsInitiallyRunning = $false parameters:
if ($dps) { fileGlob: '%WINDIR%\System32\sru\SRUDB.dat'
$isDpsInitiallyRunning = $dps.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running grantPermissions: true
if ($isDpsInitiallyRunning) { -
Write-Output "Stopping the Diagnostic Policy Service (DPS) to delete the SRUM database file." function: StartService
$dps | Stop-Service -Force parameters:
$dps.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped) serviceName: DPS
Write-Output "Successfully stopped Diagnostic Policy Service (DPS)." serviceRestartStateFile: '%APPDATA%\privacy.sexy-DPS' # Marked: refactor-with-variables (app dir should be unified, not using %TEMP% as it can be cleaned during operation)
}
} else {
Write-Output "Diagnostic Policy Service (DPS) not found. Proceeding without stopping the service."
}
try {
Remove-Item -Path $srumDatabaseFilePath -Force -ErrorAction Stop
Write-Output "Successfully deleted the SRUM database file at `"$srumDatabaseFilePath`"."
} catch {
throw "Failed to delete SRUM database file at: `"$srumDatabaseFilePath`". Error Details: $($_.Exception.Message)"
} finally {
if ($isDpsInitiallyRunning) {
try {
if ((Get-Service -Name 'DPS').Status -ne [System.ServiceProcess.ServiceControllerStatus]::Running) {
Write-Output "Restarting the Diagnostic Policy Service (DPS)."
$dps | Start-Service
}
} catch {
throw "Failed to restart the Diagnostic Policy Service (DPS). Error Details: $($_.Exception.Message)"
}
}
}
- -
name: Clear previous Windows installations name: Clear previous Windows installations
call: call:
@@ -15882,10 +15875,12 @@ actions:
category: Advanced settings category: Advanced settings
children: children:
- -
name: Set NTP (time) server to `pool.ntp.org` # Marked: stop-service-do-stuff-restart-service name: Set NTP (time) server to `pool.ntp.org`
docs: https://www.pool.ntp.org/en/use.html docs: https://www.pool.ntp.org/en/use.html
recommend: strict recommend: strict
# `sc queryex` output is same in every OS language # `sc queryex` output is same in every OS language
# Marked: refactor-with-revert-call, refactor-with-variables
# This would allow re-using `StartService` and `StopService`
code: |- code: |-
:: Configure time source :: Configure time source
w32tm /config /syncfromflags:manual /manualpeerlist:"0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org" w32tm /config /syncfromflags:manual /manualpeerlist:"0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org"
@@ -15904,7 +15899,7 @@ actions:
SC queryex "w32time"|Find "STATE"|Find /v "RUNNING">Nul||( SC queryex "w32time"|Find "STATE"|Find /v "RUNNING">Nul||(
net stop w32time net stop w32time
) )
:: Start time servie and sync now :: Start time service and sync now
net start w32time net start w32time
w32tm /config /update w32tm /config /update
w32tm /resync w32tm /resync
@@ -16717,6 +16712,8 @@ functions:
- name: defaultStartupMode # Allowed values: Boot | System | Automatic | Manual - name: defaultStartupMode # Allowed values: Boot | System | Automatic | Manual
call: call:
function: RunPowerShell function: RunPowerShell
# Marked: refactor-with-revert-call, refactor-with-variables
# Implementation of those should share similar code: `DisableService`, `StopService`, `StartService`, `DisableServiceInRegistry`
parameters: parameters:
code: |- # We do registry way because GUI, "sc config" or "Set-Service" won't not work code: |- # We do registry way because GUI, "sc config" or "Set-Service" won't not work
$serviceQuery = '{{ $serviceName }}' $serviceQuery = '{{ $serviceName }}'
@@ -16938,6 +16935,123 @@ functions:
} }
exit 1 exit 1
} }
-
name: StopService
parameters:
- name: serviceName
- name: serviceRestartStateFile # This file is created only if the service is successfully stopped.
optional: true
- name: waitUntilStopped # Makes the script wait until the service is stopped
optional: true
call:
-
function: Comment
parameters:
codeComment: >-
Stop service: {{ $serviceName }}
{{ with $serviceRestartStateFile }}(with state flag){{ end }}
{{ with $waitUntilStopped }}(wait until stopped){{ end }}
-
function: RunPowerShell
parameters:
# Marked: refactor-with-variables
# Implementation of those should share similar code: `DisableService`, `StopService`, `StartService`, `DisableServiceInRegistry`
code: |-
$serviceName = '{{ $serviceName }}'
Write-Host "Stopping service: `"$serviceName`"."
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if (!$service) {
Write-Host "Skipping, service `"$serviceName`" could not be not found, no need to stop it."
exit 0
}
if ($service.Status -ne [System.ServiceProcess.ServiceControllerStatus]::Running) {
Write-Host "Skipping, `"$serviceName`" is not running, no need to stop."
exit 0
}
Write-Host "`"$serviceName`" is running, stopping it."
try {
$service | Stop-Service -Force -ErrorAction Stop
{{ with $waitUntilStopped }}
$service.WaitForStatus([System.ServiceProcess.ServiceControllerStatus]::Stopped)
{{ end }}
} catch {
throw "Failed to stop the service `"$serviceName`": $_"
}
Write-Host "Successfully stopped the service: `"$serviceName`"."
{{ with $serviceRestartStateFile }}
$stateFilePath = '{{ . }}'
$expandedStateFilePath = [System.Environment]::ExpandEnvironmentVariables($stateFilePath)
if (Test-Path -Path $expandedStateFilePath) {
Write-Host "Skipping creating a service state file, it already exists: `"$expandedStateFilePath`"."
} else {
# Ensure the directory exists
$parentDirectory = [System.IO.Path]::GetDirectoryName($expandedStateFilePath)
if (-not (Test-Path $parentDirectory -PathType Container)) {
try {
New-Item -ItemType Directory -Path $parentDirectory -Force -ErrorAction Stop | Out-Null
} catch {
Write-Warning "Failed to create parent directory of service state file `"$parentDirectory`": $_"
}
}
# Create the state file
try {
New-Item -ItemType File -Path $expandedStateFilePath -Force -ErrorAction Stop | Out-Null
Write-Host 'The service will be started again.'
} catch {
Write-Warning "Failed to create service state file `"$expandedStateFilePath`": $_"
}
}
{{ end }}
-
name: StartService
parameters:
- name: serviceName
- name: serviceRestartStateFile # Used for "check and delete": Starts the service only if file exists, always deletes the file.
optional: true
call:
-
function: Comment
parameters:
codeComment: >-
Start service: {{ $serviceName }}
{{ with $serviceRestartStateFile }}(with state flag){{ end }}
-
function: RunPowerShell
parameters:
# Marked: refactor-with-variables
# Implementation of those should share similar code: `DisableService`, `StopService`, `StartService`, `DisableServiceInRegistry`
code: |-
$serviceName = '{{ $serviceName }}'
{{ with $serviceRestartStateFile }}
$stateFilePath = '{{ . }}'
$expandedStateFilePath = [System.Environment]::ExpandEnvironmentVariables($stateFilePath)
if (-not (Test-Path -Path $expandedStateFilePath)) {
Write-Host "Skipping starting the service: It was not running before."
} else {
try {
Remove-Item -Path $expandedStateFilePath -Force -ErrorAction Stop
Write-Host 'The service is expected to be started.'
} catch {
Write-Warning "Failed to delete the service state file `"$expandedStateFilePath`": $_"
}
}
{{ end }}
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if (!$service) {
throw "Failed to start service `"$serviceName`": Service not found."
}
if ($service.Status -eq [System.ServiceProcess.ServiceControllerStatus]::Running) {
Write-Host "Skipping, `"$serviceName`" is already running, no need to start."
exit 0
}
Write-Host "`"$serviceName`" is not running, starting it."
try {
$service | Start-Service -ErrorAction Stop
Write-Host "Successfully started the service: `"$serviceName`"."
} catch {
Write-Warning "Failed to start the service: `"$serviceName`"."
exit 1
}
- -
name: DisableService name: DisableService
parameters: parameters:
@@ -16950,6 +17064,8 @@ functions:
codeComment: "Disable service(s): `{{ $serviceName }}`" codeComment: "Disable service(s): `{{ $serviceName }}`"
revertCodeComment: "Restore service(s) to default state: `{{ $serviceName }}`" revertCodeComment: "Restore service(s) to default state: `{{ $serviceName }}`"
- -
# Marked: refactor-with-revert-call, refactor-with-variables
# Implementation of those should share similar code: `DisableService`, `StopService`, `StartService`, `DisableServiceInRegistry`
function: RunPowerShell function: RunPowerShell
# Careful with Set-Service cmdlet: # Careful with Set-Service cmdlet:
# 1. It exits with positive code even if service is disabled # 1. It exits with positive code even if service is disabled
@@ -16959,7 +17075,7 @@ functions:
# So "Disabled", "Automatic" and "Manual" are only consistent ones. # So "Disabled", "Automatic" and "Manual" are only consistent ones.
# Read more: # Read more:
# https://github.com/PowerShell/PowerShell/blob/v7.2.0/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs#L2966-L2978 # https://github.com/PowerShell/PowerShell/blob/v7.2.0/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs#L2966-L2978
# https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-service?view=powershell-7.1 # https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-service?view=powershell-7.4
parameters: parameters:
code: |- code: |-
$serviceName = '{{ $serviceName }}' $serviceName = '{{ $serviceName }}'
@@ -16982,7 +17098,6 @@ functions:
} else { } else {
Write-Host "`"$serviceName`" is not running, no need to stop." Write-Host "`"$serviceName`" is not running, no need to stop."
} }
# -- 3. Skip if already disabled # -- 3. Skip if already disabled
$startupType = $service.StartType # Does not work before .NET 4.6.1 $startupType = $service.StartType # Does not work before .NET 4.6.1
if(!$startupType) { if(!$startupType) {