Update, we've identified the issue and we've built a correction script that must be executed as an administrator on affected machines:
# Function to calculate file hash
function Get-FileHashLower {
param (
[string]$FilePath,
[string]$Algorithm = "MD5"
)
if (-not (Test-Path -Path $FilePath)) {
Write-Output "Error: File not found - $FilePath"
return $null
}
try {
return (Get-FileHash -Path $FilePath -Algorithm $Algorithm).Hash.ToLower()
} catch {
Write-Output "Error: Unable to calculate hash for $FilePath. $_"
return $null
}
}
Write-Output "Environment Variables:"
Get-ChildItem Env: | Sort-Object Name
# Enable TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Step 0: Locate "PC Monitor" service binary folder
$ServiceName = "PC Monitor"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if (-not $Service) {
Write-Output "Error: '$ServiceName' service not found."
exit 1
}
$BinaryPath = (Get-WmiObject Win32_Service | Where-Object { $_.Name -eq $ServiceName }).PathName.Trim('"')
if (-not $BinaryPath) {
Write-Output "Error: Unable to find binary path for '$ServiceName'."
exit 1
}
$BinaryFolder = Split-Path -Path $BinaryPath -Parent
Write-Output "Binary folder located: $BinaryFolder"
# Step 1: Download and verify update.zip
$UpdateUrl = "https://updates.pulseway.com/update.zip"
$DownloadedFile = "$env:Temp\update.zip"
try {
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile($UpdateUrl, $DownloadedFile)
Write-Output "File downloaded successfully to $DownloadedFile"
} catch {
Write-Output "Error: Failed to download the update file. $_"
exit 1
}
$ExpectedMD5 = "9235318c553c3f770ba95ab673566a4e"
$DownloadedMD5 = Get-FileHashLower -FilePath $DownloadedFile
if ($DownloadedMD5 -ne $ExpectedMD5) {
Write-Output "Error: MD5 hash of the downloaded file does not match. Expected: $ExpectedMD5, Found: $DownloadedMD5"
exit 1
}
# Step 2: Extract the archive
$ExtractedFolder = "$env:Temp\update_extracted"
Write-Output "Extracted Path: $TargetPath"
if (Test-Path $ExtractedFolder) { Remove-Item -Recurse -Force $ExtractedFolder }
New-Item -ItemType Directory -Path $ExtractedFolder | Out-Null
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($DownloadedFile, $ExtractedFolder)
Write-Output "Extracted Files: ----"
Get-ChildItem -Path $ExtractedFolder -Recurse | ForEach-Object { Write-Output $_.FullName }
Write-Output "Extracted Files: ++++"
# Step 3: Compare hashes of files
$DiscrepantFiles = @()
foreach ($File in Get-ChildItem -Recurse -Path $ExtractedFolder) {
# Normalize and resolve paths
$ResolvedExtractedFolder = (Get-Item $ExtractedFolder).FullName
$ResolvedFilePath = (Get-Item $File.FullName).FullName
# Calculate relative path
$RelativePath = $ResolvedFilePath.Substring($ResolvedExtractedFolder.Length).TrimStart('\')
Write-Output "Resolved Extracted Folder: $ResolvedExtractedFolder"
Write-Output "Resolved File Path: $ResolvedFilePath"
Write-Output "Calculated Relative Path: $RelativePath"
# Calculate target path
$TargetPath = Join-Path -Path $BinaryFolder -ChildPath $RelativePath
Write-Output "Calculated Target Path: $TargetPath"
if ($File.PSIsContainer) {
# Create folder if it does not exist
if (-not (Test-Path $TargetPath)) {
Write-Output "Creating folder in target path: $RelativePath"
New-Item -ItemType Directory -Path $TargetPath -Force | Out-Null
}
} else {
# Compare files
if (-not (Test-Path $TargetPath)) {
Write-Output "File not found in target folder: $RelativePath"
$DiscrepantFiles += $RelativePath
} else {
$SourceHash = Get-FileHashLower -FilePath $File.FullName
$TargetHash = Get-FileHashLower -FilePath $TargetPath
if ($SourceHash -eq $null -or $TargetHash -eq $null) {
Write-Output "Skipping comparison due to hash error: $RelativePath"
continue
}
if ($SourceHash -ne $TargetHash) {
Write-Output "Hash mismatch for file: $RelativePath"
$DiscrepantFiles += $RelativePath
}
}
}
}
# Step 3.5: Exit if no discrepancies found
if ($DiscrepantFiles.Count -eq 0) {
Write-Output "No discrepancies found. Exiting."
exit 0
}
# Step 4: Print the list of discrepant files
Write-Output "Discrepant files:"
$DiscrepantFiles | ForEach-Object { Write-Output $_ }
# Step 5: Stop "PC Monitor" service and related processes
$ProcessesToKill = @("pcmonitormanager.exe", "pcmonitorsrv.exe", "pcmontask.exe", "pcmonusertask.exe", "cli.exe", "addonmanager.exe", "pcmupdate.exe")
for ($i = 1; $i -le 5; $i++) {
if ($Service.Status -eq "Running") {
Stop-Service -Name $ServiceName -Force -ErrorAction SilentlyContinue
}
foreach ($ProcessName in $ProcessesToKill) {
Get-Process -Name $ProcessName -ErrorAction SilentlyContinue | Stop-Process -Force
}
Start-Sleep -Seconds 1
if ((Get-Service -Name $ServiceName).Status -eq "Stopped" -and -not (Get-Process -Name $ProcessesToKill -ErrorAction SilentlyContinue)) {
break
}
Write-Output "Retrying stop operation... ($i/5)"
if ($i -eq 5) {
Write-Output "Error: Failed to stop all processes after 5 attempts."
exit 1
}
}
# Step 6: Copy discrepant files
foreach ($File in $DiscrepantFiles) {
$SourceFile = Join-Path $ExtractedFolder $File
$TargetFile = Join-Path $BinaryFolder $File
$TargetFolder = Split-Path -Path $TargetFile -Parent
if (-not (Test-Path $TargetFolder)) {
New-Item -ItemType Directory -Path $TargetFolder | Out-Null
}
for ($i = 1; $i -le 5; $i++) {
Copy-Item -Path $SourceFile -Destination $TargetFile -Force -ErrorAction SilentlyContinue
if (Test-Path $TargetFile) { break }
Write-Output "Retrying copy operation for $File... ($i/5)"
Start-Sleep -Seconds 1
}
if (-not (Test-Path $TargetFile)) {
Write-Output "Error: Failed to copy $File after 5 attempts."
exit 1
}
}
# Step 7: Verify hashes again
$PostVerificationFailures = @()
foreach ($File in $DiscrepantFiles) {
$SourceFile = Join-Path $ExtractedFolder $File
$TargetFile = Join-Path $BinaryFolder $File
$SourceHash = Get-FileHashLower -FilePath $SourceFile
$TargetHash = Get-FileHashLower -FilePath $TargetFile
if ($SourceHash -ne $TargetHash) {
$PostVerificationFailures += $File
}
}
if ($PostVerificationFailures.Count -gt 0) {
Write-Output "Post-repair verification failed for the following files:"
$PostVerificationFailures | ForEach-Object { Write-Output $_ }
exit 1
}
# Step 8: Start the "PC Monitor" service
Start-Service -Name $ServiceName
Write-Output "Agent repair successful."
exit 0
We apologize for the inconvenience caused.
-Paul