mirror of
https://github.com/golang/go
synced 2024-11-21 22:24:40 -07:00
syscall: make use of StartupInfo.Reserved2 on Windows
On Windows, some applications (for example, Chromium) use _get_osfhandle to retrieve additional open files inherited from its parent process. This C Runtime function requires the parent process to pass the information about the inherited file descriptors in the field StartupInfo.Reserved2. This field is not in use as of now; and making use of it is supposed to not break anything. The MSDN just states that the field is "Reserved for use by the C Run-time". The implementation is found in these projects: nodejs: https://github.com/nodejs/node/blob/71691e53/deps/uv/src/win/process-stdio.c#L32-L37 ReactOS: https://github.com/reactos/reactos/blob/57c84dd6/sdk/lib/crt/stdio/file.c#L405-L532
This commit is contained in:
parent
ceda93ed67
commit
f0e594bdb3
@ -7,18 +7,21 @@
|
||||
package exec_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerHelperCommand("pipehandle", cmdPipeHandle)
|
||||
registerHelperCommand("crtpipehandle", cmdCRTPipeHandle)
|
||||
}
|
||||
|
||||
func cmdPipeHandle(args ...string) {
|
||||
@ -59,6 +62,104 @@ func TestPipePassing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func cmdCRTPipeHandle(args ...string) {
|
||||
get_osfhandle := syscall.NewLazyDLL("msvcrt.dll").NewProc("_get_osfhandle")
|
||||
|
||||
h3, _, _ := get_osfhandle.Call(3)
|
||||
if h3 == uintptr(syscall.InvalidHandle) {
|
||||
fmt.Fprintf(os.Stderr, "_get_osfhandle: pipe 3 is invalid\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
pipe3 := os.NewFile(h3, "in")
|
||||
defer pipe3.Close()
|
||||
|
||||
h4, _, _ := get_osfhandle.Call(4)
|
||||
if h4 == uintptr(syscall.InvalidHandle) {
|
||||
fmt.Fprintf(os.Stderr, "_get_osfhandle: pipe 4 is invalid\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
pipe4 := os.NewFile(h4, "out")
|
||||
defer pipe4.Close()
|
||||
|
||||
br := bufio.NewReader(pipe3)
|
||||
line, _, err := br.ReadLine()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "reading pipe failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if string(line) == "ping" {
|
||||
_, err := fmt.Fprintf(pipe4, "%s\n", args[0])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "writing to pipe failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "unexpected content from pipe: %q\n", line)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCRTPipePassing(t *testing.T) {
|
||||
crt := syscall.NewLazyDLL("msvcrt.dll")
|
||||
if err := crt.Load(); err != nil {
|
||||
t.Skipf("can't run test due to missing msvcrt.dll: %v", err)
|
||||
}
|
||||
|
||||
r3, w3, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Errorf("failed to create pipe 3: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
r3.Close()
|
||||
w3.Close()
|
||||
}()
|
||||
|
||||
r4, w4, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Errorf("failed to create pipe 4: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
r4.Close()
|
||||
w4.Close()
|
||||
}()
|
||||
|
||||
const marker = "pong"
|
||||
childProc := helperCommand(t, "crtpipehandle", marker)
|
||||
childProc.SysProcAttr = &syscall.SysProcAttr{
|
||||
AdditionalInheritedHandles: []syscall.Handle{
|
||||
syscall.Handle(r3.Fd()),
|
||||
syscall.Handle(w4.Fd()),
|
||||
},
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
output, err := childProc.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("child proc exited: %v. output:\n%s", err, output)
|
||||
r3.Close()
|
||||
w4.Close()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
_, err = fmt.Fprint(w3, "ping\n")
|
||||
if err != nil {
|
||||
t.Errorf("writing pipe failed: %v", err)
|
||||
}
|
||||
|
||||
br := bufio.NewReader(r4)
|
||||
response, _, err := br.ReadLine()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(response) != marker {
|
||||
t.Errorf("got %q; want %q", string(response), marker)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestNoInheritHandles(t *testing.T) {
|
||||
cmd := exec.Command("cmd", "/c exit 88")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{NoInheritHandles: true}
|
||||
|
@ -398,6 +398,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if len(fd) > 3 {
|
||||
si.CbReserved2, si.Reserved2 = createChildStdioBuffer(fd)
|
||||
}
|
||||
}
|
||||
|
||||
pi := new(ProcessInformation)
|
||||
@ -420,3 +423,59 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
||||
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
||||
return EWINDOWS
|
||||
}
|
||||
|
||||
// The C Run-time (CRT) file descriptor mode flags. See
|
||||
// https://github.com/nodejs/node/blob/71691e53/deps/uv/src/win/process-stdio.c#L57-L65
|
||||
const (
|
||||
crt_FD_OPEN uint8 = 1 << iota
|
||||
crt_FD_EOFLAG
|
||||
crt_FD_CRLF
|
||||
crt_FD_PIPE
|
||||
crt_FD_NOINHERIT
|
||||
crt_FD_APPEND
|
||||
crt_FD_DEV
|
||||
crt_FD_TEXT
|
||||
)
|
||||
|
||||
// createChildStdioBuffer creates a buffer that describes the handles that will
|
||||
// be inherited by the child process so that the child process can retrieve the
|
||||
// inherited handles by calling the CRT function _get_osfhandle.
|
||||
//
|
||||
// The buffer has the following layout:
|
||||
// int number_of_fds
|
||||
// unsigned char crt_flags[number_of_fds]
|
||||
// HANDLE os_handle[number_of_fds]
|
||||
//
|
||||
// See:
|
||||
// https://github.com/nodejs/node/blob/71691e53/deps/uv/src/win/process-stdio.c#L32-L37
|
||||
// https://github.com/reactos/reactos/blob/57c84dd6/sdk/lib/crt/stdio/file.c#L405-L532
|
||||
// https://github.com/reactos/reactos/blob/57c84dd6/sdk/lib/crt/stdio/file.c#L1588-L1599
|
||||
func createChildStdioBuffer(fd []Handle) (uint16, *byte) {
|
||||
count := int32(len(fd))
|
||||
var crtFlags uint8
|
||||
int32Size := unsafe.Sizeof(count)
|
||||
flagsSize := unsafe.Sizeof(crtFlags)
|
||||
handleSize := unsafe.Sizeof(fd[0])
|
||||
|
||||
size := uint16(int32Size + flagsSize*uintptr(count) + handleSize*uintptr(count))
|
||||
buf := make([]byte, size)
|
||||
|
||||
*(*int32)(unsafe.Pointer(&buf[0])) = count
|
||||
|
||||
for i := uintptr(0); i < uintptr(count); i++ {
|
||||
// (ZekeLu) should it be restrict to report the error for invalid or
|
||||
// unknown handles?
|
||||
ft, _ := GetFileType(fd[i])
|
||||
switch ft {
|
||||
case FILE_TYPE_DISK:
|
||||
crtFlags = crt_FD_OPEN
|
||||
case FILE_TYPE_PIPE:
|
||||
crtFlags = crt_FD_OPEN | crt_FD_PIPE
|
||||
default:
|
||||
crtFlags = crt_FD_OPEN | crt_FD_DEV
|
||||
}
|
||||
*(*uint8)(unsafe.Pointer(&buf[int32Size+flagsSize*i])) = crtFlags
|
||||
*(*Handle)(unsafe.Pointer(&buf[int32Size+flagsSize*uintptr(count)+handleSize*i])) = fd[i]
|
||||
}
|
||||
return size, &buf[0]
|
||||
}
|
||||
|
@ -483,8 +483,8 @@ type StartupInfo struct {
|
||||
FillAttribute uint32
|
||||
Flags uint32
|
||||
ShowWindow uint16
|
||||
_ uint16
|
||||
_ *byte
|
||||
CbReserved2 uint16
|
||||
Reserved2 *byte
|
||||
StdInput Handle
|
||||
StdOutput Handle
|
||||
StdErr Handle
|
||||
|
Loading…
Reference in New Issue
Block a user