Coverage Report

Created: 2025-06-22 06:59

/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
        // 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
#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
 * @since GDAL 1.10.0
504
 */
505
int CPLPipeWrite(CPL_FILE_HANDLE fout, const void *data, int length)
506
0
{
507
#ifdef __COVERITY__
508
    (void)fout;
509
    (void)data;
510
    (void)length;
511
    CPLError(CE_Failure, CPLE_AppDefined, "Not implemented");
512
    return FALSE;
513
#else
514
0
    const GByte *pabyData = static_cast<const GByte *>(data);
515
0
    int nRemain = length;
516
0
    while (nRemain > 0)
517
0
    {
518
0
        while (true)
519
0
        {
520
0
            assert(nRemain > 0);
521
            // coverity[overflow_sink]
522
0
            const ssize_t n = write(fout, pabyData, nRemain);
523
0
            if (n < 0)
524
0
            {
525
0
                if (errno == EINTR)
526
0
                    continue;
527
0
                else
528
0
                    return FALSE;
529
0
            }
530
0
            pabyData += n;
531
0
            assert(n <= nRemain);
532
0
            nRemain -= static_cast<int>(n);
533
0
            break;
534
0
        }
535
0
    }
536
0
    return TRUE;
537
0
#endif
538
0
}
539
540
/************************************************************************/
541
/*                          FillFileFromPipe()                              */
542
/************************************************************************/
543
544
static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE *fout)
545
0
{
546
0
    char buf[PIPE_BUFFER_SIZE] = {};
547
0
    while (true)
548
0
    {
549
0
        const int nRead =
550
0
            static_cast<int>(read(pipe_fd, buf, PIPE_BUFFER_SIZE));
551
0
        if (nRead <= 0)
552
0
            break;
553
0
        const int nWritten = static_cast<int>(VSIFWriteL(buf, 1, nRead, fout));
554
0
        if (nWritten < nRead)
555
0
            break;
556
0
    }
557
0
}
558
559
/************************************************************************/
560
/*                            CPLSpawnAsync()                           */
561
/************************************************************************/
562
563
struct _CPLSpawnedProcess
564
{
565
    pid_t pid;
566
    CPL_FILE_HANDLE fin;
567
    CPL_FILE_HANDLE fout;
568
    CPL_FILE_HANDLE ferr;
569
#ifdef HAVE_POSIX_SPAWNP
570
    bool bFreeActions;
571
    posix_spawn_file_actions_t actions;
572
#endif
573
};
574
575
/**
576
 * Runs an executable in another process (or fork the current process)
577
 * and return immediately.
578
 *
579
 * This function launches an executable and returns immediately, while letting
580
 * the sub-process to run asynchronously.
581
 *
582
 * It is implemented as CreateProcess() on Windows platforms, and fork()/exec()
583
 * on other platforms.
584
 *
585
 * On Unix, a pointer of function can be provided to run in the child process,
586
 * without exec()'ing a new executable.
587
 *
588
 * @param pfnMain the function to run in the child process (Unix only).
589
 * @param papszArgv argument list of the executable to run. papszArgv[0] is the
590
 *                  name of the executable.
591
 * @param bCreateInputPipe set to TRUE to create a pipe for the child
592
 * input stream.
593
 * @param bCreateOutputPipe set to TRUE to create a pipe for the child
594
 * output stream.
595
 * @param bCreateErrorPipe set to TRUE to create a pipe for the child
596
 * error stream.
597
 * @param papszOptions unused. should be set to NULL.
598
 *
599
 * @return a handle, that must be freed with CPLSpawnAsyncFinish()
600
 *
601
 * @since GDAL 1.10.0
602
 */
603
CPLSpawnedProcess *
604
CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE),
605
              const char *const papszArgv[], int bCreateInputPipe,
606
              int bCreateOutputPipe, int bCreateErrorPipe,
607
              CPL_UNUSED char **papszOptions)
