mirror of
https://github.com/golang/go
synced 2024-10-04 16:21:22 -06:00
7f9e247489
R=brainman, rsc CC=golang-dev https://golang.org/cl/1578041
200 lines
4.5 KiB
Go
200 lines
4.5 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Fork, exec, wait, etc.
|
|
|
|
package syscall
|
|
|
|
import (
|
|
"sync"
|
|
"utf16"
|
|
)
|
|
|
|
// Windows doesn't have a good concept of just Exec in the documented API.
|
|
// However, the kernel32 CreateProcess does a good job with
|
|
// ForkExec.
|
|
|
|
var ForkLock sync.RWMutex
|
|
|
|
// Joins an array of string with sep
|
|
// From the "strings" package. Modified.
|
|
func stringJoin(a []string, sep string, escape escapeFunc) string {
|
|
if len(a) == 0 {
|
|
return ""
|
|
}
|
|
if len(a) == 1 {
|
|
return a[0]
|
|
}
|
|
n := len(sep) * (len(a) - 1)
|
|
for i := 0; i < len(a); i++ {
|
|
a[i] = escape(a[i])
|
|
n += len(a[i])
|
|
}
|
|
|
|
b := make([]byte, n)
|
|
bp := 0
|
|
for i := 0; i < len(a); i++ {
|
|
s := a[i]
|
|
for j := 0; j < len(s); j++ {
|
|
b[bp] = s[j]
|
|
bp++
|
|
}
|
|
if i+1 < len(a) {
|
|
s = sep
|
|
for j := 0; j < len(s); j++ {
|
|
b[bp] = s[j]
|
|
bp++
|
|
}
|
|
}
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
//Env block is a sequence of null terminated strings followed by a null.
|
|
//Last bytes are two unicode nulls, or four null bytes.
|
|
func createEnvBlock(envv []string) *uint16 {
|
|
if len(envv) == 0 {
|
|
return &utf16.Encode([]int("\x00\x00"))[0]
|
|
}
|
|
length := 0
|
|
for _, s := range envv {
|
|
length += len(s) + 1
|
|
}
|
|
length += 1
|
|
|
|
b := make([]byte, length)
|
|
i := 0
|
|
for _, s := range envv {
|
|
l := len(s)
|
|
copy(b[i:i+l], []byte(s))
|
|
copy(b[i+l:i+l+1], []byte{0})
|
|
i = i + l + 1
|
|
}
|
|
copy(b[i:i+1], []byte{0})
|
|
|
|
return &utf16.Encode([]int(string(b)))[0]
|
|
}
|
|
|
|
type escapeFunc func(s string) string
|
|
|
|
//escapes quotes by " -> ""
|
|
//Also string -> "string"
|
|
func escapeAddQuotes(s string) string {
|
|
//normal ascii char, one byte wide
|
|
rune := byte('"')
|
|
l := len(s)
|
|
n := 0
|
|
for i := 0; i < l; i++ {
|
|
if s[i] == rune {
|
|
n++
|
|
}
|
|
}
|
|
qs := make([]byte, l+n+2)
|
|
|
|
qs[0] = rune
|
|
j := 1
|
|
for i := 0; i < l; i++ {
|
|
qs[i+j] = s[i]
|
|
if s[i] == rune {
|
|
j++
|
|
qs[i+j] = rune
|
|
}
|
|
}
|
|
qs[len(qs)-1] = rune
|
|
return string(qs)
|
|
}
|
|
|
|
|
|
func CloseOnExec(fd int) {
|
|
return
|
|
}
|
|
|
|
func SetNonblock(fd int, nonblocking bool) (errno int) {
|
|
return 0
|
|
}
|
|
|
|
|
|
// TODO(kardia): Add trace
|
|
//The command and arguments are passed via the Command line parameter.
|
|
//Thus, repeating the exec name in the first argument is unneeded.
|
|
func forkExec(argv0 string, argv []string, envv []string, traceme bool, dir string, fd []int) (pid int, err int) {
|
|
if traceme == true {
|
|
return 0, EWINDOWS
|
|
}
|
|
|
|
if len(fd) > 3 {
|
|
return 0, EWINDOWS
|
|
}
|
|
|
|
//CreateProcess will throw an error if the dir is not set to a valid dir
|
|
// thus get the working dir if dir is empty.
|
|
if len(dir) == 0 {
|
|
if wd, ok := Getwd(); ok == 0 {
|
|
dir = wd
|
|
}
|
|
}
|
|
|
|
startupInfo := new(StartupInfo)
|
|
processInfo := new(ProcessInformation)
|
|
|
|
GetStartupInfo(startupInfo)
|
|
|
|
startupInfo.Flags = STARTF_USESTDHANDLES
|
|
startupInfo.StdInput = 0
|
|
startupInfo.StdOutput = 0
|
|
startupInfo.StdErr = 0
|
|
|
|
var currentProc, _ = GetCurrentProcess()
|
|
if len(fd) > 0 && fd[0] > 0 {
|
|
if ok, err := DuplicateHandle(currentProc, int32(fd[0]), currentProc, &startupInfo.StdInput, 0, true, DUPLICATE_SAME_ACCESS); !ok {
|
|
return 0, err
|
|
}
|
|
}
|
|
if len(fd) > 1 && fd[1] > 0 {
|
|
if ok, err := DuplicateHandle(currentProc, int32(fd[1]), currentProc, &startupInfo.StdOutput, 0, true, DUPLICATE_SAME_ACCESS); !ok {
|
|
return 0, err
|
|
}
|
|
}
|
|
if len(fd) > 2 && fd[2] > 0 {
|
|
if ok, err := DuplicateHandle(currentProc, int32(fd[2]), currentProc, &startupInfo.StdErr, 0, true, DUPLICATE_SAME_ACCESS); !ok {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
// argv0 must not be longer then 256 chars
|
|
// but the entire cmd line can have up to 32k chars (msdn)
|
|
ok, err := CreateProcess(
|
|
nil,
|
|
StringToUTF16Ptr(escapeAddQuotes(argv0)+" "+stringJoin(argv, " ", escapeAddQuotes)),
|
|
nil, //ptr to struct lpProcessAttributes
|
|
nil, //ptr to struct lpThreadAttributes
|
|
true, //bInheritHandles
|
|
CREATE_UNICODE_ENVIRONMENT, //Flags
|
|
createEnvBlock(envv), //env block, NULL uses parent env
|
|
StringToUTF16Ptr(dir),
|
|
startupInfo,
|
|
processInfo)
|
|
|
|
if ok {
|
|
pid = int(processInfo.ProcessId)
|
|
CloseHandle(processInfo.Process)
|
|
CloseHandle(processInfo.Thread)
|
|
}
|
|
return
|
|
}
|
|
|
|
func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) {
|
|
return forkExec(argv0, argv, envv, false, dir, fd)
|
|
}
|
|
|
|
// PtraceForkExec is like ForkExec, but starts the child in a traced state.
|
|
func PtraceForkExec(argv0 string, argv []string, envv []string, dir string, fd []int) (pid int, err int) {
|
|
return forkExec(argv0, argv, envv, true, dir, fd)
|
|
}
|
|
|
|
// Ordinary exec.
|
|
func Exec(argv0 string, argv []string, envv []string) (err int) {
|
|
return EWINDOWS
|
|
}
|