Coverage Report

Created: 2025-08-28 06:57

/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
0
{
108
0
    CPLSpawnedProcess *sp =
109
0
        CPLSpawnAsync(nullptr, papszArgv, TRUE, TRUE, TRUE, nullptr);
110
0
    if (sp == nullptr)
111
0
        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
0
}
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
        CPLString osArg(papszArgv[i]);
300
        // We need to quote arguments with spaces or double quotes in them (if not already done).
301
        if (osArg.find_first_of(" \"") != std::string::npos &&
302
            !(osArg.size() >= 3 && osArg.front() == '"' && osArg.back() == '"'))
303
        {
304
            osCommandLine += '"';
305
            osCommandLine += osArg.replaceAll('"', "\\\"");
306
            osCommandLine += '"';
307
        }
308
        else
309
        {
310
            osCommandLine += osArg;
311
        }
312
    }
313
314
    if (!CreateProcess(nullptr, const_cast<CHAR *>(osCommandLine.c_str()),
315
                       nullptr,  // Process security attributes
316
                       nullptr,  // Primary thread security attributes
317
                       TRUE,     // Handles are inherited
318
                       CREATE_NO_WINDOW |
319
                           NORMAL_PRIORITY_CLASS,  // Creation flags
320
                       nullptr,                    // Use parent's environment
321
                       nullptr,  // Use parent's current directory
322
                       &siStartInfo, &piProcInfo))
323
    {
324
        CPLError(CE_Failure, CPLE_AppDefined, "Could not create process %s",
325
                 osCommandLine.c_str());
326
        goto err;
327
    }
328
329
    // Close unused end of pipe.
330
    if (bCreateInputPipe)
331
        CloseHandle(pipe_in[IN_FOR_PARENT]);
332
    if (bCreateOutputPipe)
333
        CloseHandle(pipe_out[OUT_FOR_PARENT]);
334
    if (bCreateErrorPipe)
335
        CloseHandle(pipe_err[OUT_FOR_PARENT]);
336
337
    {
338
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
339
            CPLMalloc(sizeof(CPLSpawnedProcess)));
340
        p->hProcess = piProcInfo.hProcess;
341
        p->nProcessId = piProcInfo.dwProcessId;
342
        p->hThread = piProcInfo.hThread;
343
        p->fin = pipe_out[IN_FOR_PARENT];
344
        p->fout = pipe_in[OUT_FOR_PARENT];
345
        p->ferr = pipe_err[IN_FOR_PARENT];
346
347
        return p;
348
    }
349
350
err_pipe:
351
    CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
352
err:
353
    for (int i = 0; i < 2; i++)
354
    {
355
        if (pipe_in[i] != nullptr)
356
            CloseHandle(pipe_in[i]);
357
        if (pipe_out[i] != nullptr)
358
            CloseHandle(pipe_out[i]);
359
        if (pipe_err[i] != nullptr)
360
            CloseHandle(pipe_err[i]);
361
    }
362
363
    return nullptr;
