Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_spawn.cpp
Line
Count
Source
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
static void FillPipeFromFile(VSILFILE *fin, CPL_FILE_HANDLE pipe_fd)
65
0
{
66
0
    char buf[PIPE_BUFFER_SIZE] = {};
67
0
    while (true)
68
0
    {
69
0
        const int nRead =
70
0
            static_cast<int>(VSIFReadL(buf, 1, PIPE_BUFFER_SIZE, fin));
71
0
        if (nRead <= 0)
72
0
            break;
73
0
        if (!CPLPipeWrite(pipe_fd, buf, nRead))
74
0
            break;
75
0
    }
76
0
}
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
int CPLSpawn(const char *const papszArgv[], VSILFILE *fin, VSILFILE *fout,
105
             int bDisplayErr)
106
0
{
107
0
    CPLSpawnedProcess *sp =
108
0
        CPLSpawnAsync(nullptr, papszArgv, TRUE, TRUE, TRUE, nullptr);
109
0
    if (sp == nullptr)
110
0
        return -1;
111
112
0
    CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp);
113
0
    if (fin != nullptr)
114
0
        FillPipeFromFile(fin, in_child);
115
0
    CPLSpawnAsyncCloseOutputFileHandle(sp);
116
117
0
    CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp);
118
0
    if (fout != nullptr)
119
0
        FillFileFromPipe(out_child, fout);
120
0
    CPLSpawnAsyncCloseInputFileHandle(sp);
121
122
0
    CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp);
123
0
    CPLString osName;
124
0
    osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID());
125
0
    VSILFILE *ferr = VSIFOpenL(osName.c_str(), "w");
126
127
0
    FillFileFromPipe(err_child, ferr);
128
0
    CPLSpawnAsyncCloseErrorFileHandle(sp);
129
130
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(ferr));
131
0
    vsi_l_offset nDataLength = 0;
132
0
    GByte *pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE);
133
0
    if (nDataLength > 0)
134
0
        pData[nDataLength - 1] = '\0';
135
0
    if (pData &&
136
0
        strstr(const_cast<const char *>(reinterpret_cast<char *>(pData)),
137
0
               "An error occurred while forking process") != nullptr)
138
0
        bDisplayErr = TRUE;
139
0
    if (pData && bDisplayErr)
140
0
        CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0],
141
0
                 pData);
142
0
    CPLFree(pData);
143
144
0
    return CPLSpawnAsyncFinish(sp, TRUE, FALSE);
