From db17d38683a51e9b6c4fcdf9fefb5f2ec52953d3 Mon Sep 17 00:00:00 2001 From: Daniel Canter Date: Mon, 15 Nov 2021 20:36:51 -0800 Subject: [PATCH 1/4] Add conpty (pseudo console) package This change adds a conpty package that houses go friendly wrappers around the Pseudo Console API in Windows. This will be used to support tty scenarios for Host Process containers. There's not many tests I can add here as you need to hook this up to a running process, where that work is coming. Signed-off-by: Daniel Canter --- internal/conpty/conpty.go | 153 ++++++++++++++++++ internal/winapi/console.go | 44 +++++ internal/winapi/process.go | 80 ++++++++- internal/winapi/winapi.go | 2 +- internal/winapi/zsyscall_windows.go | 79 ++++++++- .../hcsshim/internal/winapi/console.go | 44 +++++ .../hcsshim/internal/winapi/process.go | 80 ++++++++- .../hcsshim/internal/winapi/winapi.go | 2 +- .../internal/winapi/zsyscall_windows.go | 79 ++++++++- 9 files changed, 539 insertions(+), 24 deletions(-) create mode 100644 internal/conpty/conpty.go create mode 100644 internal/winapi/console.go create mode 100644 test/vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go diff --git a/internal/conpty/conpty.go b/internal/conpty/conpty.go new file mode 100644 index 0000000000..852be0652c --- /dev/null +++ b/internal/conpty/conpty.go @@ -0,0 +1,153 @@ +package conpty + +import ( + "errors" + "fmt" + "os" + "sync" + "unsafe" + + "github.com/Microsoft/hcsshim/internal/winapi" + "golang.org/x/sys/windows" +) + +var ( + errClosedConPty = errors.New("pseudo console is closed") + errNotInitialized = errors.New("pseudo console hasn't been initialized") +) + +// CPty is a wrapper around a Windows PseudoConsole handle. Create a new instance by calling `New()`. +type CPty struct { + // handleLock guards hpc + handleLock sync.RWMutex + // hpc is the pseudo console handle + hpc windows.Handle + // inPipe and outPipe are our end of the pipes to read/write to the pseudo console. + inPipe *os.File + outPipe *os.File +} + +// New returns a new `CPty` object. This object is not ready for IO until `UpdateProcThreadAttribute` is called and a process has been started. +func New(columns, rows int16, flags uint32) (*CPty, error) { + // First we need to make both ends of the conpty's pipes, two to get passed into a process to use as input/output, and two for us to keep to + // make use of this data. + ptyIn, inPipeOurs, err := os.Pipe() + if err != nil { + return nil, fmt.Errorf("failed to create pipes for pseudo console: %w", err) + } + + outPipeOurs, ptyOut, err := os.Pipe() + if err != nil { + return nil, fmt.Errorf("failed to create pipes for pseudo console: %w", err) + } + + var hpc windows.Handle + coord := windows.Coord{X: columns, Y: rows} + err = winapi.CreatePseudoConsole(coord, windows.Handle(ptyIn.Fd()), windows.Handle(ptyOut.Fd()), 0, &hpc) + if err != nil { + return nil, fmt.Errorf("failed to create pseudo console: %w", err) + } + + // The pty's end of its pipes can be closed here without worry. They're duped into the conhost + // that will be launched and will be released on a call to ClosePseudoConsole() (Close() on the CPty object). + if err := ptyOut.Close(); err != nil { + return nil, fmt.Errorf("failed to close pseudo console handle: %w", err) + } + if err := ptyIn.Close(); err != nil { + return nil, fmt.Errorf("failed to close pseudo console handle: %w", err) + } + + return &CPty{ + hpc: hpc, + inPipe: inPipeOurs, + outPipe: outPipeOurs, + }, nil +} + +// UpdateProcThreadAttribute updates the passed in attribute list to contain the entry necessary for use with +// CreateProcess. +func (c *CPty) UpdateProcThreadAttribute(attributeList *winapi.ProcThreadAttributeList) error { + c.handleLock.RLock() + defer c.handleLock.RUnlock() + + if c.hpc == 0 { + return errClosedConPty + } + + err := winapi.UpdateProcThreadAttribute( + attributeList, + 0, + winapi.PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + unsafe.Pointer(c.hpc), + unsafe.Sizeof(c.hpc), + nil, + nil, + ) + if err != nil { + return fmt.Errorf("failed to update proc thread attributes for pseudo console: %w", err) + } + return nil +} + +// Resize resizes the internal buffers of the pseudo console to the passed in size +func (c *CPty) Resize(columns, rows int16) error { + c.handleLock.RLock() + defer c.handleLock.RUnlock() + + if c.hpc == 0 { + return errClosedConPty + } + + coord := windows.Coord{X: columns, Y: rows} + if err := winapi.ResizePseudoConsole(c.hpc, coord); err != nil { + return fmt.Errorf("failed to resize pseudo console: %w", err) + } + return nil +} + +// Close closes the pseudo-terminal and cleans up all attached resources +func (c *CPty) Close() error { + c.handleLock.Lock() + defer c.handleLock.Unlock() + + if c.hpc == 0 { + return errClosedConPty + } + + // Close the pseudo console, set the handle to 0 to invalidate this object and then close the side of the pipes that we own. + winapi.ClosePseudoConsole(c.hpc) + c.hpc = 0 + if err := c.inPipe.Close(); err != nil { + return fmt.Errorf("failed to close pseudo console input pipe: %w", err) + } + if err := c.outPipe.Close(); err != nil { + return fmt.Errorf("failed to close pseudo console output pipe: %w", err) + } + return nil +} + +// OutPipe returns the output pipe of the pseudo console. +func (c *CPty) OutPipe() *os.File { + return c.outPipe +} + +// InPipe returns the input pipe of the pseudo console. +func (c *CPty) InPipe() *os.File { + return c.inPipe +} + +// Write writes the contents of `buf` to the pseudo console. Returns the number of bytes written and an error if there is one. +func (c *CPty) Write(buf []byte) (int, error) { + if c.inPipe == nil { + return 0, errNotInitialized + } + return c.inPipe.Write(buf) +} + +// Read reads from the pseudo console into `buf`. Returns the number of bytes read and an error if there is one. +func (c *CPty) Read(buf []byte) (int, error) { + if c.outPipe == nil { + return 0, errNotInitialized + } + return c.outPipe.Read(buf) +} diff --git a/internal/winapi/console.go b/internal/winapi/console.go new file mode 100644 index 0000000000..def9525417 --- /dev/null +++ b/internal/winapi/console.go @@ -0,0 +1,44 @@ +package winapi + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +const PSEUDOCONSOLE_INHERIT_CURSOR = 0x1 + +// CreatePseudoConsole creates a windows pseudo console. +func CreatePseudoConsole(size windows.Coord, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) error { + // We need this wrapper as the function takes a COORD struct and not a pointer to one, so we need to cast to something beforehand. + return createPseudoConsole(*((*uint32)(unsafe.Pointer(&size))), hInput, hOutput, 0, hpcon) +} + +// ResizePseudoConsole resizes the internal buffers of the pseudo console to the width and height specified in `size`. +func ResizePseudoConsole(hpcon windows.Handle, size windows.Coord) error { + // We need this wrapper as the function takes a COORD struct and not a pointer to one, so we need to cast to something beforehand. + return resizePseudoConsole(hpcon, *((*uint32)(unsafe.Pointer(&size)))) +} + +// HRESULT WINAPI CreatePseudoConsole( +// _In_ COORD size, +// _In_ HANDLE hInput, +// _In_ HANDLE hOutput, +// _In_ DWORD dwFlags, +// _Out_ HPCON* phPC +// ); +// +//sys createPseudoConsole(size uint32, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) (hr error) = kernel32.CreatePseudoConsole + +// void WINAPI ClosePseudoConsole( +// _In_ HPCON hPC +// ); +// +//sys ClosePseudoConsole(hpc windows.Handle) = kernel32.ClosePseudoConsole + +// HRESULT WINAPI ResizePseudoConsole( +// _In_ HPCON hPC , +// _In_ COORD size +// ); +// +//sys resizePseudoConsole(hPc windows.Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole diff --git a/internal/winapi/process.go b/internal/winapi/process.go index b87068327c..977df04b87 100644 --- a/internal/winapi/process.go +++ b/internal/winapi/process.go @@ -1,10 +1,80 @@ package winapi +import ( + "errors" + "unsafe" + + "golang.org/x/sys/windows" +) + const PROCESS_ALL_ACCESS uint32 = 2097151 -// DWORD GetProcessImageFileNameW( -// HANDLE hProcess, -// LPWSTR lpImageFileName, -// DWORD nSize +const ( + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x20016 + PROC_THREAD_ATTRIBUTE_JOB_LIST = 0x2000D +) + +type ProcThreadAttributeList struct { + _ [1]byte +} + +// typedef struct _STARTUPINFOEXW { +// STARTUPINFOW StartupInfo; +// LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; +// } STARTUPINFOEXW, *LPSTARTUPINFOEXW; +type StartupInfoEx struct { + // This is a recreation of the same binding from the stdlib. The x/sys/windows variant for whatever reason + // doesn't work when updating the list for the pseudo console attribute. It has the process immediately exit + // with exit code 0xc0000142 shortly after start. + windows.StartupInfo + ProcThreadAttributeList *ProcThreadAttributeList +} + +// NewProcThreadAttributeList allocates a new ProcThreadAttributeList, with +// the requested maximum number of attributes. This must be cleaned up by calling +// DeleteProcThreadAttributeList. +func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, error) { + var size uintptr + err := InitializeProcThreadAttributeList(nil, maxAttrCount, 0, &size) + if err != windows.ERROR_INSUFFICIENT_BUFFER { + if err == nil { + return nil, errors.New("unable to query buffer size from InitializeProcThreadAttributeList") + } + return nil, err + } + al := (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0])) + err = InitializeProcThreadAttributeList(al, maxAttrCount, 0, &size) + if err != nil { + return nil, err + } + return al, nil +} + +// BOOL InitializeProcThreadAttributeList( +// [out, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, +// [in] DWORD dwAttributeCount, +// DWORD dwFlags, +// [in, out] PSIZE_T lpSize // ); -//sys GetProcessImageFileName(hProcess windows.Handle, imageFileName *uint16, nSize uint32) (size uint32, err error) = kernel32.GetProcessImageFileNameW +// +//sys InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) = kernel32.InitializeProcThreadAttributeList + +// void DeleteProcThreadAttributeList( +// [in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList +// ); +// +//sys DeleteProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList) = kernel32.DeleteProcThreadAttributeList + +// BOOL UpdateProcThreadAttribute( +// [in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, +// [in] DWORD dwFlags, +// [in] DWORD_PTR Attribute, +// [in] PVOID lpValue, +// [in] SIZE_T cbSize, +// [out, optional] PVOID lpPreviousValue, +// [in, optional] PSIZE_T lpReturnSize +// ); +// +//sys UpdateProcThreadAttribute(lpAttributeList *ProcThreadAttributeList, dwFlags uint32, attribute uintptr, lpValue unsafe.Pointer, cbSize uintptr, lpPreviousValue unsafe.Pointer, lpReturnSize *uintptr) (err error) = kernel32.UpdateProcThreadAttribute + +//sys CreateProcessAsUser(token windows.Token, appName *uint16, commandLine *uint16, procSecurity *windows.SecurityAttributes, threadSecurity *windows.SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *windows.StartupInfo, outProcInfo *windows.ProcessInformation) (err error) = advapi32.CreateProcessAsUserW diff --git a/internal/winapi/winapi.go b/internal/winapi/winapi.go index ec88c0d212..1d4ba3c4f8 100644 --- a/internal/winapi/winapi.go +++ b/internal/winapi/winapi.go @@ -2,4 +2,4 @@ // be thought of as an extension to golang.org/x/sys/windows. package winapi -//go:generate go run ..\..\mksyscall_windows.go -output zsyscall_windows.go system.go net.go path.go thread.go iocp.go jobobject.go logon.go memory.go process.go processor.go devices.go filesystem.go errors.go +//go:generate go run ..\..\mksyscall_windows.go -output zsyscall_windows.go console.go system.go net.go path.go thread.go iocp.go jobobject.go logon.go memory.go process.go processor.go devices.go filesystem.go errors.go diff --git a/internal/winapi/zsyscall_windows.go b/internal/winapi/zsyscall_windows.go index 59ddee274e..caf54c82c9 100644 --- a/internal/winapi/zsyscall_windows.go +++ b/internal/winapi/zsyscall_windows.go @@ -37,12 +37,15 @@ func errnoErr(e syscall.Errno) error { } var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modntdll = windows.NewLazySystemDLL("ntdll.dll") modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll") + procCreatePseudoConsole = modkernel32.NewProc("CreatePseudoConsole") + procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole") + procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole") procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation") procSetJobCompartmentId = modiphlpapi.NewProc("SetJobCompartmentId") procSearchPathW = modkernel32.NewProc("SearchPathW") @@ -58,7 +61,10 @@ var ( procLogonUserW = modadvapi32.NewProc("LogonUserW") procLocalAlloc = modkernel32.NewProc("LocalAlloc") procLocalFree = modkernel32.NewProc("LocalFree") - procGetProcessImageFileNameW = modkernel32.NewProc("GetProcessImageFileNameW") + procInitializeProcThreadAttributeList = modkernel32.NewProc("InitializeProcThreadAttributeList") + procDeleteProcThreadAttributeList = modkernel32.NewProc("DeleteProcThreadAttributeList") + procUpdateProcThreadAttribute = modkernel32.NewProc("UpdateProcThreadAttribute") + procCreateProcessAsUserW = modadvapi32.NewProc("CreateProcessAsUserW") procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount") procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA") procCM_Get_Device_ID_ListA = modcfgmgr32.NewProc("CM_Get_Device_ID_ListA") @@ -71,6 +77,33 @@ var ( procRtlNtStatusToDosError = modntdll.NewProc("RtlNtStatusToDosError") ) +func createPseudoConsole(size uint32, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) (hr error) { + r0, _, _ := syscall.Syscall6(procCreatePseudoConsole.Addr(), 5, uintptr(size), uintptr(hInput), uintptr(hOutput), uintptr(dwFlags), uintptr(unsafe.Pointer(hpcon)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func ClosePseudoConsole(hpc windows.Handle) { + syscall.Syscall(procClosePseudoConsole.Addr(), 1, uintptr(hpc), 0, 0) + return +} + +func resizePseudoConsole(hPc windows.Handle, size uint32) (hr error) { + r0, _, _ := syscall.Syscall(procResizePseudoConsole.Addr(), 2, uintptr(hPc), uintptr(size), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + func NtQuerySystemInformation(systemInfoClass int, systemInformation uintptr, systemInfoLength uint32, returnLength *uint32) (status uint32) { r0, _, _ := syscall.Syscall6(procNtQuerySystemInformation.Addr(), 4, uintptr(systemInfoClass), uintptr(systemInformation), uintptr(systemInfoLength), uintptr(unsafe.Pointer(returnLength)), 0, 0) status = uint32(r0) @@ -227,10 +260,44 @@ func LocalFree(ptr uintptr) { return } -func GetProcessImageFileName(hProcess windows.Handle, imageFileName *uint16, nSize uint32) (size uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetProcessImageFileNameW.Addr(), 3, uintptr(hProcess), uintptr(unsafe.Pointer(imageFileName)), uintptr(nSize)) - size = uint32(r0) - if size == 0 { +func InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) { + r1, _, e1 := syscall.Syscall6(procInitializeProcThreadAttributeList.Addr(), 4, uintptr(unsafe.Pointer(lpAttributeList)), uintptr(dwAttributeCount), uintptr(dwFlags), uintptr(unsafe.Pointer(lpSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func DeleteProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList) { + syscall.Syscall(procDeleteProcThreadAttributeList.Addr(), 1, uintptr(unsafe.Pointer(lpAttributeList)), 0, 0) + return +} + +func UpdateProcThreadAttribute(lpAttributeList *ProcThreadAttributeList, dwFlags uint32, attribute uintptr, lpValue unsafe.Pointer, cbSize uintptr, lpPreviousValue unsafe.Pointer, lpReturnSize *uintptr) (err error) { + r1, _, e1 := syscall.Syscall9(procUpdateProcThreadAttribute.Addr(), 7, uintptr(unsafe.Pointer(lpAttributeList)), uintptr(dwFlags), uintptr(attribute), uintptr(lpValue), uintptr(cbSize), uintptr(lpPreviousValue), uintptr(unsafe.Pointer(lpReturnSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func CreateProcessAsUser(token windows.Token, appName *uint16, commandLine *uint16, procSecurity *windows.SecurityAttributes, threadSecurity *windows.SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *windows.StartupInfo, outProcInfo *windows.ProcessInformation) (err error) { + var _p0 uint32 + if inheritHandles { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall12(procCreateProcessAsUserW.Addr(), 11, uintptr(token), uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0) + if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go new file mode 100644 index 0000000000..def9525417 --- /dev/null +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/console.go @@ -0,0 +1,44 @@ +package winapi + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +const PSEUDOCONSOLE_INHERIT_CURSOR = 0x1 + +// CreatePseudoConsole creates a windows pseudo console. +func CreatePseudoConsole(size windows.Coord, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) error { + // We need this wrapper as the function takes a COORD struct and not a pointer to one, so we need to cast to something beforehand. + return createPseudoConsole(*((*uint32)(unsafe.Pointer(&size))), hInput, hOutput, 0, hpcon) +} + +// ResizePseudoConsole resizes the internal buffers of the pseudo console to the width and height specified in `size`. +func ResizePseudoConsole(hpcon windows.Handle, size windows.Coord) error { + // We need this wrapper as the function takes a COORD struct and not a pointer to one, so we need to cast to something beforehand. + return resizePseudoConsole(hpcon, *((*uint32)(unsafe.Pointer(&size)))) +} + +// HRESULT WINAPI CreatePseudoConsole( +// _In_ COORD size, +// _In_ HANDLE hInput, +// _In_ HANDLE hOutput, +// _In_ DWORD dwFlags, +// _Out_ HPCON* phPC +// ); +// +//sys createPseudoConsole(size uint32, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) (hr error) = kernel32.CreatePseudoConsole + +// void WINAPI ClosePseudoConsole( +// _In_ HPCON hPC +// ); +// +//sys ClosePseudoConsole(hpc windows.Handle) = kernel32.ClosePseudoConsole + +// HRESULT WINAPI ResizePseudoConsole( +// _In_ HPCON hPC , +// _In_ COORD size +// ); +// +//sys resizePseudoConsole(hPc windows.Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go index b87068327c..977df04b87 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go @@ -1,10 +1,80 @@ package winapi +import ( + "errors" + "unsafe" + + "golang.org/x/sys/windows" +) + const PROCESS_ALL_ACCESS uint32 = 2097151 -// DWORD GetProcessImageFileNameW( -// HANDLE hProcess, -// LPWSTR lpImageFileName, -// DWORD nSize +const ( + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE = 0x20016 + PROC_THREAD_ATTRIBUTE_JOB_LIST = 0x2000D +) + +type ProcThreadAttributeList struct { + _ [1]byte +} + +// typedef struct _STARTUPINFOEXW { +// STARTUPINFOW StartupInfo; +// LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; +// } STARTUPINFOEXW, *LPSTARTUPINFOEXW; +type StartupInfoEx struct { + // This is a recreation of the same binding from the stdlib. The x/sys/windows variant for whatever reason + // doesn't work when updating the list for the pseudo console attribute. It has the process immediately exit + // with exit code 0xc0000142 shortly after start. + windows.StartupInfo + ProcThreadAttributeList *ProcThreadAttributeList +} + +// NewProcThreadAttributeList allocates a new ProcThreadAttributeList, with +// the requested maximum number of attributes. This must be cleaned up by calling +// DeleteProcThreadAttributeList. +func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, error) { + var size uintptr + err := InitializeProcThreadAttributeList(nil, maxAttrCount, 0, &size) + if err != windows.ERROR_INSUFFICIENT_BUFFER { + if err == nil { + return nil, errors.New("unable to query buffer size from InitializeProcThreadAttributeList") + } + return nil, err + } + al := (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0])) + err = InitializeProcThreadAttributeList(al, maxAttrCount, 0, &size) + if err != nil { + return nil, err + } + return al, nil +} + +// BOOL InitializeProcThreadAttributeList( +// [out, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, +// [in] DWORD dwAttributeCount, +// DWORD dwFlags, +// [in, out] PSIZE_T lpSize // ); -//sys GetProcessImageFileName(hProcess windows.Handle, imageFileName *uint16, nSize uint32) (size uint32, err error) = kernel32.GetProcessImageFileNameW +// +//sys InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) = kernel32.InitializeProcThreadAttributeList + +// void DeleteProcThreadAttributeList( +// [in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList +// ); +// +//sys DeleteProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList) = kernel32.DeleteProcThreadAttributeList + +// BOOL UpdateProcThreadAttribute( +// [in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, +// [in] DWORD dwFlags, +// [in] DWORD_PTR Attribute, +// [in] PVOID lpValue, +// [in] SIZE_T cbSize, +// [out, optional] PVOID lpPreviousValue, +// [in, optional] PSIZE_T lpReturnSize +// ); +// +//sys UpdateProcThreadAttribute(lpAttributeList *ProcThreadAttributeList, dwFlags uint32, attribute uintptr, lpValue unsafe.Pointer, cbSize uintptr, lpPreviousValue unsafe.Pointer, lpReturnSize *uintptr) (err error) = kernel32.UpdateProcThreadAttribute + +//sys CreateProcessAsUser(token windows.Token, appName *uint16, commandLine *uint16, procSecurity *windows.SecurityAttributes, threadSecurity *windows.SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *windows.StartupInfo, outProcInfo *windows.ProcessInformation) (err error) = advapi32.CreateProcessAsUserW diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go index ec88c0d212..1d4ba3c4f8 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go @@ -2,4 +2,4 @@ // be thought of as an extension to golang.org/x/sys/windows. package winapi -//go:generate go run ..\..\mksyscall_windows.go -output zsyscall_windows.go system.go net.go path.go thread.go iocp.go jobobject.go logon.go memory.go process.go processor.go devices.go filesystem.go errors.go +//go:generate go run ..\..\mksyscall_windows.go -output zsyscall_windows.go console.go system.go net.go path.go thread.go iocp.go jobobject.go logon.go memory.go process.go processor.go devices.go filesystem.go errors.go diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go index 59ddee274e..caf54c82c9 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go @@ -37,12 +37,15 @@ func errnoErr(e syscall.Errno) error { } var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modntdll = windows.NewLazySystemDLL("ntdll.dll") modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll") + procCreatePseudoConsole = modkernel32.NewProc("CreatePseudoConsole") + procClosePseudoConsole = modkernel32.NewProc("ClosePseudoConsole") + procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole") procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation") procSetJobCompartmentId = modiphlpapi.NewProc("SetJobCompartmentId") procSearchPathW = modkernel32.NewProc("SearchPathW") @@ -58,7 +61,10 @@ var ( procLogonUserW = modadvapi32.NewProc("LogonUserW") procLocalAlloc = modkernel32.NewProc("LocalAlloc") procLocalFree = modkernel32.NewProc("LocalFree") - procGetProcessImageFileNameW = modkernel32.NewProc("GetProcessImageFileNameW") + procInitializeProcThreadAttributeList = modkernel32.NewProc("InitializeProcThreadAttributeList") + procDeleteProcThreadAttributeList = modkernel32.NewProc("DeleteProcThreadAttributeList") + procUpdateProcThreadAttribute = modkernel32.NewProc("UpdateProcThreadAttribute") + procCreateProcessAsUserW = modadvapi32.NewProc("CreateProcessAsUserW") procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount") procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA") procCM_Get_Device_ID_ListA = modcfgmgr32.NewProc("CM_Get_Device_ID_ListA") @@ -71,6 +77,33 @@ var ( procRtlNtStatusToDosError = modntdll.NewProc("RtlNtStatusToDosError") ) +func createPseudoConsole(size uint32, hInput windows.Handle, hOutput windows.Handle, dwFlags uint32, hpcon *windows.Handle) (hr error) { + r0, _, _ := syscall.Syscall6(procCreatePseudoConsole.Addr(), 5, uintptr(size), uintptr(hInput), uintptr(hOutput), uintptr(dwFlags), uintptr(unsafe.Pointer(hpcon)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func ClosePseudoConsole(hpc windows.Handle) { + syscall.Syscall(procClosePseudoConsole.Addr(), 1, uintptr(hpc), 0, 0) + return +} + +func resizePseudoConsole(hPc windows.Handle, size uint32) (hr error) { + r0, _, _ := syscall.Syscall(procResizePseudoConsole.Addr(), 2, uintptr(hPc), uintptr(size), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + func NtQuerySystemInformation(systemInfoClass int, systemInformation uintptr, systemInfoLength uint32, returnLength *uint32) (status uint32) { r0, _, _ := syscall.Syscall6(procNtQuerySystemInformation.Addr(), 4, uintptr(systemInfoClass), uintptr(systemInformation), uintptr(systemInfoLength), uintptr(unsafe.Pointer(returnLength)), 0, 0) status = uint32(r0) @@ -227,10 +260,44 @@ func LocalFree(ptr uintptr) { return } -func GetProcessImageFileName(hProcess windows.Handle, imageFileName *uint16, nSize uint32) (size uint32, err error) { - r0, _, e1 := syscall.Syscall(procGetProcessImageFileNameW.Addr(), 3, uintptr(hProcess), uintptr(unsafe.Pointer(imageFileName)), uintptr(nSize)) - size = uint32(r0) - if size == 0 { +func InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) { + r1, _, e1 := syscall.Syscall6(procInitializeProcThreadAttributeList.Addr(), 4, uintptr(unsafe.Pointer(lpAttributeList)), uintptr(dwAttributeCount), uintptr(dwFlags), uintptr(unsafe.Pointer(lpSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func DeleteProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList) { + syscall.Syscall(procDeleteProcThreadAttributeList.Addr(), 1, uintptr(unsafe.Pointer(lpAttributeList)), 0, 0) + return +} + +func UpdateProcThreadAttribute(lpAttributeList *ProcThreadAttributeList, dwFlags uint32, attribute uintptr, lpValue unsafe.Pointer, cbSize uintptr, lpPreviousValue unsafe.Pointer, lpReturnSize *uintptr) (err error) { + r1, _, e1 := syscall.Syscall9(procUpdateProcThreadAttribute.Addr(), 7, uintptr(unsafe.Pointer(lpAttributeList)), uintptr(dwFlags), uintptr(attribute), uintptr(lpValue), uintptr(cbSize), uintptr(lpPreviousValue), uintptr(unsafe.Pointer(lpReturnSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func CreateProcessAsUser(token windows.Token, appName *uint16, commandLine *uint16, procSecurity *windows.SecurityAttributes, threadSecurity *windows.SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *windows.StartupInfo, outProcInfo *windows.ProcessInformation) (err error) { + var _p0 uint32 + if inheritHandles { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall12(procCreateProcessAsUserW.Addr(), 11, uintptr(token), uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0) + if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { From 353d4e0497b36b8107e40b686501f819bb1b31fa Mon Sep 17 00:00:00 2001 From: Daniel Canter Date: Mon, 22 Nov 2021 17:00:34 -0800 Subject: [PATCH 2/4] Pr Review * CPty -> ConPTY * Unexport InitializeProcThreadAttributeList Signed-off-by: Daniel Canter --- internal/conpty/conpty.go | 26 +++++++++---------- internal/winapi/process.go | 6 ++--- internal/winapi/zsyscall_windows.go | 2 +- .../hcsshim/internal/winapi/process.go | 6 ++--- .../internal/winapi/zsyscall_windows.go | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/internal/conpty/conpty.go b/internal/conpty/conpty.go index 852be0652c..e17f67dc40 100644 --- a/internal/conpty/conpty.go +++ b/internal/conpty/conpty.go @@ -16,8 +16,8 @@ var ( errNotInitialized = errors.New("pseudo console hasn't been initialized") ) -// CPty is a wrapper around a Windows PseudoConsole handle. Create a new instance by calling `New()`. -type CPty struct { +// ConPTY is a wrapper around a Windows PseudoConsole handle. Create a new instance by calling `New()`. +type ConPTY struct { // handleLock guards hpc handleLock sync.RWMutex // hpc is the pseudo console handle @@ -27,8 +27,8 @@ type CPty struct { outPipe *os.File } -// New returns a new `CPty` object. This object is not ready for IO until `UpdateProcThreadAttribute` is called and a process has been started. -func New(columns, rows int16, flags uint32) (*CPty, error) { +// New returns a new `ConPTY` object. This object is not ready for IO until `UpdateProcThreadAttribute` is called and a process has been started. +func New(columns, rows int16, flags uint32) (*ConPTY, error) { // First we need to make both ends of the conpty's pipes, two to get passed into a process to use as input/output, and two for us to keep to // make use of this data. ptyIn, inPipeOurs, err := os.Pipe() @@ -49,7 +49,7 @@ func New(columns, rows int16, flags uint32) (*CPty, error) { } // The pty's end of its pipes can be closed here without worry. They're duped into the conhost - // that will be launched and will be released on a call to ClosePseudoConsole() (Close() on the CPty object). + // that will be launched and will be released on a call to ClosePseudoConsole() (Close() on the ConPTY object). if err := ptyOut.Close(); err != nil { return nil, fmt.Errorf("failed to close pseudo console handle: %w", err) } @@ -57,7 +57,7 @@ func New(columns, rows int16, flags uint32) (*CPty, error) { return nil, fmt.Errorf("failed to close pseudo console handle: %w", err) } - return &CPty{ + return &ConPTY{ hpc: hpc, inPipe: inPipeOurs, outPipe: outPipeOurs, @@ -66,7 +66,7 @@ func New(columns, rows int16, flags uint32) (*CPty, error) { // UpdateProcThreadAttribute updates the passed in attribute list to contain the entry necessary for use with // CreateProcess. -func (c *CPty) UpdateProcThreadAttribute(attributeList *winapi.ProcThreadAttributeList) error { +func (c *ConPTY) UpdateProcThreadAttribute(attributeList *winapi.ProcThreadAttributeList) error { c.handleLock.RLock() defer c.handleLock.RUnlock() @@ -90,7 +90,7 @@ func (c *CPty) UpdateProcThreadAttribute(attributeList *winapi.ProcThreadAttribu } // Resize resizes the internal buffers of the pseudo console to the passed in size -func (c *CPty) Resize(columns, rows int16) error { +func (c *ConPTY) Resize(columns, rows int16) error { c.handleLock.RLock() defer c.handleLock.RUnlock() @@ -106,7 +106,7 @@ func (c *CPty) Resize(columns, rows int16) error { } // Close closes the pseudo-terminal and cleans up all attached resources -func (c *CPty) Close() error { +func (c *ConPTY) Close() error { c.handleLock.Lock() defer c.handleLock.Unlock() @@ -127,17 +127,17 @@ func (c *CPty) Close() error { } // OutPipe returns the output pipe of the pseudo console. -func (c *CPty) OutPipe() *os.File { +func (c *ConPTY) OutPipe() *os.File { return c.outPipe } // InPipe returns the input pipe of the pseudo console. -func (c *CPty) InPipe() *os.File { +func (c *ConPTY) InPipe() *os.File { return c.inPipe } // Write writes the contents of `buf` to the pseudo console. Returns the number of bytes written and an error if there is one. -func (c *CPty) Write(buf []byte) (int, error) { +func (c *ConPTY) Write(buf []byte) (int, error) { if c.inPipe == nil { return 0, errNotInitialized } @@ -145,7 +145,7 @@ func (c *CPty) Write(buf []byte) (int, error) { } // Read reads from the pseudo console into `buf`. Returns the number of bytes read and an error if there is one. -func (c *CPty) Read(buf []byte) (int, error) { +func (c *ConPTY) Read(buf []byte) (int, error) { if c.outPipe == nil { return 0, errNotInitialized } diff --git a/internal/winapi/process.go b/internal/winapi/process.go index 977df04b87..70210e4d11 100644 --- a/internal/winapi/process.go +++ b/internal/winapi/process.go @@ -35,7 +35,7 @@ type StartupInfoEx struct { // DeleteProcThreadAttributeList. func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, error) { var size uintptr - err := InitializeProcThreadAttributeList(nil, maxAttrCount, 0, &size) + err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size) if err != windows.ERROR_INSUFFICIENT_BUFFER { if err == nil { return nil, errors.New("unable to query buffer size from InitializeProcThreadAttributeList") @@ -43,7 +43,7 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, return nil, err } al := (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0])) - err = InitializeProcThreadAttributeList(al, maxAttrCount, 0, &size) + err = initializeProcThreadAttributeList(al, maxAttrCount, 0, &size) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, // [in, out] PSIZE_T lpSize // ); // -//sys InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) = kernel32.InitializeProcThreadAttributeList +//sys initializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) = kernel32.InitializeProcThreadAttributeList // void DeleteProcThreadAttributeList( // [in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList diff --git a/internal/winapi/zsyscall_windows.go b/internal/winapi/zsyscall_windows.go index caf54c82c9..401f7a7398 100644 --- a/internal/winapi/zsyscall_windows.go +++ b/internal/winapi/zsyscall_windows.go @@ -260,7 +260,7 @@ func LocalFree(ptr uintptr) { return } -func InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) { +func initializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) { r1, _, e1 := syscall.Syscall6(procInitializeProcThreadAttributeList.Addr(), 4, uintptr(unsafe.Pointer(lpAttributeList)), uintptr(dwAttributeCount), uintptr(dwFlags), uintptr(unsafe.Pointer(lpSize)), 0, 0) if r1 == 0 { if e1 != 0 { diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go index 977df04b87..70210e4d11 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go @@ -35,7 +35,7 @@ type StartupInfoEx struct { // DeleteProcThreadAttributeList. func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, error) { var size uintptr - err := InitializeProcThreadAttributeList(nil, maxAttrCount, 0, &size) + err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size) if err != windows.ERROR_INSUFFICIENT_BUFFER { if err == nil { return nil, errors.New("unable to query buffer size from InitializeProcThreadAttributeList") @@ -43,7 +43,7 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, return nil, err } al := (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0])) - err = InitializeProcThreadAttributeList(al, maxAttrCount, 0, &size) + err = initializeProcThreadAttributeList(al, maxAttrCount, 0, &size) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList, // [in, out] PSIZE_T lpSize // ); // -//sys InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) = kernel32.InitializeProcThreadAttributeList +//sys initializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) = kernel32.InitializeProcThreadAttributeList // void DeleteProcThreadAttributeList( // [in, out] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go index caf54c82c9..401f7a7398 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go @@ -260,7 +260,7 @@ func LocalFree(ptr uintptr) { return } -func InitializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) { +func initializeProcThreadAttributeList(lpAttributeList *ProcThreadAttributeList, dwAttributeCount uint32, dwFlags uint32, lpSize *uintptr) (err error) { r1, _, e1 := syscall.Syscall6(procInitializeProcThreadAttributeList.Addr(), 4, uintptr(unsafe.Pointer(lpAttributeList)), uintptr(dwAttributeCount), uintptr(dwFlags), uintptr(unsafe.Pointer(lpSize)), 0, 0) if r1 == 0 { if e1 != 0 { From 76ba357ad989a3d35d5dcdd0fd92534464b4c670 Mon Sep 17 00:00:00 2001 From: Daniel Canter Date: Mon, 22 Nov 2021 18:51:14 -0800 Subject: [PATCH 3/4] Changed columns/rows to width/height for conpty package Signed-off-by: Daniel Canter --- internal/conpty/conpty.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/conpty/conpty.go b/internal/conpty/conpty.go index e17f67dc40..3e83f6052c 100644 --- a/internal/conpty/conpty.go +++ b/internal/conpty/conpty.go @@ -28,7 +28,7 @@ type ConPTY struct { } // New returns a new `ConPTY` object. This object is not ready for IO until `UpdateProcThreadAttribute` is called and a process has been started. -func New(columns, rows int16, flags uint32) (*ConPTY, error) { +func New(width, height int16, flags uint32) (*ConPTY, error) { // First we need to make both ends of the conpty's pipes, two to get passed into a process to use as input/output, and two for us to keep to // make use of this data. ptyIn, inPipeOurs, err := os.Pipe() @@ -42,7 +42,7 @@ func New(columns, rows int16, flags uint32) (*ConPTY, error) { } var hpc windows.Handle - coord := windows.Coord{X: columns, Y: rows} + coord := windows.Coord{X: width, Y: height} err = winapi.CreatePseudoConsole(coord, windows.Handle(ptyIn.Fd()), windows.Handle(ptyOut.Fd()), 0, &hpc) if err != nil { return nil, fmt.Errorf("failed to create pseudo console: %w", err) @@ -90,7 +90,7 @@ func (c *ConPTY) UpdateProcThreadAttribute(attributeList *winapi.ProcThreadAttri } // Resize resizes the internal buffers of the pseudo console to the passed in size -func (c *ConPTY) Resize(columns, rows int16) error { +func (c *ConPTY) Resize(width, height int16) error { c.handleLock.RLock() defer c.handleLock.RUnlock() @@ -98,7 +98,7 @@ func (c *ConPTY) Resize(columns, rows int16) error { return errClosedConPty } - coord := windows.Coord{X: columns, Y: rows} + coord := windows.Coord{X: width, Y: height} if err := winapi.ResizePseudoConsole(c.hpc, coord); err != nil { return fmt.Errorf("failed to resize pseudo console: %w", err) } From 8fd2be5e2ca3de2e32e4ae5230d9705c6cfd4571 Mon Sep 17 00:00:00 2001 From: Daniel Canter Date: Mon, 13 Dec 2021 14:06:12 -0800 Subject: [PATCH 4/4] Add a comment about swapping to x/sys/windows The issue with x/sys/windows is being solved here https://go-review.googlesource.com/c/sys/+/371276/1/windows/exec_windows.go#153. This change adds a comment to state that we'll swap once the fix is in. Signed-off-by: Daniel Canter --- internal/winapi/process.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/winapi/process.go b/internal/winapi/process.go index 70210e4d11..2cd4c76331 100644 --- a/internal/winapi/process.go +++ b/internal/winapi/process.go @@ -26,6 +26,9 @@ type StartupInfoEx struct { // This is a recreation of the same binding from the stdlib. The x/sys/windows variant for whatever reason // doesn't work when updating the list for the pseudo console attribute. It has the process immediately exit // with exit code 0xc0000142 shortly after start. + // + // TODO (dcantah): Swap to the x/sys/windows definitions after https://go-review.googlesource.com/c/sys/+/371276/1/windows/exec_windows.go#153 + // gets in. windows.StartupInfo ProcThreadAttributeList *ProcThreadAttributeList }