1 : /**********************************************************************
2 : * $Id: cpl_spawn.cpp 25766 2013-03-18 21:16:36Z jorgearevalo $
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,Even Rouault
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 25766 2013-03-18 21:16:36Z jorgearevalo $");
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 :
52 6 : static void FillPipeFromFile(VSILFILE* fin, CPL_FILE_HANDLE pipe_fd)
53 : {
54 : char buf[PIPE_BUFFER_SIZE];
55 3 : while(TRUE)
56 : {
57 6 : int nRead = (int)VSIFReadL(buf, 1, PIPE_BUFFER_SIZE, fin);
58 6 : if( nRead <= 0 )
59 3 : break;
60 3 : if (!CPLPipeWrite(pipe_fd, buf, nRead))
61 0 : break;
62 : }
63 3 : }
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 :
92 7 : int CPLSpawn(const char * const papszArgv[], VSILFILE* fin, VSILFILE* fout,
93 : int bDisplayErr)
94 : {
95 7 : CPLSpawnedProcess* sp = CPLSpawnAsync(NULL, papszArgv, TRUE, TRUE, TRUE, NULL);
96 7 : if( sp == NULL )
97 0 : return -1;
98 :
99 7 : CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp);
100 7 : if (fin != NULL)
101 3 : FillPipeFromFile(fin, in_child);
102 7 : CPLSpawnAsyncCloseOutputFileHandle(sp);
103 :
104 7 : CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp);
105 7 : if (fout != NULL)
106 7 : FillFileFromPipe(out_child, fout);
107 7 : CPLSpawnAsyncCloseInputFileHandle(sp);
108 :
109 7 : CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp);
110 7 : CPLString osName;
111 7 : osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID());
112 7 : VSILFILE* ferr = VSIFOpenL(osName.c_str(), "w");
113 :
114 7 : FillFileFromPipe(err_child, ferr);
115 7 : CPLSpawnAsyncCloseErrorFileHandle(sp);
116 :
117 7 : VSIFCloseL(ferr);
118 7 : vsi_l_offset nDataLength = 0;
119 7 : GByte* pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE);
120 7 : if( nDataLength > 0 )
121 0 : pData[nDataLength-1] = '\0';
122 7 : if( pData && strstr((const char*)pData, "An error occured while forking process") != NULL )
123 0 : bDisplayErr = TRUE;
124 7 : if( pData && bDisplayErr )
125 0 : CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0], pData);
126 7 : CPLFree(pData);
127 :
128 7 : 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 :
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 :
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 :
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 :
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 : osCommandLine += papszArgv[i];
332 : }
333 :
334 : if (!CreateProcess(NULL,
335 : (CHAR*)osCommandLine.c_str(),
336 : NULL, // process security attributes
337 : NULL, // primary thread security attributes
338 : TRUE, // handles are inherited
339 : CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS, // creation flags
340 : NULL, // use parent's environment
341 : NULL, // use parent's current directory
342 : &siStartInfo,
343 : &piProcInfo))
344 : {
345 : CPLError(CE_Failure, CPLE_AppDefined, "Could not create process %s",
346 : osCommandLine.c_str());
347 : goto err;
348 : }
349 :
350 : /* Close unused end of pipe */
351 : if( bCreateInputPipe )
352 : CloseHandle(pipe_in[IN_FOR_PARENT]);
353 : if( bCreateOutputPipe )
354 : CloseHandle(pipe_out[OUT_FOR_PARENT]);
355 : if( bCreateErrorPipe )
356 : CloseHandle(pipe_err[OUT_FOR_PARENT]);
357 :
358 : p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess));
359 : p->hProcess = piProcInfo.hProcess;
360 : p->nProcessId = piProcInfo.dwProcessId;
361 : p->hThread = piProcInfo.hThread;
362 : p->fin = pipe_out[IN_FOR_PARENT];
363 : p->fout = pipe_in[OUT_FOR_PARENT];
364 : p->ferr = pipe_err[IN_FOR_PARENT];
365 : return p;
366 :
367 : err_pipe:
368 : CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
369 : err:
370 : for(i=0;i<2;i++)
371 : {
372 : if (pipe_in[i] != NULL)
373 : CloseHandle(pipe_in[i]);
374 : if (pipe_out[i] != NULL)
375 : CloseHandle(pipe_out[i]);
376 : if (pipe_err[i] != NULL)
377 : CloseHandle(pipe_err[i]);
378 : }
379 :
380 : return NULL;
381 : }
382 :
383 : /************************************************************************/
384 : /* CPLSpawnAsyncGetChildProcessId() */
385 : /************************************************************************/
386 :
387 : CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p)
388 : {
389 : return p->nProcessId;
390 : }
391 :
392 : /************************************************************************/
393 : /* CPLSpawnAsyncFinish() */
394 : /************************************************************************/
395 :
396 : int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill)
397 : {
398 : // Get the exit code.
399 : DWORD exitCode = -1;
400 :
401 : if( bWait )
402 : {
403 : WaitForSingleObject( p->hProcess, INFINITE );
404 : GetExitCodeProcess(p->hProcess, &exitCode);
405 : }
406 : else
407 : exitCode = 0;
408 :
409 : CloseHandle(p->hProcess);
410 : CloseHandle(p->hThread);
411 :
412 : CPLSpawnAsyncCloseInputFileHandle(p);
413 : CPLSpawnAsyncCloseOutputFileHandle(p);
414 : CPLSpawnAsyncCloseErrorFileHandle(p);
415 : CPLFree(p);
416 :
417 : return (int)exitCode;
418 : }
419 :
420 : /************************************************************************/
421 : /* CPLSpawnAsyncCloseInputFileHandle() */
422 : /************************************************************************/
423 :
424 : void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p)
425 : {
426 : if( p->fin != NULL )
427 : CloseHandle(p->fin);
428 : p->fin = NULL;
429 : }
430 :
431 : /************************************************************************/
432 : /* CPLSpawnAsyncCloseOutputFileHandle() */
433 : /************************************************************************/
434 :
435 : void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p)
436 : {
437 : if( p->fout != NULL )
438 : CloseHandle(p->fout);
439 : p->fout = NULL;
440 : }
441 :
442 : /************************************************************************/
443 : /* CPLSpawnAsyncCloseErrorFileHandle() */
444 : /************************************************************************/
445 :
446 : void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p)
447 : {
448 : if( p->ferr != NULL )
449 : CloseHandle(p->ferr);
450 : p->ferr = NULL;
451 : }
452 :
453 : #else
454 :
455 : #include <unistd.h>
456 : #include <stdio.h>
457 : #include <stdlib.h>
458 : #include <sys/types.h>
459 : #include <sys/wait.h>
460 : #include <errno.h>
461 : #include <signal.h>
462 : #ifdef HAVE_POSIX_SPAWNP
463 : #include <spawn.h>
464 : #ifdef __APPLE__
465 : #include <crt_externs.h>
466 : #define environ (*_NSGetEnviron())
467 : #else
468 : extern char** environ;
469 : #endif
470 : #endif
471 :
472 : #if 0
473 : /************************************************************************/
474 : /* CPLSystem() */
475 : /************************************************************************/
476 :
477 : /**
478 : * Runs an executable in another process.
479 : *
480 : * This function runs an executable, wait for it to finish and returns
481 : * its exit code.
482 : *
483 : * It is implemented as CreateProcess() on Windows platforms, and system()
484 : * on other platforms.
485 : *
486 : * @param pszApplicationName the lpApplicationName for Windows (might be NULL),
487 : * or ignored on other platforms.
488 : * @param pszCommandLine the command line, starting with the executable name
489 : *
490 : * @return the exit code of the spawned process, or -1 in case of error.
491 : *
492 : * @since GDAL 1.10.0
493 : */
494 :
495 : int CPLSystem( const char* pszApplicationName, const char* pszCommandLine )
496 : {
497 : return system(pszCommandLine);
498 : }
499 : #endif
500 :
501 : /************************************************************************/
502 : /* CPLPipeRead() */
503 : /************************************************************************/
504 :
505 : /**
506 : * Read data from the standard output of a forked process.
507 : *
508 : * @param p handle returned by CPLSpawnAsyncGetInputFileHandle().
509 : * @param data buffer in which to write.
510 : * @param length number of bytes to read.
511 : *
512 : * @return TRUE in case of success.
513 : *
514 : * @since GDAL 1.10.0
515 : */
516 1283 : int CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length)
517 : {
518 1283 : GByte* pabyData = (GByte*)data;
519 1283 : int nRemain = length;
520 3849 : while( nRemain > 0 )
521 : {
522 0 : while(TRUE)
523 : {
524 1283 : int n = read(fin, pabyData, nRemain);
525 1283 : if( n < 0 )
526 : {
527 0 : if( errno == EINTR )
528 0 : continue;
529 : else
530 0 : return FALSE;
531 : }
532 1283 : else if( n == 0 )
533 0 : return FALSE;
534 1283 : pabyData += n;
535 1283 : nRemain -= n;
536 1283 : break;
537 : }
538 : }
539 1283 : return TRUE;
540 : }
541 :
542 : /************************************************************************/
543 : /* CPLPipeWrite() */
544 : /************************************************************************/
545 :
546 : /**
547 : * Write data to the standard input of a forked process.
548 : *
549 : * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle().
550 : * @param data buffer from which to read.
551 : * @param length number of bytes to write.
552 : *
553 : * @return TRUE in case of success.
554 : *
555 : * @since GDAL 1.10.0
556 : */
557 243 : int CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length)
558 : {
559 243 : const GByte* pabyData = (const GByte*)data;
560 243 : int nRemain = length;
561 729 : while( nRemain > 0 )
562 : {
563 0 : while(TRUE)
564 : {
565 243 : int n = write(fout, pabyData, nRemain);
566 243 : if( n < 0 )
567 : {
568 0 : if( errno == EINTR )
569 0 : continue;
570 : else
571 0 : return FALSE;
572 : }
573 243 : pabyData += n;
574 243 : nRemain -= n;
575 243 : break;
576 : }
577 : }
578 243 : return TRUE;
579 : }
580 :
581 : /************************************************************************/
582 : /* FillFileFromPipe() */
583 : /************************************************************************/
584 :
585 21 : static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout)
586 : {
587 : char buf[PIPE_BUFFER_SIZE];
588 7 : while(TRUE)
589 : {
590 21 : int nRead = read(pipe_fd, buf, PIPE_BUFFER_SIZE);
591 21 : if (nRead <= 0)
592 14 : break;
593 7 : int nWritten = (int)VSIFWriteL(buf, 1, nRead, fout);
594 7 : if (nWritten < nRead)
595 0 : break;
596 : }
597 14 : }
598 :
599 : /************************************************************************/
600 : /* CPLSpawnAsync() */
601 : /************************************************************************/
602 :
603 : struct _CPLSpawnedProcess
604 : {
605 : pid_t pid;
606 : CPL_FILE_HANDLE fin;
607 : CPL_FILE_HANDLE fout;
608 : CPL_FILE_HANDLE ferr;
609 : #ifdef HAVE_POSIX_SPAWNP
610 : int bFreeActions;
611 : posix_spawn_file_actions_t actions;
612 : #endif
613 : };
614 :
615 : /**
616 : * Runs an executable in another process (or fork the current process)
617 : * and return immediately.
618 : *
619 : * This function launches an executable and returns immediately, while letting
620 : * the sub-process to run asynchronously.
621 : *
622 : * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
623 : * on other platforms.
624 : *
625 : * On Unix, a pointer of function can be provided to run in the child process,
626 : * without exec()'ing a new executable.
627 : *
628 : * @param pfnMain the function to run in the child process (Unix only).
629 : * @param papszArgv argument list of the executable to run. papszArgv[0] is the
630 : * name of the executable.
631 : * @param bCreateInputPipe set to TRUE to create a pipe for the child input stream.
632 : * @param bCreateOutputPipe set to TRUE to create a pipe for the child output stream.
633 : * @param bCreateErrorPipe set to TRUE to create a pipe for the child error stream.
634 : *
635 : * @return a handle, that must be freed with CPLSpawnAsyncFinish()
636 : *
637 : * @since GDAL 1.10.0
638 : */
639 9 : CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
640 : const char * const papszArgv[],
641 : int bCreateInputPipe,
642 : int bCreateOutputPipe,
643 : int bCreateErrorPipe,
644 : char** papszOptions)
645 : {
646 : pid_t pid;
647 9 : int pipe_in[2] = { -1, -1 };
648 9 : int pipe_out[2] = { -1, -1 };
649 9 : int pipe_err[2] = { -1, -1 };
650 : int i;
651 9 : char** papszArgvDup = CSLDuplicate((char**)papszArgv);
652 9 : int bDup2In = bCreateInputPipe,
653 9 : bDup2Out = bCreateOutputPipe,
654 9 : bDup2Err = bCreateErrorPipe;
655 :
656 9 : if ((bCreateInputPipe && pipe(pipe_in)) ||
657 : (bCreateOutputPipe && pipe(pipe_out)) ||
658 : (bCreateErrorPipe && pipe(pipe_err)))
659 0 : goto err_pipe;
660 :
661 : /* If we don't do any file actions, posix_spawnp() might be implemented */
662 : /* efficiently as a vfork()/exec() pair (or if it is not available, we */
663 : /* can use vfork()/exec()), so if the child is cooperative */
664 : /* we pass the pipe handles as commandline arguments */
665 9 : if( papszArgv != NULL )
666 : {
667 98 : for(i=0; papszArgvDup[i] != NULL; i++)
668 : {
669 91 : if( bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0 )
670 : {
671 0 : CPLFree(papszArgvDup[i]);
672 0 : papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
673 0 : pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
674 0 : bDup2In = FALSE;
675 : }
676 91 : else if( bCreateOutputPipe && strcmp(papszArgvDup[i], "{pipe_out}") == 0 )
677 : {
678 0 : CPLFree(papszArgvDup[i]);
679 0 : papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
680 0 : pipe_out[OUT_FOR_PARENT], pipe_out[IN_FOR_PARENT]));
681 0 : bDup2Out = FALSE;
682 : }
683 91 : else if( bCreateErrorPipe && strcmp(papszArgvDup[i], "{pipe_err}") == 0 )
684 : {
685 0 : CPLFree(papszArgvDup[i]);
686 0 : papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d",
687 0 : pipe_err[OUT_FOR_PARENT], pipe_err[IN_FOR_PARENT]));
688 0 : bDup2Err = FALSE;
689 : }
690 : }
691 : }
692 :
693 : #ifdef HAVE_POSIX_SPAWNP
694 9 : if( papszArgv != NULL )
695 : {
696 7 : int bHasActions = FALSE;
697 : posix_spawn_file_actions_t actions;
698 :
699 7 : if( bDup2In )
700 : {
701 7 : if( !bHasActions ) posix_spawn_file_actions_init(&actions);
702 7 : posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT], fileno(stdin));
703 7 : posix_spawn_file_actions_addclose(&actions, pipe_in[OUT_FOR_PARENT]);
704 7 : bHasActions = TRUE;
705 : }
706 :
707 7 : if( bDup2Out )
708 : {
709 7 : if( !bHasActions ) posix_spawn_file_actions_init(&actions);
710 7 : posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT], fileno(stdout));
711 7 : posix_spawn_file_actions_addclose(&actions, pipe_out[IN_FOR_PARENT]);
712 7 : bHasActions = TRUE;
713 : }
714 :
715 7 : if( bDup2Err )
716 : {
717 7 : if( !bHasActions ) posix_spawn_file_actions_init(&actions);
718 7 : posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT], fileno(stderr));
719 7 : posix_spawn_file_actions_addclose(&actions, pipe_err[IN_FOR_PARENT]);
720 7 : bHasActions = TRUE;
721 : }
722 :
723 7 : if( posix_spawnp(&pid, papszArgvDup[0],
724 : bHasActions ? &actions : NULL,
725 : NULL,
726 : (char* const*) papszArgvDup,
727 : environ) != 0 )
728 : {
729 0 : if( bHasActions )
730 0 : posix_spawn_file_actions_destroy(&actions);
731 0 : CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
732 0 : goto err;
733 : }
734 :
735 7 : CSLDestroy(papszArgvDup);
736 :
737 : /* Close unused end of pipe */
738 7 : if( bCreateInputPipe )
739 7 : close(pipe_in[IN_FOR_PARENT]);
740 7 : if( bCreateOutputPipe )
741 7 : close(pipe_out[OUT_FOR_PARENT]);
742 7 : if( bCreateErrorPipe )
743 7 : close(pipe_err[OUT_FOR_PARENT]);
744 :
745 : /* Ignore SIGPIPE */
746 : #ifdef SIGPIPE
747 7 : signal (SIGPIPE, SIG_IGN);
748 : #endif
749 7 : CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess));
750 7 : if( bHasActions )
751 7 : memcpy(&p->actions, &actions, sizeof(actions));
752 7 : p->bFreeActions = bHasActions;
753 7 : p->pid = pid;
754 7 : p->fin = pipe_out[IN_FOR_PARENT];
755 7 : p->fout = pipe_in[OUT_FOR_PARENT];
756 7 : p->ferr = pipe_err[IN_FOR_PARENT];
757 7 : return p;
758 : }
759 : #endif // #ifdef HAVE_POSIX_SPAWNP
760 :
761 : #ifdef HAVE_VFORK
762 2 : if( papszArgv != NULL && !bDup2In && !bDup2Out && !bDup2Err )
763 0 : pid = vfork();
764 : else
765 : #endif
766 2 : pid = fork();
767 2 : if (pid == 0)
768 : {
769 : /* Close unused end of pipe */
770 0 : if( bDup2In )
771 0 : close(pipe_in[OUT_FOR_PARENT]);
772 0 : if( bDup2Out )
773 0 : close(pipe_out[IN_FOR_PARENT]);
774 0 : if( bDup2Err )
775 0 : close(pipe_err[IN_FOR_PARENT]);
776 :
777 : #ifndef HAVE_POSIX_SPAWNP
778 : if( papszArgv != NULL )
779 : {
780 : if( bDup2In )
781 : dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
782 : if( bDup2Out )
783 : dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
784 : if( bDup2Err )
785 : dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));
786 :
787 : execvp(papszArgvDup[0], (char* const*) papszArgvDup);
788 :
789 : _exit(1);
790 : }
791 : else
792 : #endif // HAVE_POSIX_SPAWNP
793 : {
794 0 : if( bCreateErrorPipe )
795 0 : close(pipe_err[OUT_FOR_PARENT]);
796 :
797 0 : int nRet = 0;
798 0 : if (pfnMain != NULL)
799 : nRet = pfnMain((bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : fileno(stdin),
800 0 : (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : fileno(stdout));
801 0 : _exit(nRet);
802 : }
803 : }
804 2 : else if( pid > 0 )
805 : {
806 2 : CSLDestroy(papszArgvDup);
807 :
808 : /* Close unused end of pipe */
809 2 : if( bCreateInputPipe )
810 2 : close(pipe_in[IN_FOR_PARENT]);
811 2 : if( bCreateOutputPipe )
812 2 : close(pipe_out[OUT_FOR_PARENT]);
813 2 : if( bCreateErrorPipe )
814 0 : close(pipe_err[OUT_FOR_PARENT]);
815 :
816 : /* Ignore SIGPIPE */
817 : #ifdef SIGPIPE
818 2 : signal (SIGPIPE, SIG_IGN);
819 : #endif
820 2 : CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess));
821 : #ifdef HAVE_POSIX_SPAWNP
822 2 : p->bFreeActions = FALSE;
823 : #endif
824 2 : p->pid = pid;
825 2 : p->fin = pipe_out[IN_FOR_PARENT];
826 2 : p->fout = pipe_in[OUT_FOR_PARENT];
827 2 : p->ferr = pipe_err[IN_FOR_PARENT];
828 2 : return p;
829 : }
830 : else
831 : {
832 0 : CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
833 0 : goto err;
834 : }
835 :
836 : err_pipe:
837 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
838 : err:
839 0 : CSLDestroy(papszArgvDup);
840 0 : for(i=0;i<2;i++)
841 : {
842 0 : if (pipe_in[i] >= 0)
843 0 : close(pipe_in[i]);
844 0 : if (pipe_out[i] >= 0)
845 0 : close(pipe_out[i]);
846 0 : if (pipe_err[i] >= 0)
847 0 : close(pipe_err[i]);
848 : }
849 :
850 0 : return NULL;
851 : }
852 :
853 : /************************************************************************/
854 : /* CPLSpawnAsyncGetChildProcessId() */
855 : /************************************************************************/
856 :
857 0 : CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p)
858 : {
859 0 : return p->pid;
860 : }
861 :
862 : /************************************************************************/
863 : /* CPLSpawnAsyncFinish() */
864 : /************************************************************************/
865 :
866 : /**
867 : * Wait for the forked process to finish.
868 : *
869 : * @param p handle returned by CPLSpawnAsync()
870 : * @param bWait set to TRUE to wait for the child to terminate. Otherwise the associated
871 : * handles are just cleaned.
872 : * @param bKill set to TRUE to force child termination (unimplemented right now).
873 : *
874 : * @return the return code of the forked process if bWait == TRUE, 0 otherwise
875 : *
876 : * @since GDAL 1.10.0
877 : */
878 9 : int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill)
879 : {
880 9 : int status = 0;
881 :
882 9 : if( bWait )
883 : {
884 0 : while(1)
885 : {
886 9 : status = -1;
887 9 : int ret = waitpid (p->pid, &status, 0);
888 9 : if (ret < 0)
889 : {
890 0 : if (errno != EINTR)
891 : {
892 0 : break;
893 : }
894 : }
895 : else
896 9 : break;
897 : }
898 : }
899 : else
900 0 : bWait = FALSE;
901 9 : CPLSpawnAsyncCloseInputFileHandle(p);
902 9 : CPLSpawnAsyncCloseOutputFileHandle(p);
903 9 : CPLSpawnAsyncCloseErrorFileHandle(p);
904 : #ifdef HAVE_POSIX_SPAWNP
905 9 : if( p->bFreeActions )
906 7 : posix_spawn_file_actions_destroy(&p->actions);
907 : #endif
908 9 : CPLFree(p);
909 9 : return status;
910 : }
911 :
912 : /************************************************************************/
913 : /* CPLSpawnAsyncCloseInputFileHandle() */
914 : /************************************************************************/
915 :
916 16 : void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p)
917 : {
918 16 : if( p->fin >= 0 )
919 9 : close(p->fin);
920 16 : p->fin = -1;
921 16 : }
922 :
923 : /************************************************************************/
924 : /* CPLSpawnAsyncCloseOutputFileHandle() */
925 : /************************************************************************/
926 :
927 16 : void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p)
928 : {
929 16 : if( p->fout >= 0 )
930 9 : close(p->fout);
931 16 : p->fout = -1;
932 16 : }
933 :
934 : /************************************************************************/
935 : /* CPLSpawnAsyncCloseErrorFileHandle() */
936 : /************************************************************************/
937 :
938 16 : void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p)
939 : {
940 16 : if( p->ferr >= 0 )
941 7 : close(p->ferr);
942 16 : p->ferr = -1;
943 16 : }
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 : */
961 9 : CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess* p)
962 : {
963 9 : 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 : */
980 9 : CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess* p)
981 : {
982 9 : 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 : */
999 7 : CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess* p)
1000 : {
1001 7 : return p->ferr;
1002 : }
|