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 13 : int CPLSpawn(const char *const papszArgv[], VSILFILE *fin, VSILFILE *fout,
106 : int bDisplayErr)
107 : {
108 : CPLSpawnedProcess *sp =
109 13 : CPLSpawnAsync(nullptr, papszArgv, TRUE, TRUE, TRUE, nullptr);
110 13 : if (sp == nullptr)
111 1 : return -1;
112 :
113 12 : CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp);
114 12 : if (fin != nullptr)
115 6 : FillPipeFromFile(fin, in_child);
116 12 : CPLSpawnAsyncCloseOutputFileHandle(sp);
117 :
118 12 : CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp);
119 12 : if (fout != nullptr)
120 10 : FillFileFromPipe(out_child, fout);
121 12 : CPLSpawnAsyncCloseInputFileHandle(sp);
122 :
123 12 : CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp);
124 24 : CPLString osName;
125 12 : osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID());
126 12 : VSILFILE *ferr = VSIFOpenL(osName.c_str(), "w");
127 :
128 12 : FillFileFromPipe(err_child, ferr);
129 12 : CPLSpawnAsyncCloseErrorFileHandle(sp);
130 :
131 12 : CPL_IGNORE_RET_VAL(VSIFCloseL(ferr));
132 12 : vsi_l_offset nDataLength = 0;
133 12 : GByte *pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE);
134 12 : if (nDataLength > 0)
135 0 : pData[nDataLength - 1] = '\0';
136 12 : 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 12 : if (pData && bDisplayErr)
141 0 : CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0],
142 : pData);
143 12 : CPLFree(pData);
144 :
145 12 : 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 : CPLString osArg(papszArgv[i]);
300 : // We need to quote arguments with spaces or double quotes in them (if not already done).
301 : if (osArg.find_first_of(" \"") != std::string::npos &&
302 : !(osArg.size() >= 3 && osArg.front() == '"' && osArg.back() == '"'))
303 : {
304 : osCommandLine += '"';
305 : osCommandLine += osArg.replaceAll('"', "\\\"");
306 : osCommandLine += '"';
307 : }
308 : else
309 : {
310 : osCommandLine += osArg;
311 : }
312 : }
313 :
314 : if (!CreateProcess(nullptr, const_cast<CHAR *>(osCommandLine.c_str()),
315 : nullptr, // Process security attributes
316 : nullptr, // Primary thread security attributes
317 : TRUE, // Handles are inherited
318 : CREATE_NO_WINDOW |
319 : NORMAL_PRIORITY_CLASS, // Creation flags
320 : nullptr, // Use parent's environment
321 : nullptr, // Use parent's current directory
322 : &siStartInfo, &piProcInfo))
323 : {
324 : CPLError(CE_Failure, CPLE_AppDefined, "Could not create process %s",
325 : osCommandLine.c_str());
326 : goto err;
327 : }
328 :
329 : // Close unused end of pipe.
330 : if (bCreateInputPipe)
331 : CloseHandle(pipe_in[IN_FOR_PARENT]);
332 : if (bCreateOutputPipe)
333 : CloseHandle(pipe_out[OUT_FOR_PARENT]);
334 : if (bCreateErrorPipe)
335 : CloseHandle(pipe_err[OUT_FOR_PARENT]);
336 :
337 : {
338 : CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
339 : CPLMalloc(sizeof(CPLSpawnedProcess)));
340 : p->hProcess = piProcInfo.hProcess;
341 : p->nProcessId = piProcInfo.dwProcessId;
342 : p->hThread = piProcInfo.hThread;
343 : p->fin = pipe_out[IN_FOR_PARENT];
344 : p->fout = pipe_in[OUT_FOR_PARENT];
345 : p->ferr = pipe_err[IN_FOR_PARENT];
346 :
347 : return p;
348 : }
349 :
350 : err_pipe:
351 : CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
352 : err:
353 : for (int i = 0; i < 2; i++)
354 : {
355 : if (pipe_in[i] != nullptr)
356 : CloseHandle(pipe_in[i]);
357 : if (pipe_out[i] != nullptr)
358 : CloseHandle(pipe_out[i]);
359 : if (pipe_err[i] != nullptr)
360 : CloseHandle(pipe_err[i]);
361 : }
362 :
363 : return nullptr;
364 : }
365 :
366 : /************************************************************************/
367 : /* CPLSpawnAsyncGetChildProcessId() */
368 : /************************************************************************/
369 :
370 : CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
371 : {
372 : return p->nProcessId;
373 : }
374 :
375 : /************************************************************************/
376 : /* CPLSpawnAsyncFinish() */
377 : /************************************************************************/
378 :
379 : int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, int /* bKill */)
380 : {
381 : // Get the exit code.
382 : DWORD exitCode = -1;
383 :
384 : if (bWait)
385 : {
386 : WaitForSingleObject(p->hProcess, INFINITE);
387 : GetExitCodeProcess(p->hProcess, &exitCode);
388 : }
389 : else
390 : {
391 : exitCode = 0;
392 : }
393 :
394 : CloseHandle(p->hProcess);
395 : CloseHandle(p->hThread);
396 :
397 : CPLSpawnAsyncCloseInputFileHandle(p);
398 : CPLSpawnAsyncCloseOutputFileHandle(p);
399 : CPLSpawnAsyncCloseErrorFileHandle(p);
400 : CPLFree(p);
401 :
402 : return static_cast<int>(exitCode);
403 : }
404 :
405 : /************************************************************************/
406 : /* CPLSpawnAsyncCloseInputFileHandle() */
407 : /************************************************************************/
408 :
409 : void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
410 : {
411 : if (p->fin != nullptr)
412 : CloseHandle(p->fin);
413 : p->fin = nullptr;
414 : }
415 :
416 : /************************************************************************/
417 : /* CPLSpawnAsyncCloseOutputFileHandle() */
418 : /************************************************************************/
419 :
420 : void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
421 : {
422 : if (p->fout != nullptr)
423 : CloseHandle(p->fout);
424 : p->fout = nullptr;
425 : }
426 :
427 : /************************************************************************/
428 : /* CPLSpawnAsyncCloseErrorFileHandle() */
429 : /************************************************************************/
430 :
431 : void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
432 : {
433 : if (p->ferr != nullptr)
434 : CloseHandle(p->ferr);
435 : p->ferr = nullptr;
436 : }
437 :
438 : #else // Not WIN32
439 :
440 : /************************************************************************/
441 : /* CPLPipeRead() */
442 : /************************************************************************/
443 :
444 : /**
445 : * Read data from the standard output of a forked process.
446 : *
447 : * @param fin handle returned by CPLSpawnAsyncGetInputFileHandle().
448 : * @param data buffer in which to write.
449 : * @param length number of bytes to read.
450 : *
451 : * @return TRUE in case of success.
452 : *
453 : * @since GDAL 1.10.0
454 : */
455 2315 : int CPLPipeRead(CPL_FILE_HANDLE fin, void *data, int length)
456 : {
457 : #ifdef __COVERITY__
458 : (void)fin;
459 : (void)data;
460 : (void)length;
461 : CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
462 : return FALSE;
463 : #else
464 2315 : GByte *pabyData = static_cast<GByte *>(data);
465 2315 : int nRemain = length;
466 4568 : while (nRemain > 0)
467 : {
468 : while (true)
469 : {
470 2315 : assert(nRemain > 0);
471 : // coverity[overflow_sink]
472 2315 : const ssize_t n = read(fin, pabyData, nRemain);
473 2315 : if (n < 0)
474 : {
475 0 : if (errno == EINTR)
476 0 : continue;
477 : else
478 0 : return FALSE;
479 : }
480 2315 : else if (n == 0)
481 62 : return FALSE;
482 2253 : pabyData += n;
483 2253 : assert(n <= nRemain);
484 2253 : nRemain -= static_cast<int>(n);
485 2253 : break;
486 0 : }
487 : }
488 2253 : return TRUE;
489 : #endif
490 : }
491 :
492 : /************************************************************************/
493 : /* CPLPipeWrite() */
494 : /************************************************************************/
495 :
496 : /**
497 : * Write data to the standard input of a forked process.
498 : *
499 : * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle().
500 : * @param data buffer from which to read.
501 : * @param length number of bytes to write.
502 : *
503 : * @return TRUE in case of success.
504 : *
505 : * @since GDAL 1.10.0
506 : */
507 424 : int CPLPipeWrite(CPL_FILE_HANDLE fout, const void *data, int length)
508 : {
509 : #ifdef __COVERITY__
510 : (void)fout;
511 : (void)data;
512 : (void)length;
513 : CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
514 : return FALSE;
515 : #else
516 424 : const GByte *pabyData = static_cast<const GByte *>(data);
517 424 : int nRemain = length;
518 848 : while (nRemain > 0)
519 : {
520 : while (true)
521 : {
522 424 : assert(nRemain > 0);
523 : // coverity[overflow_sink]
524 424 : const ssize_t n = write(fout, pabyData, nRemain);
525 424 : if (n < 0)
526 : {
527 0 : if (errno == EINTR)
528 0 : continue;
529 : else
530 0 : return FALSE;
531 : }
532 424 : pabyData += n;
533 424 : assert(n <= nRemain);
534 424 : nRemain -= static_cast<int>(n);
535 424 : break;
536 0 : }
537 : }
538 424 : return TRUE;
539 : #endif
540 : }
541 :
542 : /************************************************************************/
543 : /* FillFileFromPipe() */
544 : /************************************************************************/
545 :
546 22 : static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout)
547 : {
548 22 : char buf[PIPE_BUFFER_SIZE] = {};
549 : while (true)
550 : {
551 : const int nRead =
552 32 : static_cast<int>(read(pipe_fd, buf, PIPE_BUFFER_SIZE));
553 32 : if (nRead <= 0)
554 22 : break;
555 10 : const int nWritten = static_cast<int>(VSIFWriteL(buf, 1, nRead, fout));
556 10 : if (nWritten < nRead)
557 0 : break;
558 10 : }
559 22 : }
560 :
561 : /************************************************************************/
562 : /* CPLSpawnAsync() */
563 : /************************************************************************/
564 :
565 : struct _CPLSpawnedProcess
566 : {
567 : pid_t pid;
568 : CPL_FILE_HANDLE fin;
569 : CPL_FILE_HANDLE fout;
570 : CPL_FILE_HANDLE ferr;
571 : #ifdef HAVE_POSIX_SPAWNP
572 : bool bFreeActions;
573 : posix_spawn_file_actions_t actions;
574 : #endif
575 : };
576 :
577 : /**
578 : * Runs an executable in another process (or fork the current process)
579 : * and return immediately.
580 : *
581 : * This function launches an executable and returns immediately, while letting
582 : * the sub-process to run asynchronously.
583 : *
584 : * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
585 : * on other platforms.
586 : *
587 : * On Unix, a pointer of function can be provided to run in the child process,
588 : * without exec()'ing a new executable.
589 : *
590 : * @param pfnMain the function to run in the child process (Unix only).
591 : * @param papszArgv argument list of the executable to run. papszArgv[0] is the
592 : * name of the executable.
593 : * @param bCreateInputPipe set to TRUE to create a pipe for the child
594 : * input stream.
595 : * @param bCreateOutputPipe set to TRUE to create a pipe for the child
596 : * output stream.
597 : * @param bCreateErrorPipe set to TRUE to create a pipe for the child
598 : * error stream.
599 : * @param papszOptions unused. should be set to NULL.
600 : *
601 : * @return a handle, that must be freed with CPLSpawnAsyncFinish()
602 : *
603 : * @since GDAL 1.10.0
604 : */
605 : CPLSpawnedProcess *
606 75 : CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
607 : const char *const papszArgv[], int bCreateInputPipe,
608 : int bCreateOutputPipe, int bCreateErrorPipe,
609 : CPL_UNUSED char **papszOptions)
610 : {
611 75 : int pipe_in[2] = {-1, -1};
612 75 : int pipe_out[2] = {-1, -1};
613 75 : int pipe_err[2] = {-1, -1};
614 :
615 13 : const auto ClosePipes = [&pipe_in, &pipe_out, &pipe_err]()
616 : {
617 3 : for (int i = 0; i < 2; i++)
618 : {
619 2 : if (pipe_in[i] >= 0)
620 2 : close(pipe_in[i]);
621 2 : if (pipe_out[i] >= 0)
622 2 : close(pipe_out[i]);
623 2 : if (pipe_err[i] >= 0)
624 2 : close(pipe_err[i]);
625 : }
626 76 : };
627 :
628 75 : if ((bCreateInputPipe && pipe(pipe_in)) ||
629 225 : (bCreateOutputPipe && pipe(pipe_out)) ||
630 75 : (bCreateErrorPipe && pipe(pipe_err)))
631 : {
632 0 : CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
633 0 : ClosePipes();
634 0 : return nullptr;
635 : }
636 :
637 75 : bool bDup2In = CPL_TO_BOOL(bCreateInputPipe);
638 75 : bool bDup2Out = CPL_TO_BOOL(bCreateOutputPipe);
639 75 : bool bDup2Err = CPL_TO_BOOL(bCreateErrorPipe);
640 :
641 75 : char **papszArgvDup = CSLDuplicate(const_cast<char **>(papszArgv));
642 :
643 : // If we don't do any file actions, posix_spawnp() might be implemented
644 : // efficiently as a vfork()/exec() pair (or if it is not available, we
645 : // can use vfork()/exec()), so if the child is cooperative
646 : // we pass the pipe handles as commandline arguments.
647 75 : if (papszArgv != nullptr)
648 : {
649 1265 : for (int i = 0; papszArgvDup[i] != nullptr; i++)
650 : {
651 1214 : if (bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0)
652 : {
653 0 : CPLFree(papszArgvDup[i]);
654 0 : papszArgvDup[i] = CPLStrdup(CPLSPrintf(
655 : "%d,%d", pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
656 0 : bDup2In = false;
657 : }
658 1214 : else if (bCreateOutputPipe &&
659 1214 : strcmp(papszArgvDup[i], "{pipe_out}") == 0)
660 : {
661 0 : CPLFree(papszArgvDup[i]);
662 0 : papszArgvDup[i] =
663 0 : CPLStrdup(CPLSPrintf("%d,%d", pipe_out[OUT_FOR_PARENT],
664 : pipe_out[IN_FOR_PARENT]));
665 0 : bDup2Out = false;
666 : }
667 1214 : else if (bCreateErrorPipe &&
668 1214 : strcmp(papszArgvDup[i], "{pipe_err}") == 0)
669 : {
670 0 : CPLFree(papszArgvDup[i]);
671 0 : papszArgvDup[i] =
672 0 : CPLStrdup(CPLSPrintf("%d,%d", pipe_err[OUT_FOR_PARENT],
673 : pipe_err[IN_FOR_PARENT]));
674 0 : bDup2Err = false;
675 : }
676 : }
677 : }
678 :
679 : #ifdef HAVE_POSIX_SPAWNP
680 75 : if (papszArgv != nullptr)
681 : {
682 51 : bool bHasActions = false;
683 : posix_spawn_file_actions_t actions;
684 :
685 51 : if (bDup2In)
686 : {
687 51 : /*if( !bHasActions )*/ posix_spawn_file_actions_init(&actions);
688 51 : posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT],
689 : fileno(stdin));
690 51 : posix_spawn_file_actions_addclose(&actions,
691 : pipe_in[OUT_FOR_PARENT]);
692 51 : bHasActions = true;
693 : }
694 :
695 51 : if (bDup2Out)
696 : {
697 51 : if (!bHasActions)
698 0 : posix_spawn_file_actions_init(&actions);
699 51 : posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT],
700 : fileno(stdout));
701 51 : posix_spawn_file_actions_addclose(&actions,
702 : pipe_out[IN_FOR_PARENT]);
703 51 : bHasActions = true;
704 : }
705 :
706 51 : if (bDup2Err)
707 : {
708 51 : if (!bHasActions)
709 0 : posix_spawn_file_actions_init(&actions);
710 51 : posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT],
711 : fileno(stderr));
712 51 : posix_spawn_file_actions_addclose(&actions,
713 : pipe_err[IN_FOR_PARENT]);
714 51 : bHasActions = true;
715 : }
716 :
717 51 : pid_t pid = 0;
718 51 : assert(papszArgvDup[0] != nullptr);
719 51 : if (posix_spawnp(&pid, papszArgvDup[0],
720 : bHasActions ? &actions : nullptr, nullptr,
721 51 : papszArgvDup, environ) != 0)
722 : {
723 1 : if (bHasActions)
724 1 : posix_spawn_file_actions_destroy(&actions);
725 1 : CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
726 1 : CSLDestroy(papszArgvDup);
727 1 : ClosePipes();
728 1 : return nullptr;
729 : }
730 :
731 50 : CSLDestroy(papszArgvDup);
732 :
733 : // Close unused end of pipe.
734 50 : if (bCreateInputPipe)
735 50 : close(pipe_in[IN_FOR_PARENT]);
736 50 : if (bCreateOutputPipe)
737 50 : close(pipe_out[OUT_FOR_PARENT]);
738 50 : if (bCreateErrorPipe)
739 50 : close(pipe_err[OUT_FOR_PARENT]);
740 :
741 : // Ignore SIGPIPE.
742 : #ifdef SIGPIPE
743 50 : std::signal(SIGPIPE, SIG_IGN);
744 : #endif
745 : CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
746 50 : CPLMalloc(sizeof(CPLSpawnedProcess)));
747 50 : if (bHasActions)
748 50 : memcpy(&p->actions, &actions, sizeof(actions));
749 50 : p->bFreeActions = bHasActions;
750 50 : p->pid = pid;
751 50 : p->fin = pipe_out[IN_FOR_PARENT];
752 50 : p->fout = pipe_in[OUT_FOR_PARENT];
753 50 : p->ferr = pipe_err[IN_FOR_PARENT];
754 50 : return p;
755 : }
756 : #endif // #ifdef HAVE_POSIX_SPAWNP
757 :
758 24 : pid_t pid = 0;
759 :
760 : #if defined(HAVE_VFORK) && !defined(HAVE_POSIX_SPAWNP)
761 : if (papszArgv != nullptr && !bDup2In && !bDup2Out && !bDup2Err)
762 : {
763 : // Workaround clang static analyzer warning about unsafe use of vfork.
764 : pid_t (*p_vfork)(void) = vfork;
765 : pid = p_vfork();
766 : }
767 : else
768 : #endif
769 : {
770 24 : pid = fork();
771 : }
772 :
773 24 : if (pid == 0)
774 : {
775 : // Close unused end of pipe.
776 0 : if (bDup2In)
777 0 : close(pipe_in[OUT_FOR_PARENT]);
778 0 : if (bDup2Out)
779 0 : close(pipe_out[IN_FOR_PARENT]);
780 0 : if (bDup2Err)
781 0 : close(pipe_err[IN_FOR_PARENT]);
782 :
783 : #ifndef HAVE_POSIX_SPAWNP
784 : if (papszArgv != nullptr)
785 : {
786 : if (bDup2In)
787 : dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
788 : if (bDup2Out)
789 : dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
790 : if (bDup2Err)
791 : dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));
792 :
793 : execvp(papszArgvDup[0], papszArgvDup);
794 :
795 : _exit(1);
796 : }
797 : else
798 : #endif // HAVE_POSIX_SPAWNP
799 : {
800 0 : if (bCreateErrorPipe)
801 0 : close(pipe_err[OUT_FOR_PARENT]);
802 :
803 0 : int nRet = 0;
804 0 : if (pfnMain != nullptr)
805 0 : nRet = pfnMain(bCreateInputPipe ? pipe_in[IN_FOR_PARENT]
806 0 : : fileno(stdin),
807 : bCreateOutputPipe ? pipe_out[OUT_FOR_PARENT]
808 0 : : fileno(stdout));
809 0 : _exit(nRet);
810 : }
811 : }
812 24 : else if (pid > 0)
813 : {
814 24 : CSLDestroy(papszArgvDup);
815 :
816 : // Close unused end of pipe.
817 24 : if (bCreateInputPipe)
818 24 : close(pipe_in[IN_FOR_PARENT]);
819 24 : if (bCreateOutputPipe)
820 24 : close(pipe_out[OUT_FOR_PARENT]);
821 24 : if (bCreateErrorPipe)
822 24 : close(pipe_err[OUT_FOR_PARENT]);
823 :
824 : // Ignore SIGPIPE.
825 : #ifdef SIGPIPE
826 24 : std::signal(SIGPIPE, SIG_IGN);
827 : #endif
828 : CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
829 24 : CPLMalloc(sizeof(CPLSpawnedProcess)));
830 : #ifdef HAVE_POSIX_SPAWNP
831 24 : p->bFreeActions = false;
832 : #endif
833 24 : p->pid = pid;
834 24 : p->fin = pipe_out[IN_FOR_PARENT];
835 24 : p->fout = pipe_in[OUT_FOR_PARENT];
836 24 : p->ferr = pipe_err[IN_FOR_PARENT];
837 24 : return p;
838 : }
839 :
840 0 : CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
841 :
842 0 : CSLDestroy(papszArgvDup);
843 0 : ClosePipes();
844 0 : return nullptr;
845 : }
846 :
847 : /************************************************************************/
848 : /* CPLSpawnAsyncGetChildProcessId() */
849 : /************************************************************************/
850 :
851 62 : CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
852 : {
853 62 : return p->pid;
854 : }
855 :
856 : /************************************************************************/
857 : /* CPLSpawnAsyncFinish() */
858 : /************************************************************************/
859 :
860 : /**
861 : * \fn CPLSpawnAsyncFinish(CPLSpawnedProcess*,int,int)
862 : * Wait for the forked process to finish.
863 : *
864 : * @param p handle returned by CPLSpawnAsync()
865 : * @param bWait set to TRUE to wait for the child to terminate. Otherwise the
866 : * associated handles are just cleaned.
867 : * @param bKill set to TRUE to force child termination (unimplemented right
868 : * now).
869 : *
870 : * @return the return code of the forked process if bWait == TRUE, 0 otherwise
871 : *
872 : * @since GDAL 1.10.0
873 : */
874 :
875 74 : int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, CPL_UNUSED int bKill)
876 : {
877 74 : int status = 0;
878 :
879 74 : if (bWait)
880 : {
881 : while (true)
882 : {
883 74 : status = -1;
884 74 : const int ret = waitpid(p->pid, &status, 0);
885 74 : if (ret < 0)
886 : {
887 0 : if (errno != EINTR)
888 : {
889 0 : break;
890 : }
891 : }
892 : else
893 : {
894 74 : if (WIFEXITED(status))
895 : {
896 74 : status = WEXITSTATUS(status);
897 : }
898 : else
899 : {
900 0 : status = -1;
901 : }
902 74 : break;
903 : }
904 0 : }
905 : }
906 :
907 74 : CPLSpawnAsyncCloseInputFileHandle(p);
908 74 : CPLSpawnAsyncCloseOutputFileHandle(p);
909 74 : CPLSpawnAsyncCloseErrorFileHandle(p);
910 : #ifdef HAVE_POSIX_SPAWNP
911 74 : if (p->bFreeActions)
912 50 : posix_spawn_file_actions_destroy(&p->actions);
913 : #endif
914 74 : CPLFree(p);
915 74 : return status;
916 : }
917 :
918 : /************************************************************************/
919 : /* CPLSpawnAsyncCloseInputFileHandle() */
920 : /************************************************************************/
921 :
922 86 : void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
923 : {
924 86 : if (p->fin >= 0)
925 74 : close(p->fin);
926 86 : p->fin = -1;
927 86 : }
928 :
929 : /************************************************************************/
930 : /* CPLSpawnAsyncCloseOutputFileHandle() */
931 : /************************************************************************/
932 :
933 86 : void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
934 : {
935 86 : if (p->fout >= 0)
936 74 : close(p->fout);
937 86 : p->fout = -1;
938 86 : }
939 :
940 : /************************************************************************/
941 : /* CPLSpawnAsyncCloseErrorFileHandle() */
942 : /************************************************************************/
943 :
944 86 : void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
945 : {
946 86 : if (p->ferr >= 0)
947 74 : close(p->ferr);
948 86 : p->ferr = -1;
949 86 : }
950 :
951 : #endif
952 :
953 : /************************************************************************/
954 : /* CPLSpawnAsyncGetInputFileHandle() */
955 : /************************************************************************/
956 :
957 : /**
958 : * Return the file handle of the standard output of the forked process
959 : * from which to read.
960 : *
961 : * @param p handle returned by CPLSpawnAsync().
962 : *
963 : * @return the file handle.
964 : *
965 : * @since GDAL 1.10.0
966 : */
967 1796 : CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess *p)
968 : {
969 1796 : return p->fin;
970 : }
971 :
972 : /************************************************************************/
973 : /* CPLSpawnAsyncGetOutputFileHandle() */
974 : /************************************************************************/
975 :
976 : /**
977 : * Return the file handle of the standard input of the forked process
978 : * into which to write
979 : *
980 : * @param p handle returned by CPLSpawnAsync().
981 : *
982 : * @return the file handle.
983 : *
984 : * @since GDAL 1.10.0
985 : */
986 136 : CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess *p)
987 : {
988 136 : return p->fout;
989 : }
990 :
991 : /************************************************************************/
992 : /* CPLSpawnAsyncGetErrorFileHandle() */
993 : /************************************************************************/
994 :
995 : /**
996 : * Return the file handle of the standard error of the forked process
997 : * from which to read.
998 : *
999 : * @param p handle returned by CPLSpawnAsync().
1000 : *
1001 : * @return the file handle
1002 : *
1003 : * @since GDAL 1.10.0
1004 : */
1005 543 : CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess *p)
1006 : {
1007 543 : return p->ferr;
1008 : }
|