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