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