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.
15 var ForkLock sync
.RWMutex
17 // EscapeArg rewrites command line argument s as prescribed
18 // in https://msdn.microsoft.com/en-us/library/ms880421.
19 // This function returns "" (2 double quotes) if s is empty.
20 // Alternatively, these transformations are done:
21 // - every back slash (\) is doubled, but only if immediately
22 // followed by double quote (");
23 // - every double quote (") is escaped by back slash (\);
24 // - finally, s is wrapped with double quotes (arg -> "arg"),
25 // but only if there is space or tab inside s.
26 func EscapeArg(s
string) string {
32 for i
:= 0; i
< len(s
); i
++ {
54 for i
:= 0; i
< len(s
); i
++ {
63 for ; slashes
> 0; slashes
-- {
74 for ; slashes
> 0; slashes
-- {
84 // makeCmdLine builds a command line out of args by escaping "special"
85 // characters and joining the arguments with spaces.
86 func makeCmdLine(args
[]string) string {
88 for _
, v
:= range args
{
97 // createEnvBlock converts an array of environment strings into
98 // the representation required by CreateProcess: a sequence of NUL
99 // terminated strings followed by a nil.
100 // Last bytes are two UCS-2 NULs, or four NUL bytes.
101 func createEnvBlock(envv
[]string) *uint16 {
103 return &utf16
.Encode([]rune("\x00\x00"))[0]
106 for _
, s
:= range envv
{
111 b
:= make([]byte, length
)
113 for _
, s
:= range envv
{
115 copy(b
[i
:i
+l
], []byte(s
))
116 copy(b
[i
+l
:i
+l
+1], []byte{0})
119 copy(b
[i
:i
+1], []byte{0})
121 return &utf16
.Encode([]rune(string(b
)))[0]
124 func CloseOnExec(fd Handle
) {
125 SetHandleInformation(Handle(fd
), HANDLE_FLAG_INHERIT
, 0)
128 func SetNonblock(fd Handle
, nonblocking
bool) (err error
) {
132 // FullPath retrieves the full path of the specified file.
133 func FullPath(name
string) (path
string, err error
) {
134 p
, err
:= UTF16PtrFromString(name
)
140 buf
:= make([]uint16, n
)
141 n
, err
= GetFullPathName(p
, uint32(len(buf
)), &buf
[0], nil)
145 if n
<= uint32(len(buf
)) {
146 return UTF16ToString(buf
[:n
]), nil
151 func isSlash(c
uint8) bool {
152 return c
== '\\' || c
== '/'
155 func normalizeDir(dir
string) (name
string, err error
) {
156 ndir
, err
:= FullPath(dir
)
160 if len(ndir
) > 2 && isSlash(ndir
[0]) && isSlash(ndir
[1]) {
161 // dir cannot have \\server\share\path form
167 func volToUpper(ch
int) int {
168 if 'a' <= ch
&& ch
<= 'z' {
174 func joinExeDirAndFName(dir
, p
string) (name
string, err error
) {
178 if len(p
) > 2 && isSlash(p
[0]) && isSlash(p
[1]) {
179 // \\server\share\path form
182 if len(p
) > 1 && p
[1] == ':' {
190 d
, err
:= normalizeDir(dir
)
194 if volToUpper(int(p
[0])) == volToUpper(int(d
[0])) {
195 return FullPath(d
+ "\\" + p
[2:])
202 d
, err
:= normalizeDir(dir
)
207 return FullPath(d
[:2] + p
)
209 return FullPath(d
+ "\\" + p
)
214 type ProcAttr
struct {
221 type SysProcAttr
struct {
223 CmdLine
string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
225 Token Token
// if set, runs new process in the security context represented by the token
226 ProcessAttributes
*SecurityAttributes
// if set, applies these security attributes as the descriptor for the new process
227 ThreadAttributes
*SecurityAttributes
// if set, applies these security attributes as the descriptor for the main thread of the new process
230 var zeroProcAttr ProcAttr
231 var zeroSysProcAttr SysProcAttr
233 func StartProcess(argv0
string, argv
[]string, attr
*ProcAttr
) (pid
int, handle
uintptr, err error
) {
235 return 0, 0, EWINDOWS
242 sys
= &zeroSysProcAttr
245 if len(attr
.Files
) > 3 {
246 return 0, 0, EWINDOWS
248 if len(attr
.Files
) < 3 {
252 if len(attr
.Dir
) != 0 {
253 // StartProcess assumes that argv0 is relative to attr.Dir,
254 // because it implies Chdir(attr.Dir) before executing argv0.
255 // Windows CreateProcess assumes the opposite: it looks for
256 // argv0 relative to the current directory, and, only once the new
257 // process is started, it does Chdir(attr.Dir). We are adjusting
258 // for that difference here by making argv0 absolute.
260 argv0
, err
= joinExeDirAndFName(attr
.Dir
, argv0
)
265 argv0p
, err
:= UTF16PtrFromString(argv0
)
271 // Windows CreateProcess takes the command line as a single string:
272 // use attr.CmdLine if set, else build the command line by escaping
273 // and joining each argument with spaces
274 if sys
.CmdLine
!= "" {
275 cmdline
= sys
.CmdLine
277 cmdline
= makeCmdLine(argv
)
281 if len(cmdline
) != 0 {
282 argvp
, err
= UTF16PtrFromString(cmdline
)
289 if len(attr
.Dir
) != 0 {
290 dirp
, err
= UTF16PtrFromString(attr
.Dir
)
296 // Acquire the fork lock so that no other threads
297 // create new fds that are not yet close-on-exec
300 defer ForkLock
.Unlock()
302 p
, _
:= GetCurrentProcess()
303 fd
:= make([]Handle
, len(attr
.Files
))
304 for i
:= range attr
.Files
{
305 if attr
.Files
[i
] > 0 {
306 err
:= DuplicateHandle(p
, Handle(attr
.Files
[i
]), p
, &fd
[i
], 0, true, DUPLICATE_SAME_ACCESS
)
310 defer CloseHandle(Handle(fd
[i
]))
313 si
:= new(StartupInfo
)
314 si
.Cb
= uint32(unsafe
.Sizeof(*si
))
315 si
.Flags
= STARTF_USESTDHANDLES
317 si
.Flags |
= STARTF_USESHOWWINDOW
318 si
.ShowWindow
= SW_HIDE
324 pi
:= new(ProcessInformation
)
326 flags
:= sys
.CreationFlags | CREATE_UNICODE_ENVIRONMENT
328 err
= CreateProcessAsUser(sys
.Token
, argv0p
, argvp
, sys
.ProcessAttributes
, sys
.ThreadAttributes
, true, flags
, createEnvBlock(attr
.Env
), dirp
, si
, pi
)
330 err
= CreateProcess(argv0p
, argvp
, sys
.ProcessAttributes
, sys
.ThreadAttributes
, true, flags
, createEnvBlock(attr
.Env
), dirp
, si
, pi
)
335 defer CloseHandle(Handle(pi
.Thread
))
337 return int(pi
.ProcessId
), uintptr(pi
.Process
), nil
340 func Exec(argv0
string, argv
[]string, envv
[]string) (err error
) {