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