blob: 91b0e84857d5b8c5c41d22945104fbf5c62d6459 [file] [log] [blame]
Daniel Theophanes7f9e2472010-07-26 09:43:35 +10001// 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.
4
5// Fork, exec, wait, etc.
6
7package syscall
8
9import (
10 "sync"
Rob Pike45e3bcb2011-11-08 15:41:54 -080011 "unicode/utf16"
Alex Brainman5aad5142011-04-21 10:36:27 +100012 "unsafe"
Daniel Theophanes7f9e2472010-07-26 09:43:35 +100013)
14
Daniel Theophanes7f9e2472010-07-26 09:43:35 +100015var ForkLock sync.RWMutex
16
Vincent Vanackeref18a4e92011-05-31 10:21:38 -040017// EscapeArg rewrites command line argument s as prescribed
Alex Brainman913c8d72011-03-24 11:20:28 +110018// in http://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.
Vincent Vanackeref18a4e92011-05-31 10:21:38 -040026func EscapeArg(s string) string {
Alex Brainman913c8d72011-03-24 11:20:28 +110027 if len(s) == 0 {
28 return "\"\""
Daniel Theophanes7f9e2472010-07-26 09:43:35 +100029 }
Alex Brainman913c8d72011-03-24 11:20:28 +110030 n := len(s)
31 hasSpace := false
32 for i := 0; i < len(s); i++ {
33 switch s[i] {
34 case '"', '\\':
35 n++
36 case ' ', '\t':
37 hasSpace = true
38 }
Daniel Theophanes7f9e2472010-07-26 09:43:35 +100039 }
Alex Brainman913c8d72011-03-24 11:20:28 +110040 if hasSpace {
41 n += 2
42 }
43 if n == len(s) {
44 return s
Daniel Theophanes7f9e2472010-07-26 09:43:35 +100045 }
46
Alex Brainman913c8d72011-03-24 11:20:28 +110047 qs := make([]byte, n)
48 j := 0
49 if hasSpace {
50 qs[j] = '"'
51 j++
Daniel Theophanes7f9e2472010-07-26 09:43:35 +100052 }
Alex Brainman913c8d72011-03-24 11:20:28 +110053 slashes := 0
54 for i := 0; i < len(s); i++ {
55 switch s[i] {
56 default:
57 slashes = 0
58 qs[j] = s[i]
59 case '\\':
60 slashes++
61 qs[j] = s[i]
62 case '"':
63 for ; slashes > 0; slashes-- {
64 qs[j] = '\\'
65 j++
66 }
67 qs[j] = '\\'
68 j++
69 qs[j] = s[i]
70 }
71 j++
72 }
73 if hasSpace {
74 for ; slashes > 0; slashes-- {
75 qs[j] = '\\'
76 j++
77 }
78 qs[j] = '"'
79 j++
80 }
81 return string(qs[:j])
Daniel Theophanes7f9e2472010-07-26 09:43:35 +100082}
83
Alex Brainman913c8d72011-03-24 11:20:28 +110084// makeCmdLine builds a command line out of args by escaping "special"
85// characters and joining the arguments with spaces.
86func makeCmdLine(args []string) string {
87 var s string
88 for _, v := range args {
89 if s != "" {
90 s += " "
91 }
Vincent Vanackeref18a4e92011-05-31 10:21:38 -040092 s += EscapeArg(v)
Alex Brainman913c8d72011-03-24 11:20:28 +110093 }
94 return s
95}
96
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.
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000101func createEnvBlock(envv []string) *uint16 {
102 if len(envv) == 0 {
Russ Coxdb339592011-10-25 22:20:02 -0700103 return &utf16.Encode([]rune("\x00\x00"))[0]
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000104 }
105 length := 0
106 for _, s := range envv {
107 length += len(s) + 1
108 }
109 length += 1
110
111 b := make([]byte, length)
112 i := 0
113 for _, s := range envv {
114 l := len(s)
115 copy(b[i:i+l], []byte(s))
116 copy(b[i+l:i+l+1], []byte{0})
117 i = i + l + 1
118 }
119 copy(b[i:i+1], []byte{0})
120
Russ Coxdb339592011-10-25 22:20:02 -0700121 return &utf16.Encode([]rune(string(b)))[0]
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000122}
123
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400124func CloseOnExec(fd Handle) {
125 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000126}
127
Russ Coxc017a822011-11-13 22:44:52 -0500128func SetNonblock(fd Handle, nonblocking bool) (err error) {
129 return nil
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000130}
131
Alex Brainman2de65ca2014-08-19 14:59:56 +1000132// FullPath retrieves the full path of the specified file.
133func FullPath(name string) (path string, err error) {
Alexey Borzenkova1083692012-08-05 17:24:32 -0400134 p, err := UTF16PtrFromString(name)
135 if err != nil {
136 return "", err
137 }
Alex Brainman32e75ba2015-02-13 16:12:07 +1100138 n := uint32(100)
139 for {
140 buf := make([]uint16, n)
Alex Brainman913c8d72011-03-24 11:20:28 +1100141 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
Russ Coxc017a822011-11-13 22:44:52 -0500142 if err != nil {
Alex Brainman913c8d72011-03-24 11:20:28 +1100143 return "", err
144 }
Alex Brainman32e75ba2015-02-13 16:12:07 +1100145 if n <= uint32(len(buf)) {
146 return UTF16ToString(buf[:n]), nil
Alex Brainman913c8d72011-03-24 11:20:28 +1100147 }
148 }
Alex Brainman913c8d72011-03-24 11:20:28 +1100149}
150
151func isSlash(c uint8) bool {
152 return c == '\\' || c == '/'
153}
154
Russ Coxc017a822011-11-13 22:44:52 -0500155func normalizeDir(dir string) (name string, err error) {
Alex Brainman2de65ca2014-08-19 14:59:56 +1000156 ndir, err := FullPath(dir)
Russ Coxc017a822011-11-13 22:44:52 -0500157 if err != nil {
Alex Brainman913c8d72011-03-24 11:20:28 +1100158 return "", err
159 }
160 if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
161 // dir cannot have \\server\share\path form
162 return "", EINVAL
163 }
Russ Coxc017a822011-11-13 22:44:52 -0500164 return ndir, nil
Alex Brainman913c8d72011-03-24 11:20:28 +1100165}
166
167func volToUpper(ch int) int {
168 if 'a' <= ch && ch <= 'z' {
169 ch += 'A' - 'a'
170 }
171 return ch
172}
173
Russ Coxc017a822011-11-13 22:44:52 -0500174func joinExeDirAndFName(dir, p string) (name string, err error) {
Alex Brainman913c8d72011-03-24 11:20:28 +1100175 if len(p) == 0 {
176 return "", EINVAL
177 }
178 if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
179 // \\server\share\path form
Russ Coxc017a822011-11-13 22:44:52 -0500180 return p, nil
Alex Brainman913c8d72011-03-24 11:20:28 +1100181 }
182 if len(p) > 1 && p[1] == ':' {
183 // has drive letter
184 if len(p) == 2 {
185 return "", EINVAL
186 }
187 if isSlash(p[2]) {
Russ Coxc017a822011-11-13 22:44:52 -0500188 return p, nil
Alex Brainman913c8d72011-03-24 11:20:28 +1100189 } else {
190 d, err := normalizeDir(dir)
Russ Coxc017a822011-11-13 22:44:52 -0500191 if err != nil {
Alex Brainman913c8d72011-03-24 11:20:28 +1100192 return "", err
193 }
194 if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
Alex Brainman2de65ca2014-08-19 14:59:56 +1000195 return FullPath(d + "\\" + p[2:])
Alex Brainman913c8d72011-03-24 11:20:28 +1100196 } else {
Alex Brainman2de65ca2014-08-19 14:59:56 +1000197 return FullPath(p)
Alex Brainman913c8d72011-03-24 11:20:28 +1100198 }
199 }
200 } else {
201 // no drive letter
202 d, err := normalizeDir(dir)
Russ Coxc017a822011-11-13 22:44:52 -0500203 if err != nil {
Alex Brainman913c8d72011-03-24 11:20:28 +1100204 return "", err
205 }
206 if isSlash(p[0]) {
Alex Brainman2de65ca2014-08-19 14:59:56 +1000207 return FullPath(d[:2] + p)
Alex Brainman913c8d72011-03-24 11:20:28 +1100208 } else {
Alex Brainman2de65ca2014-08-19 14:59:56 +1000209 return FullPath(d + "\\" + p)
Alex Brainman913c8d72011-03-24 11:20:28 +1100210 }
211 }
Alex Brainman913c8d72011-03-24 11:20:28 +1100212}
213
Roger Peppeaa55c052011-03-15 14:41:19 -0400214type ProcAttr struct {
Russ Cox4d0f2e92011-06-14 10:49:34 -0400215 Dir string
216 Env []string
Brad Fitzpatrickfbab6d82012-02-11 08:47:19 +1100217 Files []uintptr
Russ Cox4d0f2e92011-06-14 10:49:34 -0400218 Sys *SysProcAttr
219}
220
221type SysProcAttr struct {
Alex Brainman0d55d982012-05-02 17:05:52 +1000222 HideWindow bool
223 CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
224 CreationFlags uint32
Alex Brainman79f6c282017-11-07 12:09:59 +1100225 Token Token // if set, runs new process in the security context represented by the token
Roger Peppeaa55c052011-03-15 14:41:19 -0400226}
227
Russ Cox4d0f2e92011-06-14 10:49:34 -0400228var zeroProcAttr ProcAttr
229var zeroSysProcAttr SysProcAttr
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000230
Wei Guangjing16ce2f92012-02-02 14:08:48 -0500231func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
Alex Brainman913c8d72011-03-24 11:20:28 +1100232 if len(argv0) == 0 {
233 return 0, 0, EWINDOWS
234 }
Roger Peppeaa55c052011-03-15 14:41:19 -0400235 if attr == nil {
Russ Cox4d0f2e92011-06-14 10:49:34 -0400236 attr = &zeroProcAttr
Roger Peppeaa55c052011-03-15 14:41:19 -0400237 }
Russ Cox4d0f2e92011-06-14 10:49:34 -0400238 sys := attr.Sys
239 if sys == nil {
240 sys = &zeroSysProcAttr
241 }
242
Roger Peppeaa55c052011-03-15 14:41:19 -0400243 if len(attr.Files) > 3 {
Alex Brainman4ecebfe2011-02-04 14:41:26 +1100244 return 0, 0, EWINDOWS
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000245 }
Alex Brainman0bafe0e2015-06-29 11:19:33 +1000246 if len(attr.Files) < 3 {
247 return 0, 0, EINVAL
248 }
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000249
Alex Brainman913c8d72011-03-24 11:20:28 +1100250 if len(attr.Dir) != 0 {
251 // StartProcess assumes that argv0 is relative to attr.Dir,
252 // because it implies Chdir(attr.Dir) before executing argv0.
253 // Windows CreateProcess assumes the opposite: it looks for
254 // argv0 relative to the current directory, and, only once the new
255 // process is started, it does Chdir(attr.Dir). We are adjusting
256 // for that difference here by making argv0 absolute.
Russ Coxc017a822011-11-13 22:44:52 -0500257 var err error
Alex Brainman913c8d72011-03-24 11:20:28 +1100258 argv0, err = joinExeDirAndFName(attr.Dir, argv0)
Russ Coxc017a822011-11-13 22:44:52 -0500259 if err != nil {
Alex Brainman913c8d72011-03-24 11:20:28 +1100260 return 0, 0, err
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000261 }
262 }
Alexey Borzenkova1083692012-08-05 17:24:32 -0400263 argv0p, err := UTF16PtrFromString(argv0)
264 if err != nil {
265 return 0, 0, err
266 }
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000267
Vincent Vanackeref18a4e92011-05-31 10:21:38 -0400268 var cmdline string
269 // Windows CreateProcess takes the command line as a single string:
270 // use attr.CmdLine if set, else build the command line by escaping
271 // and joining each argument with spaces
Russ Cox4d0f2e92011-06-14 10:49:34 -0400272 if sys.CmdLine != "" {
273 cmdline = sys.CmdLine
Vincent Vanackeref18a4e92011-05-31 10:21:38 -0400274 } else {
275 cmdline = makeCmdLine(argv)
276 }
277
Alex Brainman913c8d72011-03-24 11:20:28 +1100278 var argvp *uint16
Vincent Vanackeref18a4e92011-05-31 10:21:38 -0400279 if len(cmdline) != 0 {
Alexey Borzenkova1083692012-08-05 17:24:32 -0400280 argvp, err = UTF16PtrFromString(cmdline)
281 if err != nil {
282 return 0, 0, err
283 }
Alex Brainman913c8d72011-03-24 11:20:28 +1100284 }
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000285
Alex Brainman913c8d72011-03-24 11:20:28 +1100286 var dirp *uint16
287 if len(attr.Dir) != 0 {
Alexey Borzenkova1083692012-08-05 17:24:32 -0400288 dirp, err = UTF16PtrFromString(attr.Dir)
289 if err != nil {
290 return 0, 0, err
291 }
Alex Brainman913c8d72011-03-24 11:20:28 +1100292 }
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000293
Hector Chufb9fc882011-02-03 12:50:41 +1100294 // Acquire the fork lock so that no other threads
295 // create new fds that are not yet close-on-exec
296 // before we fork.
297 ForkLock.Lock()
298 defer ForkLock.Unlock()
299
Alex Brainman913c8d72011-03-24 11:20:28 +1100300 p, _ := GetCurrentProcess()
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400301 fd := make([]Handle, len(attr.Files))
Robert Griesemera2e28682011-04-13 15:13:59 -0700302 for i := range attr.Files {
Alex Brainman913c8d72011-03-24 11:20:28 +1100303 if attr.Files[i] > 0 {
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400304 err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
Russ Coxc017a822011-11-13 22:44:52 -0500305 if err != nil {
Alex Brainman913c8d72011-03-24 11:20:28 +1100306 return 0, 0, err
307 }
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400308 defer CloseHandle(Handle(fd[i]))
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000309 }
310 }
Alex Brainman913c8d72011-03-24 11:20:28 +1100311 si := new(StartupInfo)
Alex Brainman5aad5142011-04-21 10:36:27 +1000312 si.Cb = uint32(unsafe.Sizeof(*si))
Alex Brainman913c8d72011-03-24 11:20:28 +1100313 si.Flags = STARTF_USESTDHANDLES
Russ Cox4d0f2e92011-06-14 10:49:34 -0400314 if sys.HideWindow {
Vincent Vanackere1f590042011-04-22 00:12:06 +1000315 si.Flags |= STARTF_USESHOWWINDOW
316 si.ShowWindow = SW_HIDE
317 }
Alex Brainman913c8d72011-03-24 11:20:28 +1100318 si.StdInput = fd[0]
319 si.StdOutput = fd[1]
320 si.StdErr = fd[2]
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000321
Alex Brainman913c8d72011-03-24 11:20:28 +1100322 pi := new(ProcessInformation)
323
Alex Brainman0d55d982012-05-02 17:05:52 +1000324 flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT
Paul Quernabb983312017-11-01 15:11:52 -0700325 if sys.Token != 0 {
326 err = CreateProcessAsUser(sys.Token, argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
327 } else {
328 err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
329 }
Russ Coxc017a822011-11-13 22:44:52 -0500330 if err != nil {
Alex Brainman913c8d72011-03-24 11:20:28 +1100331 return 0, 0, err
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000332 }
Wei Guangjing63b8b9482011-07-01 10:18:07 -0400333 defer CloseHandle(Handle(pi.Thread))
Alex Brainman913c8d72011-03-24 11:20:28 +1100334
Wei Guangjing16ce2f92012-02-02 14:08:48 -0500335 return int(pi.ProcessId), uintptr(pi.Process), nil
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000336}
337
Russ Coxc017a822011-11-13 22:44:52 -0500338func Exec(argv0 string, argv []string, envv []string) (err error) {
Daniel Theophanes7f9e2472010-07-26 09:43:35 +1000339 return EWINDOWS
340}