364
}
365
366
/************************************************************************/
367
/*                  CPLSpawnAsyncGetChildProcessId()                    */
368
/************************************************************************/
369
370
CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
371
{
372
    return p->nProcessId;
373
}
374
375
/************************************************************************/
376
/*                        CPLSpawnAsyncFinish()                         */
377
/************************************************************************/
378
379
int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, int /* bKill */)
380
{
381
    // Get the exit code.
382
    DWORD exitCode = -1;
383
384
    if (bWait)
385
    {
386
        WaitForSingleObject(p->hProcess, INFINITE);
387
        GetExitCodeProcess(p->hProcess, &exitCode);
388
    }
389
    else
390
    {
391
        exitCode = 0;
392
    }
393
394
    CloseHandle(p->hProcess);
395
    CloseHandle(p->hThread);
396
397
    CPLSpawnAsyncCloseInputFileHandle(p);
398
    CPLSpawnAsyncCloseOutputFileHandle(p);
399
    CPLSpawnAsyncCloseErrorFileHandle(p);
400
    CPLFree(p);
401
402
    return static_cast<int>(exitCode);
403
}
404
405
/************************************************************************/
406
/*                 CPLSpawnAsyncCloseInputFileHandle()                  */
407
/************************************************************************/
408
409
void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
410
{
411
    if (p->fin != nullptr)
412
        CloseHandle(p->fin);
413
    p->fin = nullptr;
414
}
415
416
/************************************************************************/
417
/*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
418
/************************************************************************/
419
420
void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
421
{
422
    if (p->fout != nullptr)
423
        CloseHandle(p->fout);
424
    p->fout = nullptr;
425
}
426
427
/************************************************************************/
428
/*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
429
/************************************************************************/
430
431
void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
432
{
433
    if (p->ferr != nullptr)
434
        CloseHandle(p->ferr);
435
    p->ferr = nullptr;
436
}
437
438
#else  // Not WIN32
439
440
/************************************************************************/
441
/*                          CPLPipeRead()                               */
442
/************************************************************************/
443
444
/**
445
 * Read data from the standard output of a forked process.
446
 *
447
 * @param fin handle returned by CPLSpawnAsyncGetInputFileHandle().
448
 * @param data buffer in which to write.
449
 * @param length number of bytes to read.
450
 *
451
 * @return TRUE in case of success.
452
 *
453
 * @since GDAL 1.10.0
454
 */
455
int CPLPipeRead(CPL_FILE_HANDLE fin, void *data, int length)
456
0
{
457
#ifdef __COVERITY__
458
    (void)fin;
459
    (void)data;
460
    (void)length;
461
    CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
462
    return FALSE;
463
#else
464
0
    GByte *pabyData = static_cast<GByte *>(data);
465
0
    int nRemain = length;
466
0
    while (nRemain > 0)
467
0
    {
468
0
        while (true)
469
0
        {
470
0
            assert(nRemain > 0);
471
            // coverity[overflow_sink]
472
0
            const ssize_t n = read(fin, pabyData, nRemain);
473
0
            if (n < 0)
474
0
            {
475
0
                if (errno == EINTR)
476
0
                    continue;
477
0
                else
478
0
                    return FALSE;
479
0
            }
480
0
            else if (n == 0)
481
0
                return FALSE;
482
0
            pabyData += n;
483
0
            assert(n <= nRemain);
484
0
            nRemain -= static_cast<int>(n);
485
0
            break;
486
0
        }
487
0
    }
488
0
    return TRUE;
489
0
#endif
490
0
}
491
492
/************************************************************************/
493
/*                          CPLPipeWrite()                              */
494
/************************************************************************/
495
496
/**
497
 * Write data to the standard input of a forked process.
498
 *
499
 * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle().
500
 * @param data buffer from which to read.
501
 * @param length number of bytes to write.
502
 *
503
 * @return TRUE in case of success.
504
 *
505
 * @since GDAL 1.10.0
506
 */
