exec_windows.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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. // Fork, exec, wait, etc.
  5. package windows
  6. import (
  7. "sync"
  8. "syscall"
  9. "unicode/utf16"
  10. "unsafe"
  11. )
  12. var ForkLock sync.RWMutex
  13. // EscapeArg rewrites command line argument s as prescribed
  14. // in http://msdn.microsoft.com/en-us/library/ms880421.
  15. // This function returns "" (2 double quotes) if s is empty.
  16. // Alternatively, these transformations are done:
  17. // - every back slash (\) is doubled, but only if immediately
  18. // followed by double quote (");
  19. // - every double quote (") is escaped by back slash (\);
  20. // - finally, s is wrapped with double quotes (arg -> "arg"),
  21. // but only if there is space or tab inside s.
  22. func EscapeArg(s string) string {
  23. if len(s) == 0 {
  24. return "\"\""
  25. }
  26. n := len(s)
  27. hasSpace := false
  28. for i := 0; i < len(s); i++ {
  29. switch s[i] {
  30. case '"', '\\':
  31. n++
  32. case ' ', '\t':
  33. hasSpace = true
  34. }
  35. }
  36. if hasSpace {
  37. n += 2
  38. }
  39. if n == len(s) {
  40. return s
  41. }
  42. qs := make([]byte, n)
  43. j := 0
  44. if hasSpace {
  45. qs[j] = '"'
  46. j++
  47. }
  48. slashes := 0
  49. for i := 0; i < len(s); i++ {
  50. switch s[i] {
  51. default:
  52. slashes = 0
  53. qs[j] = s[i]
  54. case '\\':
  55. slashes++
  56. qs[j] = s[i]
  57. case '"':
  58. for ; slashes > 0; slashes-- {
  59. qs[j] = '\\'
  60. j++
  61. }
  62. qs[j] = '\\'
  63. j++
  64. qs[j] = s[i]
  65. }
  66. j++
  67. }
  68. if hasSpace {
  69. for ; slashes > 0; slashes-- {
  70. qs[j] = '\\'
  71. j++
  72. }
  73. qs[j] = '"'
  74. j++
  75. }
  76. return string(qs[:j])
  77. }
  78. // makeCmdLine builds a command line out of args by escaping "special"
  79. // characters and joining the arguments with spaces.
  80. func makeCmdLine(args []string) string {
  81. var s string
  82. for _, v := range args {
  83. if s != "" {
  84. s += " "
  85. }
  86. s += EscapeArg(v)
  87. }
  88. return s
  89. }
  90. // createEnvBlock converts an array of environment strings into
  91. // the representation required by CreateProcess: a sequence of NUL
  92. // terminated strings followed by a nil.
  93. // Last bytes are two UCS-2 NULs, or four NUL bytes.
  94. func createEnvBlock(envv []string) *uint16 {
  95. if len(envv) == 0 {
  96. return &utf16.Encode([]rune("\x00\x00"))[0]
  97. }
  98. length := 0
  99. for _, s := range envv {
  100. length += len(s) + 1
  101. }
  102. length += 1
  103. b := make([]byte, length)
  104. i := 0
  105. for _, s := range envv {
  106. l := len(s)
  107. copy(b[i:i+l], []byte(s))
  108. copy(b[i+l:i+l+1], []byte{0})
  109. i = i + l + 1
  110. }
  111. copy(b[i:i+1], []byte{0})
  112. return &utf16.Encode([]rune(string(b)))[0]
  113. }
  114. func CloseOnExec(fd Handle) {
  115. SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
  116. }
  117. func SetNonblock(fd Handle, nonblocking bool) (err error) {
  118. return nil
  119. }
  120. // getFullPath retrieves the full path of the specified file.
  121. // Just a wrapper for Windows GetFullPathName api.
  122. func getFullPath(name string) (path string, err error) {
  123. p, err := UTF16PtrFromString(name)
  124. if err != nil {
  125. return "", err
  126. }
  127. buf := make([]uint16, 100)
  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. // Windows is asking for bigger buffer.
  134. buf = make([]uint16, n)
  135. n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
  136. if err != nil {
  137. return "", err
  138. }
  139. if n > uint32(len(buf)) {
  140. return "", syscall.EINVAL
  141. }
  142. }
  143. return UTF16ToString(buf[:n]), nil
  144. }
  145. func isSlash(c uint8) bool {
  146. return c == '\\' || c == '/'
  147. }
  148. func normalizeDir(dir string) (name string, err error) {
  149. ndir, err := getFullPath(dir)
  150. if err != nil {
  151. return "", err
  152. }
  153. if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
  154. // dir cannot have \\server\share\path form
  155. return "", syscall.EINVAL
  156. }
  157. return ndir, nil
  158. }
  159. func volToUpper(ch int) int {
  160. if 'a' <= ch && ch <= 'z' {
  161. ch += 'A' - 'a'
  162. }
  163. return ch
  164. }
  165. func joinExeDirAndFName(dir, p string) (name string, err error) {
  166. if len(p) == 0 {
  167. return "", syscall.EINVAL
  168. }
  169. if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
  170. // \\server\share\path form
  171. return p, nil
  172. }
  173. if len(p) > 1 && p[1] == ':' {
  174. // has drive letter
  175. if len(p) == 2 {
  176. return "", syscall.EINVAL
  177. }
  178. if isSlash(p[2]) {
  179. return p, nil
  180. } else {
  181. d, err := normalizeDir(dir)
  182. if err != nil {
  183. return "", err
  184. }
  185. if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
  186. return getFullPath(d + "\\" + p[2:])
  187. } else {
  188. return getFullPath(p)
  189. }
  190. }
  191. } else {
  192. // no drive letter
  193. d, err := normalizeDir(dir)
  194. if err != nil {
  195. return "", err
  196. }
  197. if isSlash(p[0]) {
  198. return getFullPath(d[:2] + p)
  199. } else {
  200. return getFullPath(d + "\\" + p)
  201. }
  202. }
  203. // we shouldn't be here
  204. return "", syscall.EINVAL
  205. }
  206. type ProcAttr struct {
  207. Dir string
  208. Env []string
  209. Files []uintptr
  210. Sys *SysProcAttr
  211. }
  212. type SysProcAttr struct {
  213. HideWindow bool
  214. CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
  215. CreationFlags uint32
  216. }
  217. var zeroProcAttr ProcAttr
  218. var zeroSysProcAttr SysProcAttr
  219. func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
  220. if len(argv0) == 0 {
  221. return 0, 0, syscall.EWINDOWS
  222. }
  223. if attr == nil {
  224. attr = &zeroProcAttr
  225. }
  226. sys := attr.Sys
  227. if sys == nil {
  228. sys = &zeroSysProcAttr
  229. }
  230. if len(attr.Files) > 3 {
  231. return 0, 0, syscall.EWINDOWS
  232. }
  233. if len(attr.Dir) != 0 {
  234. // StartProcess assumes that argv0 is relative to attr.Dir,
  235. // because it implies Chdir(attr.Dir) before executing argv0.
  236. // Windows CreateProcess assumes the opposite: it looks for
  237. // argv0 relative to the current directory, and, only once the new
  238. // process is started, it does Chdir(attr.Dir). We are adjusting
  239. // for that difference here by making argv0 absolute.
  240. var err error
  241. argv0, err = joinExeDirAndFName(attr.Dir, argv0)
  242. if err != nil {
  243. return 0, 0, err
  244. }
  245. }
  246. argv0p, err := UTF16PtrFromString(argv0)
  247. if err != nil {
  248. return 0, 0, err
  249. }
  250. var cmdline string
  251. // Windows CreateProcess takes the command line as a single string:
  252. // use attr.CmdLine if set, else build the command line by escaping
  253. // and joining each argument with spaces
  254. if sys.CmdLine != "" {
  255. cmdline = sys.CmdLine
  256. } else {
  257. cmdline = makeCmdLine(argv)
  258. }
  259. var argvp *uint16
  260. if len(cmdline) != 0 {
  261. argvp, err = UTF16PtrFromString(cmdline)
  262. if err != nil {
  263. return 0, 0, err
  264. }
  265. }
  266. var dirp *uint16
  267. if len(attr.Dir) != 0 {
  268. dirp, err = UTF16PtrFromString(attr.Dir)
  269. if err != nil {
  270. return 0, 0, err
  271. }
  272. }
  273. // Acquire the fork lock so that no other threads
  274. // create new fds that are not yet close-on-exec
  275. // before we fork.
  276. ForkLock.Lock()
  277. defer ForkLock.Unlock()
  278. p, _ := GetCurrentProcess()
  279. fd := make([]Handle, len(attr.Files))
  280. for i := range attr.Files {
  281. if attr.Files[i] > 0 {
  282. err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
  283. if err != nil {
  284. return 0, 0, err
  285. }
  286. defer CloseHandle(Handle(fd[i]))
  287. }
  288. }
  289. si := new(StartupInfo)
  290. si.Cb = uint32(unsafe.Sizeof(*si))
  291. si.Flags = STARTF_USESTDHANDLES
  292. if sys.HideWindow {
  293. si.Flags |= STARTF_USESHOWWINDOW
  294. si.ShowWindow = SW_HIDE
  295. }
  296. si.StdInput = fd[0]
  297. si.StdOutput = fd[1]
  298. si.StdErr = fd[2]
  299. pi := new(ProcessInformation)
  300. flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT
  301. err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
  302. if err != nil {
  303. return 0, 0, err
  304. }
  305. defer CloseHandle(Handle(pi.Thread))
  306. return int(pi.ProcessId), uintptr(pi.Process), nil
  307. }
  308. func Exec(argv0 string, argv []string, envv []string) (err error) {
  309. return syscall.EWINDOWS
  310. }