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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/corelib/io/qprocess_unix.cpp