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