RISC-V: c implies zca, and conditionally zcf & zcd
[official-gcc.git] / libgo / go / syscall / exec_windows.go
blob9d10d6a51271f62c2b7daa86db0c774dff5084b6
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Fork, exec, wait, etc.
7 package syscall
9 import (
10 "runtime"
11 "sync"
12 "unicode/utf16"
13 "unsafe"
16 var ForkLock sync.RWMutex
18 // EscapeArg rewrites command line argument s as prescribed
19 // in https://msdn.microsoft.com/en-us/library/ms880421.
20 // This function returns "" (2 double quotes) if s is empty.
21 // Alternatively, these transformations are done:
22 // - every back slash (\) is doubled, but only if immediately
23 // followed by double quote (");
24 // - every double quote (") is escaped by back slash (\);
25 // - finally, s is wrapped with double quotes (arg -> "arg"),
26 // but only if there is space or tab inside s.
27 func EscapeArg(s string) string {
28 if len(s) == 0 {
29 return `""`
31 for i := 0; i < len(s); i++ {
32 switch s[i] {
33 case '"', '\\', ' ', '\t':
34 // Some escaping required.
35 b := make([]byte, 0, len(s)+2)
36 b = appendEscapeArg(b, s)
37 return string(b)
40 return s
43 // appendEscapeArg escapes the string s, as per escapeArg,
44 // appends the result to b, and returns the updated slice.
45 func appendEscapeArg(b []byte, s string) []byte {
46 if len(s) == 0 {
47 return append(b, `""`...)
50 needsBackslash := false
51 hasSpace := false
52 for i := 0; i < len(s); i++ {
53 switch s[i] {
54 case '"', '\\':
55 needsBackslash = true
56 case ' ', '\t':
57 hasSpace = true
61 if !needsBackslash && !hasSpace {
62 // No special handling required; normal case.
63 return append(b, s...)
65 if !needsBackslash {
66 // hasSpace is true, so we need to quote the string.
67 b = append(b, '"')
68 b = append(b, s...)
69 return append(b, '"')
72 if hasSpace {
73 b = append(b, '"')
75 slashes := 0
76 for i := 0; i < len(s); i++ {
77 c := s[i]
78 switch c {
79 default:
80 slashes = 0
81 case '\\':
82 slashes++
83 case '"':
84 for ; slashes > 0; slashes-- {
85 b = append(b, '\\')
87 b = append(b, '\\')
89 b = append(b, c)
91 if hasSpace {
92 for ; slashes > 0; slashes-- {
93 b = append(b, '\\')
95 b = append(b, '"')
98 return b
101 // makeCmdLine builds a command line out of args by escaping "special"
102 // characters and joining the arguments with spaces.
103 func makeCmdLine(args []string) string {
104 var b []byte
105 for _, v := range args {
106 if len(b) > 0 {
107 b = append(b, ' ')
109 b = appendEscapeArg(b, v)
111 return string(b)
114 // createEnvBlock converts an array of environment strings into
115 // the representation required by CreateProcess: a sequence of NUL
116 // terminated strings followed by a nil.
117 // Last bytes are two UCS-2 NULs, or four NUL bytes.
118 func createEnvBlock(envv []string) *uint16 {
119 if len(envv) == 0 {
120 return &utf16.Encode([]rune("\x00\x00"))[0]
122 length := 0
123 for _, s := range envv {
124 length += len(s) + 1
126 length += 1
128 b := make([]byte, length)
129 i := 0
130 for _, s := range envv {
131 l := len(s)
132 copy(b[i:i+l], []byte(s))
133 copy(b[i+l:i+l+1], []byte{0})
134 i = i + l + 1
136 copy(b[i:i+1], []byte{0})
138 return &utf16.Encode([]rune(string(b)))[0]
141 func CloseOnExec(fd Handle) {
142 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
145 func SetNonblock(fd Handle, nonblocking bool) (err error) {
146 return nil
149 // FullPath retrieves the full path of the specified file.
150 func FullPath(name string) (path string, err error) {
151 p, err := UTF16PtrFromString(name)
152 if err != nil {
153 return "", err
155 n := uint32(100)
156 for {
157 buf := make([]uint16, n)
158 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
159 if err != nil {
160 return "", err
162 if n <= uint32(len(buf)) {
163 return UTF16ToString(buf[:n]), nil
168 func isSlash(c uint8) bool {
169 return c == '\\' || c == '/'
172 func normalizeDir(dir string) (name string, err error) {
173 ndir, err := FullPath(dir)
174 if err != nil {
175 return "", err
177 if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
178 // dir cannot have \\server\share\path form
179 return "", EINVAL
181 return ndir, nil
184 func volToUpper(ch int) int {
185 if 'a' <= ch && ch <= 'z' {
186 ch += 'A' - 'a'
188 return ch
191 func joinExeDirAndFName(dir, p string) (name string, err error) {
192 if len(p) == 0 {
193 return "", EINVAL
195 if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
196 // \\server\share\path form
197 return p, nil
199 if len(p) > 1 && p[1] == ':' {
200 // has drive letter
201 if len(p) == 2 {
202 return "", EINVAL
204 if isSlash(p[2]) {
205 return p, nil
206 } else {
207 d, err := normalizeDir(dir)
208 if err != nil {
209 return "", err
211 if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
212 return FullPath(d + "\\" + p[2:])
213 } else {
214 return FullPath(p)
217 } else {
218 // no drive letter
219 d, err := normalizeDir(dir)
220 if err != nil {
221 return "", err
223 if isSlash(p[0]) {
224 return FullPath(d[:2] + p)
225 } else {
226 return FullPath(d + "\\" + p)
231 type ProcAttr struct {
232 Dir string
233 Env []string
234 Files []uintptr
235 Sys *SysProcAttr
238 type SysProcAttr struct {
239 HideWindow bool
240 CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
241 CreationFlags uint32
242 Token Token // if set, runs new process in the security context represented by the token
243 ProcessAttributes *SecurityAttributes // if set, applies these security attributes as the descriptor for the new process
244 ThreadAttributes *SecurityAttributes // if set, applies these security attributes as the descriptor for the main thread of the new process
245 NoInheritHandles bool // if set, each inheritable handle in the calling process is not inherited by the new process
246 AdditionalInheritedHandles []Handle // a list of additional handles, already marked as inheritable, that will be inherited by the new process
247 ParentProcess Handle // if non-zero, the new process regards the process given by this handle as its parent process, and AdditionalInheritedHandles, if set, should exist in this parent process
250 var zeroProcAttr ProcAttr
251 var zeroSysProcAttr SysProcAttr
253 func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
254 if len(argv0) == 0 {
255 return 0, 0, EWINDOWS
257 if attr == nil {
258 attr = &zeroProcAttr
260 sys := attr.Sys
261 if sys == nil {
262 sys = &zeroSysProcAttr
265 if len(attr.Files) > 3 {
266 return 0, 0, EWINDOWS
268 if len(attr.Files) < 3 {
269 return 0, 0, EINVAL
272 if len(attr.Dir) != 0 {
273 // StartProcess assumes that argv0 is relative to attr.Dir,
274 // because it implies Chdir(attr.Dir) before executing argv0.
275 // Windows CreateProcess assumes the opposite: it looks for
276 // argv0 relative to the current directory, and, only once the new
277 // process is started, it does Chdir(attr.Dir). We are adjusting
278 // for that difference here by making argv0 absolute.
279 var err error
280 argv0, err = joinExeDirAndFName(attr.Dir, argv0)
281 if err != nil {
282 return 0, 0, err
285 argv0p, err := UTF16PtrFromString(argv0)
286 if err != nil {
287 return 0, 0, err
290 var cmdline string
291 // Windows CreateProcess takes the command line as a single string:
292 // use attr.CmdLine if set, else build the command line by escaping
293 // and joining each argument with spaces
294 if sys.CmdLine != "" {
295 cmdline = sys.CmdLine
296 } else {
297 cmdline = makeCmdLine(argv)
300 var argvp *uint16
301 if len(cmdline) != 0 {
302 argvp, err = UTF16PtrFromString(cmdline)
303 if err != nil {
304 return 0, 0, err
308 var dirp *uint16
309 if len(attr.Dir) != 0 {
310 dirp, err = UTF16PtrFromString(attr.Dir)
311 if err != nil {
312 return 0, 0, err
316 var maj, min, build uint32
317 rtlGetNtVersionNumbers(&maj, &min, &build)
318 isWin7 := maj < 6 || (maj == 6 && min <= 1)
319 // NT kernel handles are divisible by 4, with the bottom 3 bits left as
320 // a tag. The fully set tag correlates with the types of handles we're
321 // concerned about here. Except, the kernel will interpret some
322 // special handle values, like -1, -2, and so forth, so kernelbase.dll
323 // checks to see that those bottom three bits are checked, but that top
324 // bit is not checked.
325 isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
327 p, _ := GetCurrentProcess()
328 parentProcess := p
329 if sys.ParentProcess != 0 {
330 parentProcess = sys.ParentProcess
332 fd := make([]Handle, len(attr.Files))
333 for i := range attr.Files {
334 if attr.Files[i] > 0 {
335 destinationProcessHandle := parentProcess
337 // On Windows 7, console handles aren't real handles, and can only be duplicated
338 // into the current process, not a parent one, which amounts to the same thing.
339 if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
340 destinationProcessHandle = p
343 err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
344 if err != nil {
345 return 0, 0, err
347 defer DuplicateHandle(parentProcess, fd[i], 0, nil, 0, false, DUPLICATE_CLOSE_SOURCE)
350 si := new(_STARTUPINFOEXW)
351 si.ProcThreadAttributeList, err = newProcThreadAttributeList(2)
352 if err != nil {
353 return 0, 0, err
355 defer deleteProcThreadAttributeList(si.ProcThreadAttributeList)
356 si.Cb = uint32(unsafe.Sizeof(*si))
357 si.Flags = STARTF_USESTDHANDLES
358 if sys.HideWindow {
359 si.Flags |= STARTF_USESHOWWINDOW
360 si.ShowWindow = SW_HIDE
362 if sys.ParentProcess != 0 {
363 err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, unsafe.Pointer(&sys.ParentProcess), unsafe.Sizeof(sys.ParentProcess), nil, nil)
364 if err != nil {
365 return 0, 0, err
368 si.StdInput = fd[0]
369 si.StdOutput = fd[1]
370 si.StdErr = fd[2]
372 fd = append(fd, sys.AdditionalInheritedHandles...)
374 // On Windows 7, console handles aren't real handles, so don't pass them
375 // through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
376 for i := range fd {
377 if isLegacyWin7ConsoleHandle(fd[i]) {
378 fd[i] = 0
382 // The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
383 // to treat the entire list as empty, so remove NULL handles.
384 j := 0
385 for i := range fd {
386 if fd[i] != 0 {
387 fd[j] = fd[i]
391 fd = fd[:j]
393 willInheritHandles := len(fd) > 0 && !sys.NoInheritHandles
395 // Do not accidentally inherit more than these handles.
396 if willInheritHandles {
397 err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
398 if err != nil {
399 return 0, 0, err
403 pi := new(ProcessInformation)
404 flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT
405 if sys.Token != 0 {
406 err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
407 } else {
408 err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, createEnvBlock(attr.Env), dirp, &si.StartupInfo, pi)
410 if err != nil {
411 return 0, 0, err
413 defer CloseHandle(Handle(pi.Thread))
414 runtime.KeepAlive(fd)
415 runtime.KeepAlive(sys)
417 return int(pi.ProcessId), uintptr(pi.Process), nil
420 func Exec(argv0 string, argv []string, envv []string) (err error) {
421 return EWINDOWS