507
int CPLPipeWrite(CPL_FILE_HANDLE fout, const void *data, int length)
508
0
{
509
#ifdef __COVERITY__
510
    (void)fout;
511
    (void)data;
512
    (void)length;
513
    CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
514
    return FALSE;
515
#else
516
0
    const GByte *pabyData = static_cast<const GByte *>(data);
517
0
    int nRemain = length;
518
0
    while (nRemain > 0)
519
0
    {
520
0
        while (true)
521
0
        {
522
0
            assert(nRemain > 0);
523
            // coverity[overflow_sink]
524
0
            const ssize_t n = write(fout, pabyData, nRemain);
525
0
            if (n < 0)
526
0
            {
527
0
                if (errno == EINTR)
528
0
                    continue;
529
0
                else
530
0
                    return FALSE;
531
0
            }
532
0
            pabyData += n;
533
0
            assert(n <= nRemain);
534
0
            nRemain -= static_cast<int>(n);
535
0
            break;
536
0
        }
537
0
    }
538
0
    return TRUE;
539
0
#endif
540
0
}
541
542
/************************************************************************/
543
/*                          FillFileFromPipe()                              */
544
/************************************************************************/
545
546
static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout)
547
0
{
548
0
    char buf[PIPE_BUFFER_SIZE] = {};
549
0
    while (true)
550
0
    {
551
0
        const int nRead =
552
0
            static_cast<int>(read(pipe_fd, buf, PIPE_BUFFER_SIZE));
553
0
        if (nRead <= 0)
554
0
            break;
555
0
        const int nWritten = static_cast<int>(VSIFWriteL(buf, 1, nRead, fout));
556
0
        if (nWritten < nRead)
557
0
            break;
558
0
    }
559
0
}
560
561
/************************************************************************/
562
/*                            CPLSpawnAsync()                           */
563
/************************************************************************/
564
565
struct _CPLSpawnedProcess
566
{
567
    pid_t pid;
568
    CPL_FILE_HANDLE fin;
569
    CPL_FILE_HANDLE fout;
570
    CPL_FILE_HANDLE ferr;
571
#ifdef HAVE_POSIX_SPAWNP
572
    bool bFreeActions;
573
    posix_spawn_file_actions_t actions;
574
#endif
575
};
576
577
/**
578
 * Runs an executable in another process (or fork the current process)
579
 * and return immediately.
580
 *
581
 * This function launches an executable and returns immediately, while letting
582
 * the sub-process to run asynchronously.
583
 *
584
 * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
585
 * on other platforms.
586
 *
587
 * On Unix, a pointer of function can be provided to run in the child process,
588
 * without exec()'ing a new executable.
589
 *
590
 * @param pfnMain the function to run in the child process (Unix only).
591
 * @param papszArgv argument list of the executable to run. papszArgv[0] is the
592
 *                  name of the executable.
593
 * @param bCreateInputPipe set to TRUE to create a pipe for the child
594
 * input stream.
595
 * @param bCreateOutputPipe set to TRUE to create a pipe for the child
596
 * output stream.
597
 * @param bCreateErrorPipe set to TRUE to create a pipe for the child
598
 * error stream.
599
 * @param papszOptions unused. should be set to NULL.
600
 *
601
 * @return a handle, that must be freed with CPLSpawnAsyncFinish()
602
 *
603
 * @since GDAL 1.10.0
604
 */
605
CPLSpawnedProcess *
606
CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
607
              const char *const papszArgv[], int bCreateInputPipe,
608
              int bCreateOutputPipe, int bCreateErrorPipe,
609
              CPL_UNUSED char **papszOptions)