608
0
{
609
0
    int pipe_in[2] = {-1, -1};
610
0
    int pipe_out[2] = {-1, -1};
611
0
    int pipe_err[2] = {-1, -1};
612
613
0
    if ((bCreateInputPipe && pipe(pipe_in)) ||
614
0
        (bCreateOutputPipe && pipe(pipe_out)) ||
615
0
        (bCreateErrorPipe && pipe(pipe_err)))
616
0
    {
617
0
        CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe");
618
0
        return nullptr;
619
0
    }
620
621
0
    bool bDup2In = CPL_TO_BOOL(bCreateInputPipe);
622
0
    bool bDup2Out = CPL_TO_BOOL(bCreateOutputPipe);
623
0
    bool bDup2Err = CPL_TO_BOOL(bCreateErrorPipe);
624
625
0
    char **papszArgvDup = CSLDuplicate(const_cast<char **>(papszArgv));
626
627
    // If we don't do any file actions, posix_spawnp() might be implemented
628
    // efficiently as a vfork()/exec() pair (or if it is not available, we
629
    // can use vfork()/exec()), so if the child is cooperative
630
    // we pass the pipe handles as commandline arguments.
631
0
    if (papszArgv != nullptr)
632
0
    {
633
0
        for (int i = 0; papszArgvDup[i] != nullptr; i++)
634
0
        {
635
0
            if (bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0)
636
0
            {
637
0
                CPLFree(papszArgvDup[i]);
638
0
                papszArgvDup[i] = CPLStrdup(CPLSPrintf(
639
0
                    "%d,%d", pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT]));
640
0
                bDup2In = false;
641
0
            }
642
0
            else if (bCreateOutputPipe &&
643
0
                     strcmp(papszArgvDup[i], "{pipe_out}") == 0)
644
0
            {
645
0
                CPLFree(papszArgvDup[i]);
646
0
                papszArgvDup[i] =
647
0
                    CPLStrdup(CPLSPrintf("%d,%d", pipe_out[OUT_FOR_PARENT],
648
0
                                         pipe_out[IN_FOR_PARENT]));
649
0
                bDup2Out = false;
650
0
            }
651
0
            else if (bCreateErrorPipe &&
652
0
                     strcmp(papszArgvDup[i], "{pipe_err}") == 0)
653
0
            {
654
0
                CPLFree(papszArgvDup[i]);
655
0
                papszArgvDup[i] =
656
0
                    CPLStrdup(CPLSPrintf("%d,%d", pipe_err[OUT_FOR_PARENT],
657
0
                                         pipe_err[IN_FOR_PARENT]));
658
0
                bDup2Err = false;
659
0
            }
660
0
        }
661
0
    }
662
663
0
#ifdef HAVE_POSIX_SPAWNP
664
0
    if (papszArgv != nullptr)
665
0
    {
666
0
        bool bHasActions = false;
667
0
        posix_spawn_file_actions_t actions;
668
669
0
        if (bDup2In)
670
0
        {
671
0
            /*if( !bHasActions )*/ posix_spawn_file_actions_init(&actions);
672
0
            posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT],
673
0
                                             fileno(stdin));
674
0
            posix_spawn_file_actions_addclose(&actions,
675
0
                                              pipe_in[OUT_FOR_PARENT]);
676
0
            bHasActions = true;
677
0
        }
678
679
0
        if (bDup2Out)
680
0
        {
681
0
            if (!bHasActions)
682
0
                posix_spawn_file_actions_init(&actions);
683
0
            posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT],
684
0
                                             fileno(stdout));
685
0
            posix_spawn_file_actions_addclose(&actions,
686
0
                                              pipe_out[IN_FOR_PARENT]);
687
0
            bHasActions = true;
688
0
        }
689
690
0
        if (bDup2Err)
691
0
        {
692
0
            if (!bHasActions)
693
0
                posix_spawn_file_actions_init(&actions);
694
0
            posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT],
695
0
                                             fileno(stderr));
696
0
            posix_spawn_file_actions_addclose(&actions,
697
0
                                              pipe_err[IN_FOR_PARENT]);
698
0
            bHasActions = true;
699
0
        }
700
701
0
        pid_t pid = 0;
702
0
        assert(papszArgvDup[0] != nullptr);
703
0
        if (posix_spawnp(&pid, papszArgvDup[0],
704
0
                         bHasActions ? &actions : nullptr, nullptr,
705
0
                         papszArgvDup, environ) != 0)
706
0
        {
707
0
            if (bHasActions)
708
0
                posix_spawn_file_actions_destroy(&actions);
709
0
            CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed");
710
0
            CSLDestroy(papszArgvDup);
711
0
            for (int i = 0; i < 2; i++)
712
0
            {
713
0
                if (pipe_in[i] >= 0)
714
0
                    close(pipe_in[i]);
715
0
                if (pipe_out[i] >= 0)
716
0
                    close(pipe_out[i]);
717
0
                if (pipe_err[i] >= 0)
718
0
                    close(pipe_err[i]);
719
0
            }
720
721
0
            return nullptr;
722
0
        }
723
724
0
        CSLDestroy(papszArgvDup);
725
726
        // Close unused end of pipe.
727
0
        if (bCreateInputPipe)
728
0
            close(pipe_in[IN_FOR_PARENT]);
729
0
        if (bCreateOutputPipe)
730
0
            close(pipe_out[OUT_FOR_PARENT]);
731
0
        if (bCreateErrorPipe)
732
0
            close(pipe_err[OUT_FOR_PARENT]);
733
734
            // Ignore SIGPIPE.
