Cackledaemon.psm1
class CackledaemonException: System.Exception { CackledaemonException([string]$Message) : base($Message) {} } class CackledaemonAlreadyRunningException: CackledaemonException { CackledaemonAlreadyRunningException([string]$Message) : base($Message) {} } class CackledaemonNotRunningException: CackledaemonException { CackledaemonNotRunningException([string]$Message) : base($Message) {} } $CackledaemonWD = Join-Path $env:APPDATA 'cackledaemon' function Ensure-CackledaemonWD { If (-not (Test-Path $CackledaemonWD)) { New-Item -Path $CackledaemonWD -ItemType directory } } $CackledaemonLogFile = Join-Path $CackledaemonWD 'log.log' $CackledaemonLogSize = 16 $CackledaemonLogRotate = 4 $CackledaemonLogCheckTime = 2 # Seconds function Write-CackledaemonLog { Param ([string]$Message) Ensure-CackledaemonWD $Line = ('[{0}] CACKLEDAEMON: {1}' -f (Get-Date -Format o), $Message) Add-Content $CackledaemonLogFile -value $Line } function Start-CackledaemonLogRotateJob { Start-Job ` -Name 'CackledaemonLogRotateJob' ` -WorkingDirectory $CackledaemonWD ` -ScriptBlock { Write-CackledaemonLog "{0} {1}" -f (Get-Item $CackledaemonLogFile).Length, $CackledaemonLogSize while ($true) { If ((Get-Item $CackledaemonLogFile).Length -ge $CackledaemonLogSize) { Write-CackledaemonLog 'Rotating logs...' ($CackledaemonLogRotate..0) | ForEach-Object { $Current = Join-Path ` $CackledaemonWD ` $(If ($_) { 'log.log.{0}' -f $_ } Else { 'log.log' }) $Next = Join-Path $CackledaemonWD ('log.log{0}' -f ($_ + 1)) If (Test-Path $Current) { Write-CackledaemonLog ('Copying {0} to {1}...' -f $Current, $Next) Copy-Item -Path $Current -Destination $Next } } Write-CackledaemonLog ('Truncating {0}...' -f $CackledaemonLogFile) Clear-Content $CackledaemonLogFile $StaleLogFile = Join-Path ` $CackledaemonWD ` ('log.log.{0}' -f ($CackledaemonLogRotate + 1)) If (Test-Path $StaleLogFile) { Write-CackledaemonLog ('Removing {0}...' -f $StaleLogFile) Remove-Item $StaleLogFile } Write-CackledaemonLog 'Done.' } Write-CackledaemonLog 'All quiet on the Western front...' Start-Sleep -Seconds $CackledaemonLogCheckTime } } } $CackledaemonProcessStateFile = Join-Path $CackledaemonWD "DaemonProcessState.json" function Write-ProcessState { param([System.Diagnostics.Process]$Process) $Process | ConvertTo-Json | Out-File $CackledaemonProcessStateFile } function Get-ProcessState { $Id = (Get-Content $CackledaemonProcessStateFile | ConvertFrom-Json).Id If (-not $Id) { return $null } return Get-Process -Id $Id } function Get-UnmanagedEmacsDaemons () { $ManagedProcess = $(Retrieve-ProcessState) return Get-CimInstance -Query " SELECT * FROM Win32_Process WHERE Name = 'emacs.exe' OR Name = 'runemacs.exe' " | Where-Object { $_.CommandLine.Contains("--daemon") } | ForEach-Object { Get-Process -Id ($_.ProcessId) } | Where-Object { -not ($_.Id -eq $ManagedProcess.Id) } } function Start-EmacsDaemon { $Process = $(Get-ProcessState) If ($Process) { Throw [CackledaemonAlreadyRunningException]::new( "The Emacs daemon is already running and being managed!" ) } If ($(Get-UnmanagedEmacsDaemons)) { Throw [CackledaemonAlreadyRunningException]::new( "The Emacs daemon has already been started by someone else and " + "is not being managed!" ) } Write-CackledaemonLog "Starting the Emacs daemon..." $Process = Start-Process ` -FilePath "emacs.exe" ` -ArgumentList "--daemon" ` -NoNewWindow ` -RedirectStandardOut $logFile ` -RedirectStandardError $logFile ` -PassThru Write-CackledaemonLog "Saving the Emacs daemon's process state..." Write-ProcessState -Process $Process Write-CackledaemonLog "Done." return $Process } function Stop-EmacsDaemon { $Process = Retrieve-ProcessState If (-not $Process) { Throw [CackledaemonNotRunningException]::new( "A managed Emacs daemon isn't running and can not be stopped!" ) } Write-CackledaemonLog "Stopping the Emacs daemon..." Stop-Process -InputObject $Process Store-ProcessState $null Write-CackledaemonLog "Done." } function Restart-EmacsDaemon { Stop-EmacsDaemon Start-EmacsDaemon } Export-ModuleMember ` -Function @( 'Ensure-CackledaemonWD', 'Write-CackledaemonLog', 'Start-CackledaemonLogRotateJob', 'Start-EmacsDaemon', 'Stop-EmacsDaemon', 'Restart-EmacsDaemon', 'Write-ProcessState', 'Get-ProcessState', 'Get-UnmanagedEmacsDaemons' ) ` -Variable @( 'CackledaemonWD', 'CackledaemonLogFile', 'CackledaemonLogSize', 'CackledaemonLogRotate', 'CackledaemonLogCheckTime', 'CackledaemonProcessStateFile' ) |