mirror of
https://github.com/rwinkhart/sys.git
synced 2026-08-28 04:46:44 -04:00
go.sys/windows: delete StartProcess and Exec
These are being deleted from go.sys because in general they can only be implemented in close coordination with the runtime. LGTM=alex.brainman R=alex.brainman CC=golang-codereviews https://golang.org/cl/132010043
This commit is contained in:
+1
-237
@@ -6,14 +6,7 @@
|
|||||||
|
|
||||||
package windows
|
package windows
|
||||||
|
|
||||||
import (
|
import "syscall"
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"unicode/utf16"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ForkLock sync.RWMutex
|
|
||||||
|
|
||||||
// EscapeArg rewrites command line argument s as prescribed
|
// EscapeArg rewrites command line argument s as prescribed
|
||||||
// in http://msdn.microsoft.com/en-us/library/ms880421.
|
// in http://msdn.microsoft.com/en-us/library/ms880421.
|
||||||
@@ -82,54 +75,10 @@ func EscapeArg(s string) string {
|
|||||||
return string(qs[:j])
|
return string(qs[:j])
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeCmdLine builds a command line out of args by escaping "special"
|
|
||||||
// characters and joining the arguments with spaces.
|
|
||||||
func makeCmdLine(args []string) string {
|
|
||||||
var s string
|
|
||||||
for _, v := range args {
|
|
||||||
if s != "" {
|
|
||||||
s += " "
|
|
||||||
}
|
|
||||||
s += EscapeArg(v)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// createEnvBlock converts an array of environment strings into
|
|
||||||
// the representation required by CreateProcess: a sequence of NUL
|
|
||||||
// terminated strings followed by a nil.
|
|
||||||
// Last bytes are two UCS-2 NULs, or four NUL bytes.
|
|
||||||
func createEnvBlock(envv []string) *uint16 {
|
|
||||||
if len(envv) == 0 {
|
|
||||||
return &utf16.Encode([]rune("\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([]rune(string(b)))[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func CloseOnExec(fd Handle) {
|
func CloseOnExec(fd Handle) {
|
||||||
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetNonblock(fd Handle, nonblocking bool) (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FullPath retrieves the full path of the specified file.
|
// FullPath retrieves the full path of the specified file.
|
||||||
func FullPath(name string) (path string, err error) {
|
func FullPath(name string) (path string, err error) {
|
||||||
p, err := UTF16PtrFromString(name)
|
p, err := UTF16PtrFromString(name)
|
||||||
@@ -154,188 +103,3 @@ func FullPath(name string) (path string, err error) {
|
|||||||
}
|
}
|
||||||
return UTF16ToString(buf[:n]), nil
|
return UTF16ToString(buf[:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSlash(c uint8) bool {
|
|
||||||
return c == '\\' || c == '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalizeDir(dir string) (name string, err error) {
|
|
||||||
ndir, err := FullPath(dir)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
|
|
||||||
// dir cannot have \\server\share\path form
|
|
||||||
return "", syscall.EINVAL
|
|
||||||
}
|
|
||||||
return ndir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func volToUpper(ch int) int {
|
|
||||||
if 'a' <= ch && ch <= 'z' {
|
|
||||||
ch += 'A' - 'a'
|
|
||||||
}
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
func joinExeDirAndFName(dir, p string) (name string, err error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return "", syscall.EINVAL
|
|
||||||
}
|
|
||||||
if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
|
|
||||||
// \\server\share\path form
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
if len(p) > 1 && p[1] == ':' {
|
|
||||||
// has drive letter
|
|
||||||
if len(p) == 2 {
|
|
||||||
return "", syscall.EINVAL
|
|
||||||
}
|
|
||||||
if isSlash(p[2]) {
|
|
||||||
return p, nil
|
|
||||||
} else {
|
|
||||||
d, err := normalizeDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
|
|
||||||
return FullPath(d + "\\" + p[2:])
|
|
||||||
} else {
|
|
||||||
return FullPath(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no drive letter
|
|
||||||
d, err := normalizeDir(dir)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if isSlash(p[0]) {
|
|
||||||
return FullPath(d[:2] + p)
|
|
||||||
} else {
|
|
||||||
return FullPath(d + "\\" + p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we shouldn't be here
|
|
||||||
return "", syscall.EINVAL
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProcAttr struct {
|
|
||||||
Dir string
|
|
||||||
Env []string
|
|
||||||
Files []uintptr
|
|
||||||
Sys *SysProcAttr
|
|
||||||
}
|
|
||||||
|
|
||||||
type SysProcAttr struct {
|
|
||||||
HideWindow bool
|
|
||||||
CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
|
|
||||||
CreationFlags uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
var zeroProcAttr ProcAttr
|
|
||||||
var zeroSysProcAttr SysProcAttr
|
|
||||||
|
|
||||||
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
|
|
||||||
if len(argv0) == 0 {
|
|
||||||
return 0, 0, syscall.EWINDOWS
|
|
||||||
}
|
|
||||||
if attr == nil {
|
|
||||||
attr = &zeroProcAttr
|
|
||||||
}
|
|
||||||
sys := attr.Sys
|
|
||||||
if sys == nil {
|
|
||||||
sys = &zeroSysProcAttr
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(attr.Files) > 3 {
|
|
||||||
return 0, 0, syscall.EWINDOWS
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(attr.Dir) != 0 {
|
|
||||||
// StartProcess assumes that argv0 is relative to attr.Dir,
|
|
||||||
// because it implies Chdir(attr.Dir) before executing argv0.
|
|
||||||
// Windows CreateProcess assumes the opposite: it looks for
|
|
||||||
// argv0 relative to the current directory, and, only once the new
|
|
||||||
// process is started, it does Chdir(attr.Dir). We are adjusting
|
|
||||||
// for that difference here by making argv0 absolute.
|
|
||||||
var err error
|
|
||||||
argv0, err = joinExeDirAndFName(attr.Dir, argv0)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argv0p, err := UTF16PtrFromString(argv0)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdline string
|
|
||||||
// Windows CreateProcess takes the command line as a single string:
|
|
||||||
// use attr.CmdLine if set, else build the command line by escaping
|
|
||||||
// and joining each argument with spaces
|
|
||||||
if sys.CmdLine != "" {
|
|
||||||
cmdline = sys.CmdLine
|
|
||||||
} else {
|
|
||||||
cmdline = makeCmdLine(argv)
|
|
||||||
}
|
|
||||||
|
|
||||||
var argvp *uint16
|
|
||||||
if len(cmdline) != 0 {
|
|
||||||
argvp, err = UTF16PtrFromString(cmdline)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var dirp *uint16
|
|
||||||
if len(attr.Dir) != 0 {
|
|
||||||
dirp, err = UTF16PtrFromString(attr.Dir)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acquire the fork lock so that no other threads
|
|
||||||
// create new fds that are not yet close-on-exec
|
|
||||||
// before we fork.
|
|
||||||
ForkLock.Lock()
|
|
||||||
defer ForkLock.Unlock()
|
|
||||||
|
|
||||||
p, _ := GetCurrentProcess()
|
|
||||||
fd := make([]Handle, len(attr.Files))
|
|
||||||
for i := range attr.Files {
|
|
||||||
if attr.Files[i] > 0 {
|
|
||||||
err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
defer CloseHandle(Handle(fd[i]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
si := new(StartupInfo)
|
|
||||||
si.Cb = uint32(unsafe.Sizeof(*si))
|
|
||||||
si.Flags = STARTF_USESTDHANDLES
|
|
||||||
if sys.HideWindow {
|
|
||||||
si.Flags |= STARTF_USESHOWWINDOW
|
|
||||||
si.ShowWindow = SW_HIDE
|
|
||||||
}
|
|
||||||
si.StdInput = fd[0]
|
|
||||||
si.StdOutput = fd[1]
|
|
||||||
si.StdErr = fd[2]
|
|
||||||
|
|
||||||
pi := new(ProcessInformation)
|
|
||||||
|
|
||||||
flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT
|
|
||||||
err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
defer CloseHandle(Handle(pi.Thread))
|
|
||||||
|
|
||||||
return int(pi.ProcessId), uintptr(pi.Process), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
|
||||||
return syscall.EWINDOWS
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user