735
0
#ifdef SIGPIPE
736
0
        std::signal(SIGPIPE, SIG_IGN);
737
0
#endif
738
0
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
739
0
            CPLMalloc(sizeof(CPLSpawnedProcess)));
740
0
        if (bHasActions)
741
0
            memcpy(&p->actions, &actions, sizeof(actions));
742
0
        p->bFreeActions = bHasActions;
743
0
        p->pid = pid;
744
0
        p->fin = pipe_out[IN_FOR_PARENT];
745
0
        p->fout = pipe_in[OUT_FOR_PARENT];
746
0
        p->ferr = pipe_err[IN_FOR_PARENT];
747
0
        return p;
748
0
    }
749
0
#endif  // #ifdef HAVE_POSIX_SPAWNP
750
751
0
    pid_t pid = 0;
752
753
#if defined(HAVE_VFORK) && !defined(HAVE_POSIX_SPAWNP)
754
    if (papszArgv != nullptr && !bDup2In && !bDup2Out && !bDup2Err)
755
    {
756
        // Workaround clang static analyzer warning about unsafe use of vfork.
757
        pid_t (*p_vfork)(void) = vfork;
758
        pid = p_vfork();
759
    }
760
    else
761
#endif
762
0
    {
763
0
        pid = fork();
764
0
    }
765
766
0
    if (pid == 0)
767
0
    {
768
        // Close unused end of pipe.
769
0
        if (bDup2In)
770
0
            close(pipe_in[OUT_FOR_PARENT]);
771
0
        if (bDup2Out)
772
0
            close(pipe_out[IN_FOR_PARENT]);
773
0
        if (bDup2Err)
774
0
            close(pipe_err[IN_FOR_PARENT]);
775
776
#ifndef HAVE_POSIX_SPAWNP
777
        if (papszArgv != nullptr)
778
        {
779
            if (bDup2In)
780
                dup2(pipe_in[IN_FOR_PARENT], fileno(stdin));
781
            if (bDup2Out)
782
                dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout));
783
            if (bDup2Err)
784
                dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr));
785
786
            execvp(papszArgvDup[0], papszArgvDup);
787
788
            _exit(1);
789
        }
790
        else
791
#endif  // HAVE_POSIX_SPAWNP
792
0
        {
793
0
            if (bCreateErrorPipe)
794
0
                close(pipe_err[OUT_FOR_PARENT]);
795
796
0
            int nRet = 0;
797
0
            if (pfnMain != nullptr)
798
0
                nRet = pfnMain(bCreateInputPipe ? pipe_in[IN_FOR_PARENT]
799
0
                                                : fileno(stdin),
800
0
                               bCreateOutputPipe ? pipe_out[OUT_FOR_PARENT]
801
0
                                                 : fileno(stdout));
802
0
            _exit(nRet);
803
0
        }
804
0
    }
805
0
    else if (pid > 0)
806
0
    {
807
0
        CSLDestroy(papszArgvDup);
808
809
        // Close unused end of pipe.
810
0
        if (bCreateInputPipe)
811
0
            close(pipe_in[IN_FOR_PARENT]);
812
0
        if (bCreateOutputPipe)
813
0
            close(pipe_out[OUT_FOR_PARENT]);
814
0
        if (bCreateErrorPipe)
815
0
            close(pipe_err[OUT_FOR_PARENT]);
816
817
            // Ignore SIGPIPE.
818
0
#ifdef SIGPIPE
819
0
        std::signal(SIGPIPE, SIG_IGN);
820
0
#endif
821
0
        CPLSpawnedProcess *p = static_cast<CPLSpawnedProcess *>(
822
0
            CPLMalloc(sizeof(CPLSpawnedProcess)));
823
0
#ifdef HAVE_POSIX_SPAWNP
824
0
        p->bFreeActions = false;
825
0
#endif
826
0
        p->pid = pid;
827
0
        p->fin = pipe_out[IN_FOR_PARENT];
828
0
        p->fout = pipe_in[OUT_FOR_PARENT];
829
0
        p->ferr = pipe_err[IN_FOR_PARENT];
830
0
        return p;
831
0
    }
832
833
0
    CPLError(CE_Failure, CPLE_AppDefined, "Fork failed");
834
835
0
    CSLDestroy(papszArgvDup);
836
0
    for (int i = 0; i < 2; i++)
837
0
    {
838
0
        if (pipe_in[i] >= 0)
839
0
            close(pipe_in[i]);
840
0
        if (pipe_out[i] >= 0)
841
0
            close(pipe_out[i]);
842
0
        if (pipe_err[i] >= 0)
843
0
            close(pipe_err[i]);
844
0
    }
845
846
0
    return nullptr;