145
0
}
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
int CPLPipeRead(CPL_FILE_HANDLE fin, void *data, int length)
454
0
{
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
0
    GByte *pabyData = static_cast<GByte *>(data);
463
0
    int nRemain = length;
464
0
    while (nRemain > 0)
465
0
    {
466
0
        while (true)
467
0
        {
468
0
            assert(nRemain > 0);
469
            // coverity[overflow_sink]
470
0
            const ssize_t n = read(fin, pabyData, nRemain);
471
0
            if (n < 0)
472
0
            {
473
0
                if (errno == EINTR)
474
0
                    continue;
475
0
                else
476
0
                    return FALSE;
477
0
            }
478
0
            else if (n == 0)
479
0
                return FALSE;
480
0
            pabyData += n;
481
0
            assert(n <= nRemain);
482
0
            nRemain -= static_cast<int>(n);
483
0
            break;
484
0
        }
485
0
    }
486
0
    return TRUE;
487
0
#endif
488
0
}
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
int CPLPipeWrite(CPL_FILE_HANDLE fout, const void *data, int length)
505
0
{
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
0
    const GByte *pabyData = static_cast<const GByte *>(data);
514
0
    int nRemain = length;
515
0
    while (nRemain > 0)
516
0
    {
517
0
        while (true)
518
0
        {
519
0
            assert(nRemain > 0);
520
            // coverity[overflow_sink]
521
0
            const ssize_t n = write(fout, pabyData, nRemain);
522
0
            if (n < 0)
523
0
            {
524
0
                if (errno == EINTR)
525
0
                    continue;
526
0
                else
527
0
                    return FALSE;
528
0
            }
529
0
            pabyData += n;
530
0
            assert(n <= nRemain);
531
0
            nRemain -= static_cast<int>(n);
532
0
            break;
533
0
        }
534
0
    }
535
0
    return TRUE;
536
0
#endif
537
0
}
538
539
/************************************************************************/
540
/*                          FillFileFromPipe()                              */
541
/************************************************************************/
542
543
static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout)
544
0
{
545
0
    char buf[PIPE_BUFFER_SIZE] = {};
546
0
    while (true)
547
0
    {
548
0
        const int nRead =
549
0
            static_cast<int>(read(pipe_fd, buf, PIPE_BUFFER_SIZE));
550
0
        if (nRead <= 0)
551
0
            break;
552
0
        const int nWritten = static_cast<int>(VSIFWriteL(buf, 1, nRead, fout));
553
0
        if (nWritten < nRead)
554
0
            break;
555
0
    }
556
0
}
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
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
0
{
607
0
    int pipe_in[2] = {-1, -1};
608
0
    int pipe_out[2] = {-1, -1};
609
0
    int pipe_err[2] = {-1, -1};
610
611
0
    const auto ClosePipes = [&pipe_in, &pipe_out, &pipe_err]()
612
0
    {
613
0
        for (int i = 0; i < 2; i++)
614
0
        {
615
0
            if (pipe_in[i] >= 0)
616
0
                close(pipe_in[i]);
617
0
            if (pipe_out[i] >= 0)
618
0
                close(pipe_out[i]);
619
0
            if (pipe_err[i] >= 0)
620
0
                close(pipe_err[i]);
621
0
        }
622
0
    };
623
624
0
    if ((bCreateInputPipe && pipe(pipe_in)) ||
625
0
        (bCreateOutputPipe && pipe(pipe_out)) ||
626
0
        (bCreateErrorPipe && pipe(pipe_err)))
627
0
    {
628
0
        CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
629
0
        ClosePipes();
630
0
        return nullptr;
631
0
    }
632
633
0
    bool bDup2In = CPL_TO_BOOL(bCreateInputPipe);
634
0
    bool bDup2Out = CPL_TO_BOOL(bCreateOutputPipe);
635
0
    bool bDup2Err = CPL_TO_BOOL(bCreateErrorPipe);
636
637
0
    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
0
    if (papszArgv != nullptr)
644
0
    {
645
0
        for (int i = 0; papszArgvDup[i] != nullptr; i++)
646
0
        {
647
0
            if (bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0)
648
0
            {
649
0
                CPLFree(papszArgvDup[i]);
650
0
                papszArgvDup[i] = CPLStrdup(CPLSPrintf(
651
0
                    "%d,%d", pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
652
0
                bDup2In = false;
653
0
            }
654
0
            else if (bCreateOutputPipe &&
655
0
                     strcmp(papszArgvDup[i], "{pipe_out}") == 0)
656
0
            {
657
0
                CPLFree(papszArgvDup[i]);
658
0
                papszArgvDup[i] =
659
0
                    CPLStrdup(CPLSPrintf("%d,%d", pipe_out[OUT_FOR_PARENT],
660
0
                                         pipe_out[IN_FOR_PARENT]));
661
0
                bDup2Out = false;
662
0
            }
663
0
            else if (bCreateErrorPipe &&
664
0
                     strcmp(papszArgvDup[i], "{pipe_err}") == 0)
665
0
            {
666
0
                CPLFree(papszArgvDup[i]);
667
0
                papszArgvDup[i] =
668
0
                    CPLStrdup(CPLSPrintf("%d,%d", pipe_err[OUT_FOR_PARENT],
669
0
                                         pipe_err[IN_FOR_PARENT]));
670
0
                bDup2Err = false;
671
0
            }
672
0
        }
673
0
    }
674
675
0
#ifdef HAVE_POSIX_SPAWNP
676
0
    if (papszArgv != nullptr)
677
0
    {
678
0
        bool bHasActions = false;
679
0
        posix_spawn_file_actions_t actions;
680
681
0
        if (bDup2In)
682
0
        {
683
0
            /*if( !bHasActions )*/ posix_spawn_file_actions_init(&actions);
684
0
            posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT],
685
0
                                             fileno(stdin));
686
0
            posix_spawn_file_actions_addclose(&actions,
687
0
                                              pipe_in[OUT_FOR_PARENT]);
688
0
            bHasActions = true;
689
0
        }
690
691
0
        if (bDup2Out)
692
0
        {
693
0
            if (!bHasActions)
694
0
                posix_spawn_file_actions_init(&actions);
695
0
            posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT],
696
0
                                             fileno(stdout));
697
0
            posix_spawn_file_actions_addclose(&actions,
698
0
                                              pipe_out[IN_FOR_PARENT]);
699
0
            bHasActions = true;
700
0
        }
