66
|
1 // 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
|
|
7 package windows
|
|
8
|
|
9 import (
|
|
10 errorspkg "errors"
|
|
11 "unsafe"
|
|
12 )
|
|
13
|
|
14 // EscapeArg rewrites command line argument s as prescribed
|
|
15 // in http://msdn.microsoft.com/en-us/library/ms880421.
|
|
16 // This function returns "" (2 double quotes) if s is empty.
|
|
17 // Alternatively, these transformations are done:
|
|
18 // - every back slash (\) is doubled, but only if immediately
|
|
19 // followed by double quote (");
|
|
20 // - every double quote (") is escaped by back slash (\);
|
|
21 // - finally, s is wrapped with double quotes (arg -> "arg"),
|
|
22 // but only if there is space or tab inside s.
|
|
23 func EscapeArg(s string) string {
|
|
24 if len(s) == 0 {
|
|
25 return "\"\""
|
|
26 }
|
|
27 n := len(s)
|
|
28 hasSpace := false
|
|
29 for i := 0; i < len(s); i++ {
|
|
30 switch s[i] {
|
|
31 case '"', '\\':
|
|
32 n++
|
|
33 case ' ', '\t':
|
|
34 hasSpace = true
|
|
35 }
|
|
36 }
|
|
37 if hasSpace {
|
|
38 n += 2
|
|
39 }
|
|
40 if n == len(s) {
|
|
41 return s
|
|
42 }
|
|
43
|
|
44 qs := make([]byte, n)
|
|
45 j := 0
|
|
46 if hasSpace {
|
|
47 qs[j] = '"'
|
|
48 j++
|
|
49 }
|
|
50 slashes := 0
|
|
51 for i := 0; i < len(s); i++ {
|
|
52 switch s[i] {
|
|
53 default:
|
|
54 slashes = 0
|
|
55 qs[j] = s[i]
|
|
56 case '\\':
|
|
57 slashes++
|
|
58 qs[j] = s[i]
|
|
59 case '"':
|
|
60 for ; slashes > 0; slashes-- {
|
|
61 qs[j] = '\\'
|
|
62 j++
|
|
63 }
|
|
64 qs[j] = '\\'
|
|
65 j++
|
|
66 qs[j] = s[i]
|
|
67 }
|
|
68 j++
|
|
69 }
|
|
70 if hasSpace {
|
|
71 for ; slashes > 0; slashes-- {
|
|
72 qs[j] = '\\'
|
|
73 j++
|
|
74 }
|
|
75 qs[j] = '"'
|
|
76 j++
|
|
77 }
|
|
78 return string(qs[:j])
|
|
79 }
|
|
80
|
|
81 // ComposeCommandLine escapes and joins the given arguments suitable for use as a Windows command line,
|
|
82 // in CreateProcess's CommandLine argument, CreateService/ChangeServiceConfig's BinaryPathName argument,
|
|
83 // or any program that uses CommandLineToArgv.
|
|
84 func ComposeCommandLine(args []string) string {
|
|
85 var commandLine string
|
|
86 for i := range args {
|
|
87 if i > 0 {
|
|
88 commandLine += " "
|
|
89 }
|
|
90 commandLine += EscapeArg(args[i])
|
|
91 }
|
|
92 return commandLine
|
|
93 }
|
|
94
|
|
95 // DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv,
|
|
96 // as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that
|
|
97 // command lines are passed around.
|
|
98 func DecomposeCommandLine(commandLine string) ([]string, error) {
|
|
99 if len(commandLine) == 0 {
|
|
100 return []string{}, nil
|
|
101 }
|
|
102 var argc int32
|
|
103 argv, err := CommandLineToArgv(StringToUTF16Ptr(commandLine), &argc)
|
|
104 if err != nil {
|
|
105 return nil, err
|
|
106 }
|
|
107 defer LocalFree(Handle(unsafe.Pointer(argv)))
|
|
108 var args []string
|
|
109 for _, v := range (*argv)[:argc] {
|
|
110 args = append(args, UTF16ToString((*v)[:]))
|
|
111 }
|
|
112 return args, nil
|
|
113 }
|
|
114
|
|
115 func CloseOnExec(fd Handle) {
|
|
116 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
|
117 }
|
|
118
|
|
119 // FullPath retrieves the full path of the specified file.
|
|
120 func FullPath(name string) (path string, err error) {
|
|
121 p, err := UTF16PtrFromString(name)
|
|
122 if err != nil {
|
|
123 return "", err
|
|
124 }
|
|
125 n := uint32(100)
|
|
126 for {
|
|
127 buf := make([]uint16, n)
|
|
128 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
|
|
129 if err != nil {
|
|
130 return "", err
|
|
131 }
|
|
132 if n <= uint32(len(buf)) {
|
|
133 return UTF16ToString(buf[:n]), nil
|
|
134 }
|
|
135 }
|
|
136 }
|
|
137
|
|
138 // NewProcThreadAttributeList allocates a new ProcThreadAttributeListContainer, with the requested maximum number of attributes.
|
|
139 func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListContainer, error) {
|
|
140 var size uintptr
|
|
141 err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size)
|
|
142 if err != ERROR_INSUFFICIENT_BUFFER {
|
|
143 if err == nil {
|
|
144 return nil, errorspkg.New("unable to query buffer size from InitializeProcThreadAttributeList")
|
|
145 }
|
|
146 return nil, err
|
|
147 }
|
|
148 alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
|
|
149 if err != nil {
|
|
150 return nil, err
|
|
151 }
|
|
152 // size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
|
|
153 al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(alloc))}
|
|
154 err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size)
|
|
155 if err != nil {
|
|
156 return nil, err
|
|
157 }
|
|
158 return al, err
|
|
159 }
|
|
160
|
|
161 // Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
|
|
162 func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error {
|
|
163 al.pointers = append(al.pointers, value)
|
|
164 return updateProcThreadAttribute(al.data, 0, attribute, value, size, nil, nil)
|
|
165 }
|
|
166
|
|
167 // Delete frees ProcThreadAttributeList's resources.
|
|
168 func (al *ProcThreadAttributeListContainer) Delete() {
|
|
169 deleteProcThreadAttributeList(al.data)
|
|
170 LocalFree(Handle(unsafe.Pointer(al.data)))
|
|
171 al.data = nil
|
|
172 al.pointers = nil
|
|
173 }
|
|
174
|
|
175 // List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx.
|
|
176 func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList {
|
|
177 return al.data
|
|
178 }
|