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 11 : int CPLSpawn(const char *const papszArgv[], VSILFILE *fin, VSILFILE *fout,
106 : int bDisplayErr)
107 : {
108 : CPLSpawnedProcess *sp =
109 11 : CPLSpawnAsync(nullptr, papszArgv, TRUE, TRUE, TRUE, nullptr);
110 11 : if (sp == nullptr)
111 1 : return -1;
112 :
113 10 : CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp);
114 10 : if (fin != nullptr)
115 6 : FillPipeFromFile(fin, in_child);
116 10 : CPLSpawnAsyncCloseOutputFileHandle(sp);
117 :
118 10 : CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp);
119 10 : if (fout != nullptr)
120 8 : FillFileFromPipe(out_child, fout);
121 10 : CPLSpawnAsyncCloseInputFileHandle(sp);
122 :
123 10 : CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp);
124 20 : CPLString osName;
125 10 : osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID());
126 10 : VSILFILE *ferr = VSIFOpenL(osName.c_str(), "w");
127 :
128 10 : FillFileFromPipe(err_child, ferr);
129 10 : CPLSpawnAsyncCloseErrorFileHandle(sp);
130 :
131 10 : CPL_IGNORE_RET_VAL(VSIFCloseL(ferr));
132 10 : vsi_l_offset nDataLength = 0;
133 10 : GByte *pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE);
134 10 : if (nDataLength > 0)
135 0 : pData[nDataLength - 1] = '\0';
136 10 : 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 10 : if (pData && bDisplayErr)
141 0 : CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0],
142 : pData);
143 10 : CPLFree(pData);
144 :
145 10 : 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 827 : int CPLPipeRead(CPL_FILE_HANDLE fin, void *data, int length)
454 : {
455 : #ifdef __COVERITY__
456 : (void)fin;
457 : (void)data;
458 : (void)length;
459 : CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
460 : return FALSE;
461 : #else
462 827 : GByte *pabyData = static_cast<GByte *>(data);
463 827 : int nRemain = length;
464 1638 : while (nRemain > 0)
465 : {
466 : while (true)
467 : {
468 827 : assert(nRemain > 0);
469 : // coverity[overflow_sink]
470 827 : const ssize_t n = read(fin, pabyData, nRemain);
471 827 : if (n < 0)
472 : {
473 0 : if (errno == EINTR)
474 0 : continue;
475 : else
476 0 : return FALSE;
477 : }
478 827 : else if (n == 0)
479 16 : return FALSE;
480 811 : pabyData += n;
481 811 : assert(n <= nRemain);
482 811 : nRemain -= static_cast<int>(n);
483 811 : break;
484 0 : }
485 : }
486 811 : return TRUE;
487 : #endif
488 : }
489 :
490 : /************************************************************************/
491 : /* CPLPipeWrite() */
492 : /************************************************************************/
493 :
494 : /**
495 : * Write data to the standard input of a forked process.
496 : *
497 : * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle().
498 : * @param data buffer from which to read.
499 : * @param length number of bytes to write.
500 : *
501 : * @return TRUE in case of success.
502 : *
503 : * @since GDAL 1.10.0
504 : */
505 170 : int CPLPipeWrite(CPL_FILE_HANDLE fout, const void *data, int length)
506 : {
507 : #ifdef __COVERITY__
508 : (void)fout;
509 : (void)data;
510 : (void)length;
511 : CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
512 : return FALSE;
513 : #else
514 170 : const GByte *pabyData = static_cast<const GByte *>(data);
515 170 : int nRemain = length;
516 340 : while (nRemain > 0)
517 : {
518 : while (true)
519 : {
520 170 : assert(nRemain > 0);
521 : // coverity[overflow_sink]
522 170 : const ssize_t n = write(fout, pabyData, nRemain);
523 170 : if (n < 0)
524 : {
525 0 : if (errno == EINTR)
526 0 : continue;
527 : else
528 0 : return FALSE;
529 : }
530 170 : pabyData += n;
531 170 : assert(n <= nRemain);
532 170 : nRemain -= static_cast<int>(n);
533 170 : break;
534 0 : }
535 : }
536 170 : return TRUE;
537 : #endif
538 : }
539 :
540 : /************************************************************************/
541 : /* FillFileFromPipe() */
542 : /************************************************************************/
543 :
544 18 : static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout)
545 : {
546 18 : char buf[PIPE_BUFFER_SIZE] = {};
547 : while (true)
548 : {
549 : const int nRead =
550 26 : static_cast<int>(read(pipe_fd, buf, PIPE_BUFFER_SIZE));
551 26 : if (nRead <= 0)
552 18 : break;
553 8 : const int nWritten = static_cast<int>(VSIFWriteL(buf, 1, nRead, fout));
554 8 : if (nWritten < nRead)
555 0 : break;
556 8 : }
557 18 : }
558 :
559 : /************************************************************************/
560 : /* CPLSpawnAsync() */
561 : /************************************************************************/
562 :
563 : struct _CPLSpawnedProcess
564 : {
565 : pid_t pid;
566 : CPL_FILE_HANDLE fin;
567 : CPL_FILE_HANDLE fout;
568 : CPL_FILE_HANDLE ferr;
569 : #ifdef HAVE_POSIX_SPAWNP
570 : bool bFreeActions;
571 : posix_spawn_file_actions_t actions;
572 : #endif
573 : };
574 :
575 : /**
576 : * Runs an executable in another process (or fork the current process)
577 : * and return immediately.
578 : *
579 : * This function launches an executable and returns immediately, while letting
580 : * the sub-process to run asynchronously.
581 : *
582 : * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
583 : * on other platforms.
584 : *
585 : * On Unix, a pointer of function can be provided to run in the child process,
586 : * without exec()'ing a new executable.
587 : *
588 : * @param pfnMain the function to run in the child process (Unix only).
589 : * @param papszArgv argument list of the executable to run. papszArgv[0] is the
590 : * name of the executable.
591 : * @param bCreateInputPipe set to TRUE to create a pipe for the child
592 : * input stream.
593 : * @param bCreateOutputPipe set to TRUE to create a pipe for the child
594 : * output stream.
595 : * @param bCreateErrorPipe set to TRUE to create a pipe for the child
596 : * error stream.
597 : * @param papszOptions unused. should be set to NULL.
598 : *
599 : * @return a handle, that must be freed with CPLSpawnAsyncFinish()
600 : *
601 : * @since GDAL 1.10.0
602 : */
603 : CPLSpawnedProcess *
604 25 : CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
605 : const char *const papszArgv[], int bCreateInputPipe,
606 : int bCreateOutputPipe, int bCreateErrorPipe,
607 : CPL_UNUSED char **papszOptions)
608 : {
609 25 : int pipe_in[2] = {-1, -1};
610 25 : int pipe_out[2] = {-1, -1};
611 25 : int pipe_err[2] = {-1, -1};
612 :
613 13 : const auto ClosePipes = [&pipe_in, &pipe_out, &pipe_err]()
614 : {
615 3 : for (int i = 0; i < 2; i++)
616 : {
617 2 : if (pipe_in[i] >= 0)
618 2 : close(pipe_in[i]);
619 2 : if (pipe_out[i] >= 0)
620 2 : close(pipe_out[i]);
621 2 : if (pipe_err[i] >= 0)
622 2 : close(pipe_err[i]);
623 : }
624 26 : };
625 :
626 25 : if ((bCreateInputPipe && pipe(pipe_in)) ||
627 75 : (bCreateOutputPipe && pipe(pipe_out)) ||
628 25 : (bCreateErrorPipe && pipe(pipe_err)))
629 : {
630 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
631 0 : ClosePipes();
632 0 : return nullptr;
633 : }
634 :
635 25 : bool bDup2In = CPL_TO_BOOL(bCreateInputPipe);
636 25 : bool bDup2Out = CPL_TO_BOOL(bCreateOutputPipe);
637 25 : bool bDup2Err = CPL_TO_BOOL(bCreateErrorPipe);
638 :
639 25 : char **papszArgvDup = CSLDuplicate(const_cast<char **>(papszArgv));
640 :
641 : // If we don't do any file actions, posix_spawnp() might be implemented
642 : // efficiently as a vfork()/exec() pair (or if it is not available, we
643 : // can use vfork()/exec()), so if the child is cooperative
644 : // we pass the pipe handles as commandline arguments.
645 25 : if (papszArgv != nullptr)
646 : {
647 523 : for (int i = 0; papszArgvDup[i] != nullptr; i++)
648 : {
649 498 : if (bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0)
650 : {
651 0 : CPLFree(papszArgvDup[i]);
652 0 : papszArgvDup[i] = CPLStrdup(CPLSPrintf(
653 : "%d,%d", pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
654 0 : bDup2In = false;
655 : }
656 498 : else if (bCreateOutputPipe &&
657 498 : strcmp(papszArgvDup[i], "{pipe_out}") == 0)
658 : {
659 0 : CPLFree(papszArgvDup[i]);
660 0 : papszArgvDup[i] =
661 0 : CPLStrdup(CPLSPrintf("%d,%d", pipe_out[OUT_FOR_PARENT],
662 : pipe_out[IN_FOR_PARENT]));
663 0 : bDup2Out = false;
664 : }
665 498 : else if (bCreateErrorPipe &&
666 498 : strcmp(papszArgvDup[i], "{pipe_err}") == 0)
667 : {
668 0 : CPLFree(papszArgvDup[i]);
669 0 : papszArgvDup[i] =
670 0 : CPLStrdup(CPLSPrintf("%d,%d", pipe_err[OUT_FOR_PARENT],
671 : pipe_err[IN_FOR_PARENT]));
672 0 : bDup2Err = false;
673 : }
674 : }
675 : }
676 :
677 : #ifdef HAVE_POSIX_SPAWNP
678 25 : if (papszArgv != nullptr)
679 : {
680 25 : bool bHasActions = false;
681 : posix_spawn_file_actions_t actions;
682 :
683 25 : if (bDup2In)
684 : {
685 25 : /*if( !bHasActions )*/ posix_spawn_file_actions_init(&actions);
686 25 : posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT],
687 : fileno(stdin));
688 25 : posix_spawn_file_actions_addclose(&actions,
689 : pipe_in[OUT_FOR_PARENT]);
690 25 : bHasActions = true;
691 : }
692 :
693 25 : if (bDup2Out)
694 : {
695 25 : if (!bHasActions)
696 0 : posix_spawn_file_actions_init(&actions);
697 25 : posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT],
698 : fileno(stdout));
699 25 : posix_spawn_file_actions_addclose(&actions,
700 : pipe_out[IN_FOR_PARENT]);
701 25 : bHasActions = true;
702 : }
703 :
704 25 : if (bDup2Err)
705 : {
706 25 : if (!bHasActions)
707 0 : posix_spawn_file_actions_init(&actions);
708 25 : posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT],
709 : fileno(stderr));
710 25 : posix_spawn_file_actions_addclose(&actions,
711 : pipe_err[IN_FOR_PARENT]);
712 25 : bHasActions = true;
713 : }
714 :
715 25 : pid_t pid = 0;
716 25 : assert(papszArgvDup[0] != nullptr);
717 25 : if (posix_spawnp(&pid, papszArgvDup[0],
718 : bHasActions ? &actions : nullptr, nullptr,
719 25 : papszArgvDup, environ) != 0)
720 : {
721 1 : if (bHasActions)
722 1 : posix_spawn_file_actions_destroy(&actions);
723 1 : CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
724 1 : CSLDestroy(papszArgvDup);
725 1 : ClosePipes();
726 1 : return nullptr;
727 : }
728 :
729 24 : CSLDestroy(papszArgvDup);
730 :
731 : // Close unused end of pipe.
732 24 : if (bCreateInputPipe)
733 24 : close(pipe_in[IN_FOR_PARENT]);
734 24 : if (bCreateOutputPipe)
735 24 : close(pipe_out[OUT_FOR_PARENT]);
736 24 : if (bCreateErrorPipe)
737 24 : close(pipe_err[OUT_FOR_PARENT]);
738 :
739 : // Ignore SIGPIPE.
740 : #ifdef SIGPIPE
741 24 : std::signal(SIGPIPE, SIG_IGN);
742 : #endif
743 : CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
744 24 : CPLMalloc(sizeof(CPLSpawnedProcess)));
745 24 : if (bHasActions)
746 24 : memcpy(&p->actions, &actions, sizeof(actions));
747 24 : p->bFreeActions = bHasActions;
748 24 : p->pid = pid;
749 24 : p->fin = pipe_out[IN_FOR_PARENT];
750 24 : p->fout = pipe_in[OUT_FOR_PARENT];
751 24 : p->ferr = pipe_err[IN_FOR_PARENT];
752 24 : return p;
753 : }
754 : #endif // #ifdef HAVE_POSIX_SPAWNP
755 :
756 0 : pid_t pid = 0;
757 :
758 : #if defined(HAVE_VFORK) && !defined(HAVE_POSIX_SPAWNP)
759 : if (papszArgv != nullptr && !bDup2In && !bDup2Out && !bDup2Err)
760 : {
761 : // Workaround clang static analyzer warning about unsafe use of vfork.
762 : pid_t (*p_vfork)(void) = vfork;
763 : pid = p_vfork();
764 : }
765 : else
766 : #endif
767 : {
768 0 : pid = fork();
769 : }
770 :
771 0 : if (pid == 0)
772 : {
773 : // Close unused end of pipe.
774 0 : if (bDup2In)
775 0 : close(pipe_in[OUT_FOR_PARENT]);
776 0 : if (bDup2Out)
777 0 : close(pipe_out[IN_FOR_PARENT]);
778 0 : if (bDup2Err)
779 0 : close(pipe_err[IN_FOR_PARENT]);
780 :
781 : #ifndef HAVE_POSIX_SPAWNP
782 : if (papszArgv != nullptr)
783 : {
784 : if (bDup2In)
785 : dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
786 : if (bDup2Out)
787 : dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
788 : if (bDup2Err)
789 : dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));
790 :
791 : execvp(papszArgvDup[0], papszArgvDup);
792 :
793 : _exit(1);
794 : }
795 : else
796 : #endif // HAVE_POSIX_SPAWNP
797 : {
798 0 : if (bCreateErrorPipe)
799 0 : close(pipe_err[OUT_FOR_PARENT]);
800 :
801 0 : int nRet = 0;
802 0 : if (pfnMain != nullptr)
803 0 : nRet = pfnMain(bCreateInputPipe ? pipe_in[IN_FOR_PARENT]
804 0 : : fileno(stdin),
805 : bCreateOutputPipe ? pipe_out[OUT_FOR_PARENT]
806 0 : : fileno(stdout));
807 0 : _exit(nRet);
808 : }
809 : }
810 0 : else if (pid > 0)
811 : {
812 0 : CSLDestroy(papszArgvDup);
813 :
814 : // Close unused end of pipe.
815 0 : if (bCreateInputPipe)
816 0 : close(pipe_in[IN_FOR_PARENT]);
817 0 : if (bCreateOutputPipe)
818 0 : close(pipe_out[OUT_FOR_PARENT]);
819 0 : if (bCreateErrorPipe)
820 0 : close(pipe_err[OUT_FOR_PARENT]);
821 :
822 : // Ignore SIGPIPE.
823 : #ifdef SIGPIPE
824 0 : std::signal(SIGPIPE, SIG_IGN);
825 : #endif
826 : CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
827 0 : CPLMalloc(sizeof(CPLSpawnedProcess)));
828 : #ifdef HAVE_POSIX_SPAWNP
829 0 : p->bFreeActions = false;
830 : #endif
831 0 : p->pid = pid;
832 0 : p->fin = pipe_out[IN_FOR_PARENT];
833 0 : p->fout = pipe_in[OUT_FOR_PARENT];
834 0 : p->ferr = pipe_err[IN_FOR_PARENT];
835 0 : return p;
836 : }
837 :
838 0 : CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
839 :
840 0 : CSLDestroy(papszArgvDup);
841 0 : ClosePipes();
842 0 : return nullptr;
843 : }
844 :
845 : /************************************************************************/
846 : /* CPLSpawnAsyncGetChildProcessId() */
847 : /************************************************************************/
848 :
849 14 : CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
850 : {
851 14 : return p->pid;
852 : }
853 :
854 : /************************************************************************/
855 : /* CPLSpawnAsyncFinish() */
856 : /************************************************************************/
857 :
858 : /**
859 : * \fn CPLSpawnAsyncFinish(CPLSpawnedProcess*,int,int)
860 : * Wait for the forked process to finish.
861 : *
862 : * @param p handle returned by CPLSpawnAsync()
863 : * @param bWait set to TRUE to wait for the child to terminate. Otherwise the
864 : * associated handles are just cleaned.
865 : * @param bKill set to TRUE to force child termination (unimplemented right
866 : * now).
867 : *
868 : * @return the return code of the forked process if bWait == TRUE, 0 otherwise
869 : *
870 : * @since GDAL 1.10.0
871 : */
872 :
873 24 : int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, CPL_UNUSED int bKill)
874 : {
875 24 : int status = 0;
876 :
877 24 : if (bWait)
878 : {
879 : while (true)
880 : {
881 24 : status = -1;
882 24 : const int ret = waitpid(p->pid, &status, 0);
883 24 : if (ret < 0)
884 : {
885 0 : if (errno != EINTR)
886 : {
887 0 : break;
888 : }
889 : }
890 : else
891 : {
892 24 : if (WIFEXITED(status))
893 : {
894 24 : status = WEXITSTATUS(status);
895 : }
896 : else
897 : {
898 0 : status = -1;
899 : }
900 24 : break;
901 : }
902 0 : }
903 : }
904 :
905 24 : CPLSpawnAsyncCloseInputFileHandle(p);
906 24 : CPLSpawnAsyncCloseOutputFileHandle(p);
907 24 : CPLSpawnAsyncCloseErrorFileHandle(p);
908 : #ifdef HAVE_POSIX_SPAWNP
909 24 : if (p->bFreeActions)
910 24 : posix_spawn_file_actions_destroy(&p->actions);
911 : #endif
912 24 : CPLFree(p);
913 24 : return status;
914 : }
915 :
916 : /************************************************************************/
917 : /* CPLSpawnAsyncCloseInputFileHandle() */
918 : /************************************************************************/
919 :
920 48 : void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
921 : {
922 48 : if (p->fin >= 0)
923 24 : close(p->fin);
924 48 : p->fin = -1;
925 48 : }
926 :
927 : /************************************************************************/
928 : /* CPLSpawnAsyncCloseOutputFileHandle() */
929 : /************************************************************************/
930 :
931 48 : void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
932 : {
933 48 : if (p->fout >= 0)
934 24 : close(p->fout);
935 48 : p->fout = -1;
936 48 : }
937 :
938 : /************************************************************************/
939 : /* CPLSpawnAsyncCloseErrorFileHandle() */
940 : /************************************************************************/
941 :
942 48 : void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
943 : {
944 48 : if (p->ferr >= 0)
945 24 : close(p->ferr);
946 48 : p->ferr = -1;
947 48 : }
948 :
949 : #endif
950 :
951 : /************************************************************************/
952 : /* CPLSpawnAsyncGetInputFileHandle() */
953 : /************************************************************************/
954 :
955 : /**
956 : * Return the file handle of the standard output of the forked process
957 : * from which to read.
958 : *
959 : * @param p handle returned by CPLSpawnAsync().
960 : *
961 : * @return the file handle.
962 : *
963 : * @since GDAL 1.10.0
964 : */
965 354 : CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess *p)
966 : {
967 354 : return p->fin;
968 : }
969 :
970 : /************************************************************************/
971 : /* CPLSpawnAsyncGetOutputFileHandle() */
972 : /************************************************************************/
973 :
974 : /**
975 : * Return the file handle of the standard input of the forked process
976 : * into which to write
977 : *
978 : * @param p handle returned by CPLSpawnAsync().
979 : *
980 : * @return the file handle.
981 : *
982 : * @since GDAL 1.10.0
983 : */
984 24 : CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess *p)
985 : {
986 24 : return p->fout;
987 : }
988 :
989 : /************************************************************************/
990 : /* CPLSpawnAsyncGetErrorFileHandle() */
991 : /************************************************************************/
992 :
993 : /**
994 : * Return the file handle of the standard error of the forked process
995 : * from which to read.
996 : *
997 : * @param p handle returned by CPLSpawnAsync().
998 : *
999 : * @return the file handle
1000 : *
1001 : * @since GDAL 1.10.0
1002 : */
1003 493 : CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess *p)
1004 : {
1005 493 : return p->ferr;
1006 : }
|