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.
5 // Fork, exec, wait, etc.
15 var ForkLock sync.RWMutex
17 // EscapeArg rewrites command line argument s as prescribed
18 // in http://msdn.microsoft.com/en-us/library/ms880421.
19 // This function returns "" (2 double quotes) if s is empty.
20 // Alternatively, these transformations are done:
21 // - every back slash (\) is doubled, but only if immediately
22 // followed by double quote (");
23 // - every double quote (") is escaped by back slash (\);
24 // - finally, s is wrapped with double quotes (arg -> "arg"),
25 // but only if there is space or tab inside s.
26 func EscapeArg(s string) string {
32 for i := 0; i < len(s); i++ {
54 for i := 0; i < len(s); i++ {
63 for ; slashes > 0; slashes-- {
74 for ; slashes > 0; slashes-- {
84 // makeCmdLine builds a command line out of args by escaping "special"
85 // characters and joining the arguments with spaces.
86 func makeCmdLine(args []string) string {
88 for _, v := range args {
97 // createEnvBlock converts an array of environment strings into
98 // the representation required by CreateProcess: a sequence of NUL
99 // terminated strings followed by a nil.
100 // Last bytes are two UCS-2 NULs, or four NUL bytes.
101 func createEnvBlock(envv []string) *uint16 {
103 return &utf16.Encode([]rune("\x00\x00"))[0]
106 for _, s := range envv {
111 b := make([]byte, length)
113 for _, s := range envv {
115 copy(b[i:i+l], []byte(s))
116 copy(b[i+l:i+l+1], []byte{0})
119 copy(b[i:i+1], []byte{0})
121 return &utf16.Encode([]rune(string(b)))[0]
124 func CloseOnExec(fd Handle) {
125 SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
128 func SetNonblock(fd Handle, nonblocking bool) (err error) {
132 // getFullPath retrieves the full path of the specified file.
133 // Just a wrapper for Windows GetFullPathName api.
134 func getFullPath(name string) (path string, err error) {
135 p, err := utf16PtrFromString(name)
139 buf := make([]uint16, 100)
140 n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
144 if n > uint32(len(buf)) {
145 // Windows is asking for bigger buffer.
146 buf = make([]uint16, n)
147 n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
151 if n > uint32(len(buf)) {
155 return UTF16ToString(buf[:n]), nil
158 func isSlash(c uint8) bool {
159 return c == '\\' || c == '/'
162 func normalizeDir(dir string) (name string, err error) {
163 ndir, err := getFullPath(dir)
167 if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
168 // dir cannot have \\server\share\path form
174 func volToUpper(ch int) int {
175 if 'a' <= ch && ch <= 'z' {
181 func joinExeDirAndFName(dir, p string) (name string, err error) {
185 if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
186 // \\server\share\path form
189 if len(p) > 1 && p[1] == ':' {
197 d, err := normalizeDir(dir)
201 if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
202 return getFullPath(d + "\\" + p[2:])
204 return getFullPath(p)
209 d, err := normalizeDir(dir)
214 return getFullPath(d[:2] + p)
216 return getFullPath(d + "\\" + p)
219 // we shouldn't be here
223 type ProcAttr struct {
230 type SysProcAttr struct {
232 CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
235 var zeroProcAttr ProcAttr
236 var zeroSysProcAttr SysProcAttr
238 func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
240 return 0, 0, EWINDOWS
247 sys = &zeroSysProcAttr
250 if len(attr.Files) > 3 {
251 return 0, 0, EWINDOWS
254 if len(attr.Dir) != 0 {
255 // StartProcess assumes that argv0 is relative to attr.Dir,
256 // because it implies Chdir(attr.Dir) before executing argv0.
257 // Windows CreateProcess assumes the opposite: it looks for
258 // argv0 relative to the current directory, and, only once the new
259 // process is started, it does Chdir(attr.Dir). We are adjusting
260 // for that difference here by making argv0 absolute.
262 argv0, err = joinExeDirAndFName(attr.Dir, argv0)
267 argv0p, err := utf16PtrFromString(argv0)
273 // Windows CreateProcess takes the command line as a single string:
274 // use attr.CmdLine if set, else build the command line by escaping
275 // and joining each argument with spaces
276 if sys.CmdLine != "" {
277 cmdline = sys.CmdLine
279 cmdline = makeCmdLine(argv)
283 if len(cmdline) != 0 {
284 argvp, err = utf16PtrFromString(cmdline)
291 if len(attr.Dir) != 0 {
292 dirp, err = utf16PtrFromString(attr.Dir)
298 // Acquire the fork lock so that no other threads
299 // create new fds that are not yet close-on-exec
302 defer ForkLock.Unlock()
304 p, _ := GetCurrentProcess()
305 fd := make([]Handle, len(attr.Files))
306 for i := range attr.Files {
307 if attr.Files[i] > 0 {
308 err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
312 defer CloseHandle(Handle(fd[i]))
315 si := new(StartupInfo)
316 si.Cb = uint32(unsafe.Sizeof(*si))
317 si.Flags = STARTF_USESTDHANDLES
319 si.Flags |= STARTF_USESHOWWINDOW
320 si.ShowWindow = SW_HIDE
326 pi := new(ProcessInformation)
328 err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi)
332 defer CloseHandle(Handle(pi.Thread))
334 return int(pi.ProcessId), uintptr(pi.Process), nil
337 func Exec(argv0 string, argv []string, envv []string) (err error) {