You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
9.3 KiB
263 lines
9.3 KiB
#!SEMICOLONS must be placed after each command as scriptdata needs to be sent to powershell directly with the -c parameter! |
|
; |
|
|
|
if ($PSVersionTable.PSVersion.Major -le 5) { |
|
# For Windows PowerShell, we want to remove any PowerShell 7 paths from PSModulePath |
|
#snipped from https://github.com/PowerShell/DSC/pull/777/commits/af9b99a4d38e0cf1e54c4bbd89cbb6a8a8598c4e |
|
#Presumably users are supposed to know not to have custom paths for powershell desktop containing a 'powershell' subfolder?? |
|
; |
|
$env:PSModulePath = ($env:PSModulePath -split ';' | Where-Object { $_ -notlike '*\powershell\*' }) -join ';'; |
|
}; |
|
|
|
$consoleModeSource = @" |
|
using System; |
|
using System.Runtime.InteropServices; |
|
|
|
public class NativeConsoleMethods |
|
{ |
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] |
|
public static extern IntPtr GetStdHandle(int handleId); |
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] |
|
public static extern bool GetConsoleMode(IntPtr hConsoleOutput, out uint dwMode); |
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] |
|
public static extern bool SetConsoleMode(IntPtr hConsoleOutput, uint dwMode); |
|
|
|
public static uint GetConsoleMode(bool input = false) |
|
{ |
|
var handle = GetStdHandle(input ? -10 : -11); |
|
uint mode; |
|
if (GetConsoleMode(handle, out mode)) |
|
{ |
|
return mode; |
|
} |
|
return 0xffffffff; |
|
} |
|
|
|
public static uint SetConsoleMode(bool input, uint mode) |
|
{ |
|
var handle = GetStdHandle(input ? -10 : -11); |
|
if (SetConsoleMode(handle, mode)) |
|
{ |
|
return GetConsoleMode(input); |
|
} |
|
return 0xffffffff; |
|
} |
|
} |
|
"@ |
|
; |
|
[Flags()] |
|
enum ConsoleModeInputFlags |
|
{ |
|
ENABLE_PROCESSED_INPUT = 0x0001 |
|
ENABLE_LINE_INPUT = 0x0002 |
|
ENABLE_ECHO_INPUT = 0x0004 |
|
ENABLE_WINDOW_INPUT = 0x0008 |
|
ENABLE_MOUSE_INPUT = 0x0010 |
|
ENABLE_INSERT_MODE = 0x0020 |
|
ENABLE_QUICK_EDIT_MODE = 0x0040 |
|
ENABLE_EXTENDED_FLAGS = 0x0080 |
|
ENABLE_AUTO_POSITION = 0x0100 |
|
ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200 |
|
}; |
|
|
|
[Flags()] |
|
enum ConsoleModeOutputFlags |
|
{ |
|
ENABLE_PROCESSED_OUTPUT = 0x0001 |
|
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002 |
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 |
|
DISABLE_NEWLINE_AUTO_RETURN = 0x0008 |
|
}; |
|
|
|
if (!('NativeConsoleMethods' -as [System.Type])) { |
|
Add-Type $consoleModeSource |
|
} |
|
|
|
function rawmode { |
|
param ( |
|
[validateSet('enable', 'disable')] |
|
[string]$Action |
|
); |
|
# $inputflags = Get-ConsoleMode -StandardInput; |
|
if (!('NativeConsoleMethods' -as [System.Type])) { |
|
Add-Type $consoleModeSource |
|
} |
|
$inputFlags = [NativeConsoleMethods]::GetConsoleMode($true); |
|
$resultflags = $inputflags; |
|
if (($inputflags -band [ConsoleModeInputFlags]::ENABLE_LINE_INPUT) -eq [ConsoleModeInputFlags]::ENABLE_LINE_INPUT) { |
|
#cooked mode |
|
$initialstate = "cooked"; |
|
if ($action -eq "enable") { |
|
#disable cooked flags |
|
$disable = [uint32](-bnot [uint32][ConsoleModeInputFlags]::ENABLE_LINE_INPUT) -band ( -bnot [uint32][ConsoleModeInputFlags]::ENABLE_ECHO_INPUT); |
|
$adjustedflags = $inputflags -band ($disable); |
|
$resultflags = [NativeConsoleMethods]::SetConsoleMode($true,$adjustedflags); |
|
} |
|
} else { |
|
#raw mode |
|
$initialstate = "raw"; |
|
if ($action -eq "disable") { |
|
#set cooked flags |
|
$adjustedflags = $inputflags -bor [ConsoleModeInputFlags]::ENABLE_LINE_INPUT -bor [ConsoleModeInputFlags]::ENABLE_ECHO_INPUT; |
|
$resultflags = [NativeConsoleMethods]::SetConsoleMode($true,$adjustedflags); |
|
} |
|
} |
|
#return in format that can act as a tcl dict |
|
#write-host "startflags: $inputflags initialstate: $initialstate action: $Action endflags: $resultflags"; |
|
}; |
|
# rawmode 'enable'; |
|
|
|
$consoleid = $args[0]; |
|
if ([string]::IsNullOrEmpty($consoleid)) { |
|
$consoleid= "<punkshell_consoleid>" |
|
}; |
|
$pipeName = "punkshell_ps_consolemode_$consoleid"; |
|
"pipename: $pipeName" |
|
#$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName); |
|
|
|
$sharedData = [hashtable]::Synchronized(@{}) #TSV |
|
$scriptblock = { |
|
param($tsv); |
|
Add-Type -AssemblyName System.IO.Pipes; |
|
#$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream( |
|
# $pipeName, |
|
# [System.IO.Pipes.PipeDirection]::In, |
|
# 1, |
|
# [System.IO.Pipes.PipeTransmissionMode]::Byte, |
|
# [System.IO.Pipes.PipeOptions]::Asynchronous |
|
#); |
|
; |
|
$serverloop = 0; |
|
while ($true) { |
|
$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName); |
|
$serverloop += 1; |
|
$pipeServer.WaitForConnection(); |
|
#write-host "Connection established"; |
|
$reader = New-Object System.IO.StreamReader($pipeServer); |
|
if ($reader -ne $null) { |
|
$message = $reader.ReadLine(); |
|
$reader.Close(); |
|
$reader.Dispose(); |
|
if ($message -ne $null) { |
|
if ($message -eq "exit") { |
|
#write-host "consolemode_server.ps1 exiting"; |
|
$tsv.State = "done"; |
|
break; |
|
} elseif ($message -eq "enableraw") { |
|
#write-host "RECEIVED: $message"; |
|
$tsv.Message = $message; |
|
#$tsv.Message = "$pipeName serverloop: $serverloop"; |
|
|
|
$tsv.Ping = Get-Date; |
|
$msync.Set(); |
|
}; |
|
} else { |
|
# write-host "consolemode_server.ps1 null-msg"; |
|
$tsv.State = "done"; |
|
break; |
|
}; |
|
}; |
|
$pipeServer.Disconnect(); |
|
$pipeServer.Dispose(); |
|
}; |
|
exit; |
|
}; |
|
|
|
$keepalive_timeout = 20; #number of seconds without ping or other message, after which we terminate the process. |
|
try { |
|
$syncEvent = New-Object System.Threading.ManualResetEvent($false); |
|
|
|
$runspace = [runspacefactory]::CreateRunspace(); |
|
[void]$runspace.Open(); |
|
$runspace.SessionStateProxy.SetVariable("pipeName", $pipeName); |
|
$runspace.SessionStateProxy.SetVariable("pipeServer", $null); |
|
$runspace.SessionStateProxy.SetVariable("msync", $syncEvent); |
|
|
|
$powershell = [System.Management.Automation.PowerShell]::Create(); |
|
$powershell.Runspace = $runspace; |
|
[void]$powershell.Addscript($scriptblock).AddArgument($sharedData); |
|
|
|
$sharedData.State = "running"; |
|
$sharedData.Ping = Get-Date; |
|
$asyncResult = $powershell.BeginInvoke(); |
|
|
|
write-Host "Started named pipe server $pipeName in runspace" |
|
$loop = 0; |
|
while ($true) { |
|
$loop += 1; |
|
#write-host "loop $loop"; |
|
[void]$syncEvent.WaitOne(($keepalive_timeout * 1000 / 2)); |
|
$msg = $sharedData.Message; |
|
#Write-Host "$pipeName Last message: $msg"; |
|
$sharedData.Message = ""; |
|
if ($msg -eq "enableraw") { |
|
$null = rawmode 'enable' |
|
} elseif ($msg -eq "disableraw") { |
|
$null = rawmode 'disable' |
|
} |
|
#write-host "STATE: $(${sharedData}.State)" |
|
if ($(${sharedData}.State) -eq "done") { |
|
break; |
|
}; |
|
$tnow = Get-Date; |
|
$elapsed = New-TimeSpan -Start $sharedData.Ping -End $tnow; |
|
if ($elapsed.TotalSeconds -lt $keepalive_timeout) { |
|
# write-host "ping ok"; |
|
} else { |
|
write-host "ping stale for pipe $pipeName - exiting"; |
|
break; |
|
} |
|
[void]$syncEvent.Reset(); |
|
# start-sleep -Milliseconds 300 |
|
}; |
|
} finally { |
|
# Failing to properly shut down the run process can leave an orphan powershell process |
|
# We need to use a client for the named pipe to send an exit message. |
|
# Write-Host "terminating process for $pipeName"; |
|
try { |
|
# Write-Host "creating cli for $pipeName"; |
|
$cli = New-Object System.IO.Pipes.NamedPipeClientStream($pipeName); |
|
$cli.connect(1000); |
|
#Write-Host "sending exit for $pipeName"; |
|
$writer = new-object System.IO.StreamWriter($cli); |
|
$writer.writeline("exit"); |
|
$writer.flush(); |
|
#Write-Host "disposing of cli for $pipeName"; |
|
$cli.Dispose(); |
|
} catch { |
|
write-host "error during cli tidyup"; |
|
Write-Error "error: $($PSItem.ToString())"; |
|
Write-Host "Detailed Exception Message: $($PSItem.Exception.Message)"; |
|
}; |
|
|
|
|
|
try { |
|
if ($null -ne $runspace) { |
|
#Write-Host "closing runspace for $pipeName"; |
|
$runspace.Close(); |
|
#Write-Host "disposing of runspace for $pipeName"; |
|
$runspace.Dispose(); |
|
}; |
|
} catch { |
|
write-host "error during runspace tidyup"; |
|
Write-Error "error: $($PSItem.ToString())"; |
|
Write-Host "Detailed Exception Message: $($PSItem.Exception.Message)"; |
|
} finally { |
|
}; |
|
|
|
try { |
|
if ($null -ne $powershell) { |
|
#Write-Host "tidying up powershell for $pipeName"; |
|
$powershell.dispose(); |
|
}; |
|
} finally { |
|
}; |
|
|
|
}; |
|
write-host "consolemode_server_async.ps1 shutdown for pipe $pipeName"; |
|
exit 0; |
|
|
|
|
|
|
|
|