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