610
0
{
611
0
    int pipe_in[2] = {-1, -1};
612
0
    int pipe_out[2] = {-1, -1};
613
0
    int pipe_err[2] = {-1, -1};
614
615
0
    const auto ClosePipes = [&pipe_in, &pipe_out, &pipe_err]()
616
0
    {
617
0
        for (int i = 0; i < 2; i++)
618
0
        {
619
0
            if (pipe_in[i] >= 0)
620
0
                close(pipe_in[i]);
621
0
            if (pipe_out[i] >= 0)
622
0
                close(pipe_out[i]);
623
0
            if (pipe_err[i] >= 0)
624
0
                close(pipe_err[i]);
625
0
        }
626
0
    };
627
628
0
    if ((bCreateInputPipe && pipe(pipe_in)) ||
629
0
        (bCreateOutputPipe && pipe(pipe_out)) ||
630
0
        (bCreateErrorPipe && pipe(pipe_err)))
631
0
    {
632
0
        CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
633
0
        ClosePipes();
634
0
        return nullptr;
635
0
    }
636
637
0
    bool bDup2In = CPL_TO_BOOL(bCreateInputPipe);
638
0
    bool bDup2Out = CPL_TO_BOOL(bCreateOutputPipe);
639
0
    bool bDup2Err = CPL_TO_BOOL(bCreateErrorPipe);
640
641
0
    char **papszArgvDup = CSLDuplicate(const_cast<char **>(papszArgv));
642
643
    // If we don't do any file actions, posix_spawnp() might be implemented
644
    // efficiently as a vfork()/exec() pair (or if it is not available, we
645
    // can use vfork()/exec()), so if the child is cooperative
646
    // we pass the pipe handles as commandline arguments.
647
0
    if (papszArgv != nullptr)
648
0
    {
649
0
        for (int i = 0; papszArgvDup[i] != nullptr; i++)
650
0
        {
651
0
            if (bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0)
652
0
            {
653
0
                CPLFree(papszArgvDup[i]);
654
0
                papszArgvDup[i] = CPLStrdup(CPLSPrintf(
655
0
                    "%d,%d", pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
656
0
                bDup2In = false;
657
0
            }
658
0
            else if (bCreateOutputPipe &&
659
0
                     strcmp(papszArgvDup[i], "{pipe_out}") == 0)
660
0
            {
661
0
                CPLFree(papszArgvDup[i]);
662
0
                papszArgvDup[i] =
663
0
                    CPLStrdup(CPLSPrintf("%d,%d", pipe_out[OUT_FOR_PARENT],
664
0
                                         pipe_out[IN_FOR_PARENT]));
665
0
                bDup2Out = false;
666
0
            }
667
0
            else if (bCreateErrorPipe &&
668
0
                     strcmp(papszArgvDup[i], "{pipe_err}") == 0)
669
0
            {
670
0
                CPLFree(papszArgvDup[i]);
671
0
                papszArgvDup[i] =
672
0
                    CPLStrdup(CPLSPrintf("%d,%d", pipe_err[OUT_FOR_PARENT],
673
0
                                         pipe_err[IN_FOR_PARENT]));
674
0
                bDup2Err = false;
675
0
            }
676
0
        }
677
0
    }
678
679
0
#ifdef HAVE_POSIX_SPAWNP
680
0
    if (papszArgv != nullptr)
681
0
    {
682
0
        bool bHasActions = false;
683
0
        posix_spawn_file_actions_t actions;
684
685
0
        if (bDup2In)
686
0
        {
687
0
            /*if( !bHasActions )*/ posix_spawn_file_actions_init(&actions);
688
0
            posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT],
689
0
                                             fileno(stdin));
690
0
            posix_spawn_file_actions_addclose(&actions,
691
0
                                              pipe_in[OUT_FOR_PARENT]);
692
0
            bHasActions = true;
693
0
        }
694
695
0
        if (bDup2Out)
696
0
        {
697
0
            if (!bHasActions)
698
0
                posix_spawn_file_actions_init(&actions);
699
0
            posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT],
700
0
                                             fileno(stdout));
701
0
            posix_spawn_file_actions_addclose(&actions,
702
0
                                              pipe_out[IN_FOR_PARENT]);
703
0
            bHasActions = true;
704
0
        }
705
706
0
        if (bDup2Err)
707
0
        {
708
0
            if (!bHasActions)
709
0
                posix_spawn_file_actions_init(&actions);
710
0
            posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT],
711
0
                                             fileno(stderr));
712
0
            posix_spawn_file_actions_addclose(&actions,
713
0
                                              pipe_err[IN_FOR_PARENT]);
714
0
            bHasActions = true;
715
0
        }
716
717
0
        pid_t pid = 0;
718
0
        assert(papszArgvDup[0] != nullptr);
719
0
        if (posix_spawnp(&pid, papszArgvDup[0],
720
0
                         bHasActions ? &actions : nullptr, nullptr,
721
0
                         papszArgvDup, environ) != 0)
722
0
        {
723
0
            if (bHasActions)
724
0
                posix_spawn_file_actions_destroy(&actions);
725
0
            CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
726
0
            CSLDestroy(papszArgvDup);
727
0
            ClosePipes();
728
0
            return nullptr;
729
0
        }
730
731
0
        CSLDestroy(papszArgvDup);
732
733
        // Close unused end of pipe.
734
0
        if (bCreateInputPipe)
735
0
            close(pipe_in[IN_FOR_PARENT]);
736
0
        if (bCreateOutputPipe)
737
0
            close(pipe_out[OUT_FOR_PARENT]);
738
0
        if (bCreateErrorPipe)
739
0
            close(pipe_err[OUT_FOR_PARENT]);
740
741
            // Ignore SIGPIPE.
742
0
#ifdef SIGPIPE
743
0
        std::signal(SIGPIPE, SIG_IGN);
744
0
#endif
745
0
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
746
0
            CPLMalloc(sizeof(CPLSpawnedProcess)));
747
0
        if (bHasActions)
748
0
            memcpy(&p->actions, &actions, sizeof(actions));
749
0
        p->bFreeActions = bHasActions;