701
702
0
        if (bDup2Err)
703
0
        {
704
0
            if (!bHasActions)
705
0
                posix_spawn_file_actions_init(&actions);
706
0
            posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT],
707
0
                                             fileno(stderr));
708
0
            posix_spawn_file_actions_addclose(&actions,
709
0
                                              pipe_err[IN_FOR_PARENT]);
710
0
            bHasActions = true;
711
0
        }
712
713
0
        pid_t pid = 0;
714
0
        assert(papszArgvDup[0] != nullptr);
715
0
        if (posix_spawnp(&pid, papszArgvDup[0],
716
0
                         bHasActions ? &actions : nullptr, nullptr,
717
0
                         papszArgvDup, environ) != 0)
718
0
        {
719
0
            if (bHasActions)
720
0
                posix_spawn_file_actions_destroy(&actions);
721
0
            CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
722
0
            CSLDestroy(papszArgvDup);
723
0
            ClosePipes();
724
0
            return nullptr;
725
0
        }
726
727
0
        CSLDestroy(papszArgvDup);
728
729
        // Close unused end of pipe.
730
0
        if (bCreateInputPipe)
731
0
            close(pipe_in[IN_FOR_PARENT]);
732
0
        if (bCreateOutputPipe)
733
0
            close(pipe_out[OUT_FOR_PARENT]);
734
0
        if (bCreateErrorPipe)
735
0
            close(pipe_err[OUT_FOR_PARENT]);
736
737
            // Ignore SIGPIPE.
738
0
#ifdef SIGPIPE
739
0
        std::signal(SIGPIPE, SIG_IGN);
740
0
#endif
741
0
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
742
0
            CPLMalloc(sizeof(CPLSpawnedProcess)));
743
0
        if (bHasActions)
744
0
            memcpy(&p->actions, &actions, sizeof(actions));
745
0
        p->bFreeActions = bHasActions;
746
0
        p->pid = pid;
747
0
        p->fin = pipe_out[IN_FOR_PARENT];
748
0
        p->fout = pipe_in[OUT_FOR_PARENT];
749
0
        p->ferr = pipe_err[IN_FOR_PARENT];
750
0
        return p;
751
0
    }
752
0
#endif  // #ifdef HAVE_POSIX_SPAWNP
753
754
0
    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
0
    {
766
0
        pid = fork();
767
0
    }
768
769
0
    if (pid == 0)
770
0
    {
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
0
        {
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
0
                               bCreateOutputPipe ? pipe_out[OUT_FOR_PARENT]
804
0
                                                 : fileno(stdout));
805
0
            _exit(nRet);
806
0
        }
807
0
    }
808
0
    else if (pid > 0)
