exec_windows.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. // FullPath retrieves the full path of the specified file.
  121. func FullPath(name string) (path string, err error) {
  122. p, err := UTF16PtrFromString(name)
  123. if err != nil {
  124. return "", err
  125. }
  126. buf := make([]uint16, 100)
  127. n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
  128. if err != nil {
  129. return "", err
  130. }
  131. if n > uint32(len(buf)) {
  132. // Windows is asking for bigger buffer.
  133. buf = make([]uint16, n)
  134. n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
  135. if err != nil {
  136. return "", err
  137. }
  138. if n > uint32(len(buf)) {
  139. return "", syscall.EINVAL
  140. }
  141. }
  142. return UTF16ToString(buf[:n]), nil
  143. }
  144. func isSlash(c uint8) bool {
  145. return c == '\\' || c == '/'
  146. }
  147. func normalizeDir(dir string) (name string, err error) {
  148. ndir, err := FullPath(dir)
  149. if err != nil {
  150. return "", err
  151. }
  152. if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
  153. // dir cannot have \\server\share\path form
  154. return "", syscall.EINVAL
  155. }
  156. return ndir, nil
  157. }
  158. func volToUpper(ch int) int {
  159. if 'a' <= ch && ch <= 'z' {
  160. ch += 'A' - 'a'
  161. }
  162. return ch
  163. }
  164. func joinExeDirAndFName(dir, p string) (name string, err error) {
  165. if len(p) == 0 {
  166. return "", syscall.EINVAL
  167. }
  168. if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
  169. // \\server\share\path form
  170. return p, nil
  171. }
  172. if len(p) > 1 && p[1] == ':' {
  173. // has drive letter
  174. if len(p) == 2 {
  175. return "", syscall.EINVAL
  176. }
  177. if isSlash(p[2]) {
  178. return p, nil
  179. } else {
  180. d, err := normalizeDir(dir)
  181. if err != nil {
  182. return "", err
  183. }
  184. if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
  185. return FullPath(d + "\\" + p[2:])
  186. } else {
  187. return FullPath(p)
  188. }
  189. }
  190. } else {
  191. // no drive letter
  192. d, err := normalizeDir(dir)
  193. if err != nil {
  194. return "", err
  195. }
  196. if isSlash(p[0]) {
  197. return FullPath(d[:2] + p)
  198. } else {
  199. return FullPath(d + "\\" + p)
  200. }
  201. }
  202. // we shouldn't be here
  203. return "", syscall.EINVAL
  204. }
  205. type ProcAttr struct {
  206. Dir string
  207. Env []string
  208. Files []uintptr
  209. Sys *SysProcAttr
  210. }
  211. type SysProcAttr struct {
  212. HideWindow bool
  213. CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
  214. CreationFlags uint32
  215. }
  216. var zeroProcAttr ProcAttr
  217. var zeroSysProcAttr SysProcAttr
  218. func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
  219. if len(argv0) == 0 {
  220. return 0, 0, syscall.EWINDOWS
  221. }
  222. if attr == nil {
  223. attr = &zeroProcAttr
  224. }
  225. sys := attr.Sys
  226. if sys == nil {
  227. sys = &zeroSysProcAttr
  228. }
  229. if len(attr.Files) > 3 {
  230. return 0, 0, syscall.EWINDOWS
  231. }
  232. if len(attr.Dir) != 0 {
  233. // StartProcess assumes that argv0 is relative to attr.Dir,
  234. // because it implies Chdir(attr.Dir) before executing argv0.
  235. // Windows CreateProcess assumes the opposite: it looks for
  236. // argv0 relative to the current directory, and, only once the new
  237. // process is started, it does Chdir(attr.Dir). We are adjusting
  238. // for that difference here by making argv0 absolute.
  239. var err error
  240. argv0, err = joinExeDirAndFName(attr.Dir, argv0)
  241. if err != nil {
  242. return 0, 0, err
  243. }
  244. }
  245. argv0p, err := UTF16PtrFromString(argv0)
  246. if err != nil {
  247. return 0, 0, err
  248. }
  249. var cmdline string
  250. // Windows CreateProcess takes the command line as a single string:
  251. // use attr.CmdLine if set, else build the command line by escaping
  252. // and joining each argument with spaces
  253. if sys.CmdLine != "" {
  254. cmdline = sys.CmdLine
  255. } else {
  256. cmdline = makeCmdLine(argv)
  257. }
  258. var argvp *uint16
  259. if len(cmdline) != 0 {
  260. argvp, err = UTF16PtrFromString(cmdline)
  261. if err != nil {
  262. return 0, 0, err
  263. }
  264. }
  265. var dirp *uint16
  266. if len(attr.Dir) != 0 {
  267. dirp, err = UTF16PtrFromString(attr.Dir)
  268. if err != nil {
  269. return 0, 0, err
  270. }
  271. }
  272. // Acquire the fork lock so that no other threads
  273. // create new fds that are not yet close-on-exec
  274. // before we fork.
  275. ForkLock.Lock()
  276. defer ForkLock.Unlock()
  277. p, _ := GetCurrentProcess()
  278. fd := make([]Handle, len(attr.Files))
  279. for i := range attr.Files {
  280. if attr.Files[i] > 0 {
  281. err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
  282. if err != nil {
  283. return 0, 0, err
  284. }
  285. defer CloseHandle(Handle(fd[i]))
  286. }
  287. }
  288. si := new(StartupInfo)
  289. si.Cb = uint32(unsafe.Sizeof(*si))
  290. si.Flags = STARTF_USESTDHANDLES
  291. if sys.HideWindow {
  292. si.Flags |= STARTF_USESHOWWINDOW
  293. si.ShowWindow = SW_HIDE
  294. }
  295. si.StdInput = fd[0]
  296. si.StdOutput = fd[1]
  297. si.StdErr = fd[2]
  298. pi := new(ProcessInformation)
  299. flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT
  300. err = CreateProcess(argv0p, argvp, nil, nil, true, flags, createEnvBlock(attr.Env), dirp, si, pi)
  301. if err != nil {
  302. return 0, 0, err
  303. }
  304. defer CloseHandle(Handle(pi.Thread))
  305. return int(pi.ProcessId), uintptr(pi.Process), nil
  306. }
  307. func Exec(argv0 string, argv []string, envv []string) (err error) {
  308. return syscall.EWINDOWS
  309. }