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