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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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