LCOV - code coverage report
Current view: top level - port - cpl_spawn.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 198 250 79.2 %
Date: 2025-09-10 17:48:50 Functions: 15 15 100.0 %

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

Generated by: LCOV version 1.14