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

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