rbuv 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +0 -1
  6. data/README.md +6 -1
  7. data/Rakefile +42 -0
  8. data/deps/libuv/.gitignore +34 -0
  9. data/deps/libuv/.mailmap +16 -0
  10. data/deps/libuv/AUTHORS +81 -0
  11. data/deps/libuv/ChangeLog +45 -0
  12. data/deps/libuv/LICENSE +41 -0
  13. data/deps/libuv/Makefile +53 -0
  14. data/deps/libuv/README.md +118 -0
  15. data/deps/libuv/build.mk +164 -0
  16. data/deps/libuv/checksparse.sh +230 -0
  17. data/deps/libuv/common.gypi +197 -0
  18. data/deps/libuv/config-mingw.mk +48 -0
  19. data/deps/libuv/config-unix.mk +167 -0
  20. data/deps/libuv/gyp_uv +98 -0
  21. data/deps/libuv/include/uv-private/ngx-queue.h +129 -0
  22. data/deps/libuv/include/uv-private/stdint-msvc2008.h +247 -0
  23. data/deps/libuv/include/uv-private/tree.h +768 -0
  24. data/deps/libuv/include/uv-private/uv-bsd.h +34 -0
  25. data/deps/libuv/include/uv-private/uv-darwin.h +61 -0
  26. data/deps/libuv/include/uv-private/uv-linux.h +34 -0
  27. data/deps/libuv/include/uv-private/uv-sunos.h +44 -0
  28. data/deps/libuv/include/uv-private/uv-unix.h +332 -0
  29. data/deps/libuv/include/uv-private/uv-win.h +585 -0
  30. data/deps/libuv/include/uv.h +1987 -0
  31. data/deps/libuv/src/fs-poll.c +248 -0
  32. data/deps/libuv/src/inet.c +298 -0
  33. data/deps/libuv/src/unix/aix.c +393 -0
  34. data/deps/libuv/src/unix/async.c +281 -0
  35. data/deps/libuv/src/unix/core.c +714 -0
  36. data/deps/libuv/src/unix/cygwin.c +93 -0
  37. data/deps/libuv/src/unix/darwin-proctitle.m +78 -0
  38. data/deps/libuv/src/unix/darwin.c +431 -0
  39. data/deps/libuv/src/unix/dl.c +83 -0
  40. data/deps/libuv/src/unix/error.c +109 -0
  41. data/deps/libuv/src/unix/freebsd.c +343 -0
  42. data/deps/libuv/src/unix/fs.c +869 -0
  43. data/deps/libuv/src/unix/fsevents.c +299 -0
  44. data/deps/libuv/src/unix/getaddrinfo.c +159 -0
  45. data/deps/libuv/src/unix/internal.h +259 -0
  46. data/deps/libuv/src/unix/kqueue.c +347 -0
  47. data/deps/libuv/src/unix/linux-core.c +724 -0
  48. data/deps/libuv/src/unix/linux-inotify.c +236 -0
  49. data/deps/libuv/src/unix/linux-syscalls.c +388 -0
  50. data/deps/libuv/src/unix/linux-syscalls.h +150 -0
  51. data/deps/libuv/src/unix/loop-watcher.c +64 -0
  52. data/deps/libuv/src/unix/loop.c +114 -0
  53. data/deps/libuv/src/unix/netbsd.c +353 -0
  54. data/deps/libuv/src/unix/openbsd.c +304 -0
  55. data/deps/libuv/src/unix/pipe.c +261 -0
  56. data/deps/libuv/src/unix/poll.c +108 -0
  57. data/deps/libuv/src/unix/process.c +501 -0
  58. data/deps/libuv/src/unix/proctitle.c +103 -0
  59. data/deps/libuv/src/unix/signal.c +455 -0
  60. data/deps/libuv/src/unix/stream.c +1380 -0
  61. data/deps/libuv/src/unix/sunos.c +647 -0
  62. data/deps/libuv/src/unix/tcp.c +357 -0
  63. data/deps/libuv/src/unix/thread.c +431 -0
  64. data/deps/libuv/src/unix/threadpool.c +286 -0
  65. data/deps/libuv/src/unix/timer.c +153 -0
  66. data/deps/libuv/src/unix/tty.c +179 -0
  67. data/deps/libuv/src/unix/udp.c +715 -0
  68. data/deps/libuv/src/uv-common.c +431 -0
  69. data/deps/libuv/src/uv-common.h +204 -0
  70. data/deps/libuv/src/version.c +60 -0
  71. data/deps/libuv/src/win/async.c +99 -0
  72. data/deps/libuv/src/win/atomicops-inl.h +56 -0
  73. data/deps/libuv/src/win/core.c +310 -0
  74. data/deps/libuv/src/win/dl.c +86 -0
  75. data/deps/libuv/src/win/error.c +164 -0
  76. data/deps/libuv/src/win/fs-event.c +506 -0
  77. data/deps/libuv/src/win/fs.c +1951 -0
  78. data/deps/libuv/src/win/getaddrinfo.c +365 -0
  79. data/deps/libuv/src/win/handle-inl.h +164 -0
  80. data/deps/libuv/src/win/handle.c +153 -0
  81. data/deps/libuv/src/win/internal.h +346 -0
  82. data/deps/libuv/src/win/loop-watcher.c +124 -0
  83. data/deps/libuv/src/win/pipe.c +1656 -0
  84. data/deps/libuv/src/win/poll.c +615 -0
  85. data/deps/libuv/src/win/process-stdio.c +503 -0
  86. data/deps/libuv/src/win/process.c +1048 -0
  87. data/deps/libuv/src/win/req-inl.h +224 -0
  88. data/deps/libuv/src/win/req.c +25 -0
  89. data/deps/libuv/src/win/signal.c +354 -0
  90. data/deps/libuv/src/win/stream-inl.h +67 -0
  91. data/deps/libuv/src/win/stream.c +198 -0
  92. data/deps/libuv/src/win/tcp.c +1422 -0
  93. data/deps/libuv/src/win/thread.c +666 -0
  94. data/deps/libuv/src/win/threadpool.c +82 -0
  95. data/deps/libuv/src/win/timer.c +230 -0
  96. data/deps/libuv/src/win/tty.c +1857 -0
  97. data/deps/libuv/src/win/udp.c +744 -0
  98. data/deps/libuv/src/win/util.c +946 -0
  99. data/deps/libuv/src/win/winapi.c +152 -0
  100. data/deps/libuv/src/win/winapi.h +4476 -0
  101. data/deps/libuv/src/win/winsock.c +560 -0
  102. data/deps/libuv/src/win/winsock.h +171 -0
  103. data/deps/libuv/test/benchmark-async-pummel.c +119 -0
  104. data/deps/libuv/test/benchmark-async.c +139 -0
  105. data/deps/libuv/test/benchmark-fs-stat.c +136 -0
  106. data/deps/libuv/test/benchmark-getaddrinfo.c +91 -0
  107. data/deps/libuv/test/benchmark-list.h +163 -0
  108. data/deps/libuv/test/benchmark-loop-count.c +90 -0
  109. data/deps/libuv/test/benchmark-million-async.c +112 -0
  110. data/deps/libuv/test/benchmark-million-timers.c +77 -0
  111. data/deps/libuv/test/benchmark-multi-accept.c +432 -0
  112. data/deps/libuv/test/benchmark-ping-pongs.c +212 -0
  113. data/deps/libuv/test/benchmark-pound.c +325 -0
  114. data/deps/libuv/test/benchmark-pump.c +459 -0
  115. data/deps/libuv/test/benchmark-sizes.c +45 -0
  116. data/deps/libuv/test/benchmark-spawn.c +163 -0
  117. data/deps/libuv/test/benchmark-tcp-write-batch.c +141 -0
  118. data/deps/libuv/test/benchmark-thread.c +64 -0
  119. data/deps/libuv/test/benchmark-udp-pummel.c +238 -0
  120. data/deps/libuv/test/blackhole-server.c +118 -0
  121. data/deps/libuv/test/dns-server.c +329 -0
  122. data/deps/libuv/test/echo-server.c +384 -0
  123. data/deps/libuv/test/fixtures/empty_file +0 -0
  124. data/deps/libuv/test/fixtures/load_error.node +1 -0
  125. data/deps/libuv/test/run-benchmarks.c +64 -0
  126. data/deps/libuv/test/run-tests.c +159 -0
  127. data/deps/libuv/test/runner-unix.c +328 -0
  128. data/deps/libuv/test/runner-unix.h +36 -0
  129. data/deps/libuv/test/runner-win.c +318 -0
  130. data/deps/libuv/test/runner-win.h +43 -0
  131. data/deps/libuv/test/runner.c +394 -0
  132. data/deps/libuv/test/runner.h +165 -0
  133. data/deps/libuv/test/task.h +122 -0
  134. data/deps/libuv/test/test-active.c +83 -0
  135. data/deps/libuv/test/test-async.c +136 -0
  136. data/deps/libuv/test/test-barrier.c +98 -0
  137. data/deps/libuv/test/test-callback-order.c +77 -0
  138. data/deps/libuv/test/test-callback-stack.c +204 -0
  139. data/deps/libuv/test/test-condvar.c +173 -0
  140. data/deps/libuv/test/test-connection-fail.c +150 -0
  141. data/deps/libuv/test/test-cwd-and-chdir.c +64 -0
  142. data/deps/libuv/test/test-delayed-accept.c +189 -0
  143. data/deps/libuv/test/test-dlerror.c +58 -0
  144. data/deps/libuv/test/test-embed.c +136 -0
  145. data/deps/libuv/test/test-error.c +59 -0
  146. data/deps/libuv/test/test-fail-always.c +29 -0
  147. data/deps/libuv/test/test-fs-event.c +504 -0
  148. data/deps/libuv/test/test-fs-poll.c +148 -0
  149. data/deps/libuv/test/test-fs.c +1899 -0
  150. data/deps/libuv/test/test-get-currentexe.c +63 -0
  151. data/deps/libuv/test/test-get-loadavg.c +36 -0
  152. data/deps/libuv/test/test-get-memory.c +38 -0
  153. data/deps/libuv/test/test-getaddrinfo.c +120 -0
  154. data/deps/libuv/test/test-getsockname.c +344 -0
  155. data/deps/libuv/test/test-hrtime.c +54 -0
  156. data/deps/libuv/test/test-idle.c +82 -0
  157. data/deps/libuv/test/test-ipc-send-recv.c +218 -0
  158. data/deps/libuv/test/test-ipc.c +625 -0
  159. data/deps/libuv/test/test-list.h +492 -0
  160. data/deps/libuv/test/test-loop-handles.c +337 -0
  161. data/deps/libuv/test/test-loop-stop.c +73 -0
  162. data/deps/libuv/test/test-multiple-listen.c +103 -0
  163. data/deps/libuv/test/test-mutexes.c +63 -0
  164. data/deps/libuv/test/test-pass-always.c +28 -0
  165. data/deps/libuv/test/test-ping-pong.c +250 -0
  166. data/deps/libuv/test/test-pipe-bind-error.c +144 -0
  167. data/deps/libuv/test/test-pipe-connect-error.c +98 -0
  168. data/deps/libuv/test/test-platform-output.c +87 -0
  169. data/deps/libuv/test/test-poll-close.c +73 -0
  170. data/deps/libuv/test/test-poll.c +575 -0
  171. data/deps/libuv/test/test-process-title.c +49 -0
  172. data/deps/libuv/test/test-ref.c +415 -0
  173. data/deps/libuv/test/test-run-nowait.c +46 -0
  174. data/deps/libuv/test/test-run-once.c +49 -0
  175. data/deps/libuv/test/test-semaphore.c +111 -0
  176. data/deps/libuv/test/test-shutdown-close.c +105 -0
  177. data/deps/libuv/test/test-shutdown-eof.c +184 -0
  178. data/deps/libuv/test/test-signal-multiple-loops.c +270 -0
  179. data/deps/libuv/test/test-signal.c +152 -0
  180. data/deps/libuv/test/test-spawn.c +938 -0
  181. data/deps/libuv/test/test-stdio-over-pipes.c +250 -0
  182. data/deps/libuv/test/test-tcp-bind-error.c +198 -0
  183. data/deps/libuv/test/test-tcp-bind6-error.c +159 -0
  184. data/deps/libuv/test/test-tcp-close-while-connecting.c +81 -0
  185. data/deps/libuv/test/test-tcp-close.c +130 -0
  186. data/deps/libuv/test/test-tcp-connect-error-after-write.c +96 -0
  187. data/deps/libuv/test/test-tcp-connect-error.c +71 -0
  188. data/deps/libuv/test/test-tcp-connect-timeout.c +86 -0
  189. data/deps/libuv/test/test-tcp-connect6-error.c +69 -0
  190. data/deps/libuv/test/test-tcp-flags.c +52 -0
  191. data/deps/libuv/test/test-tcp-open.c +175 -0
  192. data/deps/libuv/test/test-tcp-read-stop.c +73 -0
  193. data/deps/libuv/test/test-tcp-shutdown-after-write.c +132 -0
  194. data/deps/libuv/test/test-tcp-unexpected-read.c +114 -0
  195. data/deps/libuv/test/test-tcp-write-to-half-open-connection.c +136 -0
  196. data/deps/libuv/test/test-tcp-writealot.c +171 -0
  197. data/deps/libuv/test/test-thread.c +183 -0
  198. data/deps/libuv/test/test-threadpool-cancel.c +311 -0
  199. data/deps/libuv/test/test-threadpool.c +77 -0
  200. data/deps/libuv/test/test-timer-again.c +142 -0
  201. data/deps/libuv/test/test-timer.c +266 -0
  202. data/deps/libuv/test/test-tty.c +111 -0
  203. data/deps/libuv/test/test-udp-dgram-too-big.c +87 -0
  204. data/deps/libuv/test/test-udp-ipv6.c +158 -0
  205. data/deps/libuv/test/test-udp-multicast-join.c +140 -0
  206. data/deps/libuv/test/test-udp-multicast-ttl.c +87 -0
  207. data/deps/libuv/test/test-udp-open.c +154 -0
  208. data/deps/libuv/test/test-udp-options.c +87 -0
  209. data/deps/libuv/test/test-udp-send-and-recv.c +210 -0
  210. data/deps/libuv/test/test-util.c +97 -0
  211. data/deps/libuv/test/test-walk-handles.c +78 -0
  212. data/deps/libuv/uv.gyp +431 -0
  213. data/deps/libuv/vcbuild.bat +128 -0
  214. data/ext/rbuv/debug.h +27 -0
  215. data/ext/rbuv/error.c +7 -0
  216. data/ext/rbuv/error.h +10 -0
  217. data/ext/rbuv/extconf.rb +35 -0
  218. data/ext/rbuv/handle.c +40 -0
  219. data/ext/rbuv/handle.h +14 -0
  220. data/ext/rbuv/libuv.mk +12 -0
  221. data/ext/rbuv/loop.c +50 -0
  222. data/ext/rbuv/loop.h +13 -0
  223. data/ext/rbuv/rbuv.c +15 -0
  224. data/ext/rbuv/rbuv.h +27 -0
  225. data/ext/rbuv/timer.c +133 -0
  226. data/ext/rbuv/timer.h +13 -0
  227. data/lib/rbuv/timer.rb +7 -0
  228. data/lib/rbuv/version.rb +1 -1
  229. data/lib/rbuv.rb +24 -2
  230. data/rbuv.gemspec +5 -1
  231. data/spec/spec_helper.rb +22 -0
  232. data/spec/timer_spec.rb +144 -0
  233. metadata +278 -9
