]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: add ForkExec, Syscall12 on Windows
authorDaniel Theophanes <kardianos@gmail.com>
Sun, 25 Jul 2010 23:43:35 +0000 (09:43 +1000)
committerAlex Brainman <alex.brainman@gmail.com>
Sun, 25 Jul 2010 23:43:35 +0000 (09:43 +1000)
R=brainman, rsc
CC=golang-dev
https://golang.org/cl/1578041

src/pkg/runtime/windows/os.h
src/pkg/runtime/windows/syscall.goc
src/pkg/runtime/windows/thread.c
src/pkg/syscall/Makefile
src/pkg/syscall/exec_unix.go [moved from src/pkg/syscall/exec.go with 100% similarity]
src/pkg/syscall/exec_windows.go [new file with mode: 0644]
src/pkg/syscall/mksyscall_windows.sh
src/pkg/syscall/syscall_windows.go
src/pkg/syscall/zsyscall_windows_386.go
src/pkg/syscall/ztypes_windows_386.go

index 931f4991c24e61f79757d47f82218a08d8399d95..68cdd7ca44a607e3fa14ce3e4c9cb83a62e37e9a 100644 (file)
@@ -34,7 +34,7 @@ typedef struct StdcallParams StdcallParams;
 struct StdcallParams
 {
        void    *fn;
-       uintptr args[9];
+       uintptr args[12];
        uintptr r;
        uintptr err;
 };
index 362217e6bcc4af411e9668f9c257c933d996934e..8287e704146d2f574f22496825a200a591fe33cb 100644 (file)
@@ -80,6 +80,29 @@ func Syscall9(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 u
        lasterr = p.err;
 }
 
+func Syscall12(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr, a10 uintptr, a11 uintptr, a12 uintptr) (r1 uintptr, r2 uintptr, lasterr uintptr) {
+       StdcallParams p;
+       p.fn = (void*)trap;
+       p.args[0] = a1;
+       p.args[1] = a2;
+       p.args[2] = a3;
+       p.args[3] = a4;
+       p.args[4] = a5;
+       p.args[5] = a6;
+       p.args[6] = a7;
+       p.args[7] = a8;
+       p.args[8] = a9;
+       p.args[9] = a10;
+       p.args[10] = a11;
+       p.args[11] = a12;
+       ·entersyscall();
+       syscall(&p);
+       ·exitsyscall();
+       r1 = p.r;
+       r2 = 0;
+       lasterr = p.err;
+}
+
 func RawSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr) (r1 uintptr, r2 uintptr, err uintptr) {
        StdcallParams p;
        p.fn = (void*)trap;
index c65f665b1b16f3860e05dab3d664dafe2d945784..5dd013f48366a1c7f8ad1d69d218147b223d26c9 100644 (file)
@@ -285,7 +285,7 @@ void
 call_syscall(void *args)
 {
        StdcallParams *p = (StdcallParams*)args;
-       p->r = (uintptr)stdcall_raw((void*)p->fn, p->args[0], p->args[1], p->args[2], p->args[3], p->args[4], p->args[5], p->args[6], p->args[7], p->args[8]);
+       p->r = (uintptr)stdcall_raw((void*)p->fn, p->args[0], p->args[1], p->args[2], p->args[3], p->args[4], p->args[5], p->args[6], p->args[7], p->args[8], p->args[9], p->args[10], p->args[11]);
        p->err = (uintptr)stdcall_raw(GetLastError);
        return;
 }
index 3ac99bad94a99a490ec695e02d744e85634aee6f..363eb601e3d9fbd2f7969b9a686bdb9b2fd9e15b 100644 (file)
@@ -7,7 +7,6 @@ include ../../Make.$(GOARCH)
 TARG=syscall
 GOFILES=\
        str.go\
-       exec.go\
        syscall.go\
        syscall_$(GOARCH).go\
        syscall_$(GOOS).go\
@@ -20,16 +19,23 @@ GOFILES=\
 GOFILES_freebsd=\
        syscall_bsd.go\
        syscall_unix.go\
+       exec_unix.go\
 
 GOFILES_darwin=\
        syscall_bsd.go\
        syscall_unix.go\
+       exec_unix.go\
 
 GOFILES_linux=\
        syscall_unix.go\
+       exec_unix.go\
 
 GOFILES_nacl=\
        syscall_unix.go\
+       exec_unix.go\
+
+GOFILES_windows=\
+       exec_windows.go
 
 OFILES=\
        asm_$(GOOS)_$(GOARCH).$O\
diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go
new file mode 100644 (file)
index 0000000..1ac84a3
--- /dev/null
@@ -0,0 +1,199 @@
+// 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
+}
index f9b4584fc23cee8da863edefceb84d7106393cf8..ea35ba2b42911d6d139a7013cae4ee3fb21e3721 100755 (executable)
@@ -145,6 +145,10 @@ while(<>) {
                        } else {
                                push @args, "uintptr($name)", "uintptr($name >> 32)";
                        }
