| // 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. | |
| 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 | |
| } | |
| 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 | |
| } |