@@ -0,0 +1,1048 @@
1
+ /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to
5
+ * deal in the Software without restriction, including without limitation the
6
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ * sell copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
+ * IN THE SOFTWARE.
20
+ */
21
+
22
+ #include <assert.h>
23
+ #include <io.h>
24
+ #include <stdio.h>
25
+ #include <stdlib.h>
26
+ #include <signal.h>
27
+
28
+ #include "uv.h"
29
+ #include "internal.h"
30
+ #include "handle-inl.h"
31
+ #include "req-inl.h"
32
+
33
+
34
+ #define SIGKILL 9
35
+
36
+
37
+ typedef struct env_var {
38
+ const char* narrow;
39
+ const WCHAR* wide;
40
+ size_t len; /* including null or '=' */
41
+ DWORD value_len;
42
+ int supplied;
43
+ } env_var_t;
44
+
45
+ #define E_V(str) { str "=", L##str, sizeof(str), 0, 0 }
46
+
47
+
48
+ static uv_err_t uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
49
+ int ws_len, r;
50
+ WCHAR* ws;
51
+
52
+ ws_len = MultiByteToWideChar(CP_UTF8,
53
+ 0,
54
+ s,
55
+ -1,
56
+ NULL,
57
+ 0);
58
+ if (ws_len <= 0) {
59
+ return uv__new_sys_error(GetLastError());
60
+ }
61
+
62
+ ws = (WCHAR*) malloc(ws_len * sizeof(WCHAR));
63
+ if (ws == NULL) {
64
+ return uv__new_artificial_error(UV_ENOMEM);
65
+ }
66
+
67
+ r = MultiByteToWideChar(CP_UTF8,
68
+ 0,
69
+ s,
70
+ -1,
71
+ ws,
72
+ ws_len);
73
+ assert(r == ws_len);
74
+
75
+ *ws_ptr = ws;
76
+ return uv_ok_;
77
+ }
78
+
79
+
80
+ static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
81
+ uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
82
+ handle->exit_cb = NULL;
83
+ handle->pid = 0;
84
+ handle->spawn_error = uv_ok_;
85
+ handle->exit_signal = 0;
86
+ handle->wait_handle = INVALID_HANDLE_VALUE;
87
+ handle->process_handle = INVALID_HANDLE_VALUE;
88
+ handle->child_stdio_buffer = NULL;
89
+ handle->exit_cb_pending = 0;
90
+
91
+ uv_req_init(loop, (uv_req_t*)&handle->exit_req);
92
+ handle->exit_req.type = UV_PROCESS_EXIT;
93
+ handle->exit_req.data = handle;
94
+ }
95
+
96
+
97
+ /*
98
+ * Path search functions
99
+ */
100
+
101
+ /*
102
+ * Helper function for search_path
103
+ */
104
+ static WCHAR* search_path_join_test(const WCHAR* dir,
105
+ size_t dir_len,
106
+ const WCHAR* name,
107
+ size_t name_len,
108
+ const WCHAR* ext,
109
+ size_t ext_len,
110
+ const WCHAR* cwd,
111
+ size_t cwd_len) {
112
+ WCHAR *result, *result_pos;
113
+ DWORD attrs;
114
+
115
+ if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
116
+ /* It's a full path without drive letter, use cwd's drive letter only */
117
+ cwd_len = 2;
118
+ } else if (dir_len >= 2 && dir[1] == L':' &&
119
+ (dir_len < 3 || (dir[2] != L'/' && dir[2] != L'\\'))) {
120
+ /* It's a relative path with drive letter (ext.g. D:../some/file)
121
+ * Replace drive letter in dir by full cwd if it points to the same drive,
122
+ * otherwise use the dir only.
123
+ */
124
+ if (cwd_len < 2 || _wcsnicmp(cwd, dir, 2) != 0) {
125
+ cwd_len = 0;
126
+ } else {
127
+ dir += 2;
128
+ dir_len -= 2;
129
+ }
130
+ } else if (dir_len > 2 && dir[1] == L':') {
131
+ /* It's an absolute path with drive letter
132
+ * Don't use the cwd at all
133
+ */
134
+ cwd_len = 0;
135
+ }
136
+
137
+ /* Allocate buffer for output */
138
+ result = result_pos = (WCHAR*)malloc(sizeof(WCHAR) *
139
+ (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1));
140
+
141
+ /* Copy cwd */
142
+ wcsncpy(result_pos, cwd, cwd_len);
143
+ result_pos += cwd_len;
144
+
145
+ /* Add a path separator if cwd didn't end with one */
146
+ if (cwd_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
147
+ result_pos[0] = L'\\';
148
+ result_pos++;
149
+ }
150
+
151
+ /* Copy dir */
152
+ wcsncpy(result_pos, dir, dir_len);
153
+ result_pos += dir_len;
154
+
155
+ /* Add a separator if the dir didn't end with one */
156
+ if (dir_len && wcsrchr(L"\\/:", result_pos[-1]) == NULL) {
157
+ result_pos[0] = L'\\';
158
+ result_pos++;
159
+ }
160
+
161
+ /* Copy filename */
162
+ wcsncpy(result_pos, name, name_len);
163
+ result_pos += name_len;
164
+
165
+ if (ext_len) {
166
+ /* Add a dot if the filename didn't end with one */
167
+ if (name_len && result_pos[-1] != '.') {
168
+ result_pos[0] = L'.';
169
+ result_pos++;
170
+ }
171
+
172
+ /* Copy extension */
173
+ wcsncpy(result_pos, ext, ext_len);
174
+ result_pos += ext_len;
175
+ }
176
+
177
+ /* Null terminator */
178
+ result_pos[0] = L'\0';
179
+
180
+ attrs = GetFileAttributesW(result);
181
+
182
+ if (attrs != INVALID_FILE_ATTRIBUTES &&
183
+ !(attrs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))) {
184
+ return result;
185
+ }
186
+
187
+ free(result);
188
+ return NULL;
189
+ }
190
+
191
+
192
+ /*
193
+ * Helper function for search_path
194
+ */
195
+ static WCHAR* path_search_walk_ext(const WCHAR *dir,
196
+ size_t dir_len,
197
+ const WCHAR *name,
198
+ size_t name_len,
199
+ WCHAR *cwd,
200
+ size_t cwd_len,
201
+ int name_has_ext) {
202
+ WCHAR* result;
203
+
204
+ /* If the name itself has a nonempty extension, try this extension first */
205
+ if (name_has_ext) {
206
+ result = search_path_join_test(dir, dir_len,
207
+ name, name_len,
208
+ L"", 0,
209
+ cwd, cwd_len);
210
+ if (result != NULL) {
211
+ return result;
212
+ }
213
+ }
214
+
215
+ /* Try .com extension */
216
+ result = search_path_join_test(dir, dir_len,
217
+ name, name_len,
218
+ L"com", 3,
219
+ cwd, cwd_len);
220
+ if (result != NULL) {
221
+ return result;
222
+ }
223
+
224
+ /* Try .exe extension */
225
+ result = search_path_join_test(dir, dir_len,
226
+ name, name_len,
227
+ L"exe", 3,
228
+ cwd, cwd_len);
229
+ if (result != NULL) {
230
+ return result;
231
+ }
232
+
233
+ return NULL;
234
+ }
235
+
236
+
237
+ /*
238
+ * search_path searches the system path for an executable filename -
239
+ * the windows API doesn't provide this as a standalone function nor as an
240
+ * option to CreateProcess.
241
+ *
242
+ * It tries to return an absolute filename.
243
+ *
244
+ * Furthermore, it tries to follow the semantics that cmd.exe, with this
245
+ * exception that PATHEXT environment variable isn't used. Since CreateProcess
246
+ * can start only .com and .exe files, only those extensions are tried. This
247
+ * behavior equals that of msvcrt's spawn functions.
248
+ *
249
+ * - Do not search the path if the filename already contains a path (either
250
+ * relative or absolute).
251
+ *
252
+ * - If there's really only a filename, check the current directory for file,
253
+ * then search all path directories.
254
+ *
255
+ * - If filename specified has *any* extension, search for the file with the
256
+ * specified extension first.
257
+ *
258
+ * - If the literal filename is not found in a directory, try *appending*
259
+ * (not replacing) .com first and then .exe.
260
+ *
261
+ * - The path variable may contain relative paths; relative paths are relative
262
+ * to the cwd.
263
+ *
264
+ * - Directories in path may or may not end with a trailing backslash.
265
+ *
266
+ * - CMD does not trim leading/trailing whitespace from path/pathex entries
267
+ * nor from the environment variables as a whole.
268
+ *
269
+ * - When cmd.exe cannot read a directory, it will just skip it and go on
270
+ * searching. However, unlike posix-y systems, it will happily try to run a
271
+ * file that is not readable/executable; if the spawn fails it will not
272
+ * continue searching.
273
+ *
274
+ * TODO: correctly interpret UNC paths
275
+ */
276
+ static WCHAR* search_path(const WCHAR *file,
277
+ WCHAR *cwd,
278
+ const WCHAR *path) {
279
+ int file_has_dir;
280
+ WCHAR* result = NULL;
281
+ WCHAR *file_name_start;
282
+ WCHAR *dot;
283
+ const WCHAR *dir_start, *dir_end, *dir_path;
284
+ size_t dir_len;
285
+ int name_has_ext;
286
+
287
+ size_t file_len = wcslen(file);
288
+ size_t cwd_len = wcslen(cwd);
289
+
290
+ /* If the caller supplies an empty filename,
291
+ * we're not gonna return c:\windows\.exe -- GFY!
292
+ */
293
+ if (file_len == 0
294
+ || (file_len == 1 && file[0] == L'.')) {
295
+ return NULL;
296
+ }
297
+
298
+ /* Find the start of the filename so we can split the directory from the */
299
+ /* name. */
300
+ for (file_name_start = (WCHAR*)file + file_len;
301
+ file_name_start > file
302
+ && file_name_start[-1] != L'\\'
303
+ && file_name_start[-1] != L'/'
304
+ && file_name_start[-1] != L':';
305
+ file_name_start--);
306
+
307
+ file_has_dir = file_name_start != file;
308
+
309
+ /* Check if the filename includes an extension */
310
+ dot = wcschr(file_name_start, L'.');
311
+ name_has_ext = (dot != NULL && dot[1] != L'\0');
312
+
313
+ if (file_has_dir) {
314
+ /* The file has a path inside, don't use path */
315
+ result = path_search_walk_ext(
316
+ file, file_name_start - file,
317
+ file_name_start, file_len - (file_name_start - file),
318
+ cwd, cwd_len,
319
+ name_has_ext);
320
+
321
+ } else {
322
+ dir_end = path;
323
+
324
+ /* The file is really only a name; look in cwd first, then scan path */
325
+ result = path_search_walk_ext(L"", 0,
326
+ file, file_len,
327
+ cwd, cwd_len,
328
+ name_has_ext);
329
+
330
+ while (result == NULL) {
331
+ if (*dir_end == L'\0') {
332
+ break;
333
+ }
334
+
335
+ /* Skip the separator that dir_end now points to */
336
+ if (dir_end != path) {
337
+ dir_end++;
338
+ }
339
+
340
+ /* Next slice starts just after where the previous one ended */
341
+ dir_start = dir_end;
342
+
343
+ /* Slice until the next ; or \0 is found */
344
+ dir_end = wcschr(dir_start, L';');
345
+ if (dir_end == NULL) {
346
+ dir_end = wcschr(dir_start, L'\0');
347
+ }
348
+
349
+ /* If the slice is zero-length, don't bother */
350
+ if (dir_end - dir_start == 0) {
351
+ continue;
352
+ }
353
+
354
+ dir_path = dir_start;
355
+ dir_len = dir_end - dir_start;
356
+
357
+ /* Adjust if the path is quoted. */
358
+ if (dir_path[0] == '"' || dir_path[0] == '\'') {
359
+ ++dir_path;
360
+ --dir_len;
361
+ }
362
+
363
+ if (dir_path[dir_len - 1] == '"' || dir_path[dir_len - 1] == '\'') {
364
+ --dir_len;
365
+ }
366
+
367
+ result = path_search_walk_ext(dir_path, dir_len,
368
+ file, file_len,
369
+ cwd, cwd_len,
370
+ name_has_ext);
371
+ }
372
+ }
373
+
374
+ return result;
375
+ }
376
+
377
+
378
+ /*
379
+ * Quotes command line arguments
380
+ * Returns a pointer to the end (next char to be written) of the buffer
381
+ */
382
+ WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {
383
+ size_t len = wcslen(source);
384
+ size_t i;
385
+ int quote_hit;
386
+ WCHAR* start;
387
+
388
+ /*
389
+ * Check if the string must be quoted;
390
+ * if unnecessary, don't do it, it may only confuse older programs.
391
+ */
392
+ if (len == 0) {
393
+ return target;
394
+ }
395
+
396
+ if (NULL == wcspbrk(source, L" \t\"")) {
397
+ /* No quotation needed */
398
+ wcsncpy(target, source, len);
399
+ target += len;
400
+ return target;
401
+ }
402
+
403
+ if (NULL == wcspbrk(source, L"\"\\")) {
404
+ /*
405
+ * No embedded double quotes or backlashes, so I can just wrap
406
+ * quote marks around the whole thing.
407
+ */
408
+ *(target++) = L'"';
409
+ wcsncpy(target, source, len);
410
+ target += len;
411
+ *(target++) = L'"';
412
+ return target;
413
+ }
414
+
415
+ /*
416
+ * Expected input/output:
417
+ * input : hello"world
418
+ * output: "hello\"world"
419
+ * input : hello""world
420
+ * output: "hello\"\"world"
421
+ * input : hello\world
422
+ * output: hello\world
423
+ * input : hello\\world
424
+ * output: hello\\world
425
+ * input : hello\"world
426
+ * output: "hello\\\"world"
427
+ * input : hello\\"world
428
+ * output: "hello\\\\\"world"
429
+ * input : hello world\
430
+ * output: "hello world\"
431
+ */
432
+
433
+ *(target++) = L'"';
434
+ start = target;
435
+ quote_hit = 1;
436
+
437
+ for (i = len; i > 0; --i) {
438
+ *(target++) = source[i - 1];
439
+
440
+ if (quote_hit && source[i - 1] == L'\\') {
441
+ *(target++) = L'\\';
442
+ } else if(source[i - 1] == L'"') {
443
+ quote_hit = 1;
444
+ *(target++) = L'\\';
445
+ } else {
446
+ quote_hit = 0;
447
+ }
448
+ }
449
+ target[0] = L'\0';
450
+ wcsrev(start);
451
+ *(target++) = L'"';
452
+ return target;
453
+ }
454
+
455
+
456
+ uv_err_t make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) {
457
+ char** arg;
458
+ WCHAR* dst = NULL;
459
+ WCHAR* temp_buffer = NULL;
460
+ size_t dst_len = 0;
461
+ size_t temp_buffer_len = 0;
462
+ WCHAR* pos;
463
+ int arg_count = 0;
464
+ uv_err_t err = uv_ok_;
465
+
466
+ /* Count the required size. */
467
+ for (arg = args; *arg; arg++) {
468
+ DWORD arg_len;
469
+
470
+ arg_len = MultiByteToWideChar(CP_UTF8,
471
+ 0,
472
+ *arg,
473
+ -1,
474
+ NULL,
475
+ 0);
476
+ if (arg_len == 0) {
477
+ return uv__new_sys_error(GetLastError());
478
+ }
479
+
480
+ dst_len += arg_len;
481
+
482
+ if (arg_len > temp_buffer_len)
483
+ temp_buffer_len = arg_len;
484
+
485
+ arg_count++;
486
+ }
487
+
488
+ /* Adjust for potential quotes. Also assume the worst-case scenario */
489
+ /* that every character needs escaping, so we need twice as much space. */
490
+ dst_len = dst_len * 2 + arg_count * 2;
491
+
492
+ /* Allocate buffer for the final command line. */
493
+ dst = (WCHAR*) malloc(dst_len * sizeof(WCHAR));
494
+ if (dst == NULL) {
495
+ err = uv__new_artificial_error(UV_ENOMEM);
496
+ goto error;
497
+ }
498
+
499
+ /* Allocate temporary working buffer. */
500
+ temp_buffer = (WCHAR*) malloc(temp_buffer_len * sizeof(WCHAR));
501
+ if (temp_buffer == NULL) {
502
+ err = uv__new_artificial_error(UV_ENOMEM);
503
+ goto error;
504
+ }
505
+
506
+ pos = dst;
507
+ for (arg = args; *arg; arg++) {
508
+ DWORD arg_len;
509
+
510
+ /* Convert argument to wide char. */
511
+ arg_len = MultiByteToWideChar(CP_UTF8,
512
+ 0,
513
+ *arg,
514
+ -1,
515
+ temp_buffer,
516
+ (int) (dst + dst_len - pos));
517
+ if (arg_len == 0) {
518
+ goto error;
519
+ }
520
+
521
+ if (verbatim_arguments) {
522
+ /* Copy verbatim. */
523
+ wcscpy(pos, temp_buffer);
524
+ pos += arg_len - 1;
525
+ } else {
526
+ /* Quote/escape, if needed. */
527
+ pos = quote_cmd_arg(temp_buffer, pos);
528
+ }
529
+
530
+ *pos++ = *(arg + 1) ? L' ' : L'\0';
531
+ }
532
+
533
+ free(temp_buffer);
534
+
535
+ *dst_ptr = dst;
536
+ return uv_ok_;
537
+
538
+ error:
539
+ free(dst);
540
+ free(temp_buffer);
541
+ return err;
542
+ }
543
+
544
+
545
+ /*
546
+ * If we learn that people are passing in huge environment blocks
547
+ * then we should probably qsort() the array and then bsearch()
548
+ * to see if it contains this variable. But there are ownership
549
+ * issues associated with that solution; this is the caller's
550
+ * char**, and modifying it is rude.
551
+ */
552
+ static void check_required_vars_contains_var(env_var_t* required, int count,
553
+ const char* var) {
554
+ int i;
555
+ for (i = 0; i < count; ++i) {
556
+ if (_strnicmp(required[i].narrow, var, required[i].len) == 0) {
557
+ required[i].supplied = 1;
558
+ return;
559
+ }
560
+ }
561
+ }
562
+
563
+
564
+ /*
565
+ * The way windows takes environment variables is different than what C does;
566
+ * Windows wants a contiguous block of null-terminated strings, terminated
567
+ * with an additional null.
568
+ *
569
+ * Windows has a few "essential" environment variables. winsock will fail
570
+ * to initialize if SYSTEMROOT is not defined; some APIs make reference to
571
+ * TEMP. SYSTEMDRIVE is probably also important. We therefore ensure that
572
+ * these get defined if the input environment block does not contain any
573
+ * values for them.
574
+ */
575
+ uv_err_t make_program_env(char* env_block[], WCHAR** dst_ptr) {
576
+ WCHAR* dst;
577
+ WCHAR* ptr;
578
+ char** env;
579
+ size_t env_len = 1; /* room for closing null */
580
+ int len;
581
+ int i;
582
+ DWORD var_size;
583
+
584
+ env_var_t required_vars[] = {
585
+ E_V("SYSTEMROOT"),
586
+ E_V("SYSTEMDRIVE"),
587
+ E_V("TEMP"),
588
+ };
589
+
590
+ for (env = env_block; *env; env++) {
591
+ int len;
592
+ check_required_vars_contains_var(required_vars,
593
+ ARRAY_SIZE(required_vars),
594
+ *env);
595
+
596
+ len = MultiByteToWideChar(CP_UTF8,
597
+ 0,
598
+ *env,
599
+ -1,
600
+ NULL,
601
+ 0);
602
+ if (len <= 0) {
603
+ return uv__new_sys_error(GetLastError());
604
+ }
605
+
606
+ env_len += len;
607
+ }
608
+
609
+ for (i = 0; i < ARRAY_SIZE(required_vars); ++i) {
610
+ if (!required_vars[i].supplied) {
611
+ env_len += required_vars[i].len;
612
+ var_size = GetEnvironmentVariableW(required_vars[i].wide, NULL, 0);
613
+ if (var_size == 0) {
614
+ return uv__new_sys_error(GetLastError());
615
+ }
616
+ required_vars[i].value_len = var_size;
617
+ env_len += var_size;
618
+ }
619
+ }
620
+
621
+ dst = malloc(env_len * sizeof(WCHAR));
622
+ if (!dst) {
623
+ return uv__new_artificial_error(UV_ENOMEM);
624
+ }
625
+
626
+ ptr = dst;
627
+
628
+ for (env = env_block; *env; env++, ptr += len) {
629
+ len = MultiByteToWideChar(CP_UTF8,
630
+ 0,
631
+ *env,
632
+ -1,
633
+ ptr,
634
+ (int) (env_len - (ptr - dst)));
635
+ if (len <= 0) {
636
+ free(dst);
637
+ return uv__new_sys_error(GetLastError());
638
+ }
639
+ }
640
+
641
+ for (i = 0; i < ARRAY_SIZE(required_vars); ++i) {
642
+ if (!required_vars[i].supplied) {
643
+ wcscpy(ptr, required_vars[i].wide);
644
+ ptr += required_vars[i].len - 1;
645
+ *ptr++ = L'=';
646
+ var_size = GetEnvironmentVariableW(required_vars[i].wide,
647
+ ptr,
648
+ required_vars[i].value_len);
649
+ if (var_size == 0) {
650
+ uv_fatal_error(GetLastError(), "GetEnvironmentVariableW");
651
+ }
652
+ ptr += required_vars[i].value_len;
653
+ }
654
+ }
655
+
656
+ /* Terminate with an extra NULL. */
657
+ *ptr = L'\0';
658
+
659
+ *dst_ptr = dst;
660
+ return uv_ok_;
661
+ }
662
+
663
+
664
+ /*
665
+ * Called on Windows thread-pool thread to indicate that
666
+ * a child process has exited.
667
+ */
668
+ static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
669
+ uv_process_t* process = (uv_process_t*) data;
670
+ uv_loop_t* loop = process->loop;
671
+
672
+ assert(didTimeout == FALSE);
673
+ assert(process);
674
+ assert(!process->exit_cb_pending);
675
+
676
+ process->exit_cb_pending = 1;
677
+
678
+ /* Post completed */
679
+ POST_COMPLETION_FOR_REQ(loop, &process->exit_req);
680
+ }
681
+
682
+
683
+ /* Called on main thread after a child process has exited. */
684
+ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
685
+ DWORD exit_code;
686
+
687
+ assert(handle->exit_cb_pending);
688
+ handle->exit_cb_pending = 0;
689
+
690
+ /* If we're closing, don't call the exit callback. Just schedule a close */
691
+ /* callback now. */
692
+ if (handle->flags & UV__HANDLE_CLOSING) {
693
+ uv_want_endgame(loop, (uv_handle_t*) handle);
694
+ return;
695
+ }
696
+
697
+ /* Unregister from process notification. */
698
+ if (handle->wait_handle != INVALID_HANDLE_VALUE) {
699
+ UnregisterWait(handle->wait_handle);
700
+ handle->wait_handle = INVALID_HANDLE_VALUE;
701
+ }
702
+
703
+ /* Set the handle to inactive: no callbacks will be made after the exit */
704
+ /* callback.*/
705
+ uv__handle_stop(handle);
706
+
707
+ if (handle->spawn_error.code != UV_OK) {
708
+ /* Spawning failed. */
709
+ exit_code = (DWORD) -1;
710
+ } else if (!GetExitCodeProcess(handle->process_handle, &exit_code)) {
711
+ /* Unable to to obtain the exit code. This should never happen. */
712
+ exit_code = (DWORD) -1;
713
+ }
714
+
715
+ /* Fire the exit callback. */
716
+ if (handle->exit_cb) {
717
+ loop->last_err = handle->spawn_error;
718
+ handle->exit_cb(handle, exit_code, handle->exit_signal);
719
+ }
720
+ }
721
+
722
+
723
+ void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
724
+ uv__handle_closing(handle);
725
+
726
+ if (handle->wait_handle != INVALID_HANDLE_VALUE) {
727
+ /* This blocks until either the wait was cancelled, or the callback has */
728
+ /* completed. */
729
+ BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE);
730
+ if (!r) {
731
+ /* This should never happen, and if it happens, we can't recover... */
732
+ uv_fatal_error(GetLastError(), "UnregisterWaitEx");
733
+ }
734
+
735
+ handle->wait_handle = INVALID_HANDLE_VALUE;
736
+ }
737
+
738
+ if (!handle->exit_cb_pending) {
739
+ uv_want_endgame(loop, (uv_handle_t*)handle);
740
+ }
741
+ }
742
+
743
+
744
+ void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) {
745
+ assert(!handle->exit_cb_pending);
746
+ assert(handle->flags & UV__HANDLE_CLOSING);
747
+ assert(!(handle->flags & UV_HANDLE_CLOSED));
748
+
749
+ /* Clean-up the process handle. */
750
+ CloseHandle(handle->process_handle);
751
+
752
+ uv__handle_close(handle);
753
+ }
754
+
755
+
756
+ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
757
+ uv_process_options_t options) {
758
+ int i;
759
+ uv_err_t err = uv_ok_;
760
+ WCHAR* path = NULL;
761
+ BOOL result;
762
+ WCHAR* application_path = NULL, *application = NULL, *arguments = NULL,
763
+ *env = NULL, *cwd = NULL;
764
+ STARTUPINFOW startup;
765
+ PROCESS_INFORMATION info;
766
+ DWORD process_flags;
767
+
768
+ if (options.flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
769
+ uv__set_artificial_error(loop, UV_ENOTSUP);
770
+ return -1;
771
+ }
772
+
773
+ if (options.file == NULL ||
774
+ options.args == NULL) {
775
+ uv__set_artificial_error(loop, UV_EINVAL);
776
+ return -1;
777
+ }
778
+
779
+ assert(options.file != NULL);
780
+ assert(!(options.flags & ~(UV_PROCESS_DETACHED |
781
+ UV_PROCESS_SETGID |
782
+ UV_PROCESS_SETUID |
783
+ UV_PROCESS_WINDOWS_HIDE |
784
+ UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
785
+
786
+ uv_process_init(loop, process);
787
+ process->exit_cb = options.exit_cb;
788
+
789
+ err = uv_utf8_to_utf16_alloc(options.file, &application);
790
+ if (err.code != UV_OK)
791
+ goto done;
792
+
793
+ err = make_program_args(options.args,
794
+ options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
795
+ &arguments);
796
+ if (err.code != UV_OK)
797
+ goto done;
798
+
799
+ if (options.env) {
800
+ err = make_program_env(options.env, &env);
801
+ if (err.code != UV_OK)
802
+ goto done;
803
+ }
804
+
805
+ if (options.cwd) {
806
+ /* Explicit cwd */
807
+ err = uv_utf8_to_utf16_alloc(options.cwd, &cwd);
808
+ if (err.code != UV_OK)
809
+ goto done;
810
+
811
+ } else {
812
+ /* Inherit cwd */
813
+ DWORD cwd_len, r;
814
+
815
+ cwd_len = GetCurrentDirectoryW(0, NULL);
816
+ if (!cwd_len) {
817
+ err = uv__new_sys_error(GetLastError());
818
+ goto done;
819
+ }
820
+
821
+ cwd = (WCHAR*) malloc(cwd_len * sizeof(WCHAR));
822
+ if (cwd == NULL) {
823
+ err = uv__new_artificial_error(UV_ENOMEM);
824
+ goto done;
825
+ }
826
+
827
+ r = GetCurrentDirectoryW(cwd_len, cwd);
828
+ if (r == 0 || r >= cwd_len) {
829
+ err = uv__new_sys_error(GetLastError());
830
+ goto done;
831
+ }
832
+ }
833
+
834
+ /* Get PATH environment variable. */
835
+ {
836
+ DWORD path_len, r;
837
+
838
+ path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
839
+ if (path_len == 0) {
840
+ err = uv__new_sys_error(GetLastError());
841
+ goto done;
842
+ }
843
+
844
+
845
+ path = (WCHAR*) malloc(path_len * sizeof(WCHAR));
846
+ if (path == NULL) {
847
+ err = uv__new_artificial_error(UV_ENOMEM);
848
+ goto done;
849
+ }
850
+
851
+ r = GetEnvironmentVariableW(L"PATH", path, path_len);
852
+ if (r == 0 || r >= path_len) {
853
+ err = uv__new_sys_error(GetLastError());
854
+ goto done;
855
+ }
856
+ }
857
+
858
+ application_path = search_path(application,
859
+ cwd,
860
+ path);
861
+ if (application_path == NULL) {
862
+ /* Not found. */
863
+ err = uv__new_artificial_error(UV_ENOENT);
864
+ goto done;
865
+ }
866
+
867
+
868
+ err = uv__stdio_create(loop, &options, &process->child_stdio_buffer);
869
+ if (err.code != UV_OK)
870
+ goto done;
871
+
872
+ startup.cb = sizeof(startup);
873
+ startup.lpReserved = NULL;
874
+ startup.lpDesktop = NULL;
875
+ startup.lpTitle = NULL;
876
+ startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
877
+
878
+ startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer);
879
+ startup.lpReserved2 = (BYTE*) process->child_stdio_buffer;
880
+
881
+ startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0);
882
+ startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1);
883
+ startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2);
884
+
885
+ if (options.flags & UV_PROCESS_WINDOWS_HIDE) {
886
+ /* Use SW_HIDE to avoid any potential process window. */
887
+ startup.wShowWindow = SW_HIDE;
888
+ } else {
889
+ startup.wShowWindow = SW_SHOWDEFAULT;
890
+ }
891
+
892
+ process_flags = CREATE_UNICODE_ENVIRONMENT;
893
+ if (options.flags & UV_PROCESS_DETACHED) {
894
+ process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
895
+ }
896
+
897
+ if (CreateProcessW(application_path,
898
+ arguments,
899
+ NULL,
900
+ NULL,
901
+ 1,
902
+ process_flags,
903
+ env,
904
+ cwd,
905
+ &startup,
906
+ &info)) {
907
+ /* Spawn succeeded */
908
+ process->process_handle = info.hProcess;
909
+ process->pid = info.dwProcessId;
910
+
911
+ /* Set IPC pid to all IPC pipes. */
912
+ for (i = 0; i < options.stdio_count; i++) {
913
+ const uv_stdio_container_t* fdopt = &options.stdio[i];
914
+ if (fdopt->flags & UV_CREATE_PIPE &&
915
+ fdopt->data.stream->type == UV_NAMED_PIPE &&
916
+ ((uv_pipe_t*) fdopt->data.stream)->ipc) {
917
+ ((uv_pipe_t*) fdopt->data.stream)->ipc_pid = info.dwProcessId;
918
+ }
919
+ }
920
+
921
+ /* Setup notifications for when the child process exits. */
922
+ result = RegisterWaitForSingleObject(&process->wait_handle,
923
+ process->process_handle, exit_wait_callback, (void*)process, INFINITE,
924
+ WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
925
+ if (!result) {
926
+ uv_fatal_error(GetLastError(), "RegisterWaitForSingleObject");
927
+ }
928
+
929
+ CloseHandle(info.hThread);
930
+
931
+ } else {
932
+ /* CreateProcessW failed. */
933
+ err = uv__new_sys_error(GetLastError());
934
+ }
935
+
936
+ done:
937
+ free(application);
938
+ free(application_path);
939
+ free(arguments);
940
+ free(cwd);
941
+ free(env);
942
+ free(path);
943
+
944
+ process->spawn_error = err;
945
+
946
+ if (process->child_stdio_buffer != NULL) {
947
+ /* Clean up child stdio handles. */
948
+ uv__stdio_destroy(process->child_stdio_buffer);
949
+ process->child_stdio_buffer = NULL;
950
+ }
951
+
952
+ /* Make the handle active. It will remain active until the exit callback */
953
+ /* is made or the handle is closed, whichever happens first. */
954
+ uv__handle_start(process);
955
+
956
+ /* If an error happened, queue the exit req. */
957
+ if (err.code != UV_OK) {
958
+ process->exit_cb_pending = 1;
959
+ uv_insert_pending_req(loop, (uv_req_t*) &process->exit_req);
960
+ }
961
+
962
+ return 0;
963
+ }
964
+
965
+
966
+ static uv_err_t uv__kill(HANDLE process_handle, int signum) {
967
+ switch (signum) {
968
+ case SIGTERM:
969
+ case SIGKILL:
970
+ case SIGINT: {
971
+ /* Unconditionally terminate the process. On Windows, killed processes */
972
+ /* normally return 1. */
973
+ DWORD error, status;
974
+
975
+ if (TerminateProcess(process_handle, 1))
976
+ return uv_ok_;
977
+
978
+ /* If the process already exited before TerminateProcess was called, */
979
+ /* TerminateProcess will fail with ERROR_ACESS_DENIED. */
980
+ error = GetLastError();
981
+ if (error == ERROR_ACCESS_DENIED &&
982
+ GetExitCodeProcess(process_handle, &status) &&
983
+ status != STILL_ACTIVE) {
984
+ return uv__new_artificial_error(UV_ESRCH);
985
+ }
986
+
987
+ return uv__new_sys_error(error);
988
+ }
989
+
990
+ case 0: {
991
+ /* Health check: is the process still alive? */
992
+ DWORD status;
993
+
994
+ if (!GetExitCodeProcess(process_handle, &status))
995
+ return uv__new_sys_error(GetLastError());
996
+
997
+ if (status != STILL_ACTIVE)
998
+ return uv__new_artificial_error(UV_ESRCH);
999
+
1000
+ return uv_ok_;
1001
+ }
1002
+
1003
+ default:
1004
+ /* Unsupported signal. */
1005
+ return uv__new_artificial_error(UV_ENOSYS);
1006
+ }
1007
+ }
1008
+
1009
+
1010
+ int uv_process_kill(uv_process_t* process, int signum) {
1011
+ uv_err_t err;
1012
+
1013
+ if (process->process_handle == INVALID_HANDLE_VALUE) {
1014
+ uv__set_artificial_error(process->loop, UV_EINVAL);
1015
+ return -1;
1016
+ }
1017
+
1018
+ err = uv__kill(process->process_handle, signum);
1019
+
1020
+ if (err.code != UV_OK) {
1021
+ uv__set_error(process->loop, err.code, err.sys_errno_);
1022
+ return -1;
1023
+ }
1024
+
1025
+ process->exit_signal = signum;
1026
+
1027
+ return 0;
1028
+ }
1029
+
1030
+
1031
+ uv_err_t uv_kill(int pid, int signum) {
1032
+ uv_err_t err;
1033
+ HANDLE process_handle = OpenProcess(PROCESS_TERMINATE |
1034
+ PROCESS_QUERY_INFORMATION, FALSE, pid);
1035
+
1036
+ if (process_handle == NULL) {
1037
+ if (GetLastError() == ERROR_INVALID_PARAMETER) {
1038
+ return uv__new_artificial_error(UV_ESRCH);
1039
+ } else {
1040
+ return uv__new_sys_error(GetLastError());
1041
+ }
1042
+ }
1043
+
1044
+ err = uv__kill(process_handle, signum);
1045
+ CloseHandle(process_handle);
1046
+
1047
+ return err;
1048
+ }