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: 2025-01-18 12:42:00 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             :             // 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             : }

Generated by: LCOV version 1.14