1 /********************************************************************/
2 /* */
3 /* pcs_win.c Process functions which use the Windows API. */
4 /* Copyright (C) 1989 - 2014 Thomas Mertes */
5 /* */
6 /* This file is part of the Seed7 Runtime Library. */
7 /* */
8 /* The Seed7 Runtime Library is free software; you can */
9 /* redistribute it and/or modify it under the terms of the GNU */
10 /* Lesser General Public License as published by the Free Software */
11 /* Foundation; either version 2.1 of the License, or (at your */
12 /* option) any later version. */
13 /* */
14 /* The Seed7 Runtime Library is distributed in the hope that it */
15 /* will be useful, but WITHOUT ANY WARRANTY; without even the */
16 /* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /* PURPOSE. See the GNU Lesser General Public License for more */
18 /* details. */
19 /* */
20 /* You should have received a copy of the GNU Lesser General */
21 /* Public License along with this program; if not, write to the */
22 /* Free Software Foundation, Inc., 51 Franklin Street, */
23 /* Fifth Floor, Boston, MA 02110-1301, USA. */
24 /* */
25 /* Module: Seed7 Runtime Library */
26 /* File: seed7/src/pcs_win.c */
27 /* Changes: 2010, 2012 - 2014 Thomas Mertes */
28 /* Content: Process functions which use the Windows API. */
29 /* */
30 /********************************************************************/
31
32 #define LOG_FUNCTIONS 0
33 #define VERBOSE_EXCEPTIONS 0
34
35 #include "version.h"
36
37 #include "stdlib.h"
38 #include "stdio.h"
39 #include "string.h"
40 #include "windows.h"
41 #include "io.h"
42 #include "fcntl.h"
43 #include "wchar.h"
44 #include "ctype.h"
45 #include "errno.h"
46
47 #include "common.h"
48 #include "data_rtl.h"
49 #include "os_decls.h"
50 #include "heaputl.h"
51 #include "striutl.h"
52 #include "int_rtl.h"
53 #include "fil_rtl.h"
54 #include "rtl_err.h"
55
56
57 #define MAXIMUM_COMMAND_LINE_LENGTH 32768
58
59 typedef struct {
60 uintType usage_count;
61 fileType stdIn;
62 fileType stdOut;
63 fileType stdErr;
64 /* Up to here the structure is identical to struct processStruct */
65 HANDLE hProcess;
66 HANDLE hThread;
67 DWORD pid;
68 boolType isTerminated;
69 DWORD exitValue;
70 } win_processRecord, *win_processType;
71
72 typedef const win_processRecord *const_win_processType;
73
74 #if DO_HEAP_STATISTIC
75 size_t sizeof_processRecord = sizeof(win_processRecord);
76 #endif
77
78 #define to_hProcess(process) (((const_win_processType) process)->hProcess)
79 #define to_hThread(process) (((const_win_processType) process)->hThread)
80 #define to_pid(process) (((const_win_processType) process)->pid)
81 #define to_isTerminated(process) (((const_win_processType) process)->isTerminated)
82 #define to_exitValue(process) (((const_win_processType) process)->exitValue)
83
84 #define to_var_hProcess(process) (((win_processType) process)->hProcess)
85 #define to_var_hThread(process) (((win_processType) process)->hThread)
86 #define to_var_pid(process) (((win_processType) process)->pid)
87 #define to_var_isTerminated(process) (((win_processType) process)->isTerminated)
88 #define to_var_exitValue(process) (((win_processType) process)->exitValue)
89
90 #if POINTER_SIZE == 32
91 typedef int32Type intPtrType;
92 #elif POINTER_SIZE == 64
93 typedef int64Type intPtrType;
94 #endif
95
96
97
98 #if ANY_LOG_ACTIVE
printParameters(const const_rtlArrayType parameters)99 static void printParameters (const const_rtlArrayType parameters)
100
101 {
102 memSizeType paramSize;
103 memSizeType pos;
104
105 /* printParameters */
106 paramSize = arraySize(parameters);
107 for (pos = 0; pos < paramSize; pos++) {
108 printf(", \"%s\"",
109 striAsUnquotedCStri(parameters->arr[pos].value.striValue));
110 } /* for */
111 } /* printParameters */
112 #endif
113
114
115
116 /**
117 * Fill the contents of a quoted part to be used by prepareCommandLine.
118 * This function does not create the surrounding quotations (").
119 * A string with the backslash logic of Windows commandline parameters
120 * is produced. 2 * N backslashes followed by a quotation (") means
121 * N backslashes and the end of the parameter. Note that in this case
122 * the quotation is written by prepareCommandLine. 2 * N + 1 backslashes
123 * followed by a quotation (") means N backslashes and the quotation
124 * is part of the parameter. N backslashes not followed by a quotation
125 * means just N backslashes.
126 */
copyQuotedPart(os_charType * sourceChar,os_charType * destChar,os_charType * beyondDest)127 static os_charType *copyQuotedPart (os_charType *sourceChar, os_charType *destChar,
128 os_charType *beyondDest)
129
130 {
131 memSizeType countBackslash;
132
133 /* copyQuotedPart */
134 for (; *sourceChar != '\0' && destChar < beyondDest;
135 sourceChar++, destChar++) {
136 if (*sourceChar == '"') {
137 if (&destChar[2] > beyondDest) {
138 destChar = beyondDest;
139 } else {
140 *(destChar++) = '\\';
141 *destChar = *sourceChar;
142 } /* if */
143 } else if (*sourceChar == '\\') {
144 sourceChar++;
145 countBackslash = 1;
146 while (*sourceChar == '\\') {
147 sourceChar++;
148 countBackslash++;
149 } /* while */
150 /* fprintf(stderr, "countBackslash=" FMT_U_MEM "\n", countBackslash);
151 fprintf(stderr, "sourceChar=%c\n", *sourceChar); */
152 if (*sourceChar == '"' || *sourceChar == '\0') {
153 countBackslash *= 2;
154 } /* if */
155 sourceChar--;
156 if (countBackslash > MAXIMUM_COMMAND_LINE_LENGTH ||
157 &destChar[countBackslash] > beyondDest) {
158 destChar = beyondDest;
159 } else {
160 do {
161 *(destChar++) = '\\';
162 countBackslash--;
163 } while (countBackslash != 0);
164 destChar--;
165 } /* if */
166 } else {
167 *destChar = *sourceChar;
168 } /* if */
169 } /* for */
170 return destChar;
171 } /* copyQuotedPart */
172
173
174
175 /**
176 * Create a command line string that can be used by CreateProcessW().
177 * All parameters that contain a space or a quotation (") or a control
178 * character or a character byond ASCII are quoted. All other parameters
179 * are not quoted. The command line string must be freed with os_stri_free().
180 * @param err_info Unchanged if the function succeeds, and
181 * MEMORY_ERROR if a memory allocation failed, and
182 * RANGE_ERROR if the conversion of a parameter failed.
183 * @return a null terminated os_striType command line, or
184 * NULL if an error occurred.
185 */
prepareCommandLine(const const_os_striType os_command_stri,const const_rtlArrayType parameters,errInfoType * err_info)186 static os_striType prepareCommandLine (const const_os_striType os_command_stri,
187 const const_rtlArrayType parameters, errInfoType *err_info)
188
189 {
190 const_os_striType command_stri;
191 memSizeType arraySize;
192 memSizeType striSize;
193 memSizeType pos;
194 os_striType argument;
195 os_charType *sourceChar;
196 os_charType *destChar;
197 os_charType *beyondDest;
198 boolType quoteArgument;
199 os_striType command_line;
200
201 /* prepareCommandLine */
202 logFunction(printf("prepareCommandLine(\"" FMT_S_OS "\"",
203 os_command_stri);
204 printParameters(parameters);
205 printf(", %d)\n", *err_info););
206 arraySize = arraySize(parameters);
207 if (unlikely(!os_stri_alloc(command_line, MAXIMUM_COMMAND_LINE_LENGTH - 1))) {
208 *err_info = MEMORY_ERROR;
209 } else {
210 beyondDest = &command_line[MAXIMUM_COMMAND_LINE_LENGTH];
211 if (USE_EXTENDED_LENGTH_PATH &&
212 memcmp(os_command_stri, PATH_PREFIX, PREFIX_LEN * sizeof(os_charType)) == 0) {
213 /* For extended path omit the prefix. */
214 command_stri = &os_command_stri[PREFIX_LEN];
215 } else {
216 command_stri = os_command_stri;
217 } /* if */
218 /* fprintf(stderr, "\ncommand_stri=\"%ls\"\n", command_stri); */
219 #ifdef USE_MODULE_NAME_FOR_CREATE_PROCESS
220 /* Pelles C cannot start the compiler (POCC.EXE) without this fix. */
221 {
222 os_charType *lastPathDelimiter;
223
224 lastPathDelimiter = os_stri_strrchr(command_stri, OS_PATH_DELIMITER);
225 if (lastPathDelimiter != NULL) {
226 command_stri = &lastPathDelimiter[1];
227 } /* if */
228 }
229 #endif
230 striSize = os_stri_strlen(command_stri);
231 if (striSize > MAXIMUM_COMMAND_LINE_LENGTH - 2 ||
232 &command_line[striSize] > beyondDest) {
233 *err_info = MEMORY_ERROR;
234 destChar = beyondDest;
235 } else {
236 command_line[0] = '\"';
237 memcpy(&command_line[1], command_stri, sizeof(os_charType) * striSize);
238 command_line[striSize + 1] = '\"';
239 destChar = &command_line[striSize + 2];
240 } /* if */
241 for (pos = 0; pos < arraySize && *err_info == OKAY_NO_ERROR; pos++) {
242 argument = stri_to_os_stri(parameters->arr[pos].value.striValue, err_info);
243 if (argument != NULL) {
244 /* fprintf(stderr, "argument[%d]=%ls\n", pos + 1, argument); */
245 quoteArgument = FALSE;
246 for (sourceChar = argument; *sourceChar != '\0'; sourceChar++) {
247 if (*sourceChar <= ' ' || *sourceChar > '~' || *sourceChar == '"') {
248 quoteArgument = TRUE;
249 } /* if */
250 } /* for */
251 if (quoteArgument) {
252 if (&destChar[2] > beyondDest) {
253 destChar = beyondDest;
254 } else {
255 *(destChar++) = ' ';
256 *(destChar++) = '"';
257 } /* if */
258 destChar = copyQuotedPart(argument, destChar, beyondDest);
259 if (destChar >= beyondDest) {
260 *err_info = MEMORY_ERROR;
261 } else {
262 *(destChar++) = '"';
263 } /* if */
264 } else {
265 if (destChar < beyondDest) {
266 *(destChar++) = ' ';
267 } /* if */
268 for (sourceChar = argument; *sourceChar != '\0' && destChar < beyondDest;
269 sourceChar++, destChar++) {
270 *destChar = *sourceChar;
271 } /* for */
272 } /* if */
273 os_stri_free(argument);
274 } /* if */
275 } /* for */
276 if (destChar >= beyondDest) {
277 *err_info = MEMORY_ERROR;
278 } else {
279 *destChar = '\0';
280 } /* if */
281 if (unlikely(*err_info != OKAY_NO_ERROR)) {
282 os_stri_free(command_line);
283 command_line = NULL;
284 } else {
285 /* fprintf(stderr, "command_line=%ls\n", command_line); */
286 } /* if */
287 } /* if */
288 logFunction(printf("prepareCommandLine --> \"" FMT_S_OS "\"\n",
289 command_line););
290 return command_line;
291 } /* prepareCommandLine */
292
293
294
295 /**
296 * Compare two processes.
297 * @return -1, 0 or 1 if the first argument is considered to be
298 * respectively less than, equal to, or greater than the
299 * second.
300 */
pcsCmp(const const_processType process1,const const_processType process2)301 intType pcsCmp (const const_processType process1, const const_processType process2)
302
303 {
304 intType signumValue;
305
306 /* pcsCmp */
307 if (process1 == NULL) {
308 if (process2 != NULL) {
309 signumValue = -1;
310 } else {
311 signumValue = 0;
312 } /* if */
313 } else if (process2 == NULL) {
314 signumValue = 1;
315 } else if (to_pid(process1) < to_pid(process2)) {
316 signumValue = -1;
317 } else {
318 signumValue = to_pid(process1) > to_pid(process2);
319 } /* if */
320 return signumValue;
321 } /* pcsCmp */
322
323
324
325 /**
326 * Check if two processes are equal.
327 * @return TRUE if both processes are equal,
328 * FALSE otherwise.
329 */
pcsEq(const const_processType process1,const const_processType process2)330 boolType pcsEq (const const_processType process1, const const_processType process2)
331
332 { /* pcsEq */
333 if (process1 == NULL) {
334 return process2 == NULL;
335 } else if (process2 == NULL) {
336 return FALSE;
337 } else {
338 return to_pid(process1) == to_pid(process2);
339 } /* if */
340 } /* pcsEq */
341
342
343
344 /**
345 * Return the exit value of the specified process.
346 * By convention, the value 0 indicates normal termination.
347 * @return the exit value of the specified process.
348 */
pcsExitValue(const const_processType process)349 intType pcsExitValue (const const_processType process)
350
351 {
352 intType exitValue;
353
354 /* pcsExitValue */
355 if (unlikely(!to_isTerminated(process))) {
356 raise_error(FILE_ERROR);
357 exitValue = -1;
358 } else {
359 exitValue = (intType) to_exitValue(process);
360 } /* if */
361 return exitValue;
362 } /* pcsExitValue */
363
364
365
366 /**
367 * Free the memory referred by 'oldProcess'.
368 * After pcsFree is left 'oldProcess' refers to not existing memory.
369 * The memory where 'oldProcess' is stored can be freed afterwards.
370 */
pcsFree(processType oldProcess)371 void pcsFree (processType oldProcess)
372
373 { /* pcsFree */
374 logFunction(printf("pcsFree(" FMT_U32 ") (usage=" FMT_U ")\n",
375 (uint32Type) (oldProcess != NULL ? to_pid(oldProcess) : 0),
376 oldProcess != NULL ? oldProcess->usage_count : (uintType) 0););
377 CloseHandle(to_hProcess(oldProcess));
378 CloseHandle(to_hThread(oldProcess));
379 FREE_RECORD(oldProcess, win_processRecord, count.process);
380 } /* pcsFree */
381
382
383
384 /**
385 * Compute the hash value of a process.
386 * @return the hash value.
387 */
pcsHashCode(const const_processType process)388 intType pcsHashCode (const const_processType process)
389
390 {
391 intType hashCode;
392
393 /* pcsHashCode */
394 if (process == NULL) {
395 hashCode = 0;
396 } else {
397 hashCode = to_pid(process);
398 } /* if */
399 return hashCode;
400 } /* pcsHashCode */
401
402
403
404 /**
405 * Test whether the specified process is alive.
406 * @return TRUE if the specified process has not yet terminated,
407 * FALSE otherwise.
408 */
pcsIsAlive(const processType process)409 boolType pcsIsAlive (const processType process)
410
411 {
412 DWORD exitCode = 0;
413 boolType isAlive;
414
415 /* pcsIsAlive */
416 logFunction(printf("pcsIsAlive(" FMT_U32 ") (hProcess=" FMT_U_MEM ")\n",
417 (uint32Type) (process != NULL ? to_pid(process) : 0),
418 process != NULL ? (memSizeType) to_hProcess(process) : (memSizeType) 0););
419 if (to_isTerminated(process)) {
420 isAlive = FALSE;
421 } else {
422 if (GetExitCodeProcess(to_hProcess(process), &exitCode) != 0) {
423 if (exitCode == STILL_ACTIVE) {
424 if (WaitForSingleObject(to_hProcess(process), 0) == WAIT_OBJECT_0) {
425 to_var_isTerminated(process) = TRUE;
426 } /* if */
427 } else {
428 to_var_isTerminated(process) = TRUE;
429 } /* if */
430 isAlive = !to_isTerminated(process);
431 if (!isAlive) {
432 to_var_exitValue(process) = exitCode;
433 } /* if */
434 } else {
435 logError(printf("pcsIsAlive: GetExitCodeProcess(" FMT_U_MEM ", 0) failed:\n"
436 "GetLastError=" FMT_U32 "\n",
437 (memSizeType) to_hProcess(process), (uint32Type) GetLastError());
438 printf("PID=" FMT_U32 "\n", (uint32Type) to_pid(process)););
439 raise_error(FILE_ERROR);
440 isAlive = TRUE;
441 } /* if */
442 } /* if */
443 return isAlive;
444 } /* pcsIsAlive */
445
446
447
448 /**
449 * Kill the specified process.
450 * @exception FILE_ERROR It was not possible to kill the process.
451 */
pcsKill(const processType process)452 void pcsKill (const processType process)
453
454 { /* pcsKill */
455 logFunction(printf("pcsKill(" FMT_U32 ") (hProcess=" FMT_U_MEM ")\n",
456 (uint32Type) (process != NULL ? to_pid(process) : 0),
457 process != NULL ? (memSizeType) to_hProcess(process) : (memSizeType) 0););
458 if (unlikely(process == NULL)) {
459 logError(printf("pcsKill: process == NULL\n"););
460 raise_error(FILE_ERROR);
461 } else if (unlikely(TerminateProcess(to_hProcess(process), 0) == 0)) {
462 logError(printf("pcsKill: TerminateProcess(" FMT_U_MEM ", 0) failed:\n"
463 "GetLastError=" FMT_U32 "\n",
464 (memSizeType) to_hProcess(process), (uint32Type) GetLastError());
465 printf("PID=" FMT_U32 "\n", (uint32Type) to_pid(process)););
466 raise_error(FILE_ERROR);
467 } /* if */
468 } /* pcsKill */
469
470
471
pcsPipe2(const const_striType command,const const_rtlArrayType parameters,fileType * childStdin,fileType * childStdout)472 void pcsPipe2 (const const_striType command, const const_rtlArrayType parameters,
473 fileType *childStdin, fileType *childStdout)
474
475 {
476 os_striType os_command_stri;
477 os_striType command_line;
478 fileType childStdinFile = NULL;
479 fileType childStdoutFile = NULL;
480 SECURITY_ATTRIBUTES saAttr;
481 HANDLE childInputRead = INVALID_HANDLE_VALUE;
482 HANDLE childInputWrite = INVALID_HANDLE_VALUE;
483 HANDLE childOutputRead = INVALID_HANDLE_VALUE;
484 HANDLE childOutputWrite = INVALID_HANDLE_VALUE;
485 STARTUPINFOW startupInfo;
486 PROCESS_INFORMATION processInformation;
487 int path_info = PATH_IS_NORMAL;
488 errInfoType err_info = OKAY_NO_ERROR;
489
490 /* pcsPipe2 */
491 logFunction(printf("pcsPipe2(\"%s\"", striAsUnquotedCStri(command));
492 printParameters(parameters);
493 printf(")\n"););
494 os_command_stri = cp_to_os_path(command, &path_info, &err_info);
495 if (likely(os_command_stri != NULL)) {
496 command_line = prepareCommandLine(os_command_stri, parameters, &err_info);
497 if (likely(command_line != NULL)) {
498 if (unlikely(!ALLOC_RECORD(childStdinFile, fileRecord, count.files) ||
499 !ALLOC_RECORD(childStdoutFile, fileRecord, count.files))) {
500 err_info = MEMORY_ERROR;
501 } else {
502 /* printf("pcsPipe2(%ls, %ls)\n", os_command_stri, command_line); */
503 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
504 saAttr.bInheritHandle = TRUE;
505 saAttr.lpSecurityDescriptor = NULL;
506 if (unlikely(CreatePipe(&childInputRead,
507 &childInputWrite, &saAttr, 0) == 0 ||
508 CreatePipe(&childOutputRead,
509 &childOutputWrite, &saAttr, 0) == 0)) {
510 logError(printf("pcsPipe2(\"%s\", ...): CreatePipe() failed.\n",
511 striAsUnquotedCStri(command)););
512 err_info = FILE_ERROR;
513 } else if (unlikely(SetHandleInformation(childInputWrite,
514 HANDLE_FLAG_INHERIT, 0) == 0 ||
515 SetHandleInformation(childOutputRead,
516 HANDLE_FLAG_INHERIT, 0) == 0)) {
517 logError(printf("pcsPipe2(\"%s\", ...): SetHandleInformation() failed.\n",
518 striAsUnquotedCStri(command)););
519 err_info = FILE_ERROR;
520 } else {
521 memset(&startupInfo, 0, sizeof(startupInfo));
522 /* memset(&processInformation, 0, sizeof(processInformation)); */
523 startupInfo.cb = sizeof(startupInfo);
524 startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
525 startupInfo.wShowWindow = SW_HIDE;
526 startupInfo.hStdInput = childInputRead;
527 startupInfo.hStdOutput = childOutputWrite;
528 startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);;
529 logMessage(printf("before CreateProcessW(\"" FMT_S_OS "\", \"" FMT_S_OS "\", ...)\n",
530 os_command_stri, command_line););
531 if (CreateProcessW(os_command_stri,
532 command_line /* lpCommandLine */,
533 NULL /* lpProcessAttributes */,
534 NULL /* lpThreadAttributes */,
535 1 /* bInheritHandles */,
536 CREATE_UNICODE_ENVIRONMENT /* dwCreationFlags */,
537 NULL /* lpEnvironment */,
538 NULL /* lpCurrentDirectory */,
539 &startupInfo,
540 &processInformation) != 0) {
541 CloseHandle(childInputRead);
542 CloseHandle(childOutputWrite);
543 filDestr(*childStdin);
544 initFileType(childStdinFile, 1);
545 childStdinFile->cFile = fdopen(_open_osfhandle((intPtrType) (childInputWrite), _O_TEXT), "w");
546 *childStdin = childStdinFile;
547 filDestr(*childStdout);
548 initFileType(childStdoutFile, 1);
549 childStdoutFile->cFile = fdopen(_open_osfhandle((intPtrType) (childOutputRead), _O_TEXT), "r");
550 *childStdout = childStdoutFile;
551 CloseHandle(processInformation.hProcess);
552 CloseHandle(processInformation.hThread);
553 } else {
554 logError(printf("pcsPipe2: CreateProcessW(\"" FMT_S_OS "\", \"" FMT_S_OS "\", ...) failed.\n"
555 "GetLastError=" FMT_U32 "\n",
556 os_command_stri, command_line, (uint32Type) GetLastError());
557 printf("ERROR_FILE_NOT_FOUND=%d\n", (int) ERROR_FILE_NOT_FOUND););
558 err_info = FILE_ERROR;
559 } /* if */
560 /* printf("after CreateProcessW\n"); */
561 } /* if */
562 } /* if */
563 os_stri_free(command_line);
564 } /* if */
565 os_stri_free(os_command_stri);
566 } /* if */
567 if (unlikely(err_info != OKAY_NO_ERROR)) {
568 if (childStdinFile != NULL) {
569 FREE_RECORD(childStdinFile, fileRecord, count.files);
570 } /* if */
571 if (childStdoutFile != NULL) {
572 FREE_RECORD(childStdoutFile, fileRecord, count.files);
573 } /* if */
574 if (childInputRead != INVALID_HANDLE_VALUE) {
575 CloseHandle(childInputRead);
576 } /* if */
577 if (childInputWrite != INVALID_HANDLE_VALUE) {
578 CloseHandle(childInputWrite);
579 } /* if */
580 if (childOutputRead != INVALID_HANDLE_VALUE) {
581 CloseHandle(childOutputRead);
582 } /* if */
583 if (childOutputWrite != INVALID_HANDLE_VALUE) {
584 CloseHandle(childOutputWrite);
585 } /* if */
586 raise_error(err_info);
587 } /* if */
588 logFunction(printf("pcsPipe2 ->\n"););
589 } /* pcsPipe2 */
590
591
592
pcsPty(const const_striType command,const const_rtlArrayType parameters,fileType * childStdin,fileType * childStdout)593 void pcsPty (const const_striType command, const const_rtlArrayType parameters,
594 fileType *childStdin, fileType *childStdout)
595
596 { /* pcsPty */
597 pcsPipe2(command, parameters, childStdin, childStdout);
598 } /* pcsPty */
599
600
601
getHandleFromFile(cFileType aFile,errInfoType * err_info)602 static HANDLE getHandleFromFile (cFileType aFile, errInfoType *err_info)
603
604 {
605 int file_no;
606 HANDLE fileHandle;
607
608 /* getHandleFromFile */
609 if (aFile == NULL) {
610 fileHandle = CreateFile(NULL_DEVICE, GENERIC_READ | GENERIC_WRITE,
611 FILE_SHARE_READ | FILE_SHARE_WRITE,
612 NULL, OPEN_EXISTING, 0, NULL);
613 if (unlikely(fileHandle == INVALID_HANDLE_VALUE)) {
614 logError(printf("CreateFile(\"nul:\", ...) failed.\n"););
615 } /* if */
616 } else {
617 file_no = fileno(aFile);
618 if (unlikely(file_no == -1)) {
619 logError(printf("getHandleFromFile(%d): fileno(%d) failed:\n"
620 "errno=%d\nerror: %s\n",
621 safe_fileno(aFile), safe_fileno(aFile),
622 errno, strerror(errno)););
623 *err_info = FILE_ERROR;
624 fileHandle = INVALID_HANDLE_VALUE;
625 } else {
626 fileHandle = (HANDLE) _get_osfhandle(file_no);
627 if (unlikely(fileHandle == INVALID_HANDLE_VALUE)) {
628 logError(printf("getHandleFromFile(%d): _get_osfhandle(%d) failed:\n"
629 "errno=%d\nerror: %s\n",
630 safe_fileno(aFile), safe_fileno(aFile),
631 errno, strerror(errno)););
632 *err_info = FILE_ERROR;
633 } /* if */
634 } /* if */
635 } /* if */
636 return fileHandle;
637 } /* getHandleFromFile */
638
639
640
pcsStart(const const_striType command,const const_rtlArrayType parameters,fileType redirectStdin,fileType redirectStdout,fileType redirectStderr)641 processType pcsStart (const const_striType command, const const_rtlArrayType parameters,
642 fileType redirectStdin, fileType redirectStdout, fileType redirectStderr)
643
644 {
645 cFileType childStdin;
646 cFileType childStdout;
647 cFileType childStderr;
648 os_striType os_command_stri;
649 os_striType command_line;
650 STARTUPINFOW startupInfo;
651 PROCESS_INFORMATION processInformation;
652 int path_info = PATH_IS_NORMAL;
653 HANDLE stdinFileHandle;
654 HANDLE stdoutFileHandle;
655 HANDLE stderrFileHandle;
656 errInfoType err_info = OKAY_NO_ERROR;
657 win_processType process = NULL;
658
659 /* pcsStart */
660 logFunction(printf("pcsStart(\"%s\"", striAsUnquotedCStri(command));
661 printParameters(parameters);
662 printf(", %s%d, %s%d, %s%d)\n",
663 redirectStdin == NULL ? "NULL " : "",
664 redirectStdin != NULL ? safe_fileno(redirectStdin->cFile) : 0,
665 redirectStdout == NULL ? "NULL " : "",
666 redirectStdout != NULL ? safe_fileno(redirectStdout->cFile) : 0,
667 redirectStderr == NULL ? "NULL " : "",
668 redirectStderr != NULL ? safe_fileno(redirectStderr->cFile) : 0););
669 childStdin = redirectStdin->cFile;
670 childStdout = redirectStdout->cFile;
671 childStderr = redirectStderr->cFile;
672 stdinFileHandle = getHandleFromFile(childStdin, &err_info);
673 stdoutFileHandle = getHandleFromFile(childStdout, &err_info);
674 stderrFileHandle = getHandleFromFile(childStderr, &err_info);
675 if (likely(err_info == OKAY_NO_ERROR)) {
676 os_command_stri = cp_to_os_path(command, &path_info, &err_info);
677 if (likely(os_command_stri != NULL)) {
678 command_line = prepareCommandLine(os_command_stri, parameters, &err_info);
679 if (likely(command_line != NULL)) {
680 if (!ALLOC_RECORD(process, win_processRecord, count.process)) {
681 err_info = MEMORY_ERROR;
682 } else {
683 memset(&startupInfo, 0, sizeof(startupInfo));
684 /* memset(&processInformation, 0, sizeof(processInformation)); */
685 startupInfo.cb = sizeof(startupInfo);
686 startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
687 startupInfo.wShowWindow = SW_SHOWNORMAL;
688 startupInfo.hStdInput = stdinFileHandle;
689 startupInfo.hStdOutput = stdoutFileHandle;
690 startupInfo.hStdError = stderrFileHandle;
691 logMessage(printf("before CreateProcessW(\"" FMT_S_OS "\", \"" FMT_S_OS "\", ...)\n",
692 os_command_stri, command_line););
693 if (CreateProcessW(os_command_stri,
694 command_line /* lpCommandLine */,
695 NULL /* lpProcessAttributes */,
696 NULL /* lpThreadAttributes */,
697 1 /* bInheritHandles */,
698 CREATE_UNICODE_ENVIRONMENT /* dwCreationFlags */,
699 NULL /* lpEnvironment */,
700 NULL /* lpCurrentDirectory */,
701 &startupInfo,
702 &processInformation) != 0) {
703 /* printf("pcsStart: pProcess=" FMT_U_MEM "\n",
704 (memSizeType) (processInformation.hProcess)); */
705 /* printf("pcsStart: PID=" FMT_U32 "\n", processInformation.dwProcessId); */
706 memset(process, 0, sizeof(win_processRecord));
707 process->usage_count = 1;
708 process->hProcess = processInformation.hProcess;
709 process->hThread = processInformation.hThread;
710 process->pid = processInformation.dwProcessId;
711 process->isTerminated = FALSE;
712 } else {
713 logError(printf("pcsStart: CreateProcessW(\"" FMT_S_OS "\", \"" FMT_S_OS "\", ...) failed.\n"
714 "GetLastError=" FMT_U32 "\n",
715 os_command_stri, command_line, (uint32Type) GetLastError());
716 printf("ERROR_FILE_NOT_FOUND=%d\n", (int) ERROR_FILE_NOT_FOUND););
717 FREE_RECORD(process, win_processRecord, count.process);
718 process = NULL;
719 err_info = FILE_ERROR;
720 } /* if */
721 /* printf("after CreateProcessW\n"); */
722 } /* if */
723 os_stri_free(command_line);
724 } /* if */
725 os_stri_free(os_command_stri);
726 } /* if */
727 } /* if */
728 if (childStdin == NULL && stdinFileHandle != INVALID_HANDLE_VALUE) {
729 CloseHandle(stdinFileHandle);
730 } /* if */
731 if (childStdout == NULL && stdoutFileHandle != INVALID_HANDLE_VALUE) {
732 CloseHandle(stdoutFileHandle);
733 } /* if */
734 if (childStderr == NULL && stderrFileHandle != INVALID_HANDLE_VALUE) {
735 CloseHandle(stderrFileHandle);
736 } /* if */
737 if (unlikely(err_info != OKAY_NO_ERROR)) {
738 raise_error(err_info);
739 } /* if */
740 logFunction(printf("pcsStart -> " FMT_U32 "\n",
741 (uint32Type) (process != NULL ? process->pid : 0)););
742 return (processType) process;
743 } /* pcsStart */
744
745
746
pcsStartPipe(const const_striType command,const const_rtlArrayType parameters)747 processType pcsStartPipe (const const_striType command, const const_rtlArrayType parameters)
748
749 {
750 os_striType os_command_stri;
751 os_striType command_line;
752 fileType childStdinFile = NULL;
753 fileType childStdoutFile = NULL;
754 fileType childStderrFile = NULL;
755 SECURITY_ATTRIBUTES saAttr;
756 HANDLE childInputRead = INVALID_HANDLE_VALUE;
757 HANDLE childInputWrite = INVALID_HANDLE_VALUE;
758 HANDLE childOutputRead = INVALID_HANDLE_VALUE;
759 HANDLE childOutputWrite = INVALID_HANDLE_VALUE;
760 HANDLE childErrorRead = INVALID_HANDLE_VALUE;
761 HANDLE childErrorWrite = INVALID_HANDLE_VALUE;
762 STARTUPINFOW startupInfo;
763 PROCESS_INFORMATION processInformation;
764 int path_info = PATH_IS_NORMAL;
765 errInfoType err_info = OKAY_NO_ERROR;
766 win_processType process = NULL;
767
768 /* pcsStartPipe */
769 logFunction(printf("pcsStartPipe(\"%s\"", striAsUnquotedCStri(command));
770 printParameters(parameters);
771 printf(")\n"););
772 os_command_stri = cp_to_os_path(command, &path_info, &err_info);
773 if (likely(os_command_stri != NULL)) {
774 command_line = prepareCommandLine(os_command_stri, parameters, &err_info);
775 if (likely(command_line != NULL)) {
776 if (unlikely(!ALLOC_RECORD(process, win_processRecord, count.process) ||
777 !ALLOC_RECORD(childStdinFile, fileRecord, count.files) ||
778 !ALLOC_RECORD(childStdoutFile, fileRecord, count.files) ||
779 !ALLOC_RECORD(childStderrFile, fileRecord, count.files))) {
780 err_info = MEMORY_ERROR;
781 } else {
782 /* printf("pcsStartPipe(%ls, %ls)\n", os_command_stri, command_line); */
783 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
784 saAttr.bInheritHandle = TRUE;
785 saAttr.lpSecurityDescriptor = NULL;
786 if (unlikely(CreatePipe(&childInputRead,
787 &childInputWrite, &saAttr, 0) == 0 ||
788 CreatePipe(&childOutputRead,
789 &childOutputWrite, &saAttr, 0) == 0 ||
790 CreatePipe(&childErrorRead,
791 &childErrorWrite, &saAttr, 0) == 0)) {
792 logError(printf("pcsPipe2(\"%s\", ...): CreatePipe() failed.\n",
793 striAsUnquotedCStri(command)););
794 err_info = FILE_ERROR;
795 } else if (unlikely(SetHandleInformation(childInputWrite,
796 HANDLE_FLAG_INHERIT, 0) == 0 ||
797 SetHandleInformation(childOutputRead,
798 HANDLE_FLAG_INHERIT, 0) == 0 ||
799 SetHandleInformation(childErrorRead,
800 HANDLE_FLAG_INHERIT, 0) == 0)) {
801 logError(printf("pcsPipe2(\"%s\", ...): SetHandleInformation() failed.\n",
802 striAsUnquotedCStri(command)););
803 err_info = FILE_ERROR;
804 } else {
805 memset(&startupInfo, 0, sizeof(startupInfo));
806 /* memset(&processInformation, 0, sizeof(processInformation)); */
807 startupInfo.cb = sizeof(startupInfo);
808 startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
809 startupInfo.wShowWindow = SW_HIDE;
810 startupInfo.hStdInput = childInputRead;
811 startupInfo.hStdOutput = childOutputWrite;
812 startupInfo.hStdError = childErrorWrite;
813 logMessage(printf("before CreateProcessW(\"" FMT_S_OS "\", \"" FMT_S_OS "\", ...)\n",
814 os_command_stri, command_line););
815 if (CreateProcessW(os_command_stri,
816 command_line /* lpCommandLine */,
817 NULL /* lpProcessAttributes */,
818 NULL /* lpThreadAttributes */,
819 1 /* bInheritHandles */,
820 CREATE_UNICODE_ENVIRONMENT /* dwCreationFlags */,
821 NULL /* lpEnvironment */,
822 NULL /* lpCurrentDirectory */,
823 &startupInfo,
824 &processInformation) != 0) {
825 CloseHandle(childInputRead);
826 CloseHandle(childOutputWrite);
827 CloseHandle(childErrorWrite);
828 memset(process, 0, sizeof(win_processRecord));
829 process->usage_count = 1;
830 process->hProcess = processInformation.hProcess;
831 process->hThread = processInformation.hThread;
832 process->pid = processInformation.dwProcessId;
833 process->isTerminated = FALSE;
834 initFileType(childStdinFile, 1);
835 childStdinFile->cFile = fdopen(_open_osfhandle((intPtrType) (childInputWrite), _O_TEXT), "w");
836 process->stdIn = childStdinFile;
837 initFileType(childStdoutFile, 1);
838 childStdoutFile->cFile = fdopen(_open_osfhandle((intPtrType) (childOutputRead), _O_TEXT), "r");
839 process->stdOut = childStdoutFile;
840 initFileType(childStderrFile, 1);
841 childStderrFile->cFile = fdopen(_open_osfhandle((intPtrType) (childErrorRead), _O_TEXT), "r");
842 process->stdErr = childStderrFile;
843 } else {
844 logError(printf("pcsStartPipe: CreateProcessW(\"" FMT_S_OS "\", \"" FMT_S_OS "\", ...) failed.\n"
845 "GetLastError=" FMT_U32 "\n",
846 os_command_stri, command_line, (uint32Type) GetLastError());
847 printf("ERROR_FILE_NOT_FOUND=%d\n", (int) ERROR_FILE_NOT_FOUND););
848 FREE_RECORD(process, win_processRecord, count.process);
849 process = NULL;
850 err_info = FILE_ERROR;
851 } /* if */
852 /* printf("after CreateProcessW\n"); */
853 } /* if */
854 } /* if */
855 os_stri_free(command_line);
856 } /* if */
857 os_stri_free(os_command_stri);
858 } /* if */
859 if (unlikely(err_info != OKAY_NO_ERROR)) {
860 if (childStdinFile != NULL) {
861 FREE_RECORD(childStdinFile, fileRecord, count.files);
862 } /* if */
863 if (childStdoutFile != NULL) {
864 FREE_RECORD(childStdoutFile, fileRecord, count.files);
865 } /* if */
866 if (childStderrFile != NULL) {
867 FREE_RECORD(childStderrFile, fileRecord, count.files);
868 } /* if */
869 if (childInputRead != INVALID_HANDLE_VALUE) {
870 CloseHandle(childInputRead);
871 } /* if */
872 if (childInputWrite != INVALID_HANDLE_VALUE) {
873 CloseHandle(childInputWrite);
874 } /* if */
875 if (childOutputRead != INVALID_HANDLE_VALUE) {
876 CloseHandle(childOutputRead);
877 } /* if */
878 if (childOutputWrite != INVALID_HANDLE_VALUE) {
879 CloseHandle(childOutputWrite);
880 } /* if */
881 if (childErrorRead != INVALID_HANDLE_VALUE) {
882 CloseHandle(childErrorRead);
883 } /* if */
884 if (childErrorWrite != INVALID_HANDLE_VALUE) {
885 CloseHandle(childErrorWrite);
886 } /* if */
887 if (process != NULL) {
888 FREE_RECORD(process, win_processRecord, count.process);
889 process = NULL;
890 } /* if */
891 raise_error(err_info);
892 } /* if */
893 logFunction(printf("pcsStartPipe -> " FMT_U32 "\n",
894 (uint32Type) (process != NULL ? process->pid : 0)););
895 return (processType) process;
896 } /* pcsStartPipe */
897
898
899
900 /**
901 * Convert a 'process' to a string.
902 * The process is converted to a string with the process identifier (PID).
903 * @return the string result of the conversion.
904 * @exception MEMORY_ERROR Not enough memory to represent the result.
905 */
pcsStr(const const_processType process)906 striType pcsStr (const const_processType process)
907
908 {
909 striType result;
910
911 /* pcsStr */
912 if (process == NULL) {
913 result = CSTRI_LITERAL_TO_STRI("NULL");
914 if (unlikely(result == NULL)) {
915 raise_error(MEMORY_ERROR);
916 } /* if */
917 } else {
918 result = intStr((intType) to_pid(process));
919 } /* if */
920 return result;
921 } /* pcsStr */
922
923
924
925 /**
926 * Wait until the specified child process has terminated.
927 * Suspend the execution of the calling process until the
928 * specified child has terminated.
929 */
pcsWaitFor(const processType process)930 void pcsWaitFor (const processType process)
931
932 {
933 DWORD exitCode = 0;
934
935 /* pcsWaitFor */
936 logFunction(printf("pcsWaitFor(" FMT_U32 ") (hProcess=" FMT_U_MEM ")\n",
937 (uint32Type) (process != NULL ? to_pid(process) : 0),
938 process != NULL ? (memSizeType) to_hProcess(process) : (memSizeType) 0););
939 if (!to_isTerminated(process)) {
940 if (WaitForSingleObject(to_hProcess(process), INFINITE) == WAIT_OBJECT_0) {
941 if (GetExitCodeProcess(to_hProcess(process), &exitCode) != 0) {
942 to_var_isTerminated(process) = TRUE;
943 to_var_exitValue(process) = exitCode;
944 } else {
945 logError(printf("pcsWaitFor: GetExitCodeProcess(" FMT_U_MEM ", *) failed:\n"
946 "GetLastError=" FMT_U32 "\n",
947 (memSizeType) to_hProcess(process), (uint32Type) GetLastError());
948 printf("PID=" FMT_U32 "\n", (uint32Type) to_pid(process)););
949 raise_error(FILE_ERROR);
950 } /* if */
951 } else {
952 logError(printf("pcsWaitFor: WaitForSingleObject(" FMT_U_MEM ", INFINITE) failed:\n"
953 "GetLastError=" FMT_U32 "\n",
954 (memSizeType) to_hProcess(process), (uint32Type) GetLastError());
955 printf("PID=" FMT_U32 "\n", (uint32Type) to_pid(process)););
956 raise_error(FILE_ERROR);
957 } /* if */
958 } /* if */
959 logFunction(printf("pcsWaitFor -->\n"););
960 } /* pcsWaitFor */
961