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

Generated by: LCOV version 1.14