750
0
        p->pid = pid;
751
0
        p->fin = pipe_out[IN_FOR_PARENT];
752
0
        p->fout = pipe_in[OUT_FOR_PARENT];
753
0
        p->ferr = pipe_err[IN_FOR_PARENT];
754
0
        return p;
755
0
    }
756
0
#endif  // #ifdef HAVE_POSIX_SPAWNP
757
758
0
    pid_t pid = 0;
759
760
#if defined(HAVE_VFORK) && !defined(HAVE_POSIX_SPAWNP)
761
    if (papszArgv != nullptr && !bDup2In && !bDup2Out && !bDup2Err)
762
    {
763
        // Workaround clang static analyzer warning about unsafe use of vfork.
764
        pid_t (*p_vfork)(void) = vfork;
765
        pid = p_vfork();
766
    }
767
    else
768
#endif
769
0
    {
770
0
        pid = fork();
771
0
    }
772
773
0
    if (pid == 0)
774
0
    {
775
        // Close unused end of pipe.
776
0
        if (bDup2In)
777
0
            close(pipe_in[OUT_FOR_PARENT]);
778
0
        if (bDup2Out)
779
0
            close(pipe_out[IN_FOR_PARENT]);
780
0
        if (bDup2Err)
781
0
            close(pipe_err[IN_FOR_PARENT]);
782
783
#ifndef HAVE_POSIX_SPAWNP
784
        if (papszArgv != nullptr)
785
        {
786
            if (bDup2In)
787
                dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
788
            if (bDup2Out)
789
                dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
790
            if (bDup2Err)
791
                dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));
792
793
            execvp(papszArgvDup[0], papszArgvDup);
794
795
            _exit(1);
796
        }
797
        else
798
#endif  // HAVE_POSIX_SPAWNP
799
0
        {
800
0
            if (bCreateErrorPipe)
801
0
                close(pipe_err[OUT_FOR_PARENT]);
802
803
0
            int nRet = 0;
804
0
            if (pfnMain != nullptr)
805
0
                nRet = pfnMain(bCreateInputPipe ? pipe_in[IN_FOR_PARENT]
806
0
                                                : fileno(stdin),
807
0
                               bCreateOutputPipe ? pipe_out[OUT_FOR_PARENT]
808
0
                                                 : fileno(stdout));
809
0
            _exit(nRet);
810
0
        }
811
0
    }
812
0
    else if (pid > 0)
813
0
    {
814
0
        CSLDestroy(papszArgvDup);
815
816
        // Close unused end of pipe.
817
0
        if (bCreateInputPipe)
818
0
            close(pipe_in[IN_FOR_PARENT]);
819
0
        if (bCreateOutputPipe)
820
0
            close(pipe_out[OUT_FOR_PARENT]);
821
0
        if (bCreateErrorPipe)
822
0
            close(pipe_err[OUT_FOR_PARENT]);
823
824
            // Ignore SIGPIPE.
825
0
#ifdef SIGPIPE
826
0
        std::signal(SIGPIPE, SIG_IGN);
827
0
#endif
828
0
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
829
0
            CPLMalloc(sizeof(CPLSpawnedProcess)));
830
0
#ifdef HAVE_POSIX_SPAWNP
831
0
        p->bFreeActions = false;
832
0
#endif
833
0
        p->pid = pid;
834
0
        p->fin = pipe_out[IN_FOR_PARENT];
835
0
        p->fout = pipe_in[OUT_FOR_PARENT];
836
0
        p->ferr = pipe_err[IN_FOR_PARENT];
837
0
        return p;
838
0
    }
839
840
0
    CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
841
842
0
    CSLDestroy(papszArgvDup);
843
0
    ClosePipes();
844
0
    return nullptr;
