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