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.
266 lines
8.8 KiB
266 lines
8.8 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_LINE_INPUT = 0x0002 |
|
ENABLE_ECHO_INPUT = 0x0004 |
|
}; |
|
|
|
if (!('NativeConsoleMethods' -as [System.Type])) { |
|
Add-Type $consoleModeSource |
|
} |
|
|
|
function psmain { |
|
param ( |
|
[validateSet('enableRaw', 'disableRaw')] |
|
[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 "enableraw") { |
|
#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 "disableraw") { |
|
#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"; |
|
}; |
|
# psmain 'enableRaw' |
|
; |
|
$consoleid = $args[0]; |
|
if ([string]::IsNullOrEmpty($consoleid)) { |
|
$consoleid= "<punkshell_consoleid>" |
|
}; |
|
$pipeName = "punkshell_ps_consolemode_$consoleid"; |
|
"pipename: $pipeName" |
|
# Create the NamedPipeServerStream |
|
$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream( |
|
$pipeName, |
|
[System.IO.Pipes.PipeDirection]::In, |
|
1, |
|
[System.IO.Pipes.PipeTransmissionMode]::Message, |
|
[System.IO.Pipes.PipeOptions]::Asynchronous |
|
); |
|
; |
|
#$pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName); |
|
; |
|
|
|
|
|
# Create a synchronization object (ManualResetEvent) to signal completion |
|
; |
|
$syncEvent = New-Object System.Threading.ManualResetEvent($false) |
|
|
|
# Define the callback function for BeginWaitForConnection |
|
$connectionCallback = [System.AsyncCallback]{ |
|
param($ar,$s); |
|
# Get the NamedPipeServerStream from the AsyncResult object |
|
; |
|
$server = $ar.AsyncState; |
|
# End the asynchronous wait operation |
|
; |
|
$server.EndWaitForConnection($ar); |
|
Write-Host "Client connected!"; |
|
# Create a new runspace to handle client communication |
|
; |
|
$runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace(); |
|
$runspace.Open(); |
|
# Create a PowerShell pipeline within the runspace |
|
; |
|
$powershell = [System.Management.Automation.PowerShell]::Create(); |
|
$powershell.Runspace = $runspace; |
|
|
|
$scriptBlock = { |
|
param($pipeStream); |
|
$reader = New-Object System.IO.StreamReader($pipeStream); |
|
#$writer = New-Object System.IO.StreamWriter($pipeStream); |
|
#$writer.AutoFlush = $true; |
|
|
|
$message = $reader.ReadLine(); |
|
Write-Host "Received from client: $message"; |
|
|
|
#$response = "Server received: $message" |
|
#$writer.WriteLine($response) |
|
#Write-Host "Sent to client: $response" |
|
|
|
# Disconnect the pipe to allow new connections if desired |
|
; |
|
$pipeStream.Disconnect(); |
|
}; |
|
# Add the script block to the PowerShell pipeline and pass the pipe stream |
|
; |
|
$powershell.AddScript($scriptBlock).AddArgument($server); |
|
# Invoke the pipeline asynchronously |
|
; |
|
$asyncResult = $powershell.BeginInvoke(); |
|
# You can do other work here while the client communication happens in the runspace |
|
; |
|
; |
|
# Wait for the pipeline to complete and close the runspace |
|
$powershell.EndInvoke($asyncResult); |
|
$powershell.Dispose(); |
|
$runspace.Close(); |
|
$runspace.Dispose(); |
|
|
|
Write-Host "Client communication handled. Waiting for next connection..."; |
|
|
|
$s.Set(); |
|
|
|
# Begin waiting for the next connection; |
|
#$server.BeginWaitForConnection($callback, $server); |
|
; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
$global:keep_listening = $true; |
|
|
|
|
|
while ($global:keep_listening) { |
|
# Begin waiting for a client connection asynchronously |
|
; |
|
|
|
$runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace(); |
|
$runspace.Open(); |
|
# Create a PowerShell pipeline within the runspace |
|
; |
|
$powershell = [System.Management.Automation.PowerShell]::Create(); |
|
$powershell.Runspace = $runspace; |
|
|
|
$scriptblock = { |
|
param($p,$s); |
|
Write-Host "Waiting for client connection on pipe: $p"; |
|
$p.BeginWaitForConnection($connectionCallback, $p,$s); |
|
$s.WaitOne(10000); |
|
} |
|
$powershell.AddScript($scriptBlock) |
|
[void]$powershell.AddParameter('p',$pipeName); |
|
[void]$powershell.AddParameter('s',$syncEvent); |
|
$asyncResult = $powershell.BeginInvoke(); |
|
# You can do other work here while the client communication happens in the runspace |
|
; |
|
write-host "interim" |
|
; |
|
# Wait for the pipeline to complete and close the runspace |
|
$powershell.EndInvoke($asyncResult); |
|
$powershell.Dispose(); |
|
$runspace.Close(); |
|
$runspace.Dispose(); |
|
write-host "looping" |
|
}; |
|
|
|
#$pipeServer.BeginWaitForConnection($connectionCallback, $pipeServer); |
|
|
|
Write-Host "Server shutting down."; |
|
$pipeServer.Dispose(); |
|
|
|
|
|
|
|
#try { |
|
# while ($true) { |
|
# #"Waiting for connection on '$pipeName'"; |
|
# $pipeServer.WaitForConnection(); |
|
# #"Connection established"; |
|
# $pipeReader = New-Object System.IO.StreamReader($pipeServer); |
|
# #$pipeWriter = New-Object System.IO.StreamWriter($pipeServer); |
|
# #$pipeWriter.AutoFlush = $true; |
|
# $request = $pipeReader.ReadLine(); |
|
# # "Received request: $request"; |
|
# if ($request -eq "exit") { |
|
# "consolemode_server.ps1 Exiting"; |
|
# exit; |
|
# } elseif ($request -eq "") { |
|
# #"Empty input"; |
|
# $pipeServer.Disconnect(); |
|
# #"Disconnected"; |
|
# continue; |
|
# } elseif ($request -eq $none) { |
|
# "Remote disconnected before sending"; |
|
# $pipeServer.Disconnect(); |
|
# "Disconnected"; |
|
# continue; |
|
# } elseif ($request -eq "enableraw") { |
|
# #$result = psmain 'enableRaw'; |
|
# $null = psmain 'enableRaw' |
|
# # "Sending result: '$result'"; |
|
# #$pipeWriter.Write($result); |
|
# $pipeServer.Disconnect(); |
|
# continue; |
|
# } else { |
|
# "consolemode_server.ps1 ignoring request: $request"; |
|
# $pipeServer.Disconnect(); |
|
# continue; |
|
# } |
|
# } |
|
#} |
|
#finally { |
|
# $pipeServer.Dispose(); |
|
#}; |
|
|
|
|
|
|