1 /**********************************************************************
2  * $Id: cpl_spawn.cpp 27745 2014-09-27 16:38:57Z goatbar $
3  *
4  * Project:  CPL - Common Portability Library
5  * Purpose:  Implement CPLSystem().
6  * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
7  *
8  **********************************************************************
9  * Copyright (c) 2012-2013, Even Rouault <even dot rouault at mines-paris dot org>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_spawn.h"
31 
32 #include "cpl_error.h"
33 #include "cpl_conv.h"
34 #include "cpl_string.h"
35 #include "cpl_multiproc.h"
36 
37 #define PIPE_BUFFER_SIZE    4096
38 
39 #define IN_FOR_PARENT   0
40 #define OUT_FOR_PARENT  1
41 
42 CPL_CVSID("$Id: cpl_spawn.cpp 27745 2014-09-27 16:38:57Z goatbar $");
43 
44 static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout);
45 
46 //int CPL_DLL CPLSystem( const char* pszApplicationName, const char* pszCommandLine );
47 
48 /************************************************************************/
49 /*                        FillPipeFromFile()                            */
50 /************************************************************************/
51 
FillPipeFromFile(VSILFILE * fin,CPL_FILE_HANDLE pipe_fd)52 static void FillPipeFromFile(VSILFILE* fin, CPL_FILE_HANDLE pipe_fd)
53 {
54     char buf[PIPE_BUFFER_SIZE];
55     while(TRUE)
56     {
57         int nRead = (int)VSIFReadL(buf, 1, PIPE_BUFFER_SIZE, fin);
58         if( nRead <= 0 )
59             break;
60         if (!CPLPipeWrite(pipe_fd, buf, nRead))
61             break;
62     }
63 }
64 
65 /************************************************************************/
66 /*                            CPLSpawn()                                */
67 /************************************************************************/
68 
69 /**
70  * Runs an executable in another process.
71  *
72  * This function runs an executable, wait for it to finish and returns
73  * its exit code.
74  *
75  * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
76  * on other platforms.
77  *
78  * @param papszArgv argument list of the executable to run. papszArgv[0] is the
79  *                  name of the executable
80  * @param fin File handle for input data to feed to the standard input of the
81  *            sub-process. May be NULL.
82  * @param fout File handle for output data to extract from the standard output of the
83  *            sub-process. May be NULL.
84  * @param bDisplayErr Set to TRUE to emit the content of the standard error stream of
85  *                    the sub-process with CPLError().
86  *
87  * @return the exit code of the spawned process, or -1 in case of error.
88  *
89  * @since GDAL 1.10.0
90  */
91 
CPLSpawn(const char * const papszArgv[],VSILFILE * fin,VSILFILE * fout,int bDisplayErr)92 int CPLSpawn(const char * const papszArgv[], VSILFILE* fin, VSILFILE* fout,
93              int bDisplayErr)
94 {
95     CPLSpawnedProcess* sp = CPLSpawnAsync(NULL, papszArgv, TRUE, TRUE, TRUE, NULL);
96     if( sp == NULL )
97         return -1;
98 
99     CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp);
100     if (fin != NULL)
101         FillPipeFromFile(fin, in_child);
102     CPLSpawnAsyncCloseOutputFileHandle(sp);
103 
104     CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp);
105     if (fout != NULL)
106         FillFileFromPipe(out_child, fout);
107     CPLSpawnAsyncCloseInputFileHandle(sp);
108 
109     CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp);
110     CPLString osName;
111     osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID());
112     VSILFILE* ferr = VSIFOpenL(osName.c_str(), "w");
113 
114     FillFileFromPipe(err_child, ferr);
115     CPLSpawnAsyncCloseErrorFileHandle(sp);
116 
117     VSIFCloseL(ferr);
118     vsi_l_offset nDataLength = 0;
119     GByte* pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE);
120     if( nDataLength > 0 )
121         pData[nDataLength-1] = '\0';
122     if( pData && strstr((const char*)pData, "An error occured while forking process") != NULL )
123         bDisplayErr = TRUE;
124     if( pData && bDisplayErr )
125         CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0], pData);
126     CPLFree(pData);
127 
128     return CPLSpawnAsyncFinish(sp, TRUE, FALSE);
129 }
130 
131 #if defined(WIN32)
132 
133 #include <windows.h>
134 
135 #if 0
136 /************************************************************************/
137 /*                            CPLSystem()                               */
138 /************************************************************************/
139 
140 int CPLSystem( const char* pszApplicationName, const char* pszCommandLine )
141 {
142     int nRet = -1;
143     PROCESS_INFORMATION processInfo;
144     STARTUPINFO startupInfo;
145     ZeroMemory( &processInfo, sizeof(PROCESS_INFORMATION) );
146     ZeroMemory( &startupInfo, sizeof(STARTUPINFO) );
147     startupInfo.cb = sizeof(STARTUPINFO);
148 
149     char* pszDupedCommandLine = (pszCommandLine) ? CPLStrdup(pszCommandLine) : NULL;
150 
151     if( !CreateProcess( pszApplicationName,
152                         pszDupedCommandLine,
153                         NULL,
154                         NULL,
155                         FALSE,
156                         CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS,
157                         NULL,
158                         NULL,
159                         &startupInfo,
160                         &processInfo) )
161     {
162         DWORD err = GetLastError();
163         CPLDebug("CPL", "'%s' failed : err = %d", pszCommandLine, (int)err);
164         nRet = -1;
165     }
166     else
167     {
168         WaitForSingleObject( processInfo.hProcess, INFINITE );
169 
170         DWORD exitCode;
171 
172         // Get the exit code.
173         int err = GetExitCodeProcess(processInfo.hProcess, &exitCode);
174 
175         CloseHandle(processInfo.hProcess);
176         CloseHandle(processInfo.hThread);
177 
178         if( !err )
179         {
180             CPLDebug("CPL", "GetExitCodeProcess() failed : err = %d", err);
181         }
182         else
183             nRet = exitCode;
184     }
185 
186     CPLFree(pszDupedCommandLine);
187 
188     return nRet;
189 }
190 #endif
191 
192 /************************************************************************/
193 /*                          CPLPipeRead()                               */
194 /************************************************************************/
195 
CPLPipeRead(CPL_FILE_HANDLE fin,void * data,int length)196 int CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length)
197 {
198     GByte* pabyData = (GByte*)data;
199     int nRemain = length;
200     while( nRemain > 0 )
201     {
202         DWORD nRead = 0;
203         if (!ReadFile( fin, pabyData, nRemain, &nRead, NULL))
204             return FALSE;
205         pabyData += nRead;
206         nRemain -= nRead;
207     }
208     return TRUE;
209 }
210 
211 /************************************************************************/
212 /*                         CPLPipeWrite()                               */
213 /************************************************************************/
214 
CPLPipeWrite(CPL_FILE_HANDLE fout,const void * data,int length)215 int CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length)
216 {
217     const GByte* pabyData = (const GByte*)data;
218     int nRemain = length;
219     while( nRemain > 0 )
220     {
221         DWORD nWritten = 0;
222         if (!WriteFile(fout, pabyData, nRemain, &nWritten, NULL))
223             return FALSE;
224         pabyData += nWritten;
225         nRemain -= nWritten;
226     }
227     return TRUE;
228 }
229 
230 /************************************************************************/
231 /*                        FillFileFromPipe()                            */
232 /************************************************************************/
233 
FillFileFromPipe(CPL_FILE_HANDLE pipe_fd,VSILFILE * fout)234 static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout)
235 {
236     char buf[PIPE_BUFFER_SIZE];
237     while(TRUE)
238     {
239         DWORD nRead;
240         if (!ReadFile( pipe_fd, buf, PIPE_BUFFER_SIZE, &nRead, NULL))
241             break;
242         if (nRead <= 0)
243             break;
244         int nWritten = (int)VSIFWriteL(buf, 1, nRead, fout);
245         if (nWritten < (int)nRead)
246             break;
247     }
248 }
249 
250 struct _CPLSpawnedProcess
251 {
252     HANDLE hProcess;
253     DWORD  nProcessId;
254     HANDLE hThread;
255     CPL_FILE_HANDLE fin;
256     CPL_FILE_HANDLE fout;
257     CPL_FILE_HANDLE ferr;
258 };
259 
260 /************************************************************************/
261 /*                            CPLSpawnAsync()                           */
262 /************************************************************************/
263 
CPLSpawnAsync(int (* pfnMain)(CPL_FILE_HANDLE,CPL_FILE_HANDLE),const char * const papszArgv[],int bCreateInputPipe,int bCreateOutputPipe,int bCreateErrorPipe,char ** papszOptions)264 CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
265                                  const char * const papszArgv[],
266                                  int bCreateInputPipe,
267                                  int bCreateOutputPipe,
268                                  int bCreateErrorPipe,
269                                  char** papszOptions)
270 {
271     HANDLE pipe_in[2] = {NULL, NULL};
272     HANDLE pipe_out[2] = {NULL, NULL};
273     HANDLE pipe_err[2] = {NULL, NULL};
274     SECURITY_ATTRIBUTES saAttr;
275     PROCESS_INFORMATION piProcInfo;
276     STARTUPINFO siStartInfo;
277     CPLString osCommandLine;
278     int i;
279     CPLSpawnedProcess* p = NULL;
280 
281     if( papszArgv == NULL )
282     {
283         CPLError(CE_Failure, CPLE_AppDefined,
284                  "On Windows, papszArgv argument must not be NULL");
285         return NULL;
286     }
287 
288     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
289     saAttr.bInheritHandle = TRUE;
290     saAttr.lpSecurityDescriptor = NULL;
291 
292     if( bCreateInputPipe )
293     {
294         if (!CreatePipe(&pipe_in[IN_FOR_PARENT],&pipe_in[OUT_FOR_PARENT],&saAttr, 0))
295             goto err_pipe;
296         /* The child must not inherit from the write side of the pipe_in */
297         if (!SetHandleInformation(pipe_in[OUT_FOR_PARENT],HANDLE_FLAG_INHERIT,0))
298             goto err_pipe;
299     }
300 
301     if( bCreateOutputPipe )
302     {
303         if (!CreatePipe(&pipe_out[IN_FOR_PARENT],&pipe_out[OUT_FOR_PARENT],&saAttr, 0))
304             goto err_pipe;
305         /* The child must not inherit from the read side of the pipe_out */
306         if (!SetHandleInformation(pipe_out[IN_FOR_PARENT],HANDLE_FLAG_INHERIT,0))
307             goto err_pipe;
308     }
309 
310     if( bCreateErrorPipe )
311     {
312         if (!CreatePipe(&pipe_err[IN_FOR_PARENT],&pipe_err[OUT_FOR_PARENT],&saAttr, 0))
313             goto err_pipe;
314         /* The child must not inherit from the read side of the pipe_err */
315         if (!SetHandleInformation(pipe_err[IN_FOR_PARENT],HANDLE_FLAG_INHERIT,0))
316             goto err_pipe;
317     }
318 
319     memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
320     memset(&siStartInfo, 0, sizeof(STARTUPINFO));
321     siStartInfo.cb = sizeof(STARTUPINFO);
322     siStartInfo.hStdInput = (bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : GetStdHandle(STD_INPUT_HANDLE);
323     siStartInfo.hStdOutput = (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : GetStdHandle(STD_OUTPUT_HANDLE);
324     siStartInfo.hStdError = (bCreateErrorPipe) ? pipe_err[OUT_FOR_PARENT] : GetStdHandle(STD_ERROR_HANDLE);
325     siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
326 
327     for(i=0;papszArgv[i] != NULL;i++)
328     {
329         if (i > 0)
330             osCommandLine += " ";
331         /* We need to quote arguments with spaces in them (if not already done) */
332         if( strchr(papszArgv[i], ' ') != NULL &&
333             papszArgv[i][0] != '"' )
334         {
335             osCommandLine += "\"";
336             osCommandLine += papszArgv[i];
337             osCommandLine += "\"";
338         }
339         else
340             osCommandLine += papszArgv[i];
341     }
342 
343     if (!CreateProcess(NULL,
344                        (CHAR*)osCommandLine.c_str(),
345                        NULL,          // process security attributes
346                        NULL,          // primary thread security attributes
347                        TRUE,          // handles are inherited
348                        CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS,             // creation flags
349                        NULL,          // use parent's environment
350                        NULL,          // use parent's current directory
351                        &siStartInfo,
352                        &piProcInfo))
353     {
354         CPLError(CE_Failure, CPLE_AppDefined, "Could not create process %s",
355                  osCommandLine.c_str());
356         goto err;
357     }
358 
359     /* Close unused end of pipe */
360     if( bCreateInputPipe )
361         CloseHandle(pipe_in[IN_FOR_PARENT]);
362     if( bCreateOutputPipe )
363         CloseHandle(pipe_out[OUT_FOR_PARENT]);
364     if( bCreateErrorPipe )
365         CloseHandle(pipe_err[OUT_FOR_PARENT]);
366 
367     p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess));
368     p->hProcess = piProcInfo.hProcess;
369     p->nProcessId = piProcInfo.dwProcessId;
370     p->hThread = piProcInfo.hThread;
371     p->fin = pipe_out[IN_FOR_PARENT];
372     p->fout = pipe_in[OUT_FOR_PARENT];
373     p->ferr = pipe_err[IN_FOR_PARENT];
374     return p;
375 
376 err_pipe:
377     CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
378 err:
379     for(i=0;i<2;i++)
380     {
381         if (pipe_in[i] != NULL)
382             CloseHandle(pipe_in[i]);
383         if (pipe_out[i] != NULL)
384             CloseHandle(pipe_out[i]);
385         if (pipe_err[i] != NULL)
386             CloseHandle(pipe_err[i]);
387     }
388 
389     return NULL;
390 }
391 
392 /************************************************************************/
393 /*                  CPLSpawnAsyncGetChildProcessId()                    */
394 /************************************************************************/
395 
CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess * p)396 CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p)
397 {
398     return p->nProcessId;
399 }
400 
401 /************************************************************************/
402 /*                        CPLSpawnAsyncFinish()                         */
403 /************************************************************************/
404 
CPLSpawnAsyncFinish(CPLSpawnedProcess * p,int bWait,int bKill)405 int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill)
406 {
407     // Get the exit code.
408     DWORD exitCode = -1;
409 
410     if( bWait )
411     {
412         WaitForSingleObject( p->hProcess, INFINITE );
413         GetExitCodeProcess(p->hProcess, &exitCode);
414     }
415     else
416         exitCode = 0;
417 
418     CloseHandle(p->hProcess);
419     CloseHandle(p->hThread);
420 
421     CPLSpawnAsyncCloseInputFileHandle(p);
422     CPLSpawnAsyncCloseOutputFileHandle(p);
423     CPLSpawnAsyncCloseErrorFileHandle(p);
424     CPLFree(p);
425 
426     return (int)exitCode;
427 }
428 
429 /************************************************************************/
430 /*                 CPLSpawnAsyncCloseInputFileHandle()                  */
431 /************************************************************************/
432 
CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess * p)433 void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p)
434 {
435     if( p->fin != NULL )
436         CloseHandle(p->fin);
437     p->fin = NULL;
438 }
439 
440 /************************************************************************/
441 /*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
442 /************************************************************************/
443 
CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess * p)444 void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p)
445 {
446     if( p->fout != NULL )
447         CloseHandle(p->fout);
448     p->fout = NULL;
449 }
450 
451 /************************************************************************/
452 /*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
453 /************************************************************************/
454 
CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess * p)455 void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p)
456 {
457     if( p->ferr != NULL )
458         CloseHandle(p->ferr);
459     p->ferr = NULL;
460 }
461 
462 #else
463 
464 #include <unistd.h>
465 #include <stdio.h>
466 #include <stdlib.h>
467 #include <sys/types.h>
468 #include <sys/wait.h>
469 #include <errno.h>
470 #include <signal.h>
471 #ifdef HAVE_POSIX_SPAWNP
472     #include <spawn.h>
473     #ifdef __APPLE__
474         #include <TargetConditionals.h>
475     #endif
476     #if defined(__APPLE__) && (!defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE==0)
477         #include <crt_externs.h>
478         #define environ (*_NSGetEnviron())
479     #else
480         extern char** environ;
481     #endif
482 #endif
483 
484 #if 0
485 /************************************************************************/
486 /*                            CPLSystem()                               */
487 /************************************************************************/
488 
489 /**
490  * Runs an executable in another process.
491  *
492  * This function runs an executable, wait for it to finish and returns
493  * its exit code.
494  *
495  * It is implemented as CreateProcess() on Windows platforms, and system()
496  * on other platforms.
497  *
498  * @param pszApplicationName the lpApplicationName for Windows (might be NULL),
499  *                           or ignored on other platforms.
500  * @param pszCommandLine the command line, starting with the executable name
501  *
502  * @return the exit code of the spawned process, or -1 in case of error.
503  *
504  * @since GDAL 1.10.0
505  */
506 
507 int CPLSystem( const char* pszApplicationName, const char* pszCommandLine )
508 {
509     return system(pszCommandLine);
510 }
511 #endif
512 
513 /************************************************************************/
514 /*                          CPLPipeRead()                               */
515 /************************************************************************/
516 
517 /**
518  * Read data from the standard output of a forked process.
519  *
520  * @param p handle returned by CPLSpawnAsyncGetInputFileHandle().
521  * @param data buffer in which to write.
522  * @param length number of bytes to read.
523  *
524  * @return TRUE in case of success.
525  *
526  * @since GDAL 1.10.0
527  */
CPLPipeRead(CPL_FILE_HANDLE fin,void * data,int length)528 int CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length)
529 {
530     GByte* pabyData = (GByte*)data;
531     int nRemain = length;
532     while( nRemain > 0 )
533     {
534         while(TRUE)
535         {
536             int n = read(fin, pabyData, nRemain);
537             if( n < 0 )
538             {
539                 if( errno == EINTR )
540                     continue;
541                 else
542                     return FALSE;
543             }
544             else if( n == 0 )
545                 return FALSE;
546             pabyData += n;
547             nRemain -= n;
548             break;
549         }
550     }
551     return TRUE;
552 }
553 
554 /************************************************************************/
555 /*                          CPLPipeWrite()                              */
556 /************************************************************************/
557 
558 /**
559  * Write data to the standard input of a forked process.
560  *
561  * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle().
562  * @param data buffer from which to read.
563  * @param length number of bytes to write.
564  *
565  * @return TRUE in case of success.
566  *
567  * @since GDAL 1.10.0
568  */
CPLPipeWrite(CPL_FILE_HANDLE fout,const void * data,int length)569 int CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length)
570 {
571     const GByte* pabyData = (const GByte*)data;
572     int nRemain = length;
573     while( nRemain > 0 )
574     {
575         while(TRUE)
576         {
577             int n = write(fout, pabyData, nRemain);
578             if( n < 0 )
579             {
580                 if( errno == EINTR )
581                     continue;
582                 else
583                     return FALSE;
584             }
585             pabyData += n;
586             nRemain -= n;
587             break;
588         }
589     }
590     return TRUE;
591 }
592 
593 /************************************************************************/
594 /*                          FillFileFromPipe()                              */
595 /************************************************************************/
596 
FillFileFromPipe(CPL_FILE_HANDLE pipe_fd,VSILFILE * fout)597 static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout)
598 {
599     char buf[PIPE_BUFFER_SIZE];
600     while(TRUE)
601     {
602         int nRead = read(pipe_fd, buf, PIPE_BUFFER_SIZE);
603         if (nRead <= 0)
604             break;
605         int nWritten = (int)VSIFWriteL(buf, 1, nRead, fout);
606         if (nWritten < nRead)
607             break;
608     }
609 }
610 
611 /************************************************************************/
612 /*                            CPLSpawnAsync()                           */
613 /************************************************************************/
614 
615 struct _CPLSpawnedProcess
616 {
617     pid_t pid;
618     CPL_FILE_HANDLE fin;
619     CPL_FILE_HANDLE fout;
620     CPL_FILE_HANDLE ferr;
621 #ifdef HAVE_POSIX_SPAWNP
622     int bFreeActions;
623     posix_spawn_file_actions_t actions;
624 #endif
625 };
626 
627 /**
628  * Runs an executable in another process (or fork the current process)
629  * and return immediately.
630  *
631  * This function launches an executable and returns immediately, while letting
632  * the sub-process to run asynchronously.
633  *
634  * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
635  * on other platforms.
636  *
637  * On Unix, a pointer of function can be provided to run in the child process,
638  * without exec()'ing a new executable.
639  *
640  * @param pfnMain the function to run in the child process (Unix only).
641  * @param papszArgv argument list of the executable to run. papszArgv[0] is the
642  *                  name of the executable.
643  * @param bCreateInputPipe set to TRUE to create a pipe for the child input stream.
644  * @param bCreateOutputPipe set to TRUE to create a pipe for the child output stream.
645  * @param bCreateErrorPipe set to TRUE to create a pipe for the child error stream.
646  *
647  * @return a handle, that must be freed with CPLSpawnAsyncFinish()
648  *
649  * @since GDAL 1.10.0
650  */
CPLSpawnAsync(int (* pfnMain)(CPL_FILE_HANDLE,CPL_FILE_HANDLE),const char * const papszArgv[],int bCreateInputPipe,int bCreateOutputPipe,int bCreateErrorPipe,CPL_UNUSED char ** papszOptions)651 CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
652                                  const char * const papszArgv[],
653                                  int bCreateInputPipe,
654                                  int bCreateOutputPipe,
655                                  int bCreateErrorPipe,
656                                  CPL_UNUSED char** papszOptions)
657 {
658     pid_t pid;
659     int pipe_in[2] = { -1, -1 };
660     int pipe_out[2] = { -1, -1 };
661     int pipe_err[2] = { -1, -1 };
662     int i;
663     char** papszArgvDup = CSLDuplicate((char**)papszArgv);
664     int bDup2In = bCreateInputPipe,
665         bDup2Out = bCreateOutputPipe,
666         bDup2Err = bCreateErrorPipe;
667 
668     if ((bCreateInputPipe && pipe(pipe_in)) ||
669         (bCreateOutputPipe && pipe(pipe_out)) ||
670         (bCreateErrorPipe && pipe(pipe_err)))
671         goto err_pipe;
672 
673     /* If we don't do any file actions, posix_spawnp() might be implemented */
674     /* efficiently as a vfork()/exec() pair (or if it is not available, we */
675     /* can use vfork()/exec()), so if the child is cooperative */
676     /* we pass the pipe handles as commandline arguments */
677     if( papszArgv != NULL )
678     {
679         for(i=0; papszArgvDup[i] != NULL; i++)
680         {
681             if( bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0 )
682             {
683                 CPLFree(papszArgvDup[i]);
684                 papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
685                     pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
686                 bDup2In = FALSE;
687             }
688             else if( bCreateOutputPipe && strcmp(papszArgvDup[i], "{pipe_out}") == 0 )
689             {
690                 CPLFree(papszArgvDup[i]);
691                 papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
692                     pipe_out[OUT_FOR_PARENT], pipe_out[IN_FOR_PARENT]));
693                 bDup2Out = FALSE;
694             }
695             else if( bCreateErrorPipe && strcmp(papszArgvDup[i], "{pipe_err}") == 0 )
696             {
697                 CPLFree(papszArgvDup[i]);
698                 papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
699                     pipe_err[OUT_FOR_PARENT], pipe_err[IN_FOR_PARENT]));
700                 bDup2Err = FALSE;
701             }
702         }
703     }
704 
705 #ifdef HAVE_POSIX_SPAWNP
706     if( papszArgv != NULL )
707     {
708         int bHasActions = FALSE;
709         posix_spawn_file_actions_t actions;
710 
711         if( bDup2In )
712         {
713             if( !bHasActions ) posix_spawn_file_actions_init(&actions);
714             posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT], fileno(stdin));
715             posix_spawn_file_actions_addclose(&actions, pipe_in[OUT_FOR_PARENT]);
716             bHasActions = TRUE;
717         }
718 
719         if( bDup2Out )
720         {
721             if( !bHasActions ) posix_spawn_file_actions_init(&actions);
722             posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT], fileno(stdout));
723             posix_spawn_file_actions_addclose(&actions, pipe_out[IN_FOR_PARENT]);
724             bHasActions = TRUE;
725         }
726 
727         if( bDup2Err )
728         {
729             if( !bHasActions ) posix_spawn_file_actions_init(&actions);
730             posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT], fileno(stderr));
731             posix_spawn_file_actions_addclose(&actions, pipe_err[IN_FOR_PARENT]);
732             bHasActions = TRUE;
733         }
734 
735         if( posix_spawnp(&pid, papszArgvDup[0],
736                          bHasActions ? &actions : NULL,
737                          NULL,
738                          (char* const*) papszArgvDup,
739                          environ) != 0 )
740         {
741             if( bHasActions )
742                 posix_spawn_file_actions_destroy(&actions);
743             CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
744             goto err;
745         }
746 
747         CSLDestroy(papszArgvDup);
748 
749         /* Close unused end of pipe */
750         if( bCreateInputPipe )
751             close(pipe_in[IN_FOR_PARENT]);
752         if( bCreateOutputPipe )
753             close(pipe_out[OUT_FOR_PARENT]);
754         if( bCreateErrorPipe )
755             close(pipe_err[OUT_FOR_PARENT]);
756 
757         /* Ignore SIGPIPE */
758     #ifdef SIGPIPE
759         signal (SIGPIPE, SIG_IGN);
760     #endif
761         CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess));
762         if( bHasActions )
763             memcpy(&p->actions, &actions, sizeof(actions));
764         p->bFreeActions = bHasActions;
765         p->pid = pid;
766         p->fin = pipe_out[IN_FOR_PARENT];
767         p->fout = pipe_in[OUT_FOR_PARENT];
768         p->ferr = pipe_err[IN_FOR_PARENT];
769         return p;
770     }
771 #endif // #ifdef HAVE_POSIX_SPAWNP
772 
773 #ifdef HAVE_VFORK
774     if( papszArgv != NULL && !bDup2In && !bDup2Out && !bDup2Err )
775         pid = vfork();
776     else
777 #endif
778         pid = fork();
779     if (pid == 0)
780     {
781         /* Close unused end of pipe */
782         if( bDup2In )
783             close(pipe_in[OUT_FOR_PARENT]);
784         if( bDup2Out )
785             close(pipe_out[IN_FOR_PARENT]);
786         if( bDup2Err )
787             close(pipe_err[IN_FOR_PARENT]);
788 
789 #ifndef HAVE_POSIX_SPAWNP
790         if( papszArgv != NULL )
791         {
792             if( bDup2In )
793                 dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
794             if( bDup2Out )
795                 dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
796             if( bDup2Err )
797                 dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));
798 
799             execvp(papszArgvDup[0], (char* const*) papszArgvDup);
800 
801             _exit(1);
802         }
803         else
804 #endif // HAVE_POSIX_SPAWNP
805         {
806             if( bCreateErrorPipe )
807                 close(pipe_err[OUT_FOR_PARENT]);
808 
809             int nRet = 0;
810             if (pfnMain != NULL)
811                 nRet = pfnMain((bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : fileno(stdin),
812                                (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : fileno(stdout));
813             _exit(nRet);
814         }
815     }
816     else if( pid > 0 )
817     {
818         CSLDestroy(papszArgvDup);
819 
820         /* Close unused end of pipe */
821         if( bCreateInputPipe )
822             close(pipe_in[IN_FOR_PARENT]);
823         if( bCreateOutputPipe )
824             close(pipe_out[OUT_FOR_PARENT]);
825         if( bCreateErrorPipe )
826             close(pipe_err[OUT_FOR_PARENT]);
827 
828         /* Ignore SIGPIPE */
829 #ifdef SIGPIPE
830         signal (SIGPIPE, SIG_IGN);
831 #endif
832         CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess));
833 #ifdef HAVE_POSIX_SPAWNP
834         p->bFreeActions = FALSE;
835 #endif
836         p->pid = pid;
837         p->fin = pipe_out[IN_FOR_PARENT];
838         p->fout = pipe_in[OUT_FOR_PARENT];
839         p->ferr = pipe_err[IN_FOR_PARENT];
840         return p;
841     }
842     else
843     {
844         CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
845         goto err;
846     }
847 
848 err_pipe:
849     CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
850 err:
851     CSLDestroy(papszArgvDup);
852     for(i=0;i<2;i++)
853     {
854         if (pipe_in[i] >= 0)
855             close(pipe_in[i]);
856         if (pipe_out[i] >= 0)
857             close(pipe_out[i]);
858         if (pipe_err[i] >= 0)
859             close(pipe_err[i]);
860     }
861 
862     return NULL;
863 }
864 
865 /************************************************************************/
866 /*                  CPLSpawnAsyncGetChildProcessId()                    */
867 /************************************************************************/
868 
CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess * p)869 CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p)
870 {
871     return p->pid;
872 }
873 
874 /************************************************************************/
875 /*                        CPLSpawnAsyncFinish()                         */
876 /************************************************************************/
877 
878 /**
879  * Wait for the forked process to finish.
880  *
881  * @param p handle returned by CPLSpawnAsync()
882  * @param bWait set to TRUE to wait for the child to terminate. Otherwise the associated
883  *              handles are just cleaned.
884  * @param bKill set to TRUE to force child termination (unimplemented right now).
885  *
886  * @return the return code of the forked process if bWait == TRUE, 0 otherwise
887  *
888  * @since GDAL 1.10.0
889  */
CPLSpawnAsyncFinish(CPLSpawnedProcess * p,int bWait,CPL_UNUSED int bKill)890 int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, CPL_UNUSED int bKill)
891 {
892     int status = 0;
893 
894     if( bWait )
895     {
896         while(1)
897         {
898             status = -1;
899             int ret = waitpid (p->pid, &status, 0);
900             if (ret < 0)
901             {
902                 if (errno != EINTR)
903                 {
904                     break;
905                 }
906             }
907             else
908                 break;
909         }
910     }
911     else
912         bWait = FALSE;
913     CPLSpawnAsyncCloseInputFileHandle(p);
914     CPLSpawnAsyncCloseOutputFileHandle(p);
915     CPLSpawnAsyncCloseErrorFileHandle(p);
916 #ifdef HAVE_POSIX_SPAWNP
917     if( p->bFreeActions )
918         posix_spawn_file_actions_destroy(&p->actions);
919 #endif
920     CPLFree(p);
921     return status;
922 }
923 
924 /************************************************************************/
925 /*                 CPLSpawnAsyncCloseInputFileHandle()                  */
926 /************************************************************************/
927 
CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess * p)928 void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p)
929 {
930     if( p->fin >= 0 )
931         close(p->fin);
932     p->fin = -1;
933 }
934 
935 /************************************************************************/
936 /*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
937 /************************************************************************/
938 
CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess * p)939 void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p)
940 {
941     if( p->fout >= 0 )
942         close(p->fout);
943     p->fout = -1;
944 }
945 
946 /************************************************************************/
947 /*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
948 /************************************************************************/
949 
CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess * p)950 void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p)
951 {
952     if( p->ferr >= 0 )
953         close(p->ferr);
954     p->ferr = -1;
955 }
956 
957 #endif
958 
959 /************************************************************************/
960 /*                    CPLSpawnAsyncGetInputFileHandle()                 */
961 /************************************************************************/
962 
963 /**
964  * Return the file handle of the standard output of the forked process
965  * from which to read.
966  *
967  * @param p handle returned by CPLSpawnAsync().
968  *
969  * @return the file handle.
970  *
971  * @since GDAL 1.10.0
972  */
CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess * p)973 CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess* p)
974 {
975     return p->fin;
976 }
977 
978 /************************************************************************/
979 /*                   CPLSpawnAsyncGetOutputFileHandle()                 */
980 /************************************************************************/
981 
982 /**
983  * Return the file handle of the standard input of the forked process
984  * into which to write
985  *
986  * @param p handle returned by CPLSpawnAsync().
987  *
988  * @return the file handle.
989  *
990  * @since GDAL 1.10.0
991  */
CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess * p)992 CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess* p)
993 {
994     return p->fout;
995 }
996 
997 /************************************************************************/
998 /*                    CPLSpawnAsyncGetErrorFileHandle()                 */
999 /************************************************************************/
1000 
1001 /**
1002  * Return the file handle of the standard error of the forked process
1003  * from which to read.
1004  *
1005  * @param p handle returned by CPLSpawnAsync().
1006  *
1007  * @return the file handle
1008  *
1009  * @since GDAL 1.10.0
1010  */
CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess * p)1011 CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess* p)
1012 {
1013     return p->ferr;
1014 }
1015