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 <fcntl.h>
23 #include <io.h>
24 #include <malloc.h>
25 #include <stdio.h>
26 #include <process.h>
27 #if !defined(__MINGW32__)
28 # include <crtdbg.h>
29 #endif
30
31
32 #include "task.h"
33 #include "runner.h"
34
35
36 /*
37 * Define the stuff that MinGW doesn't have
38 */
39 #ifndef GetFileSizeEx
40 WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile,
41 PLARGE_INTEGER lpFileSize);
42 #endif
43
44
45 /* Do platform-specific initialization. */
platform_init(int argc,char ** argv)46 int platform_init(int argc, char **argv) {
47 /* Disable the "application crashed" popup. */
48 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
49 SEM_NOOPENFILEERRORBOX);
50 #if !defined(__MINGW32__)
51 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
52 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
53 #endif
54
55 _setmode(0, _O_BINARY);
56 _setmode(1, _O_BINARY);
57 _setmode(2, _O_BINARY);
58
59 #ifdef _MSC_VER
60 _set_fmode(_O_BINARY);
61 #else
62 _fmode = _O_BINARY;
63 #endif
64
65 /* Disable stdio output buffering. */
66 setvbuf(stdout, NULL, _IONBF, 0);
67 setvbuf(stderr, NULL, _IONBF, 0);
68
69 strcpy(executable_path, argv[0]);
70
71 return 0;
72 }
73
74
process_start(char * name,char * part,process_info_t * p,int is_helper)75 int process_start(char *name, char *part, process_info_t *p, int is_helper) {
76 HANDLE file = INVALID_HANDLE_VALUE;
77 HANDLE nul = INVALID_HANDLE_VALUE;
78 WCHAR path[MAX_PATH], filename[MAX_PATH];
79 WCHAR image[MAX_PATH + 1];
80 WCHAR args[MAX_PATH * 2];
81 STARTUPINFOW si;
82 PROCESS_INFORMATION pi;
83 DWORD result;
84
85 if (!is_helper) {
86 /* Give the helpers time to settle. Race-y, fix this. */
87 uv_sleep(250);
88 }
89
90 if (GetTempPathW(sizeof(path) / sizeof(WCHAR), (WCHAR*)&path) == 0)
91 goto error;
92 if (GetTempFileNameW((WCHAR*)&path, L"uv", 0, (WCHAR*)&filename) == 0)
93 goto error;
94
95 file = CreateFileW((WCHAR*)filename,
96 GENERIC_READ | GENERIC_WRITE,
97 0,
98 NULL,
99 CREATE_ALWAYS,
100 FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
101 NULL);
102 if (file == INVALID_HANDLE_VALUE)
103 goto error;
104
105 if (!SetHandleInformation(file, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
106 goto error;
107
108 nul = CreateFileA("nul",
109 GENERIC_READ,
110 FILE_SHARE_READ | FILE_SHARE_WRITE,
111 NULL,
112 OPEN_EXISTING,
113 FILE_ATTRIBUTE_NORMAL,
114 NULL);
115 if (nul == INVALID_HANDLE_VALUE)
116 goto error;
117
118 if (!SetHandleInformation(nul, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
119 goto error;
120
121 result = GetModuleFileNameW(NULL,
122 (WCHAR*) &image,
123 sizeof(image) / sizeof(WCHAR));
124 if (result == 0 || result == sizeof(image))
125 goto error;
126
127 if (part) {
128 if (_snwprintf((WCHAR*)args,
129 sizeof(args) / sizeof(WCHAR),
130 L"\"%s\" %S %S",
131 image,
132 name,
133 part) < 0) {
134 goto error;
135 }
136 } else {
137 if (_snwprintf((WCHAR*)args,
138 sizeof(args) / sizeof(WCHAR),
139 L"\"%s\" %S",
140 image,
141 name) < 0) {
142 goto error;
143 }
144 }
145
146 memset((void*)&si, 0, sizeof(si));
147 si.cb = sizeof(si);
148 si.dwFlags = STARTF_USESTDHANDLES;
149 si.hStdInput = nul;
150 si.hStdOutput = file;
151 si.hStdError = file;
152
153 if (!CreateProcessW(image, args, NULL, NULL, TRUE,
154 0, NULL, NULL, &si, &pi))
155 goto error;
156
157 CloseHandle(pi.hThread);
158
159 SetHandleInformation(nul, HANDLE_FLAG_INHERIT, 0);
160 SetHandleInformation(file, HANDLE_FLAG_INHERIT, 0);
161
162 p->stdio_in = nul;
163 p->stdio_out = file;
164 p->process = pi.hProcess;
165 p->name = part;
166
167 return 0;
168
169 error:
170 if (file != INVALID_HANDLE_VALUE)
171 CloseHandle(file);
172 if (nul != INVALID_HANDLE_VALUE)
173 CloseHandle(nul);
174
175 return -1;
176 }
177
178
179 /* Timeout is in msecs. Set timeout < 0 to never time out. Returns 0 when all
180 * processes are terminated, -2 on timeout. */
process_wait(process_info_t * vec,int n,int timeout)181 int process_wait(process_info_t *vec, int n, int timeout) {
182 int i;
183 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
184 DWORD timeout_api, result;
185
186 /* If there's nothing to wait for, return immediately. */
187 if (n == 0)
188 return 0;
189
190 ASSERT(n <= MAXIMUM_WAIT_OBJECTS);
191
192 for (i = 0; i < n; i++)
193 handles[i] = vec[i].process;
194
195 if (timeout >= 0) {
196 timeout_api = (DWORD)timeout;
197 } else {
198 timeout_api = INFINITE;
199 }
200
201 result = WaitForMultipleObjects(n, handles, TRUE, timeout_api);
202
203 if (result < WAIT_OBJECT_0 + n) {
204 /* All processes are terminated. */
205 return 0;
206 }
207 if (result == WAIT_TIMEOUT) {
208 return -2;
209 }
210 return -1;
211 }
212
213
process_output_size(process_info_t * p)214 long int process_output_size(process_info_t *p) {
215 LARGE_INTEGER size;
216 if (!GetFileSizeEx(p->stdio_out, &size))
217 return -1;
218 return (long int)size.QuadPart;
219 }
220
221
process_copy_output(process_info_t * p,FILE * stream)222 int process_copy_output(process_info_t* p, FILE* stream) {
223 char buf[1024];
224 int fd, r;
225
226 fd = _open_osfhandle((intptr_t)p->stdio_out, _O_RDONLY | _O_TEXT);
227 if (fd == -1)
228 return -1;
229
230 r = _lseek(fd, 0, SEEK_SET);
231 if (r < 0)
232 return -1;
233
234 while ((r = _read(fd, buf, sizeof(buf))) != 0)
235 print_lines(buf, r, stream);
236
237 _close(fd);
238 return 0;
239 }
240
241
process_read_last_line(process_info_t * p,char * buffer,size_t buffer_len)242 int process_read_last_line(process_info_t *p,
243 char * buffer,
244 size_t buffer_len) {
245 DWORD size;
246 DWORD read;
247 DWORD start;
248 OVERLAPPED overlapped;
249
250 ASSERT(buffer_len > 0);
251
252 size = GetFileSize(p->stdio_out, NULL);
253 if (size == INVALID_FILE_SIZE)
254 return -1;
255
256 if (size == 0) {
257 buffer[0] = '\0';
258 return 1;
259 }
260
261 memset(&overlapped, 0, sizeof overlapped);
262 if (size >= buffer_len)
263 overlapped.Offset = size - buffer_len - 1;
264
265 if (!ReadFile(p->stdio_out, buffer, buffer_len - 1, &read, &overlapped))
266 return -1;
267
268 start = read;
269 while (start-- > 0) {
270 if (buffer[start] == '\n' || buffer[start] == '\r')
271 break;
272 }
273
274 if (start > 0)
275 memmove(buffer, buffer + start, read - start);
276
277 buffer[read - start] = '\0';
278
279 return 0;
280 }
281
282
process_get_name(process_info_t * p)283 char* process_get_name(process_info_t *p) {
284 return p->name;
285 }
286
287
process_terminate(process_info_t * p)288 int process_terminate(process_info_t *p) {
289 if (!TerminateProcess(p->process, 1))
290 return -1;
291 return 0;
292 }
293
294
process_reap(process_info_t * p)295 int process_reap(process_info_t *p) {
296 DWORD exitCode;
297 if (!GetExitCodeProcess(p->process, &exitCode))
298 return -1;
299 return (int)exitCode;
300 }
301
302
process_cleanup(process_info_t * p)303 void process_cleanup(process_info_t *p) {
304 CloseHandle(p->process);
305 CloseHandle(p->stdio_in);
306 }
307
308
clear_line(void)309 static int clear_line(void) {
310 HANDLE handle;
311 CONSOLE_SCREEN_BUFFER_INFO info;
312 COORD coord;
313 DWORD written;
314
315 handle = (HANDLE)_get_osfhandle(fileno(stderr));
316 if (handle == INVALID_HANDLE_VALUE)
317 return -1;
318
319 if (!GetConsoleScreenBufferInfo(handle, &info))
320 return -1;
321
322 coord = info.dwCursorPosition;
323 if (coord.Y <= 0)
324 return -1;
325
326 coord.X = 0;
327
328 if (!SetConsoleCursorPosition(handle, coord))
329 return -1;
330
331 if (!FillConsoleOutputCharacterW(handle,
332 0x20,
333 info.dwSize.X,
334 coord,
335 &written)) {
336 return -1;
337 }
338
339 return 0;
340 }
341
342
rewind_cursor()343 void rewind_cursor() {
344 if (clear_line() == -1) {
345 /* If clear_line fails (stdout is not a console), print a newline. */
346 fprintf(stderr, "\n");
347 }
348 }
349