diff options
Diffstat (limited to 'src/pkg/syscall/exec_windows.go')
-rw-r--r-- | src/pkg/syscall/exec_windows.go | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go new file mode 100644 index 000000000..c3ed3ba98 --- /dev/null +++ b/src/pkg/syscall/exec_windows.go @@ -0,0 +1,203 @@ +// 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 +} |