mirror of https://github.com/liamg/aminal.git
247 lines
6.7 KiB
Go
247 lines
6.7 KiB
Go
// +build windows,cgo
|
|
|
|
package platform
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/MaxRis/w32"
|
|
"syscall"
|
|
)
|
|
|
|
// #include "windows.h"
|
|
//
|
|
// /* Until we can specify the platform SDK and target version for Windows.h *
|
|
// * without breaking our ability to gracefully display an error message, these *
|
|
// * definitions will be copied from the platform SDK headers and made to work. *
|
|
// */
|
|
//
|
|
// typedef HRESULT (* CreatePseudoConsoleProcType)( COORD, HANDLE, HANDLE, DWORD, uintptr_t * );
|
|
// typedef HRESULT (* ResizePseudoConsoleProcType)( uintptr_t, COORD );
|
|
// typedef HRESULT (* ClosePseudoConsoleProcType)( uintptr_t );
|
|
//
|
|
//
|
|
// CreatePseudoConsoleProcType pfnCreatePseudoConsole = NULL;
|
|
// ResizePseudoConsoleProcType pfnResizePseudoConsole = NULL;
|
|
// ClosePseudoConsoleProcType pfnClosePseudoConsole = NULL;
|
|
//
|
|
// HMODULE hLibKernel32_Kern = NULL;
|
|
//
|
|
// DWORD initPtyKernFuncs()
|
|
// {
|
|
// hLibKernel32_Kern = LoadLibrary( "kernel32.dll" );
|
|
// if( hLibKernel32_Kern == NULL )
|
|
// {
|
|
// return -1;
|
|
// }
|
|
//
|
|
// pfnCreatePseudoConsole = (CreatePseudoConsoleProcType) GetProcAddress(hLibKernel32_Kern, "CreatePseudoConsole" );
|
|
// if( pfnCreatePseudoConsole == NULL )
|
|
// {
|
|
// return -1;
|
|
// }
|
|
//
|
|
// pfnResizePseudoConsole = (ResizePseudoConsoleProcType) GetProcAddress(hLibKernel32_Kern, "ResizePseudoConsole" );
|
|
// if( pfnResizePseudoConsole == NULL )
|
|
// {
|
|
// return -1;
|
|
// }
|
|
//
|
|
// pfnClosePseudoConsole = (ClosePseudoConsoleProcType) GetProcAddress(hLibKernel32_Kern, "ClosePseudoConsole" );
|
|
// if( pfnClosePseudoConsole == NULL )
|
|
// {
|
|
// return -1;
|
|
// }
|
|
//
|
|
// return 0;
|
|
// }
|
|
//
|
|
// DWORD createPtyHelper( int xSize, int ySize, HANDLE input, HANDLE output, DWORD flags, uintptr_t * phPC )
|
|
// {
|
|
// COORD size;
|
|
// size.X = xSize;
|
|
// size.Y = ySize;
|
|
// return (DWORD) (*pfnCreatePseudoConsole)( size, input, output, flags, phPC );
|
|
// }
|
|
//
|
|
// DWORD resizePtyHelper( uintptr_t hpc, int xSize, int ySize )
|
|
// {
|
|
// COORD size;
|
|
// size.X = xSize;
|
|
// size.Y = ySize;
|
|
// return (DWORD) (*pfnResizePseudoConsole)( hpc, size );
|
|
// }
|
|
//
|
|
// DWORD closePtyHelper( uintptr_t hpc )
|
|
// {
|
|
// return (DWORD) (*pfnClosePseudoConsole)( hpc );
|
|
// }
|
|
//
|
|
// int hr_succeeded( DWORD hResult )
|
|
// {
|
|
// return SUCCEEDED( hResult );
|
|
// }
|
|
import "C"
|
|
|
|
var ptyInitSucceeded = false
|
|
|
|
func init() {
|
|
ret := int(C.initPtyKernFuncs())
|
|
ptyInitSucceeded = (ret == 0)
|
|
}
|
|
|
|
type winConPty struct {
|
|
inPipe syscall.Handle
|
|
outPipe syscall.Handle
|
|
innerInPipe syscall.Handle
|
|
innerOutPipe syscall.Handle
|
|
hcon uintptr
|
|
processID uint32 // required to obtain old-style console handle (for standard console functions)
|
|
platformDependentSettings PlatformDependentSettings
|
|
}
|
|
|
|
func (pty *winConPty) Read(p []byte) (n int, err error) {
|
|
return syscall.Read(pty.inPipe, p)
|
|
}
|
|
|
|
func (pty *winConPty) Write(p []byte) (n int, err error) {
|
|
return syscall.Write(pty.outPipe, p)
|
|
}
|
|
|
|
func (pty *winConPty) Close() error {
|
|
C.closePtyHelper(C.uintptr_t(pty.hcon))
|
|
|
|
err := syscall.CloseHandle(pty.inPipe)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = syscall.CloseHandle(pty.outPipe)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = syscall.CloseHandle(pty.innerInPipe)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = syscall.CloseHandle(pty.innerOutPipe)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pty.hcon = 0
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pty *winConPty) CreateGuestProcess(imagePath string) (Process, error) {
|
|
process, err := createPtyChildProcess(imagePath, pty.hcon)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pty.processID = process.processID
|
|
|
|
return process, nil
|
|
}
|
|
|
|
func (pty *winConPty) Resize(x, y int) error {
|
|
cret := C.resizePtyHelper(C.uintptr_t(pty.hcon), C.int(x), C.int(y))
|
|
|
|
if int(C.hr_succeeded(cret)) == 0 {
|
|
return errors.New("Failed to resize ConPTY")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pty *winConPty) GetPlatformDependentSettings() PlatformDependentSettings {
|
|
return pty.platformDependentSettings
|
|
}
|
|
|
|
func (pty *winConPty) Clear() {
|
|
C.FreeConsole()
|
|
defer C.AttachConsole(^C.DWORD(0)) // attach to parent process console
|
|
|
|
if C.AttachConsole(C.DWORD(pty.processID)) == 0 {
|
|
return
|
|
}
|
|
defer C.FreeConsole()
|
|
hConsole := C.GetStdHandle(C.STD_OUTPUT_HANDLE)
|
|
|
|
var coordScreen C.COORD
|
|
coordScreen.X = 0
|
|
coordScreen.Y = 0
|
|
var cCharsWritten C.DWORD
|
|
var csbi C.CONSOLE_SCREEN_BUFFER_INFO
|
|
|
|
// Get the number of character cells in the current buffer.
|
|
if C.GetConsoleScreenBufferInfo(hConsole, &csbi) != C.BOOL(0) {
|
|
|
|
dwConSize := C.DWORD(csbi.dwSize.X * csbi.dwSize.Y)
|
|
|
|
// Fill the entire screen with blanks.
|
|
C.FillConsoleOutputCharacterA(hConsole, // Handle to console screen buffer
|
|
' ', // Character to write to the buffer
|
|
dwConSize, // Number of cells to write
|
|
coordScreen, // Coordinates of first cell
|
|
&cCharsWritten) // Receive number of characters written
|
|
|
|
// Get the current text attribute.
|
|
if C.GetConsoleScreenBufferInfo(hConsole, &csbi) != C.BOOL(0) {
|
|
// Set the buffer's attributes accordingly.
|
|
|
|
C.FillConsoleOutputAttribute(hConsole, // Handle to console screen buffer
|
|
csbi.wAttributes, // Character attributes to use
|
|
dwConSize, // Number of cells to set attribute
|
|
coordScreen, // Coordinates of first cell
|
|
&cCharsWritten) // Receive number of characters written
|
|
}
|
|
}
|
|
|
|
// Put the cursor at its home coordinates.
|
|
C.SetConsoleCursorPosition(hConsole, coordScreen)
|
|
}
|
|
|
|
// NewPty creates a new instance of a Pty implementation for Windows on a newly allocated ConPTY
|
|
func NewPty(x, y int) (pty Pty, err error) {
|
|
if !ptyInitSucceeded {
|
|
w32.MessageBox(0, "Aminal requires APIs that are only available on Windows 10 1809 (October 2018 Update) or above. Please upgrade", "Aminal", 0)
|
|
return nil, errors.New("Windows PseudoConsole API unavailable on this version of Windows")
|
|
}
|
|
pty = nil
|
|
|
|
var inputReadSide, inputWriteSide syscall.Handle
|
|
var outputReadSide, outputWriteSide syscall.Handle
|
|
|
|
err = syscall.CreatePipe(&inputReadSide, &inputWriteSide, nil, 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
err = syscall.CreatePipe(&outputReadSide, &outputWriteSide, nil, 0)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var hc C.uintptr_t
|
|
|
|
cret := C.createPtyHelper(C.int(x), C.int(y), C.HANDLE(inputReadSide), C.HANDLE(outputWriteSide), 0, &hc)
|
|
ret := int(cret)
|
|
|
|
if ret != 0 {
|
|
return nil, errors.New("Failed to allocate a ConPTY instance")
|
|
}
|
|
|
|
pty = &winConPty{
|
|
inPipe: outputReadSide,
|
|
outPipe: inputWriteSide,
|
|
innerInPipe: inputReadSide,
|
|
innerOutPipe: outputWriteSide,
|
|
hcon: uintptr(hc),
|
|
platformDependentSettings: PlatformDependentSettings{
|
|
OSCTerminators: map[rune]struct{}{0x00: {}, 0x07: {}},
|
|
},
|
|
}
|
|
|
|
return pty, nil
|
|
}
|