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

Generated by: LCOV version 1.14