Coverage Report

Created: 2025-06-09 08:44

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