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