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