Files
Joseph Doherty b330faff03 mbproxy: cross-platform support — Linux/systemd alongside Windows
Make the service build, run, and install on Linux as a first-class
target while keeping the Windows Service + Event Log behaviour intact.

- Build: drop the hardcoded win-x64 RID — single-file publish now works
  for any RID. publish.ps1 gains -Rid; new publish.sh for Linux hosts.
- Diagnostics: DiagnosticSinkSelector picks the Error+ sink per host —
  Windows Event Log under the SCM, local syslog under systemd
  (Serilog.Sinks.SyslogMessages), none for interactive runs. The
  EventLog truncation helper is extracted so it is testable cross-OS.
- Host: Program.cs registers AddSystemd() alongside AddWindowsService().
- Config: a RID-conditioned appsettings template ships Windows or Unix
  paths; both templates are schema-validated by a test.
- Install: systemd unit (Type=exec) plus install.sh / uninstall.sh.
  Also fixes two cross-platform bugs found while testing: install.ps1
  and uninstall.ps1 used New-EventLog / Remove-EventLog (absent in
  PowerShell 7), and the E2E sim launcher hardcoded Windows venv paths.
- Docs updated across README, CLAUDE.md, and docs/ for dual-platform.

413 tests pass on Windows; 374 (all non-simulator) on Linux.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 09:41:59 -04:00

142 lines
5.9 KiB
PowerShell

#Requires -RunAsAdministrator
<#
.SYNOPSIS
Removes the mbproxy Windows Service and its installed files.
.DESCRIPTION
Stops the service, deletes the service registration, removes the binary
install directory, and (unless -KeepConfig is specified) removes the data
directory. Log files are always preserved: they are moved to a timestamped
archive directory so post-uninstall diagnostics remain accessible.
.PARAMETER ServiceName
Windows Service name to uninstall.
Default: mbproxy
.PARAMETER InstallPath
Directory that was used as the install target.
Default: C:\Program Files\Mbproxy
.PARAMETER KeepConfig
If specified, leaves %ProgramData%\mbproxy\appsettings.json in place.
Logs are always preserved regardless of this flag.
.EXAMPLE
.\uninstall.ps1
.EXAMPLE
.\uninstall.ps1 -KeepConfig
#>
[CmdletBinding()]
param(
[string]$ServiceName = 'mbproxy',
[string]$InstallPath = 'C:\Program Files\Mbproxy',
[switch]$KeepConfig
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
Write-Host "Uninstalling mbproxy service..." -ForegroundColor Cyan
Write-Host " ServiceName : $ServiceName"
Write-Host " InstallPath : $InstallPath"
Write-Host " KeepConfig : $KeepConfig"
# ── 1. Stop the service ───────────────────────────────────────────────────────────────────
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($svc) {
if ($svc.Status -eq 'Running') {
Write-Host "Stopping service '$ServiceName'..."
sc.exe stop $ServiceName | Out-Null
$deadline = [DateTime]::UtcNow.AddSeconds(30)
do {
Start-Sleep -Milliseconds 500
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
} while ($svc -and $svc.Status -ne 'Stopped' -and [DateTime]::UtcNow -lt $deadline)
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($svc -and $svc.Status -ne 'Stopped') {
Write-Warning "Service did not stop within 30 s — attempting force delete."
}
}
# ── 2. Delete the service ─────────────────────────────────────────────────────────────
Write-Host "Deleting service registration '$ServiceName'..."
sc.exe delete $ServiceName | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Warning "sc.exe delete returned $LASTEXITCODE — the service entry may already be gone."
}
} else {
Write-Host "Service '$ServiceName' not found — skipping stop/delete."
}
# ── 3. Archive log files ─────────────────────────────────────────────────────────────────
# Logs are ALWAYS archived (never deleted) so post-uninstall crash diagnostics survive.
$dataDir = Join-Path $env:ProgramData 'mbproxy'
$logDir = Join-Path $dataDir 'logs'
if (Test-Path $logDir) {
$timestamp = [DateTime]::UtcNow.ToString('yyyyMMddTHHmmssZ')
$archiveName = "mbproxy.archived-$timestamp"
$archiveRoot = Join-Path $env:ProgramData $archiveName
$archiveLogs = Join-Path $archiveRoot 'logs'
Write-Host "Archiving logs to '$archiveLogs'..."
New-Item -ItemType Directory -Force $archiveLogs | Out-Null
Get-ChildItem -Path $logDir | ForEach-Object {
Move-Item -Path $_.FullName -Destination $archiveLogs -Force
}
Write-Host " Logs archived to: $archiveLogs" -ForegroundColor Yellow
}
# ── 4. Remove data directory ─────────────────────────────────────────────────────────────
if (Test-Path $dataDir) {
if ($KeepConfig) {
# Remove everything except appsettings.json; then remove the now-empty log dir.
Write-Host "Keeping config at '$dataDir\appsettings.json' (-KeepConfig specified)."
$logDirPath = Join-Path $dataDir 'logs'
if (Test-Path $logDirPath) {
Remove-Item -Recurse -Force $logDirPath -ErrorAction SilentlyContinue
}
} else {
Write-Host "Removing data directory '$dataDir'..."
Remove-Item -Recurse -Force $dataDir -ErrorAction SilentlyContinue
}
}
# ── 5. Remove binary install directory ───────────────────────────────────────────────────
if (Test-Path $InstallPath) {
Write-Host "Removing install directory '$InstallPath'..."
Remove-Item -Recurse -Force $InstallPath -ErrorAction SilentlyContinue
} else {
Write-Host "Install directory '$InstallPath' not found — skipping."
}
# ── 6. Remove Windows Event Log source ───────────────────────────────────────────────────
if ([System.Diagnostics.EventLog]::SourceExists('mbproxy')) {
Write-Host "Removing Windows Event Log source 'mbproxy'..."
try {
# .NET API, not Remove-EventLog: the *-EventLog cmdlets exist only in
# Windows PowerShell 5.1, not PowerShell 7+. Symmetric with the
# SourceExists check above.
[System.Diagnostics.EventLog]::DeleteEventSource('mbproxy')
} catch {
Write-Warning "Could not remove Event Log source: $_"
}
} else {
Write-Host "Windows Event Log source 'mbproxy' not registered — skipping."
}
Write-Host ""
Write-Host "Uninstall complete." -ForegroundColor Green
if (Test-Path (Join-Path $env:ProgramData 'mbproxy.archived-*')) {
Write-Host "Archived logs can be found under: $env:ProgramData\mbproxy.archived-*" -ForegroundColor Yellow
}