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