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