LCOV - code coverage report
Current view: top level - port - cpl_spawn.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 143 245 58.4 %
Date: 2024-04-28 21:03:45 Functions: 12 14 85.7 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement CPLSystem().
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2012-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      21             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_port.h"
      30             : #include "cpl_spawn.h"
      31             : 
      32             : #include <cstring>
      33             : 
      34             : #include "cpl_config.h"
      35             : #include "cpl_conv.h"
      36             : #include "cpl_error.h"
      37             : #include "cpl_multiproc.h"
      38             : #include "cpl_string.h"
      39             : 
      40             : #if defined(_WIN32)
      41             : #include <windows.h>
      42             : #else
      43             : #include <cassert>
      44             : #include <cerrno>
      45             : #include <csignal>
      46             : #include <cstdio>
      47             : #include <cstdlib>
      48             : #include <sys/types.h>
      49             : #include <sys/wait.h>
      50             : #include <unistd.h>
      51             : #ifdef HAVE_POSIX_SPAWNP
      52             : #include <spawn.h>
      53             : #ifdef __APPLE__
      54             : #include <TargetConditionals.h>
      55             : #endif
      56             : #if defined(__APPLE__) && (!defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE == 0)
      57             : #include <crt_externs.h>
      58             : #define environ (*_NSGetEnviron())
      59             : #else
      60             : #if defined(__FreeBSD__)
      61             : extern __attribute__((__weak__)) char **environ;
      62             : #else
      63             : extern char **environ;
      64             : #endif
      65             : #endif
      66             : #endif
      67             : #endif
      68             : 
      69             : constexpr int PIPE_BUFFER_SIZE = 4096;
      70             : 
      71             : constexpr int IN_FOR_PARENT = 0;
      72             : constexpr int OUT_FOR_PARENT = 1;
      73             : 
      74             : static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout);
      75             : 
      76             : /************************************************************************/
      77             : /*                        FillPipeFromFile()                            */
      78             : /************************************************************************/
      79             : 
      80           6 : static void FillPipeFromFile(VSILFILE *fin, CPL_FILE_HANDLE pipe_fd)
      81             : {
      82           6 :     char buf[PIPE_BUFFER_SIZE] = {};
      83             :     while (true)
      84             :     {
      85             :         const int nRead =
      86          12 :             static_cast<int>(VSIFReadL(buf, 1, PIPE_BUFFER_SIZE, fin));
      87          12 :         if (nRead <= 0)
      88           6 :             break;
      89           6 :         if (!CPLPipeWrite(pipe_fd, buf, nRead))
      90           0 :             break;
      91           6 :     }
      92           6 : }
      93             : 
      94             : /************************************************************************/
      95             : /*                            CPLSpawn()                                */
      96             : /************************************************************************/
      97             : 
      98             : /**
      99             :  * Runs an executable in another process.
     100             :  *
     101             :  * This function runs an executable, wait for it to finish and returns
     102             :  * its exit code.
     103             :  *
     104             :  * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
     105             :  * on other platforms.
     106             :  *
     107             :  * @param papszArgv argument list of the executable to run. papszArgv[0] is the
     108             :  *                  name of the executable
     109             :  * @param fin File handle for input data to feed to the standard input of the
     110             :  *            sub-process. May be NULL.
     111             :  * @param fout File handle for output data to extract from the standard output
     112             :  *            of the sub-process. May be NULL.
     113             :  * @param bDisplayErr Set to TRUE to emit the content of the standard error
     114             :  *                    stream of the sub-process with CPLError().
     115             :  *
     116             :  * @return the exit code of the spawned process, or -1 in case of error.
     117             :  *
     118             :  * @since GDAL 1.10.0
     119             :  */
     120             : 
     121           6 : int CPLSpawn(const char *const papszArgv[], VSILFILE *fin, VSILFILE *fout,
     122             :              int bDisplayErr)
     123             : {
     124             :     CPLSpawnedProcess *sp =
     125           6 :         CPLSpawnAsync(nullptr, papszArgv, TRUE, TRUE, TRUE, nullptr);
     126           6 :     if (sp == nullptr)
     127           0 :         return -1;
     128             : 
     129           6 :     CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp);
     130           6 :     if (fin != nullptr)
     131           6 :         FillPipeFromFile(fin, in_child);
     132           6 :     CPLSpawnAsyncCloseOutputFileHandle(sp);
     133             : 
     134           6 :     CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp);
     135           6 :     if (fout != nullptr)
     136           6 :         FillFileFromPipe(out_child, fout);
     137           6 :     CPLSpawnAsyncCloseInputFileHandle(sp);
     138             : 
     139           6 :     CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp);
     140          12 :     CPLString osName;
     141           6 :     osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID());
     142           6 :     VSILFILE *ferr = VSIFOpenL(osName.c_str(), "w");
     143             : 
     144           6 :     FillFileFromPipe(err_child, ferr);
     145           6 :     CPLSpawnAsyncCloseErrorFileHandle(sp);
     146             : 
     147           6 :     CPL_IGNORE_RET_VAL(VSIFCloseL(ferr));
     148           6 :     vsi_l_offset nDataLength = 0;
     149           6 :     GByte *pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE);
     150           6 :     if (nDataLength > 0)
     151           0 :         pData[nDataLength - 1] = '\0';
     152           6 :     if (pData &&
     153           0 :         strstr(const_cast<const char *>(reinterpret_cast<char *>(pData)),
     154             :                "An error occurred while forking process") != nullptr)
     155           0 :         bDisplayErr = TRUE;
     156           6 :     if (pData && bDisplayErr)
     157           0 :         CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0],
     158             :                  pData);
     159           6 :     CPLFree(pData);
     160             : 
     161           6 :     return CPLSpawnAsyncFinish(sp, TRUE, FALSE);
     162             : }
     163             : 
     164             : #if defined(_WIN32)
     165             : 
     166             : /************************************************************************/
     167             : /*                          CPLPipeRead()                               */
     168             : /************************************************************************/
     169             : 
     170             : int CPLPipeRead(CPL_FILE_HANDLE fin, void *data, int length)
     171             : {
     172             :     GByte *pabyData = static_cast<GByte *>(data);
     173             :     int nRemain = length;
     174             :     while (nRemain > 0)
     175             :     {
     176             :         DWORD nRead = 0;
     177             :         if (!ReadFile(fin, pabyData, nRemain, &nRead, nullptr))
     178             :             return FALSE;
     179             :         pabyData += nRead;
     180             :         nRemain -= nRead;
     181             :     }
     182             :     return TRUE;
     183             : }
     184             : 
     185             : /************************************************************************/
     186             : /*                         CPLPipeWrite()                               */
     187             : /************************************************************************/
     188             : 
     189             : int CPLPipeWrite(CPL_FILE_HANDLE fout, const void *data, int length)
     190             : {
     191             :     const GByte *pabyData = static_cast<const GByte *>(data);
     192             :     int nRemain = length;
     193             :     while (nRemain > 0)
     194             :     {
     195             :         DWORD nWritten = 0;
     196             :         if (!WriteFile(fout, pabyData, nRemain, &nWritten, nullptr))
     197             :             return FALSE;
     198             :         pabyData += nWritten;
     199             :         nRemain -= nWritten;
     200             :     }
     201             :     return TRUE;
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                        FillFileFromPipe()                            */
     206             : /************************************************************************/
     207             : 
     208             : static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout)
     209             : {
     210             :     char buf[PIPE_BUFFER_SIZE] = {};
     211             :     while (true)
     212             :     {
     213             :         DWORD nRead = 0;
     214             :         if (!ReadFile(pipe_fd, buf, PIPE_BUFFER_SIZE, &nRead, nullptr))
     215             :             break;
     216             :         if (nRead <= 0)
     217             :             break;
     218             :         const int nWritten = static_cast<int>(VSIFWriteL(buf, 1, nRead, fout));
     219             :         if (nWritten < static_cast<int>(nRead))
     220             :             break;
     221             :     }
     222             : }
     223             : 
     224             : struct _CPLSpawnedProcess
     225             : {
     226             :     HANDLE hProcess;
     227             :     DWORD nProcessId;
     228             :     HANDLE hThread;
     229             :     CPL_FILE_HANDLE fin;
     230             :     CPL_FILE_HANDLE fout;
     231             :     CPL_FILE_HANDLE ferr;
     232             : };
     233             : 
     234             : /************************************************************************/
     235             : /*                            CPLSpawnAsync()                           */
     236             : /************************************************************************/
     237             : 
     238             : CPLSpawnedProcess *
     239             : CPLSpawnAsync(CPL_UNUSED int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
     240             :               const char *const papszArgv[], int bCreateInputPipe,
     241             :               int bCreateOutputPipe, int bCreateErrorPipe,
     242             :               char ** /* papszOptions */)
     243             : {
     244             :     if (papszArgv == nullptr)
     245             :     {
     246             :         CPLError(CE_Failure, CPLE_AppDefined,
     247             :                  "On Windows, papszArgv argument must not be NULL");
     248             :         return nullptr;
     249             :     }
     250             : 
     251             :     // TODO(schwehr): Consider initializing saAttr.
     252             :     SECURITY_ATTRIBUTES saAttr;
     253             :     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
     254             :     saAttr.bInheritHandle = TRUE;
     255             :     saAttr.lpSecurityDescriptor = nullptr;
     256             : 
     257             :     // TODO(schwehr): Move these to where they are used after gotos are removed.
     258             :     HANDLE pipe_out[2] = {nullptr, nullptr};
     259             :     HANDLE pipe_err[2] = {nullptr, nullptr};
     260             :     CPLString osCommandLine;
     261             : 
     262             :     HANDLE pipe_in[2] = {nullptr, nullptr};
     263             :     if (bCreateInputPipe)
     264             :     {
     265             :         if (!CreatePipe(&pipe_in[IN_FOR_PARENT], &pipe_in[OUT_FOR_PARENT],
     266             :                         &saAttr, 0))
     267             :             goto err_pipe;
     268             :         // The child must not inherit from the write side of the pipe_in.
     269             :         if (!SetHandleInformation(pipe_in[OUT_FOR_PARENT], HANDLE_FLAG_INHERIT,
     270             :                                   0))
     271             :             goto err_pipe;
     272             :     }
     273             : 
     274             :     if (bCreateOutputPipe)
     275             :     {
     276             :         if (!CreatePipe(&pipe_out[IN_FOR_PARENT], &pipe_out[OUT_FOR_PARENT],
     277             :                         &saAttr, 0))
     278             :             goto err_pipe;
     279             :         // The child must not inherit from the read side of the pipe_out.
     280             :         if (!SetHandleInformation(pipe_out[IN_FOR_PARENT], HANDLE_FLAG_INHERIT,
     281             :                                   0))
     282             :             goto err_pipe;
     283             :     }
     284             : 
     285             :     if (bCreateErrorPipe)
     286             :     {
     287             :         if (!CreatePipe(&pipe_err[IN_FOR_PARENT], &pipe_err[OUT_FOR_PARENT],
     288             :                         &saAttr, 0))
     289             :             goto err_pipe;
     290             :         // The child must not inherit from the read side of the pipe_err.
     291             :         if (!SetHandleInformation(pipe_err[IN_FOR_PARENT], HANDLE_FLAG_INHERIT,
     292             :                                   0))
     293             :             goto err_pipe;
     294             :     }
     295             : 
     296             :     // TODO(schwehr): Consider initializing piProcInfo.
     297             :     PROCESS_INFORMATION piProcInfo;
     298             :     memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
     299             :     STARTUPINFO siStartInfo;
     300             :     memset(&siStartInfo, 0, sizeof(STARTUPINFO));
     301             :     siStartInfo.cb = sizeof(STARTUPINFO);
     302             :     siStartInfo.hStdInput = bCreateInputPipe ? pipe_in[IN_FOR_PARENT]
     303             :                                              : GetStdHandle(STD_INPUT_HANDLE);
     304             :     siStartInfo.hStdOutput = bCreateOutputPipe
     305             :                                  ? pipe_out[OUT_FOR_PARENT]
     306             :                                  : GetStdHandle(STD_OUTPUT_HANDLE);
     307             :     siStartInfo.hStdError = bCreateErrorPipe ? pipe_err[OUT_FOR_PARENT]
     308             :                                              : GetStdHandle(STD_ERROR_HANDLE);
     309             :     siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
     310             : 
     311             :     for (int i = 0; papszArgv[i] != nullptr; i++)
     312             :     {
     313             :         if (i > 0)
     314             :             osCommandLine += " ";
     315             :         // We need to quote arguments with spaces in them (if not already done).
     316             :         if (strchr(papszArgv[i], ' ') != nullptr && papszArgv[i][0] != '"')
     317             :         {
     318             :             osCommandLine += "\"";
     319             :             osCommandLine += papszArgv[i];
     320             :             osCommandLine += "\"";
     321             :         }
     322             :         else
     323             :         {
     324             :             osCommandLine += papszArgv[i];
     325             :         }
     326             :     }
     327             : 
     328             :     if (!CreateProcess(nullptr, const_cast<CHAR *>(osCommandLine.c_str()),
     329             :                        nullptr,  // Process security attributes
     330             :                        nullptr,  // Primary thread security attributes
     331             :                        TRUE,     // Handles are inherited
     332             :                        CREATE_NO_WINDOW |
     333             :                            NORMAL_PRIORITY_CLASS,  // Creation flags
     334             :                        nullptr,                    // Use parent's environment
     335             :                        nullptr,  // Use parent's current directory
     336             :                        &siStartInfo, &piProcInfo))
     337             :     {
     338             :         CPLError(CE_Failure, CPLE_AppDefined, "Could not create process %s",
     339             :                  osCommandLine.c_str());
     340             :         goto err;
     341             :     }
     342             : 
     343             :     // Close unused end of pipe.
     344             :     if (bCreateInputPipe)
     345             :         CloseHandle(pipe_in[IN_FOR_PARENT]);
     346             :     if (bCreateOutputPipe)
     347             :         CloseHandle(pipe_out[OUT_FOR_PARENT]);
     348             :     if (bCreateErrorPipe)
     349             :         CloseHandle(pipe_err[OUT_FOR_PARENT]);
     350             : 
     351             :     {
     352             :         CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
     353             :             CPLMalloc(sizeof(CPLSpawnedProcess)));
     354             :         p->hProcess = piProcInfo.hProcess;
     355             :         p->nProcessId = piProcInfo.dwProcessId;
     356             :         p->hThread = piProcInfo.hThread;
     357             :         p->fin = pipe_out[IN_FOR_PARENT];
     358             :         p->fout = pipe_in[OUT_FOR_PARENT];
     359             :         p->ferr = pipe_err[IN_FOR_PARENT];
     360             : 
     361             :         return p;
     362             :     }
     363             : 
     364             : err_pipe:
     365             :     CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
     366             : err:
     367             :     for (int i = 0; i < 2; i++)
     368             :     {
     369             :         if (pipe_in[i] != nullptr)
     370             :             CloseHandle(pipe_in[i]);
     371             :         if (pipe_out[i] != nullptr)
     372             :             CloseHandle(pipe_out[i]);
     373             :         if (pipe_err[i] != nullptr)
     374             :             CloseHandle(pipe_err[i]);
     375             :     }
     376             : 
     377             :     return nullptr;
     378             : }
     379             : 
     380             : /************************************************************************/
     381             : /*                  CPLSpawnAsyncGetChildProcessId()                    */
     382             : /************************************************************************/
     383             : 
     384             : CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
     385             : {
     386             :     return p->nProcessId;
     387             : }
     388             : 
     389             : /************************************************************************/
     390             : /*                        CPLSpawnAsyncFinish()                         */
     391             : /************************************************************************/
     392             : 
     393             : int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, int /* bKill */)
     394             : {
     395             :     // Get the exit code.
     396             :     DWORD exitCode = -1;
     397             : 
     398             :     if (bWait)
     399             :     {
     400             :         WaitForSingleObject(p->hProcess, INFINITE);
     401             :         GetExitCodeProcess(p->hProcess, &exitCode);
     402             :     }
     403             :     else
     404             :     {
     405             :         exitCode = 0;
     406             :     }
     407             : 
     408             :     CloseHandle(p->hProcess);
     409             :     CloseHandle(p->hThread);
     410             : 
     411             :     CPLSpawnAsyncCloseInputFileHandle(p);
     412             :     CPLSpawnAsyncCloseOutputFileHandle(p);
     413             :     CPLSpawnAsyncCloseErrorFileHandle(p);
     414             :     CPLFree(p);
     415             : 
     416             :     return static_cast<int>(exitCode);
     417             : }
     418             : 
     419             : /************************************************************************/
     420             : /*                 CPLSpawnAsyncCloseInputFileHandle()                  */
     421             : /************************************************************************/
     422             : 
     423             : void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
     424             : {
     425             :     if (p->fin != nullptr)
     426             :         CloseHandle(p->fin);
     427             :     p->fin = nullptr;
     428             : }
     429             : 
     430             : /************************************************************************/
     431             : /*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
     432             : /************************************************************************/
     433             : 
     434             : void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
     435             : {
     436             :     if (p->fout != nullptr)
     437             :         CloseHandle(p->fout);
     438             :     p->fout = nullptr;
     439             : }
     440             : 
     441             : /************************************************************************/
     442             : /*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
     443             : /************************************************************************/
     444             : 
     445             : void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
     446             : {
     447             :     if (p->ferr != nullptr)
     448             :         CloseHandle(p->ferr);
     449             :     p->ferr = nullptr;
     450             : }
     451             : 
     452             : #else  // Not WIN32
     453             : 
     454             : /************************************************************************/
     455             : /*                          CPLPipeRead()                               */
     456             : /************************************************************************/
     457             : 
     458             : /**
     459             :  * Read data from the standard output of a forked process.
     460             :  *
     461             :  * @param fin handle returned by CPLSpawnAsyncGetInputFileHandle().
     462             :  * @param data buffer in which to write.
     463             :  * @param length number of bytes to read.
     464             :  *
     465             :  * @return TRUE in case of success.
     466             :  *
     467             :  * @since GDAL 1.10.0
     468             :  */
     469           0 : int CPLPipeRead(CPL_FILE_HANDLE fin, void *data, int length)
     470             : {
     471           0 :     GByte *pabyData = static_cast<GByte *>(data);
     472           0 :     int nRemain = length;
     473           0 :     while (nRemain > 0)
     474             :     {
     475             :         while (true)
     476             :         {
     477           0 :             const int n = static_cast<int>(read(fin, pabyData, nRemain));
     478           0 :             if (n < 0)
     479             :             {
     480           0 :                 if (errno == EINTR)
     481           0 :                     continue;
     482             :                 else
     483           0 :                     return FALSE;
     484             :             }
     485           0 :             else if (n == 0)
     486           0 :                 return FALSE;
     487           0 :             pabyData += n;
     488           0 :             nRemain -= n;
     489           0 :             break;
     490           0 :         }
     491             :     }
     492           0 :     return TRUE;
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                          CPLPipeWrite()                              */
     497             : /************************************************************************/
     498             : 
     499             : /**
     500             :  * Write data to the standard input of a forked process.
     501             :  *
     502             :  * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle().
     503             :  * @param data buffer from which to read.
     504             :  * @param length number of bytes to write.
     505             :  *
     506             :  * @return TRUE in case of success.
     507             :  *
     508             :  * @since GDAL 1.10.0
     509             :  */
     510           6 : int CPLPipeWrite(CPL_FILE_HANDLE fout, const void *data, int length)
     511             : {
     512           6 :     const GByte *pabyData = static_cast<const GByte *>(data);
     513           6 :     int nRemain = length;
     514          12 :     while (nRemain > 0)
     515             :     {
     516             :         while (true)
     517             :         {
     518           6 :             const int n = static_cast<int>(write(fout, pabyData, nRemain));
     519           6 :             if (n < 0)
     520             :             {
     521           0 :                 if (errno == EINTR)
     522           0 :                     continue;
     523             :                 else
     524           0 :                     return FALSE;
     525             :             }
     526           6 :             pabyData += n;
     527           6 :             nRemain -= n;
     528           6 :             break;
     529           0 :         }
     530             :     }
     531           6 :     return TRUE;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                          FillFileFromPipe()                              */
     536             : /************************************************************************/
     537             : 
     538          12 : static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout)
     539             : {
     540          12 :     char buf[PIPE_BUFFER_SIZE] = {};
     541             :     while (true)
     542             :     {
     543             :         const int nRead =
     544          18 :             static_cast<int>(read(pipe_fd, buf, PIPE_BUFFER_SIZE));
     545          18 :         if (nRead <= 0)
     546          12 :             break;
     547           6 :         const int nWritten = static_cast<int>(VSIFWriteL(buf, 1, nRead, fout));
     548           6 :         if (nWritten < nRead)
     549           0 :             break;
     550           6 :     }
     551          12 : }
     552             : 
     553             : /************************************************************************/
     554             : /*                            CPLSpawnAsync()                           */
     555             : /************************************************************************/
     556             : 
     557             : struct _CPLSpawnedProcess
     558             : {
     559             :     pid_t pid;
     560             :     CPL_FILE_HANDLE fin;
     561             :     CPL_FILE_HANDLE fout;
     562             :     CPL_FILE_HANDLE ferr;
     563             : #ifdef HAVE_POSIX_SPAWNP
     564             :     bool bFreeActions;
     565             :     posix_spawn_file_actions_t actions;
     566             : #endif
     567             : };
     568             : 
     569             : /**
     570             :  * Runs an executable in another process (or fork the current process)
     571             :  * and return immediately.
     572             :  *
     573             :  * This function launches an executable and returns immediately, while letting
     574             :  * the sub-process to run asynchronously.
     575             :  *
     576             :  * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
     577             :  * on other platforms.
     578             :  *
     579             :  * On Unix, a pointer of function can be provided to run in the child process,
     580             :  * without exec()'ing a new executable.
     581             :  *
     582             :  * @param pfnMain the function to run in the child process (Unix only).
     583             :  * @param papszArgv argument list of the executable to run. papszArgv[0] is the
     584             :  *                  name of the executable.
     585             :  * @param bCreateInputPipe set to TRUE to create a pipe for the child
     586             :  * input stream.
     587             :  * @param bCreateOutputPipe set to TRUE to create a pipe for the child
     588             :  * output stream.
     589             :  * @param bCreateErrorPipe set to TRUE to create a pipe for the child
     590             :  * error stream.
     591             :  * @param papszOptions unused. should be set to NULL.
     592             :  *
     593             :  * @return a handle, that must be freed with CPLSpawnAsyncFinish()
     594             :  *
     595             :  * @since GDAL 1.10.0
     596             :  */
     597             : CPLSpawnedProcess *
     598           6 : CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
     599             :               const char *const papszArgv[], int bCreateInputPipe,
     600             :               int bCreateOutputPipe, int bCreateErrorPipe,
     601             :               CPL_UNUSED char **papszOptions)
     602             : {
     603           6 :     int pipe_in[2] = {-1, -1};
     604           6 :     int pipe_out[2] = {-1, -1};
     605           6 :     int pipe_err[2] = {-1, -1};
     606             : 
     607           6 :     if ((bCreateInputPipe && pipe(pipe_in)) ||
     608          18 :         (bCreateOutputPipe && pipe(pipe_out)) ||
     609           6 :         (bCreateErrorPipe && pipe(pipe_err)))
     610             :     {
     611           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
     612           0 :         return nullptr;
     613             :     }
     614             : 
     615           6 :     bool bDup2In = CPL_TO_BOOL(bCreateInputPipe);
     616           6 :     bool bDup2Out = CPL_TO_BOOL(bCreateOutputPipe);
     617           6 :     bool bDup2Err = CPL_TO_BOOL(bCreateErrorPipe);
     618             : 
     619           6 :     char **papszArgvDup = CSLDuplicate(const_cast<char **>(papszArgv));
     620             : 
     621             :     // If we don't do any file actions, posix_spawnp() might be implemented
     622             :     // efficiently as a vfork()/exec() pair (or if it is not available, we
     623             :     // can use vfork()/exec()), so if the child is cooperative
     624             :     // we pass the pipe handles as commandline arguments.
     625           6 :     if (papszArgv != nullptr)
     626             :     {
     627          63 :         for (int i = 0; papszArgvDup[i] != nullptr; i++)
     628             :         {
     629          57 :             if (bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0)
     630             :             {
     631           0 :                 CPLFree(papszArgvDup[i]);
     632           0 :                 papszArgvDup[i] = CPLStrdup(CPLSPrintf(
     633             :                     "%d,%d", pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
     634           0 :                 bDup2In = false;
     635             :             }
     636          57 :             else if (bCreateOutputPipe &&
     637          57 :                      strcmp(papszArgvDup[i], "{pipe_out}") == 0)
     638             :             {
     639           0 :                 CPLFree(papszArgvDup[i]);
     640           0 :                 papszArgvDup[i] =
     641           0 :                     CPLStrdup(CPLSPrintf("%d,%d", pipe_out[OUT_FOR_PARENT],
     642             :                                          pipe_out[IN_FOR_PARENT]));
     643           0 :                 bDup2Out = false;
     644             :             }
     645          57 :             else if (bCreateErrorPipe &&
     646          57 :                      strcmp(papszArgvDup[i], "{pipe_err}") == 0)
     647             :             {
     648           0 :                 CPLFree(papszArgvDup[i]);
     649           0 :                 papszArgvDup[i] =
     650           0 :                     CPLStrdup(CPLSPrintf("%d,%d", pipe_err[OUT_FOR_PARENT],
     651             :                                          pipe_err[IN_FOR_PARENT]));
     652           0 :                 bDup2Err = false;
     653             :             }
     654             :         }
     655             :     }
     656             : 
     657             : #ifdef HAVE_POSIX_SPAWNP
     658           6 :     if (papszArgv != nullptr)
     659             :     {
     660           6 :         bool bHasActions = false;
     661             :         posix_spawn_file_actions_t actions;
     662             : 
     663           6 :         if (bDup2In)
     664             :         {
     665           6 :             /*if( !bHasActions )*/ posix_spawn_file_actions_init(&actions);
     666           6 :             posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT],
     667             :                                              fileno(stdin));
     668           6 :             posix_spawn_file_actions_addclose(&actions,
     669             :                                               pipe_in[OUT_FOR_PARENT]);
     670           6 :             bHasActions = true;
     671             :         }
     672             : 
     673           6 :         if (bDup2Out)
     674             :         {
     675           6 :             if (!bHasActions)
     676           0 :                 posix_spawn_file_actions_init(&actions);
     677           6 :             posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT],
     678             :                                              fileno(stdout));
     679           6 :             posix_spawn_file_actions_addclose(&actions,
     680             :                                               pipe_out[IN_FOR_PARENT]);
     681           6 :             bHasActions = true;
     682             :         }
     683             : 
     684           6 :         if (bDup2Err)
     685             :         {
     686           6 :             if (!bHasActions)
     687           0 :                 posix_spawn_file_actions_init(&actions);
     688           6 :             posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT],
     689             :                                              fileno(stderr));
     690           6 :             posix_spawn_file_actions_addclose(&actions,
     691             :                                               pipe_err[IN_FOR_PARENT]);
     692           6 :             bHasActions = true;
     693             :         }
     694             : 
     695           6 :         pid_t pid = 0;
     696           6 :         assert(papszArgvDup[0] != nullptr);
     697           6 :         if (posix_spawnp(&pid, papszArgvDup[0],
     698             :                          bHasActions ? &actions : nullptr, nullptr,
     699           6 :                          papszArgvDup, environ) != 0)
     700             :         {
     701           0 :             if (bHasActions)
     702           0 :                 posix_spawn_file_actions_destroy(&actions);
     703           0 :             CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
     704           0 :             CSLDestroy(papszArgvDup);
     705           0 :             for (int i = 0; i < 2; i++)
     706             :             {
     707           0 :                 if (pipe_in[i] >= 0)
     708           0 :                     close(pipe_in[i]);
     709           0 :                 if (pipe_out[i] >= 0)
     710           0 :                     close(pipe_out[i]);
     711           0 :                 if (pipe_err[i] >= 0)
     712           0 :                     close(pipe_err[i]);
     713             :             }
     714             : 
     715           0 :             return nullptr;
     716             :         }
     717             : 
     718           6 :         CSLDestroy(papszArgvDup);
     719             : 
     720             :         // Close unused end of pipe.
     721           6 :         if (bCreateInputPipe)
     722           6 :             close(pipe_in[IN_FOR_PARENT]);
     723           6 :         if (bCreateOutputPipe)
     724           6 :             close(pipe_out[OUT_FOR_PARENT]);
     725           6 :         if (bCreateErrorPipe)
     726           6 :             close(pipe_err[OUT_FOR_PARENT]);
     727             : 
     728             :             // Ignore SIGPIPE.
     729             : #ifdef SIGPIPE
     730           6 :         std::signal(SIGPIPE, SIG_IGN);
     731             : #endif
     732             :         CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
     733           6 :             CPLMalloc(sizeof(CPLSpawnedProcess)));
     734           6 :         if (bHasActions)
     735           6 :             memcpy(&p->actions, &actions, sizeof(actions));
     736           6 :         p->bFreeActions = bHasActions;
     737           6 :         p->pid = pid;
     738           6 :         p->fin = pipe_out[IN_FOR_PARENT];
     739           6 :         p->fout = pipe_in[OUT_FOR_PARENT];
     740           6 :         p->ferr = pipe_err[IN_FOR_PARENT];
     741           6 :         return p;
     742             :     }
     743             : #endif  // #ifdef HAVE_POSIX_SPAWNP
     744             : 
     745           0 :     pid_t pid = 0;
     746             : 
     747             : #if defined(HAVE_VFORK) && !defined(HAVE_POSIX_SPAWNP)
     748             :     if (papszArgv != nullptr && !bDup2In && !bDup2Out && !bDup2Err)
     749             :     {
     750             :         // Workaround clang static analyzer warning about unsafe use of vfork.
     751             :         pid_t (*p_vfork)(void) = vfork;
     752             :         pid = p_vfork();
     753             :     }
     754             :     else
     755             : #endif
     756             :     {
     757           0 :         pid = fork();
     758             :     }
     759             : 
     760           0 :     if (pid == 0)
     761             :     {
     762             :         // Close unused end of pipe.
     763           0 :         if (bDup2In)
     764           0 :             close(pipe_in[OUT_FOR_PARENT]);
     765           0 :         if (bDup2Out)
     766           0 :             close(pipe_out[IN_FOR_PARENT]);
     767           0 :         if (bDup2Err)
     768           0 :             close(pipe_err[IN_FOR_PARENT]);
     769             : 
     770             : #ifndef HAVE_POSIX_SPAWNP
     771             :         if (papszArgv != nullptr)
     772             :         {
     773             :             if (bDup2In)
     774             :                 dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
     775             :             if (bDup2Out)
     776             :                 dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
     777             :             if (bDup2Err)
     778             :                 dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));
     779             : 
     780             :             execvp(papszArgvDup[0], papszArgvDup);
     781             : 
     782             :             _exit(1);
     783             :         }
     784             :         else
     785             : #endif  // HAVE_POSIX_SPAWNP
     786             :         {
     787           0 :             if (bCreateErrorPipe)
     788           0 :                 close(pipe_err[OUT_FOR_PARENT]);
     789             : 
     790           0 :             int nRet = 0;
     791           0 :             if (pfnMain != nullptr)
     792           0 :                 nRet = pfnMain(bCreateInputPipe ? pipe_in[IN_FOR_PARENT]
     793           0 :                                                 : fileno(stdin),
     794             :                                bCreateOutputPipe ? pipe_out[OUT_FOR_PARENT]
     795           0 :                                                  : fileno(stdout));
     796           0 :             _exit(nRet);
     797             :         }
     798             :     }
     799           0 :     else if (pid > 0)
     800             :     {
     801           0 :         CSLDestroy(papszArgvDup);
     802             : 
     803             :         // Close unused end of pipe.
     804           0 :         if (bCreateInputPipe)
     805           0 :             close(pipe_in[IN_FOR_PARENT]);
     806           0 :         if (bCreateOutputPipe)
     807           0 :             close(pipe_out[OUT_FOR_PARENT]);
     808           0 :         if (bCreateErrorPipe)
     809           0 :             close(pipe_err[OUT_FOR_PARENT]);
     810             : 
     811             :             // Ignore SIGPIPE.
     812             : #ifdef SIGPIPE
     813           0 :         std::signal(SIGPIPE, SIG_IGN);
     814             : #endif
     815             :         CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
     816           0 :             CPLMalloc(sizeof(CPLSpawnedProcess)));
     817             : #ifdef HAVE_POSIX_SPAWNP
     818           0 :         p->bFreeActions = false;
     819             : #endif
     820           0 :         p->pid = pid;
     821           0 :         p->fin = pipe_out[IN_FOR_PARENT];
     822           0 :         p->fout = pipe_in[OUT_FOR_PARENT];
     823           0 :         p->ferr = pipe_err[IN_FOR_PARENT];
     824           0 :         return p;
     825             :     }
     826             : 
     827           0 :     CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
     828             : 
     829           0 :     CSLDestroy(papszArgvDup);
     830           0 :     for (int i = 0; i < 2; i++)
     831             :     {
     832           0 :         if (pipe_in[i] >= 0)
     833           0 :             close(pipe_in[i]);
     834           0 :         if (pipe_out[i] >= 0)
     835           0 :             close(pipe_out[i]);
     836           0 :         if (pipe_err[i] >= 0)
     837           0 :             close(pipe_err[i]);
     838             :     }
     839             : 
     840           0 :     return nullptr;
     841             : }
     842             : 
     843             : /************************************************************************/
     844             : /*                  CPLSpawnAsyncGetChildProcessId()                    */
     845             : /************************************************************************/
     846             : 
     847           0 : CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
     848             : {
     849           0 :     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             :  * @since GDAL 1.10.0
     869             :  */
     870             : 
     871           6 : int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, CPL_UNUSED int bKill)
     872             : {
     873           6 :     int status = 0;
     874             : 
     875           6 :     if (bWait)
     876             :     {
     877             :         while (true)
     878             :         {
     879           6 :             status = -1;
     880           6 :             const int ret = waitpid(p->pid, &status, 0);
     881           6 :             if (ret < 0)
     882             :             {
     883           0 :                 if (errno != EINTR)
     884             :                 {
     885           0 :                     break;
     886             :                 }
     887             :             }
     888             :             else
     889             :             {
     890           6 :                 break;
     891             :             }
     892           0 :         }
     893             :     }
     894             : 
     895           6 :     CPLSpawnAsyncCloseInputFileHandle(p);
     896           6 :     CPLSpawnAsyncCloseOutputFileHandle(p);
     897           6 :     CPLSpawnAsyncCloseErrorFileHandle(p);
     898             : #ifdef HAVE_POSIX_SPAWNP
     899           6 :     if (p->bFreeActions)
     900           6 :         posix_spawn_file_actions_destroy(&p->actions);
     901             : #endif
     902           6 :     CPLFree(p);
     903           6 :     return status;
     904             : }
     905             : 
     906             : /************************************************************************/
     907             : /*                 CPLSpawnAsyncCloseInputFileHandle()                  */
     908             : /************************************************************************/
     909             : 
     910          12 : void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
     911             : {
     912          12 :     if (p->fin >= 0)
     913           6 :         close(p->fin);
     914          12 :     p->fin = -1;
     915          12 : }
     916             : 
     917             : /************************************************************************/
     918             : /*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
     919             : /************************************************************************/
     920             : 
     921          12 : void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
     922             : {
     923          12 :     if (p->fout >= 0)
     924           6 :         close(p->fout);
     925          12 :     p->fout = -1;
     926          12 : }
     927             : 
     928             : /************************************************************************/
     929             : /*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
     930             : /************************************************************************/
     931             : 
     932          12 : void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
     933             : {
     934          12 :     if (p->ferr >= 0)
     935           6 :         close(p->ferr);
     936          12 :     p->ferr = -1;
     937          12 : }
     938             : 
     939             : #endif
     940             : 
     941             : /************************************************************************/
     942             : /*                    CPLSpawnAsyncGetInputFileHandle()                 */
     943             : /************************************************************************/
     944             : 
     945             : /**
     946             :  * Return the file handle of the standard output of the forked process
     947             :  * from which to read.
     948             :  *
     949             :  * @param p handle returned by CPLSpawnAsync().
     950             :  *
     951             :  * @return the file handle.
     952             :  *
     953             :  * @since GDAL 1.10.0
     954             :  */
     955           6 : CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess *p)
     956             : {
     957           6 :     return p->fin;
     958             : }
     959             : 
     960             : /************************************************************************/
     961             : /*                   CPLSpawnAsyncGetOutputFileHandle()                 */
     962             : /************************************************************************/
     963             : 
     964             : /**
     965             :  * Return the file handle of the standard input of the forked process
     966             :  * into which to write
     967             :  *
     968             :  * @param p handle returned by CPLSpawnAsync().
     969             :  *
     970             :  * @return the file handle.
     971             :  *
     972             :  * @since GDAL 1.10.0
     973             :  */
     974           6 : CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess *p)
     975             : {
     976           6 :     return p->fout;
     977             : }
     978             : 
     979             : /************************************************************************/
     980             : /*                    CPLSpawnAsyncGetErrorFileHandle()                 */
     981             : /************************************************************************/
     982             : 
     983             : /**
     984             :  * Return the file handle of the standard error of the forked process
     985             :  * from which to read.
     986             :  *
     987             :  * @param p handle returned by CPLSpawnAsync().
     988             :  *
     989             :  * @return the file handle
     990             :  *
     991             :  * @since GDAL 1.10.0
     992             :  */
     993           6 : CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess *p)
     994             : {
     995           6 :     return p->ferr;
     996             : }

Generated by: LCOV version 1.14