+               } elsif($type eq "bool") {
+                       $text .= "\tvar _p$n uint32;\n";
+                       $text .= "\tif $name { _p$n = 1; } else { _p$n = 0;}\n";
+                       push @args, "uintptr(_p$n)";
                } else {
                        push @args, "uintptr($name)";
                }
@@ -167,6 +171,11 @@ while(<>) {
                while(@args < 9) {
                        push @args, "0";
                }
+       } elsif(@args <= 12) {
+               $asm = "Syscall12";
+               while(@args < 12) {
+                       push @args, "0";
+               }
        } else {
                print STDERR "$ARGV:$.: too many arguments to system call\n";
        }
index 6aef0ded0e77d5f2d3e8c4a961c922997355a5d2..159b9d6b155877c6780abf6ccd0f2358db72ee24 100644 (file)
@@ -80,6 +80,7 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
 
 // implemented in ../pkg/runtime/windows/syscall.cgo
 func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, lasterr uintptr)
+func Syscall12(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, lasterr uintptr)
 func loadlibraryex(filename uintptr) (handle uint32)
 func getprocaddress(handle uint32, procname uintptr) (proc uintptr)
 
@@ -131,6 +132,11 @@ func getSysProcAddr(m uint32, pname string) uintptr {
 //sys  GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) [failretval=0xffffffff]
 //sys  CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int)
 //sys  GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (ok bool, errno int)
+//sys  CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation)  (ok bool, errno int) = CreateProcessW
+//sys  GetStartupInfo(startupInfo *StartupInfo)  (ok bool, errno int) = GetStartupInfoW
+//sys  GetCurrentProcess() (pseudoHandle int32, errno int)
+//sys  DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (ok bool, errno int)
+//sys  WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) [failretval=0xffffffff]
 //sys  GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) = GetTempPathW
 //sys  CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (ok bool, errno int) = advapi32.CryptAcquireContextW
 //sys  CryptReleaseContext(provhandle uint32, flags uint32) (ok bool, errno int) = advapi32.CryptReleaseContext
index 55f26734d0952b7d37451b4e0f422831b8734a9c..7c75d2b7739f061a4b794e6026417403742ac84d 100644 (file)
@@ -1,4 +1,4 @@
-// mksyscall_windows.sh -l32 syscall_windows.go syscall_windows_386.go
+// mksyscall_windows.sh -l32 syscall_windows.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
 package syscall
@@ -41,6 +41,11 @@ var (
        procGetTimeZoneInformation     = getSysProcAddr(modkernel32, "GetTimeZoneInformation")
        procCreateIoCompletionPort     = getSysProcAddr(modkernel32, "CreateIoCompletionPort")
        procGetQueuedCompletionStatus  = getSysProcAddr(modkernel32, "GetQueuedCompletionStatus")
+       procCreateProcessW             = getSysProcAddr(modkernel32, "CreateProcessW")
+       procGetStartupInfoW            = getSysProcAddr(modkernel32, "GetStartupInfoW")
+       procGetCurrentProcess          = getSysProcAddr(modkernel32, "GetCurrentProcess")
+       procDuplicateHandle            = getSysProcAddr(modkernel32, "DuplicateHandle")
+       procWaitForSingleObject        = getSysProcAddr(modkernel32, "WaitForSingleObject")
        procGetTempPathW               = getSysProcAddr(modkernel32, "GetTempPathW")
        procCryptAcquireContextW       = getSysProcAddr(modadvapi32, "CryptAcquireContextW")
        procCryptReleaseContext        = getSysProcAddr(modadvapi32, "CryptReleaseContext")
@@ -380,6 +385,73 @@ func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlap
        return
 }
 
+func CreateProcess(appName *int16, commandLine *uint16, procSecurity *int16, threadSecurity *int16, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (ok bool, errno int) {
+       var _p0 uint32
+       if inheritHandles {
+               _p0 = 1
+       } else {
+               _p0 = 0
+       }
+       r0, _, e1 := Syscall12(procCreateProcessW, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0)
+       ok = bool(r0 != 0)
+       if !ok {
+               errno = int(e1)
+       } else {
+               errno = 0
+       }
+       return
+}
+
+func GetStartupInfo(startupInfo *StartupInfo) (ok bool, errno int) {
+       r0, _, e1 := Syscall(procGetStartupInfoW, uintptr(unsafe.Pointer(startupInfo)), 0, 0)
+       ok = bool(r0 != 0)
+       if !ok {
+               errno = int(e1)
+       } else {
+               errno = 0
+       }
+       return
+}
+
+func GetCurrentProcess() (pseudoHandle int32, errno int) {
+       r0, _, e1 := Syscall(procGetCurrentProcess, 0, 0, 0)
+       pseudoHandle = int32(r0)
+       if pseudoHandle == 0 {
+               errno = int(e1)
+       } else {
+               errno = 0
+       }
+       return
+}
+
+func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (ok bool, errno int) {
+       var _p0 uint32
+       if bInheritHandle {
+               _p0 = 1
+       } else {
+               _p0 = 0
+       }
+       r0, _, e1 := Syscall9(procDuplicateHandle, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0)
+       ok = bool(r0 != 0)
+       if !ok {
+               errno = int(e1)
+       } else {
+               errno = 0
+       }
+       return
+}
+
+func WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) {
+       r0, _, e1 := Syscall(procWaitForSingleObject, uintptr(handle), uintptr(waitMilliseconds), 0)
+       event = uint32(r0)
+       if event == 0xffffffff {
+               errno = int(e1)
+       } else {
+               errno = 0
+       }
+       return
+}
+
 func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) {
        r0, _, e1 := Syscall(procGetTempPathW, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0)
        n = uint32(r0)
index 4d3507868526c211cf99448d4c34218d6e4e4134..3f50480e427a9f330fd99ca92f3ea22517385135 100644 (file)
@@ -59,6 +59,10 @@ const (
        OPEN_ALWAYS       = 4
        TRUNCATE_EXISTING = 5
 
+       STARTF_USESTDHANDLES   = 0x00000100
+       DUPLICATE_CLOSE_SOURCE = 0x00000001
+       DUPLICATE_SAME_ACCESS  = 0x00000002
+
        STD_INPUT_HANDLE  = -10
        STD_OUTPUT_HANDLE = -11
        STD_ERROR_HANDLE  = -12
@@ -75,7 +79,8 @@ const (
        FORMAT_MESSAGE_ARGUMENT_ARRAY  = 8192
        FORMAT_MESSAGE_MAX_WIDTH_MASK  = 255
 
-       MAX_PATH = 260
+       MAX_PATH      = 260
+       MAX_LONG_PATH = 32768
 
        MAX_COMPUTERNAME_LENGTH = 15
 
@@ -83,9 +88,12 @@ const (
        TIME_ZONE_ID_STANDARD = 1
 
        TIME_ZONE_ID_DAYLIGHT = 2
+       IGNORE                = 0
        INFINITE              = 0xffffffff
 
        WAIT_TIMEOUT = 258
+
+       CREATE_UNICODE_ENVIRONMENT = 0x00000400
 )
 
 const (
@@ -181,6 +189,34 @@ type ByHandleFileInformation struct {
        FileIndexLow       uint32
 }
 
+type StartupInfo struct {
+       Cb            uint32
+       _             *uint16
+       Desktop       *uint16
+       Title         *uint16
+       X             uint32
+       Y             uint32
+       XSize         uint32
+       YSize         uint32
+       XCountChars   uint32
+       YCountChars   uint32
+       FillAttribute uint32
+       Flags         uint32
+       ShowWindow    uint16
+       _             uint16
+       _             *byte
+       StdInput      int32
+       StdOutput     int32
+       StdErr        int32
+}
+
+type ProcessInformation struct {
+       Process   int32
+       Thread    int32
+       ProcessId uint32
+       ThreadId  uint32
+}
+
 // Invented values to support what package os expects.
 type Stat_t struct {
        Windata Win32finddata