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. "unicode/utf16"
  9. "unsafe"
  10. )
  11. var ForkLock sync.RWMutex
  12. // EscapeArg rewrites command line argument s as prescribed
  13. // in http://msdn.microsoft.com/en-us/library/ms880421.
  14. // This function returns "" (2 double quotes) if s is empty.
  15. // Alternatively, these transformations are done:
  16. // - every back slash (\) is doubled, but only if immediately
  17. // followed by double quote (");
  18. // - every double quote (") is escaped by back slash (\);
  19. // - finally, s is wrapped with double quotes (arg -> "arg"),
  20. // but only if there is space or tab inside s.
  21. func EscapeArg(s string) string {
  22. if len(s) == 0 {
  23. return "\"\""
  24. }
  25. n := len(s)
  26. hasSpace := false
  27. for i := 0; i < len(s); i++ {
  28. switch s[i] {
  29. case '"', '\\':
  30. n++
  31. case ' ', '\t':
  32. hasSpace = true
  33. }
  34. }
  35. if hasSpace {
  36. n += 2
  37. }
  38. if n == len(s) {
  39. return s
  40. }
  41. qs := make([]byte, n)
  42. j := 0
  43. if hasSpace {
  44. qs[j] = '"'
  45. j++
  46. }
  47. slashes := 0
  48. for i := 0; i < len(s); i++ {
  49. switch s[i] {
  50. default:
  51. slashes = 0
  52. qs[j] = s[i]
  53. case '\\':
  54. slashes++
  55. qs[j] = s[i]
  56. case '"':
  57. for ; slashes > 0; slashes-- {
  58. qs[j] = '\\'
  59. j++
  60. }
  61. qs[j] = '\\'
  62. j++
  63. qs[j] = s[i]
  64. }
  65. j++
  66. }
  67. if hasSpace {
  68. for ; slashes > 0; slashes-- {
  69. qs[j] = '\\'
  70. j++
  71. }
  72. qs[j] = '"'
  73. j++
  74. }
  75. return string(qs[:j])
  76. }
  77. // makeCmdLine builds a command line out of args by escaping "special"
  78. // characters and joining the arguments with spaces.
  79. func makeCmdLine(args []string) string {
  80. var s string
  81. for _, v := range args {
  82. if s != "" {
  83. s += " "
  84. }
  85. s += EscapeArg(v)
  86. }
  87. return s
  88. }
  89. // createEnvBlock converts an array of environment strings into
  90. // the representation required by CreateProcess: a sequence of NUL
  91. // terminated strings followed by a nil.
  92. // Last bytes are two UCS-2 NULs, or four NUL bytes.
  93. func createEnvBlock(envv []string) *uint16 {
  94. if len(envv) == 0 {
  95. return &utf16.Encode([]rune("\x00\x00"))[0]
  96. }
  97. length := 0
  98. for _, s := range envv {
  99. length += len(s) + 1
  100. }
  101. length += 1
  102. b := make([]byte, length)
  103. i := 0
  104. for _, s := range envv {
  105. l := len(s)
  106. copy(b[i:i+l], []byte(s))
  107. copy(b[i+l:i+l+1], []byte{0})
  108. i = i + l + 1
  109. }
  110. copy(b[i:i+1], []byte{0})
  111. return &utf16.Encode([]rune(string(b)))[0]
  112. }
  113. func CloseOnExec(fd Handle) {
  114. SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
  115. }
  116. func SetNonblock(fd Handle, nonblocking bool) (err error) {
  117. return nil
  118. }
  119. // getFullPath retrieves the full path of the specified file.
  120. // Just a wrapper for Windows GetFullPathName api.
  121. func getFullPath(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 "", 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 := getFullPath(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 "", 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 "", 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 "", 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 getFullPath(d + "\\" + p[2:])
  186. } else {
  187. return getFullPath(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 getFullPath(d[:2] + p)
  198. } else {
  199. return getFullPath(d + "\\" + p)
  200. }
  201. }
  202. // we shouldn't be here
  203. return "", 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, 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, 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 EWINDOWS
  309. }