Source file
src/syscall/exec_windows.go
1
2
3
4
5
6
7 package syscall
8
9 import (
10 "internal/bytealg"
11 "runtime"
12 "sync"
13 "unicode/utf16"
14 "unsafe"
15 )
16
17 var ForkLock sync.RWMutex
18
19
20
21
22
23
24
25
26
27
28 func EscapeArg(s string) string {
29 if len(s) == 0 {
30 return `""`
31 }
32 for i := 0; i < len(s); i++ {
33 switch s[i] {
34 case '"', '\\', ' ', '\t':
35
36 b := make([]byte, 0, len(s)+2)
37 b = appendEscapeArg(b, s)
38 return string(b)
39 }
40 }
41 return s
42 }
43
44
45
46 func appendEscapeArg(b []byte, s string) []byte {
47 if len(s) == 0 {
48 return append(b, `""`...)
49 }
50
51 needsBackslash := false
52 hasSpace := false
53 for i := 0; i < len(s); i++ {
54 switch s[i] {
55 case '"', '\\':
56 needsBackslash = true
57 case ' ', '\t':
58 hasSpace = true
59 }
60 }
61
62 if !needsBackslash && !hasSpace {
63
64 return append(b, s...)
65 }
66 if !needsBackslash {
67
68 b = append(b, '"')
69 b = append(b, s...)
70 return append(b, '"')
71 }
72
73 if hasSpace {
74 b = append(b, '"')
75 }
76 slashes := 0
77 for i := 0; i < len(s); i++ {
78 c := s[i]
79 switch c {
80 default:
81 slashes = 0
82 case '\\':
83 slashes++
84 case '"':
85 for ; slashes > 0; slashes-- {
86 b = append(b, '\\')
87 }
88 b = append(b, '\\')
89 }
90 b = append(b, c)
91 }
92 if hasSpace {
93 for ; slashes > 0; slashes-- {
94 b = append(b, '\\')
95 }
96 b = append(b, '"')
97 }
98
99 return b
100 }
101
102
103
104 func makeCmdLine(args []string) string {
105 var b []byte
106 for _, v := range args {
107 if len(b) > 0 {
108 b = append(b, ' ')
109 }
110 b = appendEscapeArg(b, v)
111 }
112 return string(b)
113 }
114
115
116
117
118
119
120 func createEnvBlock(envv []string) (*uint16, error) {
121 if len(envv) == 0 {
122 return &utf16.Encode([]rune("\x00\x00"))[0], nil
123 }
124 length := 0
125 for _, s := range envv {
126 if bytealg.IndexByteString(s, 0) != -1 {
127 return nil, EINVAL
128 }
129 length += len(s) + 1
130 }
131 length += 1
132
133 b := make([]byte, length)
134 i := 0
135 for _, s := range envv {
136 l := len(s)
137 copy(b[i:i+l], []byte(s))
138 copy(b[i+l:i+l+1], []byte{0})
139 i = i + l + 1
140 }
141 copy(b[i:i+1], []byte{0})
142
143 return &utf16.Encode([]rune(string(b)))[0], nil
144 }
145
146 func CloseOnExec(fd Handle) {
147 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
148 }
149
150 func SetNonblock(fd Handle, nonblocking bool) (err error) {
151 return nil
152 }
153
154
155 func FullPath(name string) (path string, err error) {
156 p, err := UTF16PtrFromString(name)
157 if err != nil {
158 return "", err
159 }
160 n := uint32(100)
161 for {
162 buf := make([]uint16, n)
163 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
164 if err != nil {
165 return "", err
166 }
167 if n <= uint32(len(buf)) {
168 return UTF16ToString(buf[:n]), nil
169 }
170 }
171 }
172
173 func isSlash(c uint8) bool {
174 return c == '\\' || c == '/'
175 }
176
177 func normalizeDir(dir string) (name string, err error) {
178 ndir, err := FullPath(dir)
179 if err != nil {
180 return "", err
181 }
182 if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
183
184 return "", EINVAL
185 }
186 return ndir, nil
187 }
188
189 func volToUpper(ch int) int {
190 if 'a' <= ch && ch <= 'z' {
191 ch += 'A' - 'a'
192 }
193 return ch
194 }
195
196 func joinExeDirAndFName(dir, p string) (name string, err error) {
197 if len(p) == 0 {
198 return "", EINVAL
199 }
200 if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
201
202 return p, nil
203 }
204 if len(p) > 1 && p[1] == ':' {
205
206 if len(p) == 2 {
207 return "", EINVAL
208 }
209 if isSlash(p[2]) {
210 return p, nil
211 } else {
212 d, err := normalizeDir(dir)
213 if err != nil {
214 return "", err
215 }
216 if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
217 return FullPath(d + "\\" + p[2:])
218 } else {
219 return FullPath(p)
220 }
221 }
222 } else {
223
224 d, err := normalizeDir(dir)
225 if err != nil {
226 return "", err
227 }
228 if isSlash(p[0]) {
229 return FullPath(d[:2] + p)
230 } else {
231 return FullPath(d + "\\" + p)
232 }
233 }
234 }
235
236 type ProcAttr struct {
237 Dir string
238 Env []string
239 Files []uintptr
240 Sys *SysProcAttr
241 }
242
243 type SysProcAttr struct {
244 HideWindow bool
245 CmdLine string
246 CreationFlags uint32
247 Token Token
248 ProcessAttributes *SecurityAttributes
249 ThreadAttributes *SecurityAttributes
250 NoInheritHandles bool
251 AdditionalInheritedHandles []Handle
252 ParentProcess Handle
253 }
254
255 var zeroProcAttr ProcAttr
256 var zeroSysProcAttr SysProcAttr
257
258 func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
259 if len(argv0) == 0 {
260 return 0, 0, EWINDOWS
261 }
262 if attr == nil {
263 attr = &zeroProcAttr
264 }
265 sys := attr.Sys
266 if sys == nil {
267 sys = &zeroSysProcAttr
268 }
269
270 if len(attr.Files) > 3 {
271 return 0, 0, EWINDOWS
272 }
273 if len(attr.Files) < 3 {
274 return 0, 0, EINVAL
275 }
276
277 if len(attr.Dir) != 0 {
278
279
280
281
282
283
284 var err error
285 argv0, err = joinExeDirAndFName(attr.Dir, argv0)
286 if err != nil {
287 return 0, 0, err
288 }
289 }
290 argv0p, err := UTF16PtrFromString(argv0)
291 if err != nil {
292 return 0, 0, err
293 }
294
295 var cmdline string
296
297
298
299 if sys.CmdLine != "" {
300 cmdline = sys.CmdLine
301 } else {
302 cmdline = makeCmdLine(argv)
303 }
304
305 var argvp *uint16
306 if len(cmdline) != 0 {
307 argvp, err = UTF16PtrFromString(cmdline)
308 if err != nil {
309 return 0, 0, err
310 }
311 }
312
313 var dirp *uint16
314 if len(attr.Dir) != 0 {
315 dirp, err = UTF16PtrFromString(attr.Dir)
316 if err != nil {
317 return 0, 0, err
318 }
319 }
320
321 var maj, min, build uint32
322 rtlGetNtVersionNumbers(&maj, &min, &build)
323 isWin7 := maj < 6 || (maj == 6 && min <= 1)
324
325
326
327
328
329
330 isLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }
331
332 p, _ := GetCurrentProcess()
333 parentProcess := p
334 if sys.ParentProcess != 0 {
335 parentProcess = sys.ParentProcess
336 }
337 fd := make([]Handle, len(attr.Files))
338 for i := range attr.Files {
339 if attr.Files[i] > 0 {
340 destinationProcessHandle := parentProcess
341
342
343
344 if parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {
345 destinationProcessHandle = p
346 }
347
348 err := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
349 if err != nil {
350 return 0, 0, err
351 }
352 defer DuplicateHandle(parentProcess, fd[i], 0, nil, 0, false, DUPLICATE_CLOSE_SOURCE)
353 }
354 }
355 si := new(_STARTUPINFOEXW)
356 si.ProcThreadAttributeList, err = newProcThreadAttributeList(2)
357 if err != nil {
358 return 0, 0, err
359 }
360 defer deleteProcThreadAttributeList(si.ProcThreadAttributeList)
361 si.Cb = uint32(unsafe.Sizeof(*si))
362 si.Flags = STARTF_USESTDHANDLES
363 if sys.HideWindow {
364 si.Flags |= STARTF_USESHOWWINDOW
365 si.ShowWindow = SW_HIDE
366 }
367 if sys.ParentProcess != 0 {
368 err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, unsafe.Pointer(&sys.ParentProcess), unsafe.Sizeof(sys.ParentProcess), nil, nil)
369 if err != nil {
370 return 0, 0, err
371 }
372 }
373 si.StdInput = fd[0]
374 si.StdOutput = fd[1]
375 si.StdErr = fd[2]
376
377 fd = append(fd, sys.AdditionalInheritedHandles...)
378
379
380
381 for i := range fd {
382 if isLegacyWin7ConsoleHandle(fd[i]) {
383 fd[i] = 0
384 }
385 }
386
387
388
389 j := 0
390 for i := range fd {
391 if fd[i] != 0 {
392 fd[j] = fd[i]
393 j++
394 }
395 }
396 fd = fd[:j]
397
398 willInheritHandles := len(fd) > 0 && !sys.NoInheritHandles
399
400
401 if willInheritHandles {
402 err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
403 if err != nil {
404 return 0, 0, err
405 }
406 }
407
408 envBlock, err := createEnvBlock(attr.Env)
409 if err != nil {
410 return 0, 0, err
411 }
412
413 pi := new(ProcessInformation)
414 flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT
415 if sys.Token != 0 {
416 err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, envBlock, dirp, &si.StartupInfo, pi)
417 } else {
418 err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, envBlock, dirp, &si.StartupInfo, pi)
419 }
420 if err != nil {
421 return 0, 0, err
422 }
423 defer CloseHandle(Handle(pi.Thread))
424 runtime.KeepAlive(fd)
425 runtime.KeepAlive(sys)
426
427 return int(pi.ProcessId), uintptr(pi.Process), nil
428 }
429
430 func Exec(argv0 string, argv []string, envv []string) (err error) {
431 return EWINDOWS
432 }
433
View as plain text