845
0
}
846
847
/************************************************************************/
848
/*                  CPLSpawnAsyncGetChildProcessId()                    */
849
/************************************************************************/
850
851
CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
852
0
{
853
0
    return p->pid;
854
0
}
855
856
/************************************************************************/
857
/*                        CPLSpawnAsyncFinish()                         */
858
/************************************************************************/
859
860
/**
861
 * \fn CPLSpawnAsyncFinish(CPLSpawnedProcess*,int,int)
862
 * Wait for the forked process to finish.
863
 *
864
 * @param p handle returned by CPLSpawnAsync()
865
 * @param bWait set to TRUE to wait for the child to terminate. Otherwise the
866
 *              associated handles are just cleaned.
867
 * @param bKill set to TRUE to force child termination (unimplemented right
868
 *              now).
869
 *
870
 * @return the return code of the forked process if bWait == TRUE, 0 otherwise
871
 *
872
 * @since GDAL 1.10.0
873
 */
874
875
int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, CPL_UNUSED int bKill)
876
0
{
877
0
    int status = 0;
878
879
0
    if (bWait)
880
0
    {
881
0
        while (true)
882
0
        {
883
0
            status = -1;
884
0
            const int ret = waitpid(p->pid, &status, 0);
885
0
            if (ret < 0)
886
0
            {
887
0
                if (errno != EINTR)
888
0
                {
889
0
                    break;
890
0
                }
891
0
            }
892
0
            else
893
0
            {
894
0
                if (WIFEXITED(status))
895
0
                {
896
0
                    status = WEXITSTATUS(status);
897
0
                }
898
0
                else
899
0
                {
900
0
                    status = -1;
901
0
                }
902
0
                break;
903
0
            }
904
0
        }
905
0
    }
906
907
0
    CPLSpawnAsyncCloseInputFileHandle(p);
908
0
    CPLSpawnAsyncCloseOutputFileHandle(p);
909
0
    CPLSpawnAsyncCloseErrorFileHandle(p);
910
0
#ifdef HAVE_POSIX_SPAWNP
911
0
    if (p->bFreeActions)
912
0
        posix_spawn_file_actions_destroy(&p->actions);
913
0
#endif
914
0
    CPLFree(p);
915
0
    return status;
916
0
}
917
918
/************************************************************************/
919
/*                 CPLSpawnAsyncCloseInputFileHandle()                  */
920
/************************************************************************/
921
922
void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
923
0
{
924
0
    if (p->fin >= 0)
925
0
        close(p->fin);
926
0
    p->fin = -1;
927
0
}
928
929
/************************************************************************/
930
/*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
931
/************************************************************************/
932
933
void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
934
0
{
935
0
    if (p->fout >= 0)
936
0
        close(p->fout);
937
0
    p->fout = -1;
938
0
}
939
940
/************************************************************************/
941
/*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
942
/************************************************************************/
943
944
void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
945
0
{
946
0
    if (p->ferr >= 0)
947
0
        close(p->ferr);
948
0
    p->ferr = -1;
949
0
}
950
951
#endif
952
953
/************************************************************************/
954
/*                    CPLSpawnAsyncGetInputFileHandle()                 */
955
/************************************************************************/
956
957
/**
958
 * Return the file handle of the standard output of the forked process
959
 * from which to read.
960
 *
961
 * @param p handle returned by CPLSpawnAsync().
962
 *
963
 * @return the file handle.
964
 *
965
 * @since GDAL 1.10.0
966
 */
967
CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess *p)
968
0
{
969
0
    return p->fin;
970
0
}
971
972
/************************************************************************/
973
/*                   CPLSpawnAsyncGetOutputFileHandle()                 */
974
/************************************************************************/
975
976
/**
977
 * Return the file handle of the standard input of the forked process
978
 * into which to write
979
 *
980
 * @param p handle returned by CPLSpawnAsync().
981
 *
982
 * @return the file handle.
983
 *
984
 * @since GDAL 1.10.0
985
 */
986
CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess *p)
987
0
{
988
0
    return p->fout;
989
0
}
990
991
/************************************************************************/
992
/*                    CPLSpawnAsyncGetErrorFileHandle()                 */
993
/************************************************************************/
994
995
/**
996
 * Return the file handle of the standard error of the forked process
997
 * from which to read.
998
 *
999
 * @param p handle returned by CPLSpawnAsync().
1000
 *
1001
 * @return the file handle
1002
 *
1003
 * @since GDAL 1.10.0
1004
 */
1005
CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess *p)
1006
0
{
1007
0
    return p->ferr;
1008
0
}