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. */ 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 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. */ 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(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 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 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 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(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 281 char* process_get_name(process_info_t *p) { 282 return p->name; 283 } 284 285 286 int process_terminate(process_info_t *p) { 287 if (!TerminateProcess(p->process, 1)) 288 return -1; 289 return 0; 290 } 291 292 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 301 void process_cleanup(process_info_t *p) { 302 CloseHandle(p->process); 303 CloseHandle(p->stdio_in); 304 } 305 306 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 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