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