// 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) { SetHandleInformation(int32(fd), HANDLE_FLAG_INHERIT, 0) } 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. 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 // Acquire the fork lock so that no other threads // create new fds that are not yet close-on-exec // before we fork. ForkLock.Lock() defer ForkLock.Unlock() 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 } defer CloseHandle(int32(startupInfo.StdInput)) } 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 } defer CloseHandle(int32(startupInfo.StdOutput)) } 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 } defer CloseHandle(int32(startupInfo.StdErr)) } if len(argv) == 0 { argv = []string{""} } // 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[1:], " ", 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 }