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