809
0
    {
810
0
        CSLDestroy(papszArgvDup);
811
812
        // Close unused end of pipe.
813
0
        if (bCreateInputPipe)
814
0
            close(pipe_in[IN_FOR_PARENT]);
815
0
        if (bCreateOutputPipe)
816
0
            close(pipe_out[OUT_FOR_PARENT]);
817
0
        if (bCreateErrorPipe)
818
0
            close(pipe_err[OUT_FOR_PARENT]);
819
820
            // Ignore SIGPIPE.
821
0
#ifdef SIGPIPE
822
0
        std::signal(SIGPIPE, SIG_IGN);
823
0
#endif
824
0
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
825
0
            CPLMalloc(sizeof(CPLSpawnedProcess)));
826
0
#ifdef HAVE_POSIX_SPAWNP
827
0
        p->bFreeActions = false;
828
0
#endif
829
0
        p->pid = pid;
830
0
        p->fin = pipe_out[IN_FOR_PARENT];
831
0
        p->fout = pipe_in[OUT_FOR_PARENT];
832
0
        p->ferr = pipe_err[IN_FOR_PARENT];
833
0
        return p;
834
0
    }
835
836
0
    CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
837
838
0
    CSLDestroy(papszArgvDup);
839
0
    ClosePipes();
840
0
    return nullptr;
841
0
}
842
843
/************************************************************************/
844
/*                  CPLSpawnAsyncGetChildProcessId()                    */
845
/************************************************************************/
846
847
CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
848
0
{
849
0
    return p->pid;
850
0
}
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
int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, CPL_UNUSED int bKill)
871
0
{
872
0
    int status = 0;
873
874
0
    if (bWait)
875
0
    {
876
0
        while (true)
877
0
        {
878
0
            status = -1;
879
0
            const int ret = waitpid(p->pid, &status, 0);
880
0
            if (ret < 0)
881
0
            {
882
0
                if (errno != EINTR)
883
0
                {
884
0
                    break;
885
0
                }
886
0
            }
887
0
            else
888
0
            {
889
0
                if (WIFEXITED(status))
890
0
                {
891
0
                    status = WEXITSTATUS(status);
892
0
                }
893
0
                else
894
0
                {
895
0
                    status = -1;
896
0
                }
897
0
                break;
898
0
            }
899
0
        }
900
0
    }
901
902
0
    CPLSpawnAsyncCloseInputFileHandle(p);
903
0
    CPLSpawnAsyncCloseOutputFileHandle(p);
904
0
    CPLSpawnAsyncCloseErrorFileHandle(p);
905
0
#ifdef HAVE_POSIX_SPAWNP
906
0
    if (p->bFreeActions)
907
0
        posix_spawn_file_actions_destroy(&p->actions);
908
0
#endif
909
0
    CPLFree(p);
910
0
    return status;
911
0
}
912
913
/************************************************************************/
914
/*                 CPLSpawnAsyncCloseInputFileHandle()                  */
915
/************************************************************************/
916
917
void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
918
0
{
919
0
    if (p->fin >= 0)
920
0
        close(p->fin);
921
0
    p->fin = -1;
922
0
}
923
924
/************************************************************************/
925
/*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
926
/************************************************************************/
927
928
void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
929
0
{
930
0
    if (p->fout >= 0)
931
0
        close(p->fout);
932
0
    p->fout = -1;
933
0
}
934
935
/************************************************************************/
936
/*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
937
/************************************************************************/
938
939
void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
940
0
{
941
0
    if (p->ferr >= 0)
942
0
        close(p->ferr);
943
0
    p->ferr = -1;
944
0
}
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
CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess *p)
962
0
{
963
0
    return p->fin;
964
0
}
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
CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess *p)
980
0
{
981
0
    return p->fout;
982
0
}
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
CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess *p)
998
0
{
999
0
    return p->ferr;
1000
0
}