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