Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qprocess_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// Copyright (C) 2021 Alex Trotsenko.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5// Qt-Security score:critical reason:execute-external-code
6
7//#define QPROCESS_DEBUG
8#include "qdebug.h"
9#include <private/qdebug_p.h>
10#include "qplatformdefs.h"
11
12#include "qprocess.h"
13#include "qprocess_p.h"
14#include "qstandardpaths.h"
15#include "private/qcore_unix_p.h"
16#include "private/qlocking_p.h"
17
18#ifdef Q_OS_DARWIN
19#include <private/qcore_mac_p.h>
20#endif
21
22#include <private/qcoreapplication_p.h>
23#include <private/qthread_p.h>
24#include <qfile.h>
25#include <qfileinfo.h>
26#include <qdir.h>
27#include <qlist.h>
28#include <qmutex.h>
29#include <qsocketnotifier.h>
30#include <qthread.h>
31
32#ifdef Q_OS_QNX
33# include <sys/neutrino.h>
34#endif
35
36#include <errno.h>
37#include <limits.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/resource.h>
41#include <termios.h>
42#include <unistd.h>
43
44#if __has_include(<paths.h>)
45# include <paths.h>
46#endif
47
48#if QT_CONFIG(process)
49#include <forkfd.h>
50#endif
51
52#ifndef O_PATH
53# define O_PATH 0
54#endif
55#ifndef _PATH_DEV
56# define _PATH_DEV "/dev/"
57#endif
58#ifndef _PATH_TTY
59# define _PATH_TTY _PATH_DEV "tty"
60#endif
61
62#ifdef Q_OS_FREEBSD
63__attribute__((weak))
64#endif
65extern char **environ;
66
67QT_BEGIN_NAMESPACE
68
69using namespace Qt::StringLiterals;
70
71#if !defined(Q_OS_DARWIN)
72
73QProcessEnvironment QProcessEnvironment::systemEnvironment()
74{
75 QProcessEnvironment env;
76 const char *entry;
77 for (int count = 0; (entry = environ[count]); ++count) {
78 const char *equal = strchr(entry, '=');
79 if (!equal)
80 continue;
81
82 QByteArray name(entry, equal - entry);
83 QByteArray value(equal + 1);
84 env.d->vars.insert(QProcessEnvironmentPrivate::Key(name),
85 QProcessEnvironmentPrivate::Value(value));
86 }
87 return env;
88}
89
90#endif // !defined(Q_OS_DARWIN)
91
92#if QT_CONFIG(process)
93
94namespace QtVforkSafe {
95// Certain libc functions we need to call in the child process scenario aren't
96// safe under vfork() because they do more than just place the system call to
97// the kernel and set errno on return. For those, we'll create a function
98// pointer like:
99// static constexpr auto foobar = __libc_foobar;
100// while for all other OSes, it'll be
101// using ::foobar;
102// allowing the code for the child side of the vfork to simply use
103// QtVforkSafe::foobar(args);
104//
105// Currently known issues are:
106//
107// - FreeBSD's libthr sigaction() wrapper locks a rwlock
108// https://github.com/freebsd/freebsd-src/blob/8dad5ece49479ba6cdcd5bb4c2799bbd61add3e6/lib/libthr/thread/thr_sig.c#L575-L641
109// - MUSL's sigaction() locks a mutex if the signal is SIGABR
110// https://github.com/bminor/musl/blob/718f363bc2067b6487900eddc9180c84e7739f80/src/signal/sigaction.c#L63-L85
111//
112// All other functions called in the child side are vfork-safe, provided that
113// PThread cancellation is disabled and Unix signals are blocked.
114#if defined(__MUSL__)
115# define LIBC_PREFIX __libc_
116#elif defined(Q_OS_FREEBSD)
117// will cause QtCore to link to ELF version "FBSDprivate_1.0"
118# define LIBC_PREFIX _
119#endif
120
121#ifdef LIBC_PREFIX
122# define CONCAT(x, y) CONCAT2(x, y)
123# define CONCAT2(x, y) x ## y
124# define DECLARE_FUNCTIONS(NAME)
125 extern decltype(::NAME) CONCAT(LIBC_PREFIX, NAME);
126 static constexpr auto NAME = std::addressof(CONCAT(LIBC_PREFIX, NAME));
127#else // LIBC_PREFIX
128# define DECLARE_FUNCTIONS(NAME) using ::NAME;
129#endif // LIBC_PREFIX
130
131extern "C" {
132DECLARE_FUNCTIONS(sigaction)
133}
134
135#undef LIBC_PREFIX
136#undef DECLARE_FUNCTIONS
137
138// similar to qt_ignore_sigpipe() in qcore_unix_p.h, but vfork-safe
139static void change_sigpipe(decltype(SIG_DFL) new_handler)
140{
141 struct sigaction sa;
142 memset(&sa, 0, sizeof(sa));
143 sa.sa_handler = new_handler;
144 sigaction(SIGPIPE, &sa, nullptr);
145}
146} // namespace QtVforkSafe
147
148static int opendirfd(QByteArray encodedName)
149{
150 // We append "/." to the name to ensure that the directory is actually
151 // traversable (i.e., has the +x bit set). This avoids later problems
152 // with fchdir().
153 if (encodedName != "/" && !encodedName.endsWith("/."))
154 encodedName += "/.";
155 return qt_safe_open(encodedName, QT_OPEN_RDONLY | O_DIRECTORY | O_PATH);
156}
157
158namespace {
159struct AutoPipe
160{
161 int pipe[2] = { -1, -1 };
162 AutoPipe(int flags = 0)
163 {
164 qt_safe_pipe(pipe, flags);
165 }
166 ~AutoPipe()
167 {
168 for (int fd : pipe) {
169 if (fd >= 0)
170 qt_safe_close(fd);
171 }
172 }
173
174 explicit operator bool() const { return pipe[0] >= 0; }
175 int &operator[](int idx) { return pipe[idx]; }
176 int operator[](int idx) const { return pipe[idx]; }
177};
178
179struct ChildError
180{
181 int code;
182 char function[_POSIX_PIPE_BUF - sizeof(code)];
183};
184static_assert(std::is_trivially_copy_constructible_v<ChildError>);
185#ifdef PIPE_BUF
186static_assert(PIPE_BUF >= sizeof(ChildError)); // PIPE_BUF may be bigger
187#endif
188
189// we need an errno value to use to indicate the child process modifier threw,
190// something the regular operations shouldn't set.
191static constexpr int FakeErrnoForThrow = std::numeric_limits<int>::max();
192
193static QString errorMessageForSyscall(QUtf8StringView fnName, int errnoCode = -1)
194{
195 QString msg = qt_error_string(errnoCode);
196 return QProcess::tr("Child process set up failed: %1: %2").arg(fnName, std::move(msg));
197}
198
199static QString startFailureErrorMessage(ChildError &err, [[maybe_unused]] ssize_t bytesRead)
200{
201 // ChildError is less than the POSIX pipe buffer atomic size, so the read
202 // must not have been truncated
203 Q_ASSERT(bytesRead == sizeof(err));
204
205 qsizetype len = qstrnlen(err.function, sizeof(err.function));
206 const QUtf8StringView complement(err.function, len);
207 if (err.code == FakeErrnoForThrow)
208 return QProcess::tr("Child process modifier threw an exception: %1")
209 .arg(complement);
210 if (err.code == 0)
211 return QProcess::tr("Child process modifier reported error: %1")
212 .arg(complement);
213 if (err.code < 0)
214 return QProcess::tr("Child process modifier reported error: %1: %2")
215 .arg(complement, qt_error_string(-err.code));
216 return errorMessageForSyscall(complement, err.code);
217}
218
219struct QProcessPoller
220{
221 QProcessPoller(const QProcessPrivate &proc);
222
223 int poll(const QDeadlineTimer &deadline);
224
225 pollfd &stdinPipe() { return pfds[0]; }
226 pollfd &stdoutPipe() { return pfds[1]; }
227 pollfd &stderrPipe() { return pfds[2]; }
228 pollfd &forkfd() { return pfds[3]; }
229
230 enum { n_pfds = 4 };
231 pollfd pfds[n_pfds];
232};
233
234QProcessPoller::QProcessPoller(const QProcessPrivate &proc)
235{
236 for (int i = 0; i < n_pfds; i++)
237 pfds[i] = qt_make_pollfd(-1, POLLIN);
238
239 stdoutPipe().fd = proc.stdoutChannel.pipe[0];
240 stderrPipe().fd = proc.stderrChannel.pipe[0];
241
242 if (!proc.writeBuffer.isEmpty()) {
243 stdinPipe().fd = proc.stdinChannel.pipe[1];
244 stdinPipe().events = POLLOUT;
245 }
246
247 forkfd().fd = proc.forkfd;
248}
249
250int QProcessPoller::poll(const QDeadlineTimer &deadline)
251{
252 return qt_safe_poll(pfds, n_pfds, deadline);
253}
254
255struct QChildProcess
256{
257 // Used for argv and envp arguments to execve()
258 struct CharPointerList
259 {
260 std::unique_ptr<char *[]> pointers;
261
262 CharPointerList(const QString &argv0, const QStringList &args);
263 explicit CharPointerList(const QProcessEnvironmentPrivate *env);
264 /*implicit*/ operator char **() const { return pointers.get(); }
265
266 private:
267 QByteArray data;
268 void updatePointers(qsizetype count);
269 };
270
271 const QProcessPrivate *d;
272 CharPointerList argv;
273 CharPointerList envp;
274 sigset_t oldsigset;
275 int workingDirectory = -2;
276 bool isUsingVfork = usingVfork();
277
278 bool ok() const
279 {
280 return workingDirectory != -1;
281 }
282
283 QChildProcess(QProcessPrivate *d)
284 : d(d), argv(resolveExecutable(d->program), d->arguments),
285 envp(d->environmentPrivate())
286 {
287 // Open the working directory first, because this operation can fail.
288 // That way, if it does, we don't have anything to clean up.
289 if (!d->workingDirectory.isEmpty()) {
290 workingDirectory = opendirfd(QFile::encodeName(d->workingDirectory));
291 if (workingDirectory < 0) {
292 d->setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("chdir"));
293 d->cleanup();
294
295 // make sure our destructor does nothing
296 isUsingVfork = false;
297 return;
298 }
299 }
300
301 // Block Unix signals, to ensure the user's handlers aren't run in the
302 // child side and do something weird, especially if the handler and the
303 // user of QProcess are completely different codebases.
304 maybeBlockSignals();
305
306 // Disable PThread cancellation until the child has successfully been
307 // executed. We make a number of POSIX calls in the child that are thread
308 // cancellation points and could cause an unexpected stack unwind. That
309 // would be bad enough with regular fork(), but it's likely fatal with
310 // vfork().
311 disableThreadCancellations();
312 }
313 ~QChildProcess() noexcept(false)
314 {
315 cleanup();
316 }
317
318 void cleanup() noexcept(false)
319 {
320 if (workingDirectory >= 0)
321 close(workingDirectory);
322 workingDirectory = -1;
323
324 restoreSignalMask();
325 restoreThreadCancellations();
326 }
327
328 void maybeBlockSignals() noexcept
329 {
330 // We only block Unix signals if we're using vfork(), to avoid a
331 // changing behavior to the user's modifier and because in some OSes
332 // this action would block crashing signals too.
333 if (isUsingVfork) {
334 sigset_t emptyset;
335 sigfillset(&emptyset);
336 pthread_sigmask(SIG_SETMASK, &emptyset, &oldsigset);
337 }
338 }
339
340 void restoreSignalMask() const noexcept
341 {
342 // this function may be called more than once
343 if (isUsingVfork)
344 pthread_sigmask(SIG_SETMASK, &oldsigset, nullptr);
345 }
346
347 bool usingVfork() const noexcept;
348
349 template <typename Lambda> int doFork(Lambda &&childLambda)
350 {
351 pid_t pid;
352 if (isUsingVfork) {
353 QT_IGNORE_DEPRECATIONS(pid = vfork();)
354 } else {
355 pid = fork();
356 }
357 if (pid == 0)
358 _exit(childLambda());
359 return pid;
360 }
361
362 int startChild(pid_t *pid)
363 {
364 int ffdflags = FFD_CLOEXEC | (isUsingVfork ? 0 : FFD_USE_FORK);
365 return ::vforkfd(ffdflags, pid, &QChildProcess::startProcess, this);
366 }
367
368private:
369 Q_NORETURN void startProcess() const noexcept;
370 static int startProcess(void *self) noexcept
371 {
372 static_cast<QChildProcess *>(self)->startProcess();
373 Q_UNREACHABLE_RETURN(-1);
374 }
375
376#if defined(PTHREAD_CANCEL_DISABLE)
377 int oldstate = PTHREAD_CANCEL_DISABLE;
378 void disableThreadCancellations() noexcept
379 {
380 // the following is *not* noexcept, but it won't throw while disabling
381 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
382 }
383 void restoreThreadCancellations() noexcept(false)
384 {
385 // ensure we don't call pthread_setcancelstate() again
386 int oldoldstate = std::exchange(oldstate, PTHREAD_CANCEL_DISABLE);
387 if (oldoldstate != PTHREAD_CANCEL_DISABLE) {
388 // this doesn't touch errno
389 pthread_setcancelstate(oldoldstate, nullptr);
390 }
391 }
392#else
393 void disableThreadCancellations() noexcept {}
394 void restoreThreadCancellations() {}
395#endif
396
397 static QString resolveExecutable(const QString &program);
398};
399
400QChildProcess::CharPointerList::CharPointerList(const QString &program, const QStringList &args)
401{
402 qsizetype count = 1 + args.size();
403 pointers.reset(new char *[count + 1]);
404 pointers[count] = nullptr;
405
406 // we abuse the pointer array to store offsets first (QByteArray will
407 // reallocate, after all)
408 pointers[0] = reinterpret_cast<char *>(0);
409 data = QFile::encodeName(program);
410 data += '\0';
411
412 const auto end = args.end();
413 auto it = args.begin();
414 for (qsizetype i = 1; it != end; ++it, ++i) {
415 pointers[i] = reinterpret_cast<char *>(data.size());
416 data += QFile::encodeName(*it);
417 data += '\0';
418 }
419
420 updatePointers(count);
421}
422
423QChildProcess::CharPointerList::CharPointerList(const QProcessEnvironmentPrivate *environment)
424{
425 if (!environment)
426 return;
427
428 const QProcessEnvironmentPrivate::Map &env = environment->vars;
429 qsizetype count = env.size();
430 pointers.reset(new char *[count + 1]);
431 pointers[count] = nullptr;
432
433 const auto end = env.end();
434 auto it = env.begin();
435 for (qsizetype i = 0; it != end; ++it, ++i) {
436 // we abuse the pointer array to store offsets first (QByteArray will
437 // reallocate, after all)
438 pointers[i] = reinterpret_cast<char *>(data.size());
439
440 data += it.key();
441 data += '=';
442 data += it->bytes();
443 data += '\0';
444 }
445
446 updatePointers(count);
447}
448
449void QChildProcess::CharPointerList::updatePointers(qsizetype count)
450{
451 char *const base = const_cast<char *>(data.constBegin());
452 for (qsizetype i = 0; i < count; ++i)
453 pointers[i] = base + qptrdiff(pointers[i]);
454}
455} // anonymous namespace
456
457static bool qt_pollfd_check(const pollfd &pfd, short revents)
458{
459 return pfd.fd >= 0 && (pfd.revents & (revents | POLLHUP | POLLERR | POLLNVAL)) != 0;
460}
461
462static int qt_create_pipe(int *pipe)
463{
464 if (pipe[0] != -1)
465 qt_safe_close(pipe[0]);
466 if (pipe[1] != -1)
467 qt_safe_close(pipe[1]);
468 int pipe_ret = qt_safe_pipe(pipe);
469 if (pipe_ret != 0) {
470 QScopedValueRollback rollback(errno);
471 qErrnoWarning("QProcess: Cannot create pipe");
472 }
473 return pipe_ret;
474}
475
476void QProcessPrivate::destroyPipe(int *pipe)
477{
478 if (pipe[1] != -1) {
479 qt_safe_close(pipe[1]);
480 pipe[1] = -1;
481 }
482 if (pipe[0] != -1) {
483 qt_safe_close(pipe[0]);
484 pipe[0] = -1;
485 }
486}
487
488void QProcessPrivate::closeChannel(Channel *channel)
489{
490 delete channel->notifier;
491 channel->notifier = nullptr;
492
493 destroyPipe(channel->pipe);
494}
495
496void QProcessPrivate::cleanup()
497{
498 q_func()->setProcessState(QProcess::NotRunning);
499
500 closeChannels();
501 delete stateNotifier;
502 stateNotifier = nullptr;
503 destroyPipe(childStartedPipe);
504 pid = 0;
505 if (forkfd != -1) {
506 qt_safe_close(forkfd);
507 forkfd = -1;
508 }
509}
510
511/*
512 Create the pipes to a QProcessPrivate::Channel.
513*/
514bool QProcessPrivate::openChannel(Channel &channel)
515{
516 Q_Q(QProcess);
517
518 if (channel.type == Channel::Normal) {
519 // we're piping this channel to our own process
520 if (qt_create_pipe(channel.pipe) != 0) {
521 setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
522 return false;
523 }
524
525 // create the socket notifiers
526 if (threadData.loadRelaxed()->hasEventDispatcher()) {
527 if (&channel == &stdinChannel) {
528 channel.notifier = new QSocketNotifier(QSocketNotifier::Write, q);
529 channel.notifier->setSocket(channel.pipe[1]);
530 QObjectPrivate::connect(channel.notifier, &QSocketNotifier::activated, this,
531 &QProcessPrivate::_q_canWrite);
532 } else {
533 channel.notifier = new QSocketNotifier(QSocketNotifier::Read, q);
534 channel.notifier->setSocket(channel.pipe[0]);
535 auto receiver = &channel == &stdoutChannel ? &QProcessPrivate::_q_canReadStandardOutput
536 : &QProcessPrivate::_q_canReadStandardError;
537 QObjectPrivate::connect(channel.notifier, &QSocketNotifier::activated, this,
538 receiver);
539 }
540 }
541
542 return true;
543 } else if (channel.type == Channel::Redirect) {
544 // we're redirecting the channel to/from a file
545 QByteArray fname = QFile::encodeName(channel.file);
546
547 if (&channel == &stdinChannel) {
548 // try to open in read-only mode
549 channel.pipe[1] = -1;
550 if ( (channel.pipe[0] = qt_safe_open(fname, O_RDONLY)) != -1)
551 return true; // success
552 setErrorAndEmit(QProcess::FailedToStart,
553 QProcess::tr("Could not open input redirection for reading"));
554 } else {
555 int mode = O_WRONLY | O_CREAT;
556 if (channel.append)
557 mode |= O_APPEND;
558 else
559 mode |= O_TRUNC;
560
561 channel.pipe[0] = -1;
562 if ( (channel.pipe[1] = qt_safe_open(fname, mode, 0666)) != -1)
563 return true; // success
564
565 setErrorAndEmit(QProcess::FailedToStart,
566 QProcess::tr("Could not open input redirection for reading"));
567 }
568 return false;
569 } else {
570 Q_ASSERT_X(channel.process, "QProcess::start", "Internal error");
571
572 Channel *source;
573 Channel *sink;
574
575 if (channel.type == Channel::PipeSource) {
576 // we are the source
577 source = &channel;
578 sink = &channel.process->stdinChannel;
579
580 Q_ASSERT(source == &stdoutChannel);
581 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink);
582 } else {
583 // we are the sink;
584 source = &channel.process->stdoutChannel;
585 sink = &channel;
586
587 Q_ASSERT(sink == &stdinChannel);
588 Q_ASSERT(source->process == this && source->type == Channel::PipeSource);
589 }
590
591 if (source->pipe[1] != INVALID_Q_PIPE || sink->pipe[0] != INVALID_Q_PIPE) {
592 // already created, do nothing
593 return true;
594 } else {
595 Q_ASSERT(source->pipe[0] == INVALID_Q_PIPE && source->pipe[1] == INVALID_Q_PIPE);
596 Q_ASSERT(sink->pipe[0] == INVALID_Q_PIPE && sink->pipe[1] == INVALID_Q_PIPE);
597
598 Q_PIPE pipe[2] = { -1, -1 };
599 if (qt_create_pipe(pipe) != 0) {
600 setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
601 return false;
602 }
603 sink->pipe[0] = pipe[0];
604 source->pipe[1] = pipe[1];
605
606 return true;
607 }
608 }
609}
610
611void QProcessPrivate::commitChannels() const
612{
613 // copy the stdin socket if asked to (without closing on exec)
614 if (stdinChannel.pipe[0] != INVALID_Q_PIPE)
615 qt_safe_dup2(stdinChannel.pipe[0], STDIN_FILENO, 0);
616
617 // copy the stdout and stderr if asked to
618 if (stdoutChannel.pipe[1] != INVALID_Q_PIPE)
619 qt_safe_dup2(stdoutChannel.pipe[1], STDOUT_FILENO, 0);
620 if (stderrChannel.pipe[1] != INVALID_Q_PIPE) {
621 qt_safe_dup2(stderrChannel.pipe[1], STDERR_FILENO, 0);
622 } else {
623 // merge stdout and stderr if asked to
624 if (processChannelMode == QProcess::MergedChannels)
625 qt_safe_dup2(STDOUT_FILENO, STDERR_FILENO, 0);
626 }
627}
628
629inline QString QChildProcess::resolveExecutable(const QString &program)
630{
631#ifdef Q_OS_DARWIN
632 // allow invoking of .app bundles on the Mac.
633 QFileInfo fileInfo(program);
634 if (program.endsWith(".app"_L1) && fileInfo.isDir()) {
635 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
636 QCFString(fileInfo.absoluteFilePath()),
637 kCFURLPOSIXPathStyle, true);
638 {
639 // CFBundle is not reentrant, since CFBundleCreate might return a reference
640 // to a cached bundle object. Protect the bundle calls with a mutex lock.
641 Q_CONSTINIT static QBasicMutex cfbundleMutex;
642 const auto locker = qt_scoped_lock(cfbundleMutex);
643 QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
644 // 'executableURL' can be either relative or absolute ...
645 QCFType<CFURLRef> executableURL = CFBundleCopyExecutableURL(bundle);
646 // not to depend on caching - make sure it's always absolute.
647 url = CFURLCopyAbsoluteURL(executableURL);
648 }
649 if (url) {
650 const QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
651 return QString::fromCFString(str);
652 }
653 }
654#endif
655
656 if (!program.contains(u'/')) {
657 // findExecutable() returns its argument if it's an absolute path,
658 // otherwise it searches $PATH; returns empty if not found (we handle
659 // that case much later)
660 return QStandardPaths::findExecutable(program);
661 }
662 return program;
663}
664
665extern "C" {
666__attribute__((weak)) pid_t __interceptor_vfork();
667}
668
669inline bool globalUsingVfork() noexcept
670{
671#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
672 // ASan writes to global memory, so we mustn't use vfork().
673 return false;
674#endif
675#if defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer)
676 // Ditto, apparently
677 return false;
678#endif
679#if defined(Q_OS_LINUX) && !QT_CONFIG(forkfd_pidfd)
680 // some broken environments are known to have problems with the new Linux
681 // API, so we have a way for users to opt-out during configure time (see
682 // QTBUG-86285)
683 return false;
684#endif
685#if defined(Q_OS_DARWIN)
686 // Using vfork() for startDetached() is causing problems. We don't know
687 // why: without the tools to investigate why it happens, we didn't bother.
688 return false;
689#endif
690#if defined(Q_OS_CYGWIN)
691 // Fails to link Qt6Core, so we avoid that..
692 return false;
693#endif
694
695 // Dynamically detect whether libasan or libtsan are loaded into the
696 // process' memory. We need this because the user's code may be compiled
697 // with ASan or TSan, but not Qt.
698 return __interceptor_vfork == nullptr;
699}
700
701inline bool QChildProcess::usingVfork() const noexcept
702{
703 if (!globalUsingVfork())
704 return false;
705
706 if (!d->unixExtras || !d->unixExtras->childProcessModifier)
707 return true; // no modifier was supplied
708
709 // if a modifier was supplied, use fork() unless the user opts in to
710 // vfork()
711 auto flags = d->unixExtras->processParameters.flags;
712 return flags.testFlag(QProcess::UnixProcessFlag::UseVFork);
713}
714
715#ifdef QT_BUILD_INTERNAL
716Q_AUTOTEST_EXPORT bool _qprocessUsingVfork() noexcept
717{
718 return globalUsingVfork();
719}
720#endif
721
722void QProcessPrivate::startProcess()
723{
724 Q_Q(QProcess);
725 q->setProcessState(QProcess::Starting);
726
727#if defined (QPROCESS_DEBUG)
728 qDebug("QProcessPrivate::startProcess()");
729#endif
730
731 // Initialize pipes
732 if (!openChannels()) {
733 // openChannel sets the error string
734 Q_ASSERT(!errorString.isEmpty());
735 cleanup();
736 return;
737 }
738 if (qt_create_pipe(childStartedPipe) != 0) {
739 setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
740 cleanup();
741 return;
742 }
743
744 if (threadData.loadRelaxed()->hasEventDispatcher()) {
745 // Set up to notify about startup completion (and premature death).
746 // Once the process has started successfully, we reconfigure the
747 // notifier to watch the fork_fd for expected death.
748 stateNotifier = new QSocketNotifier(childStartedPipe[0],
749 QSocketNotifier::Read, q);
750 QObjectPrivate::connect(stateNotifier, &QSocketNotifier::activated, this,
751 &QProcessPrivate::_q_startupNotification);
752 }
753
754 // Prepare the arguments and the environment
755 QChildProcess childProcess(this);
756 if (!childProcess.ok()) {
757 Q_ASSERT(processError != QProcess::UnknownError);
758 return;
759 }
760
761 // Start the child.
762 forkfd = childProcess.startChild(&pid);
763 int lastForkErrno = errno;
764
765 if (forkfd == -1) {
766 // Cleanup, report error and return
767 childProcess.cleanup();
768#if defined (QPROCESS_DEBUG)
769 qDebug("fork failed: %ls", qUtf16Printable(qt_error_string(lastForkErrno)));
770#endif
771 q->setProcessState(QProcess::NotRunning);
772 setErrorAndEmit(QProcess::FailedToStart,
773 QProcess::tr("Resource error (fork failure): %1").arg(qt_error_string(lastForkErrno)));
774 cleanup();
775 return;
776 }
777
778 Q_ASSERT(pid > 0);
779
780 // parent
781 // close the ends we don't use and make all pipes non-blocking
782 qt_safe_close(childStartedPipe[1]);
783 childStartedPipe[1] = -1;
784
785 if (stdinChannel.pipe[0] != -1) {
786 qt_safe_close(stdinChannel.pipe[0]);
787 stdinChannel.pipe[0] = -1;
788 }
789
790 if (stdinChannel.pipe[1] != -1)
791 ::fcntl(stdinChannel.pipe[1], F_SETFL, ::fcntl(stdinChannel.pipe[1], F_GETFL) | O_NONBLOCK);
792
793 if (stdoutChannel.pipe[1] != -1) {
794 qt_safe_close(stdoutChannel.pipe[1]);
795 stdoutChannel.pipe[1] = -1;
796 }
797
798 if (stdoutChannel.pipe[0] != -1)
799 ::fcntl(stdoutChannel.pipe[0], F_SETFL, ::fcntl(stdoutChannel.pipe[0], F_GETFL) | O_NONBLOCK);
800
801 if (stderrChannel.pipe[1] != -1) {
802 qt_safe_close(stderrChannel.pipe[1]);
803 stderrChannel.pipe[1] = -1;
804 }
805 if (stderrChannel.pipe[0] != -1)
806 ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
807}
808
809Q_NORETURN void
810failChildProcess(const QProcessPrivate *d, const char *description, int code) noexcept
811{
812 ChildError error = {};
813 error.code = code;
814 qstrncpy(error.function, description, sizeof(error.function));
815 qt_safe_write(d->childStartedPipe[1], &error, sizeof(error));
816 _exit(-1);
817}
818
819void QProcess::failChildProcessModifier(const char *description, int error) noexcept
820{
821 // We signal user errors with negative errnos
822 failChildProcess(d_func(), description, -error);
823}
824
825// See IMPORTANT notice below
826static const char *applyProcessParameters(const QProcess::UnixProcessParameters &params)
827{
828 // Apply Unix signal handler parameters.
829 // We don't expect signal() to fail, so we ignore its return value
830 bool ignore_sigpipe = params.flags.testFlag(QProcess::UnixProcessFlag::IgnoreSigPipe);
831 if (ignore_sigpipe)
832 QtVforkSafe::change_sigpipe(SIG_IGN);
833 if (params.flags.testFlag(QProcess::UnixProcessFlag::ResetSignalHandlers)) {
834 struct sigaction sa = {};
835 sa.sa_handler = SIG_DFL;
836 for (int sig = 1; sig < NSIG; ++sig) {
837 if (!ignore_sigpipe || sig != SIGPIPE)
838 QtVforkSafe::sigaction(sig, &sa, nullptr);
839 }
840
841 // and unmask all signals
842 sigset_t set;
843 sigemptyset(&set);
844 sigprocmask(SIG_SETMASK, &set, nullptr);
845 }
846
847 // Close all file descriptors above stderr.
848 // This isn't expected to fail, so we ignore close()'s return value.
849 if (params.flags.testFlag(QProcess::UnixProcessFlag::CloseFileDescriptors)) {
850 int r = -1;
851 int fd = qMax(STDERR_FILENO + 1, params.lowestFileDescriptorToClose);
852#ifdef CLOSE_RANGE_CLOEXEC
853 // Mark the file descriptors for closing upon execve() - we delay
854 // closing so we don't close the ones QProcess needs for itself.
855 // On FreeBSD, this probably won't fail.
856 // On Linux, this will fail with ENOSYS before kernel 5.9 and EINVAL
857 // before 5.11.
858 r = close_range(fd, INT_MAX, CLOSE_RANGE_CLOEXEC);
859#endif
860 if (r == -1) {
861 // We *could* read /dev/fd to find out what file descriptors are
862 // open, but we won't. We CANNOT use opendir() here because it
863 // allocates memory. Using getdents(2) plus either strtoul() or
864 // std::from_chars() would be acceptable.
865 int max_fd = INT_MAX;
866 if (struct rlimit limit; getrlimit(RLIMIT_NOFILE, &limit) == 0)
867 max_fd = limit.rlim_cur;
868 for ( ; fd < max_fd; ++fd)
869 fcntl(fd, F_SETFD, FD_CLOEXEC);
870 }
871 }
872
873 // Apply session and process group settings. This may fail.
874 if (params.flags.testFlag(QProcess::UnixProcessFlag::CreateNewSession)) {
875 if (setsid() < 0)
876 return "setsid";
877 }
878
879 // Disconnect from the controlling TTY. This probably won't fail. Must be
880 // done after the session settings from above.
881 if (params.flags.testFlag(QProcess::UnixProcessFlag::DisconnectControllingTerminal)) {
882#ifdef TIOCNOTTY
883 if (int fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY); fd >= 0) {
884 // we still have a controlling TTY; give it up
885 int r = ioctl(fd, TIOCNOTTY);
886 int savedErrno = errno;
887 close(fd);
888 if (r != 0) {
889 errno = savedErrno;
890 return "ioctl";
891 }
892 }
893#endif
894 }
895
896 // Disable core dumps near the end. This isn't expected to fail.
897 if (params.flags.testFlag(QProcess::UnixProcessFlag::DisableCoreDumps)) {
898 if (struct rlimit lim; getrlimit(RLIMIT_CORE, &lim) == 0 && lim.rlim_cur) {
899 // We'll leave rlim_max untouched, so the child can set it back if it
900 // wants to. We don't expect setrlimit() to fail, so we ignore its
901 // return value.
902 lim.rlim_cur = 0;
903 (void) setrlimit(RLIMIT_CORE, &lim);
904 }
905 }
906
907 // Apply UID and GID parameters last. This isn't expected to fail either:
908 // either we're trying to impersonate what we already are, or we're EUID or
909 // EGID root, in which case we are allowed to do this.
910 if (params.flags.testFlag(QProcess::UnixProcessFlag::ResetIds)) {
911 int r = setgid(getgid());
912 r = setuid(getuid());
913 (void) r;
914 }
915
916 return nullptr;
917}
918
919// the noexcept here adds an extra layer of protection
920static void callChildProcessModifier(const QProcessPrivate *d) noexcept
921{
922 QT_TRY {
923 if (d->unixExtras->childProcessModifier)
924 d->unixExtras->childProcessModifier();
925 } QT_CATCH (std::exception &e) {
926 failChildProcess(d, e.what(), FakeErrnoForThrow);
927 } QT_CATCH (...) {
928 failChildProcess(d, "throw", FakeErrnoForThrow);
929 }
930}
931
932// IMPORTANT:
933//
934// This function is called in a vfork() context on some OSes (notably, Linux
935// with forkfd), so it MUST NOT modify any non-local variable because it's
936// still sharing memory with the parent process.
937void QChildProcess::startProcess() const noexcept
938{
939 // Render channels configuration.
940 d->commitChannels();
941
942 // make sure this fd is closed if execv() succeeds
943 qt_safe_close(d->childStartedPipe[0]);
944
945 // enter the working directory
946 if (workingDirectory >= 0 && fchdir(workingDirectory) == -1)
947 failChildProcess(d, "fchdir", errno);
948
949 bool sigpipeHandled = false;
950 bool sigmaskHandled = false;
951 if (d->unixExtras) {
952 // FIRST we call the user modifier function, before we dropping
953 // privileges or closing non-standard file descriptors
954 callChildProcessModifier(d);
955
956 // then we apply our other user-provided parameters
957 if (const char *what = applyProcessParameters(d->unixExtras->processParameters))
958 failChildProcess(d, what, errno);
959
960 auto flags = d->unixExtras->processParameters.flags;
961 using P = QProcess::UnixProcessFlag;
962 sigpipeHandled = flags.testAnyFlags(P::ResetSignalHandlers | P::IgnoreSigPipe);
963 sigmaskHandled = flags.testFlag(P::ResetSignalHandlers);
964 }
965 if (!sigpipeHandled) {
966 // reset the signal that we ignored
967 QtVforkSafe::change_sigpipe(SIG_DFL); // reset the signal that we ignored
968 }
969 if (!sigmaskHandled) {
970 // restore the signal mask from the parent, if applyProcessParameters()
971 // hasn't completely reset it
972 restoreSignalMask();
973 }
974
975 // execute the process
976 if (!envp.pointers)
977 qt_safe_execv(argv[0], argv);
978 else
979 qt_safe_execve(argv[0], argv, envp);
980 failChildProcess(d, "execve", errno);
981}
982
983bool QProcessPrivate::processStarted(QString *errorMessage)
984{
985 Q_Q(QProcess);
986
987 ChildError buf;
988 ssize_t ret = qt_safe_read(childStartedPipe[0], &buf, sizeof(buf));
989
990 if (stateNotifier) {
991 stateNotifier->setEnabled(false);
992 stateNotifier->disconnect(q);
993 }
994 qt_safe_close(childStartedPipe[0]);
995 childStartedPipe[0] = -1;
996
997#if defined (QPROCESS_DEBUG)
998 qDebug("QProcessPrivate::processStarted() == %s", ret <= 0 ? "true" : "false");
999#endif
1000
1001 if (ret <= 0) { // process successfully started
1002 if (stateNotifier) {
1003 QObjectPrivate::connect(stateNotifier, &QSocketNotifier::activated, this,
1004 &QProcessPrivate::_q_processDied);
1005 stateNotifier->setSocket(forkfd);
1006 stateNotifier->setEnabled(true);
1007 }
1008 if (stdoutChannel.notifier)
1009 stdoutChannel.notifier->setEnabled(true);
1010 if (stderrChannel.notifier)
1011 stderrChannel.notifier->setEnabled(true);
1012
1013 return true;
1014 }
1015
1016 // did we read an error message?
1017 if (errorMessage)
1018 *errorMessage = startFailureErrorMessage(buf, ret);
1019
1020 return false;
1021}
1022
1023qint64 QProcessPrivate::bytesAvailableInChannel(const Channel *channel) const
1024{
1025 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
1026 int nbytes = 0;
1027 qint64 available = 0;
1028 if (::ioctl(channel->pipe[0], FIONREAD, (char *) &nbytes) >= 0)
1029 available = (qint64) nbytes;
1030#if defined (QPROCESS_DEBUG)
1031 qDebug("QProcessPrivate::bytesAvailableInChannel(%d) == %lld", int(channel - &stdinChannel), available);
1032#endif
1033 return available;
1034}
1035
1036qint64 QProcessPrivate::readFromChannel(const Channel *channel, char *data, qint64 maxlen)
1037{
1038 Q_ASSERT(channel->pipe[0] != INVALID_Q_PIPE);
1039 qint64 bytesRead = qt_safe_read(channel->pipe[0], data, maxlen);
1040#if defined QPROCESS_DEBUG
1041 int save_errno = errno;
1042 qDebug("QProcessPrivate::readFromChannel(%d, %p \"%s\", %lld) == %lld",
1043 int(channel - &stdinChannel),
1044 data, QtDebugUtils::toPrintable(data, bytesRead, 16).constData(), maxlen, bytesRead);
1045 errno = save_errno;
1046#endif
1047 if (bytesRead == -1 && errno == EWOULDBLOCK)
1048 return -2;
1049 return bytesRead;
1050}
1051
1052/*! \reimp
1053*/
1054qint64 QProcess::writeData(const char *data, qint64 len)
1055{
1056 Q_D(QProcess);
1057
1058 if (d->stdinChannel.closed) {
1059#if defined QPROCESS_DEBUG
1060 qDebug("QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)",
1061 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len);
1062#endif
1063 return 0;
1064 }
1065
1066 d->write(data, len);
1067 if (d->stdinChannel.notifier)
1068 d->stdinChannel.notifier->setEnabled(true);
1069
1070#if defined QPROCESS_DEBUG
1071 qDebug("QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)",
1072 data, QtDebugUtils::toPrintable(data, len, 16).constData(), len, len);
1073#endif
1074 return len;
1075}
1076
1077bool QProcessPrivate::_q_canWrite()
1078{
1079 if (writeBuffer.isEmpty()) {
1080 if (stdinChannel.notifier)
1081 stdinChannel.notifier->setEnabled(false);
1082#if defined QPROCESS_DEBUG
1083 qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer).");
1084#endif
1085 return false;
1086 }
1087
1088 const bool writeSucceeded = writeToStdin();
1089
1090 if (writeBuffer.isEmpty() && stdinChannel.closed)
1091 closeWriteChannel();
1092 else if (stdinChannel.notifier)
1093 stdinChannel.notifier->setEnabled(!writeBuffer.isEmpty());
1094
1095 return writeSucceeded;
1096}
1097
1098bool QProcessPrivate::writeToStdin()
1099{
1100 const char *data = writeBuffer.readPointer();
1101 const qint64 bytesToWrite = writeBuffer.nextDataBlockSize();
1102
1103 qint64 written = qt_safe_write_nosignal(stdinChannel.pipe[1], data, bytesToWrite);
1104#if defined QPROCESS_DEBUG
1105 qDebug("QProcessPrivate::writeToStdin(), write(%p \"%s\", %lld) == %lld", data,
1106 QtDebugUtils::toPrintable(data, bytesToWrite, 16).constData(), bytesToWrite, written);
1107 if (written == -1)
1108 qDebug("QProcessPrivate::writeToStdin(), failed to write (%ls)", qUtf16Printable(qt_error_string(errno)));
1109#endif
1110 if (written == -1) {
1111 // If the O_NONBLOCK flag is set and If some data can be written without blocking
1112 // the process, write() will transfer what it can and return the number of bytes written.
1113 // Otherwise, it will return -1 and set errno to EAGAIN
1114 if (errno == EAGAIN)
1115 return true;
1116
1117 closeChannel(&stdinChannel);
1118 setErrorAndEmit(QProcess::WriteError);
1119 return false;
1120 }
1121 writeBuffer.free(written);
1122 if (!emittedBytesWritten && written != 0) {
1123 emittedBytesWritten = true;
1124 emit q_func()->bytesWritten(written);
1125 emittedBytesWritten = false;
1126 }
1127 return true;
1128}
1129
1130void QProcessPrivate::terminateProcess()
1131{
1132#if defined (QPROCESS_DEBUG)
1133 qDebug("QProcessPrivate::terminateProcess() pid=%jd", intmax_t(pid));
1134#endif
1135 if (pid > 0)
1136 ::kill(pid, SIGTERM);
1137}
1138
1139void QProcessPrivate::killProcess()
1140{
1141#if defined (QPROCESS_DEBUG)
1142 qDebug("QProcessPrivate::killProcess() pid=%jd", intmax_t(pid));
1143#endif
1144 if (pid > 0)
1145 ::kill(pid, SIGKILL);
1146}
1147
1148bool QProcessPrivate::waitForStarted(const QDeadlineTimer &deadline)
1149{
1150#if defined (QPROCESS_DEBUG)
1151 const qint64 msecs = deadline.remainingTime();
1152 qDebug("QProcessPrivate::waitForStarted(%lld) waiting for child to start (fd = %d)",
1153 msecs, childStartedPipe[0]);
1154#endif
1155
1156 pollfd pfd = qt_make_pollfd(childStartedPipe[0], POLLIN);
1157
1158 if (qt_safe_poll(&pfd, 1, deadline) == 0) {
1159 setError(QProcess::Timedout);
1160#if defined (QPROCESS_DEBUG)
1161 qDebug("QProcessPrivate::waitForStarted(%lld) == false (timed out)", msecs);
1162#endif
1163 return false;
1164 }
1165
1166 bool startedEmitted = _q_startupNotification();
1167#if defined (QPROCESS_DEBUG)
1168 qDebug("QProcessPrivate::waitForStarted() == %s", startedEmitted ? "true" : "false");
1169#endif
1170 return startedEmitted;
1171}
1172
1173bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
1174{
1175#if defined (QPROCESS_DEBUG)
1176 qDebug("QProcessPrivate::waitForReadyRead(%lld)", deadline.remainingTime());
1177#endif
1178
1179 forever {
1180 QProcessPoller poller(*this);
1181
1182 int ret = poller.poll(deadline);
1183
1184 if (ret < 0) {
1185 break;
1186 }
1187 if (ret == 0) {
1188 setError(QProcess::Timedout);
1189 return false;
1190 }
1191
1192 // This calls QProcessPrivate::tryReadFromChannel(), which returns true
1193 // if we emitted readyRead() signal on the current read channel.
1194 bool readyReadEmitted = false;
1195 if (qt_pollfd_check(poller.stdoutPipe(), POLLIN) && _q_canReadStandardOutput())
1196 readyReadEmitted = true;
1197 if (qt_pollfd_check(poller.stderrPipe(), POLLIN) && _q_canReadStandardError())
1198 readyReadEmitted = true;
1199
1200 if (readyReadEmitted)
1201 return true;
1202
1203 if (qt_pollfd_check(poller.stdinPipe(), POLLOUT))
1204 _q_canWrite();
1205
1206 // Signals triggered by I/O may have stopped this process:
1207 if (processState == QProcess::NotRunning)
1208 return false;
1209
1210 // We do this after checking the pipes, so we cannot reach it as long
1211 // as there is any data left to be read from an already dead process.
1212 if (qt_pollfd_check(poller.forkfd(), POLLIN)) {
1213 processFinished();
1214 return false;
1215 }
1216 }
1217 return false;
1218}
1219
1220bool QProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline)
1221{
1222#if defined (QPROCESS_DEBUG)
1223 qDebug("QProcessPrivate::waitForBytesWritten(%lld)", deadline.remainingTime());
1224#endif
1225
1226 while (!writeBuffer.isEmpty()) {
1227 QProcessPoller poller(*this);
1228
1229 int ret = poller.poll(deadline);
1230
1231 if (ret < 0) {
1232 break;
1233 }
1234
1235 if (ret == 0) {
1236 setError(QProcess::Timedout);
1237 return false;
1238 }
1239
1240 if (qt_pollfd_check(poller.stdinPipe(), POLLOUT))
1241 return _q_canWrite();
1242
1243 if (qt_pollfd_check(poller.stdoutPipe(), POLLIN))
1244 _q_canReadStandardOutput();
1245
1246 if (qt_pollfd_check(poller.stderrPipe(), POLLIN))
1247 _q_canReadStandardError();
1248
1249 // Signals triggered by I/O may have stopped this process:
1250 if (processState == QProcess::NotRunning)
1251 return false;
1252
1253 if (qt_pollfd_check(poller.forkfd(), POLLIN)) {
1254 processFinished();
1255 return false;
1256 }
1257 }
1258
1259 return false;
1260}
1261
1262bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline)
1263{
1264#if defined (QPROCESS_DEBUG)
1265 qDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime());
1266#endif
1267
1268 forever {
1269 QProcessPoller poller(*this);
1270
1271 int ret = poller.poll(deadline);
1272
1273 if (ret < 0) {
1274 break;
1275 }
1276 if (ret == 0) {
1277 setError(QProcess::Timedout);
1278 return false;
1279 }
1280
1281 if (qt_pollfd_check(poller.stdinPipe(), POLLOUT))
1282 _q_canWrite();
1283
1284 if (qt_pollfd_check(poller.stdoutPipe(), POLLIN))
1285 _q_canReadStandardOutput();
1286
1287 if (qt_pollfd_check(poller.stderrPipe(), POLLIN))
1288 _q_canReadStandardError();
1289
1290 // Signals triggered by I/O may have stopped this process:
1291 if (processState == QProcess::NotRunning)
1292 return true;
1293
1294 if (qt_pollfd_check(poller.forkfd(), POLLIN)) {
1295 processFinished();
1296 return true;
1297 }
1298 }
1299 return false;
1300}
1301
1302void QProcessPrivate::waitForDeadChild()
1303{
1304 Q_ASSERT(forkfd != -1);
1305
1306 // read the process information from our fd
1307 forkfd_info info = {}; // Silence -Wmaybe-uninitialized; Thiago says forkfd_wait cannot fail here
1308 // (QTBUG-119081)
1309 int ret;
1310 QT_EINTR_LOOP(ret, forkfd_wait(forkfd, &info, nullptr));
1311
1312 exitCode = info.status;
1313 exitStatus = info.code == CLD_EXITED ? QProcess::NormalExit : QProcess::CrashExit;
1314
1315 delete stateNotifier;
1316 stateNotifier = nullptr;
1317
1318 QT_EINTR_LOOP(ret, forkfd_close(forkfd));
1319 forkfd = -1; // Child is dead, don't try to kill it anymore
1320
1321#if defined QPROCESS_DEBUG
1322 qDebug() << "QProcessPrivate::waitForDeadChild() dead with exitCode"
1323 << exitCode << ", crashed?" << (info.code != CLD_EXITED);
1324#endif
1325}
1326
1327bool QProcessPrivate::startDetached(qint64 *pid)
1328{
1329 AutoPipe startedPipe, pidPipe;
1330 if (!startedPipe || !pidPipe) {
1331 setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("pipe"));
1332 return false;
1333 }
1334
1335 if (!openChannelsForDetached()) {
1336 // openChannel sets the error string
1337 closeChannels();
1338 return false;
1339 }
1340
1341 // see startProcess() for more information
1342 QChildProcess childProcess(this);
1343 if (!childProcess.ok()) {
1344 Q_ASSERT(processError != QProcess::UnknownError);
1345 return false;
1346 }
1347
1348 childStartedPipe[1] = startedPipe[1]; // for failChildProcess()
1349 pid_t childPid = childProcess.doFork([&] {
1350 ::setsid();
1351
1352 qt_safe_close(startedPipe[0]);
1353 qt_safe_close(pidPipe[0]);
1354
1355 pid_t doubleForkPid;
1356 if (childProcess.startChild(&doubleForkPid) == -1)
1357 failChildProcess(this, "fork", errno);
1358
1359 // success
1360 qt_safe_write(pidPipe[1], &doubleForkPid, sizeof(pid_t));
1361 return 0;
1362 });
1363 childStartedPipe[1] = -1;
1364
1365 int savedErrno = errno;
1366 closeChannels();
1367
1368 if (childPid == -1) {
1369 childProcess.cleanup();
1370 setErrorAndEmit(QProcess::FailedToStart, errorMessageForSyscall("fork", savedErrno));
1371 return false;
1372 }
1373
1374 // close the writing ends of the pipes so we can properly get EOFs
1375 qt_safe_close(pidPipe[1]);
1376 qt_safe_close(startedPipe[1]);
1377 pidPipe[1] = startedPipe[1] = -1;
1378
1379 // This read() will block until we're cleared to proceed. If it returns 0
1380 // (EOF), it means the direct child has exited and the grandchild
1381 // successfully execve()'d the target process. If it returns any positive
1382 // result, it means one of the two children wrote an error result. Negative
1383 // values should not happen.
1384 ChildError childStatus;
1385 ssize_t startResult = qt_safe_read(startedPipe[0], &childStatus, sizeof(childStatus));
1386
1387 // reap the intermediate child
1388 int result;
1389 qt_safe_waitpid(childPid, &result, 0);
1390
1391 bool success = (startResult == 0); // nothing written -> no error
1392 if (success && pid) {
1393 pid_t actualPid;
1394 if (qt_safe_read(pidPipe[0], &actualPid, sizeof(pid_t)) != sizeof(pid_t))
1395 actualPid = 0; // this shouldn't happen!
1396 *pid = actualPid;
1397 } else if (!success) {
1398 if (pid)
1399 *pid = -1;
1400 childProcess.cleanup();
1401 setErrorAndEmit(QProcess::FailedToStart,
1402 startFailureErrorMessage(childStatus, startResult));
1403 }
1404 return success;
1405}
1406
1407#endif // QT_CONFIG(process)
1408
1409QT_END_NAMESPACE
#define __has_include(x)
#define _PATH_TTY
#define _PATH_DEV
char ** environ