summaryrefslogtreecommitdiff
path: root/src/pkg/syscall/exec_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/syscall/exec_windows.go')
-rw-r--r--src/pkg/syscall/exec_windows.go203
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
+}