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