847
0
}
848
849
/************************************************************************/
850
/*                  CPLSpawnAsyncGetChildProcessId()                    */
851
/************************************************************************/
852
853
CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess *p)
854
0
{
855
0
    return p->pid;
856
0
}
857
858
/************************************************************************/
859
/*                        CPLSpawnAsyncFinish()                         */
860
/************************************************************************/
861
862
/**
863
 * \fn CPLSpawnAsyncFinish(CPLSpawnedProcess*,int,int)
864
 * Wait for the forked process to finish.
865
 *
866
 * @param p handle returned by CPLSpawnAsync()
867
 * @param bWait set to TRUE to wait for the child to terminate. Otherwise the
868
 *              associated handles are just cleaned.
869
 * @param bKill set to TRUE to force child termination (unimplemented right
870
 *              now).
871
 *
872
 * @return the return code of the forked process if bWait == TRUE, 0 otherwise
873
 *
874
 * @since GDAL 1.10.0
875
 */
876
877
int CPLSpawnAsyncFinish(CPLSpawnedProcess *p, int bWait, CPL_UNUSED int bKill)
878
0
{
879
0
    int status = 0;
880
881
0
    if (bWait)
882
0
    {
883
0
        while (true)
884
0
        {
885
0
            status = -1;
886
0
            const int ret = waitpid(p->pid, &status, 0);
887
0
            if (ret < 0)
888
0
            {
889
0
                if (errno != EINTR)
890
0
                {
891
0
                    break;
892
0
                }
893
0
            }
894
0
            else
895
0
            {
896
0
                if (WIFEXITED(status))
897
0
                {
898
0
                    status = WEXITSTATUS(status);
899
0
                }
900
0
                else
901
0
                {
902
0
                    status = -1;
903
0
                }
904
0
                break;
905
0
            }
906
0
        }
907
0
    }
908
909
0
    CPLSpawnAsyncCloseInputFileHandle(p);
910
0
    CPLSpawnAsyncCloseOutputFileHandle(p);
911
0
    CPLSpawnAsyncCloseErrorFileHandle(p);
912
0
#ifdef HAVE_POSIX_SPAWNP
913
0
    if (p->bFreeActions)
914
0
        posix_spawn_file_actions_destroy(&p->actions);
915
0
#endif
916
0
    CPLFree(p);
917
0
    return status;
918
0
}
919
920
/************************************************************************/
921
/*                 CPLSpawnAsyncCloseInputFileHandle()                  */
922
/************************************************************************/
923
924
void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess *p)
925
0
{
926
0
    if (p->fin >= 0)
927
0
        close(p->fin);
928
0
    p->fin = -1;
929
0
}
930
931
/************************************************************************/
932
/*                 CPLSpawnAsyncCloseOutputFileHandle()                 */
933
/************************************************************************/
934
935
void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess *p)
936
0
{
937
0
    if (p->fout >= 0)
938
0
        close(p->fout);
939
0
    p->fout = -1;
940
0
}
941
942
/************************************************************************/
943
/*                 CPLSpawnAsyncCloseErrorFileHandle()                  */
944
/************************************************************************/
945
946
void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess *p)
947
0
{
948
0
    if (p->ferr >= 0)
949
0
        close(p->ferr);
950
0
    p->ferr = -1;
951
0
}
952
953
#endif
954
955
/************************************************************************/
956
/*                    CPLSpawnAsyncGetInputFileHandle()                 */
957
/************************************************************************/
958
959
/**
960
 * Return the file handle of the standard output of the forked process
961
 * from which to read.
962
 *
963
 * @param p handle returned by CPLSpawnAsync().
964
 *
965
 * @return the file handle.
966
 *
967
 * @since GDAL 1.10.0
968
 */
969
CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess *p)
970
0
{
971
0
    return p->fin;
972
0
}
973
974
/************************************************************************/
975
/*                   CPLSpawnAsyncGetOutputFileHandle()                 */
976
/************************************************************************/
977
978
/**
979
 * Return the file handle of the standard input of the forked process
980
 * into which to write
981
 *
982
 * @param p handle returned by CPLSpawnAsync().
983
 *
984
 * @return the file handle.
985
 *
986
 * @since GDAL 1.10.0
987
 */
988
CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess *p)
989
0
{
990
0
    return p->fout;
991
0
}
992
993
/************************************************************************/
994
/*                    CPLSpawnAsyncGetErrorFileHandle()                 */
995
/************************************************************************/
996
997
/**
998
 * Return the file handle of the standard error of the forked process
999
 * from which to read.
1000
 *
1001
 * @param p handle returned by CPLSpawnAsync().
1002
 *
1003
 * @return the file handle
1004
 *
1005
 * @since GDAL 1.10.0
1006
 */
1007
CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess *p)
1008
0
{
1009
0
    return p->ferr;
1010
0
}