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