LCOV - code coverage report
Current view: top level - port - cpl_spawn.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 158 248 63.7 %
Date: 2024-11-21 22:18:42 Functions: 12 14 85.7 %

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

Generated by: LCOV version 1.14