diff --git a/src/pkg/runtime/windows/os.h b/src/pkg/runtime/windows/os.h index 931f4991c2..68cdd7ca44 100644 --- a/src/pkg/runtime/windows/os.h +++ b/src/pkg/runtime/windows/os.h @@ -34,7 +34,7 @@ typedef struct StdcallParams StdcallParams; struct StdcallParams { void *fn; - uintptr args[9]; + uintptr args[12]; uintptr r; uintptr err; }; diff --git a/src/pkg/runtime/windows/syscall.goc b/src/pkg/runtime/windows/syscall.goc index 362217e6bc..8287e70414 100644 --- a/src/pkg/runtime/windows/syscall.goc +++ b/src/pkg/runtime/windows/syscall.goc @@ -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; diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c index c65f665b1b..5dd013f483 100644 --- a/src/pkg/runtime/windows/thread.c +++ b/src/pkg/runtime/windows/thread.c @@ -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; } diff --git a/src/pkg/syscall/Makefile b/src/pkg/syscall/Makefile index 3ac99bad94..363eb601e3 100644 --- a/src/pkg/syscall/Makefile +++ b/src/pkg/syscall/Makefile @@ -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.go b/src/pkg/syscall/exec_unix.go similarity index 100% rename from src/pkg/syscall/exec.go rename to src/pkg/syscall/exec_unix.go diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go new file mode 100644 index 0000000000..1ac84a3d28 --- /dev/null +++ b/src/pkg/syscall/exec_windows.go @@ -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 +} diff --git a/src/pkg/syscall/mksyscall_windows.sh b/src/pkg/syscall/mksyscall_windows.sh index f9b4584fc2..ea35ba2b42 100755 --- a/src/pkg/syscall/mksyscall_windows.sh +++ b/src/pkg/syscall/mksyscall_windows.sh @@ -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"; } diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index 6aef0ded0e..159b9d6b15 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -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 diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index 55f26734d0..7c75d2b773 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -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) diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go index 4d35078685..3f50480e42 100644 --- a/src/pkg/syscall/ztypes_windows_386.go +++ b/src/pkg/syscall/ztypes_windows_386.go @@ -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