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