7#include <private/qdebug_p.h>
20#include <private/qsystemlibrary_p.h>
21#include <private/qthread_p.h>
23#include "private/qfsfileengine_p.h"
25#ifndef PIPE_REJECT_REMOTE_CLIENTS
26#define PIPE_REJECT_REMOTE_CLIENTS 0x08
40 if (
wchar_t *envStrings = GetEnvironmentStringsW()) {
41 for (
const wchar_t *
entry = envStrings; *
entry; ) {
42 const int entryLen = int(wcslen(
entry));
44 if (
const wchar_t *
equal = wcschr(
entry + 1, L
'=')) {
50 entry += entryLen + 1;
52 FreeEnvironmentStringsW(envStrings);
62 QProcessPoller(
const QProcessPrivate &proc);
66 enum { maxHandles = 4 };
67 HANDLE handles[maxHandles];
68 DWORD handleCount = 0;
71QProcessPoller::QProcessPoller(
const QProcessPrivate &proc)
73 if (proc.stdinChannel.writer)
74 handles[handleCount++] = proc.stdinChannel.writer->syncEvent();
75 if (proc.stdoutChannel.reader)
76 handles[handleCount++] = proc.stdoutChannel.reader->syncEvent();
77 if (proc.stderrChannel.reader)
78 handles[handleCount++] = proc.stderrChannel.reader->syncEvent();
80 handles[handleCount++] = proc.pid->hProcess;
88 waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
90 }
while (waitRet == WAIT_IO_COMPLETION);
92 if (waitRet - WAIT_OBJECT_0 < handleCount)
95 return (waitRet == WAIT_TIMEOUT) ? 0 : -1;
99static bool qt_create_pipe(
Q_PIPE *pipe,
bool isInputPipe, BOOL defInheritFlag)
108 SECURITY_ATTRIBUTES secAtt = {
sizeof(SECURITY_ATTRIBUTES), 0, defInheritFlag };
111 wchar_t pipeName[256];
112 unsigned int attempts = 1000;
114 _snwprintf(pipeName,
sizeof(pipeName) /
sizeof(pipeName[0]),
118 DWORD dwOpenMode = FILE_FLAG_OVERLAPPED;
119 DWORD dwOutputBufferSize = 0;
120 DWORD dwInputBufferSize = 0;
121 const DWORD dwPipeBufferSize = 1024 * 1024;
123 dwOpenMode |= PIPE_ACCESS_OUTBOUND;
124 dwOutputBufferSize = dwPipeBufferSize;
126 dwOpenMode |= PIPE_ACCESS_INBOUND;
127 dwInputBufferSize = dwPipeBufferSize;
130 hServer = CreateNamedPipe(pipeName,
138 if (hServer != INVALID_HANDLE_VALUE)
140 DWORD dwError = GetLastError();
141 if (dwError != ERROR_PIPE_BUSY || !--attempts) {
143 SetLastError(dwError);
148 secAtt.bInheritHandle = TRUE;
149 const HANDLE hClient = CreateFile(pipeName,
150 (isInputPipe ? (GENERIC_READ | FILE_WRITE_ATTRIBUTES)
155 FILE_FLAG_OVERLAPPED,
157 if (hClient == INVALID_HANDLE_VALUE) {
158 DWORD dwError = GetLastError();
160 CloseHandle(hServer);
161 SetLastError(dwError);
166 OVERLAPPED overlapped;
167 ZeroMemory(&overlapped,
sizeof(overlapped));
168 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
169 if (ConnectNamedPipe(hServer, &overlapped) == 0) {
170 DWORD dwError = GetLastError();
172 case ERROR_PIPE_CONNECTED:
174 case ERROR_IO_PENDING:
175 WaitForSingleObject(overlapped.hEvent, INFINITE);
178 dwError = GetLastError();
179 qErrnoWarning(dwError,
"QProcess: ConnectNamedPipe failed.");
180 CloseHandle(overlapped.hEvent);
181 CloseHandle(hClient);
182 CloseHandle(hServer);
183 SetLastError(dwError);
187 CloseHandle(overlapped.hEvent);
202bool QProcessPrivate::openChannel(Channel &
channel)
207 case Channel::Normal: {
209 if (&
channel == &stdinChannel) {
210 if (!qt_create_pipe(
channel.pipe,
true, FALSE)) {
211 setErrorAndEmit(QProcess::FailedToStart,
"pipe: "_L1 +
qt_error_string(errno));
217 if (&
channel == &stdoutChannel) {
218 if (!stdoutChannel.reader) {
220 q->connect(stdoutChannel.reader,
SIGNAL(readyRead()),
SLOT(_q_canReadStandardOutput()));
223 if (!stderrChannel.reader) {
225 q->connect(stderrChannel.reader,
SIGNAL(readyRead()),
SLOT(_q_canReadStandardError()));
228 if (!qt_create_pipe(
channel.pipe,
false, FALSE)) {
229 setErrorAndEmit(QProcess::FailedToStart,
"pipe: "_L1 +
qt_error_string(errno));
234 channel.reader->startAsyncRead();
237 case Channel::Redirect: {
239 SECURITY_ATTRIBUTES secAtt = {
sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
241 if (&
channel == &stdinChannel) {
245 CreateFile((
const wchar_t*)QFSFileEnginePrivate::longFileName(
channel.file).utf16(),
247 FILE_SHARE_READ | FILE_SHARE_WRITE,
250 FILE_ATTRIBUTE_NORMAL,
256 setErrorAndEmit(QProcess::FailedToStart,
257 QProcess::tr(
"Could not open input redirection for reading"));
262 CreateFile((
const wchar_t *)QFSFileEnginePrivate::longFileName(
channel.file).utf16(),
264 FILE_SHARE_READ | FILE_SHARE_WRITE,
266 channel.append ? OPEN_ALWAYS : CREATE_ALWAYS,
267 FILE_ATTRIBUTE_NORMAL,
272 SetFilePointer(
channel.pipe[1], 0, NULL, FILE_END);
277 setErrorAndEmit(QProcess::FailedToStart,
278 QProcess::tr(
"Could not open output redirection for writing"));
282 case Channel::PipeSource: {
296 if (!qt_create_pipe(
source->pipe,
false, TRUE)) {
297 setErrorAndEmit(QProcess::FailedToStart,
"pipe: "_L1 +
qt_error_string(errno));
306 case Channel::PipeSink: {
318 if (!qt_create_pipe(
sink->pipe,
true, TRUE)) {
319 setErrorAndEmit(QProcess::FailedToStart,
"pipe: "_L1 +
qt_error_string(errno));
332void QProcessPrivate::destroyPipe(
Q_PIPE pipe[2])
335 CloseHandle(pipe[0]);
339 CloseHandle(pipe[1]);
344void QProcessPrivate::closeChannel(Channel *
channel)
346 if (
channel == &stdinChannel) {
356void QProcessPrivate::cleanup()
358 q_func()->setProcessState(QProcess::NotRunning);
361 delete processFinishedNotifier;
362 processFinishedNotifier =
nullptr;
364 CloseHandle(pid->hThread);
365 CloseHandle(pid->hProcess);
372 const QString &nativeArguments)
377 if (!programName.startsWith(u
'\"') && !programName.endsWith(u
'\"') && programName.contains(u
' '))
378 programName = u
'\"' + programName + u
'\"';
379 programName.
replace(u
'/', u
'\\');
382 args = programName + u
' ';
404 while (
i > 0 && tmp.
at(
i - 1) == u
'\\')
412 if (!nativeArguments.isEmpty()) {
415 args += nativeArguments;
428 if (!
copy.contains(pathKey)) {
436 if (!
copy.contains(rootKey)) {
438 if (!systemRoot.isEmpty())
443 auto it =
copy.constBegin();
444 const auto end =
copy.constEnd();
446 static const wchar_t equal = L
'=';
447 static const wchar_t nul = L
'\0';
450 qsizetype tmpSize =
sizeof(wchar_t) * (
it.key().length() +
it.value().length() + 2);
452 if (tmpSize ==
sizeof(
wchar_t) * 2)
454 envlist.resize(envlist.size() + tmpSize);
456 tmpSize =
it.key().length() *
sizeof(wchar_t);
457 memcpy(envlist.data() +
pos,
it.key().data(), tmpSize);
460 memcpy(envlist.data() +
pos, &
equal,
sizeof(
wchar_t));
461 pos +=
sizeof(wchar_t);
463 tmpSize =
it.value().length() *
sizeof(wchar_t);
464 memcpy(envlist.data() +
pos,
it.value().data(), tmpSize);
467 memcpy(envlist.data() +
pos, &nul,
sizeof(
wchar_t));
468 pos +=
sizeof(wchar_t);
471 envlist.resize(envlist.size() + 4);
480static Q_PIPE pipeOrStdHandle(
Q_PIPE pipe, DWORD handleNumber)
482 return pipe !=
INVALID_Q_PIPE ? pipe : GetStdHandle(handleNumber);
485STARTUPINFOW QProcessPrivate::createStartupInfo()
487 Q_PIPE stdinPipe = pipeOrStdHandle(stdinChannel.pipe[0], STD_INPUT_HANDLE);
488 Q_PIPE stdoutPipe = pipeOrStdHandle(stdoutChannel.pipe[1], STD_OUTPUT_HANDLE);
489 Q_PIPE stderrPipe = stderrChannel.pipe[1];
491 stderrPipe = (processChannelMode == QProcess::MergedChannels)
493 : GetStdHandle(STD_ERROR_HANDLE);
497 sizeof(STARTUPINFOW), 0, 0, 0,
501 STARTF_USESTDHANDLES,
503 stdinPipe, stdoutPipe, stderrPipe
507bool QProcessPrivate::callCreateProcess(QProcess::CreateProcessArguments *cpargs)
509 if (modifyCreateProcessArgs)
510 modifyCreateProcessArgs(cpargs);
511 bool success = CreateProcess(cpargs->applicationName, cpargs->arguments,
512 cpargs->processAttributes, cpargs->threadAttributes,
513 cpargs->inheritHandles, cpargs->flags, cpargs->environment,
514 cpargs->currentDirectory, cpargs->startupInfo,
515 cpargs->processInformation);
517 CloseHandle(stdinChannel.pipe[0]);
521 CloseHandle(stdoutChannel.pipe[1]);
525 CloseHandle(stderrChannel.pipe[1]);
531void QProcessPrivate::startProcess()
535 pid =
new PROCESS_INFORMATION;
536 memset(pid, 0,
sizeof(PROCESS_INFORMATION));
538 q->setProcessState(QProcess::Starting);
540 if (!openChannels()) {
549 if (!environment.inheritsFromParent())
550 envlist = qt_create_environment(environment.d.constData()->vars);
552#if defined QPROCESS_DEBUG
553 qDebug(
"Creating process");
556 qDebug(
" pass environment : %s", environment.
isEmpty() ?
"no" :
"yes");
564 DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
565 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
566 STARTUPINFOW startupInfo = createStartupInfo();
568 QProcess::CreateProcessArguments cpargs = {
569 nullptr,
reinterpret_cast<wchar_t *
>(
const_cast<ushort *
>(
args.utf16())),
570 nullptr,
nullptr,
true, dwCreationFlags,
571 environment.inheritsFromParent() ?
nullptr : envlist.data(),
572 nativeWorkingDirectory.
isEmpty()
573 ?
nullptr :
reinterpret_cast<const wchar_t *
>(nativeWorkingDirectory.utf16()),
577 if (!callCreateProcess(&cpargs)) {
581 setErrorAndEmit(QProcess::FailedToStart, errorString);
588 if (stdinChannel.writer)
589 stdinChannel.writer->setHandle(stdinChannel.pipe[1]);
591 q->setProcessState(QProcess::Running);
597 if (threadData.loadRelaxed()->hasEventDispatcher()) {
600 processFinishedNotifier->setEnabled(
true);
603 _q_startupNotification();
606bool QProcessPrivate::processStarted(
QString * )
608 return processState == QProcess::Running;
611qint64 QProcessPrivate::bytesAvailableInChannel(
const Channel *
channel)
const
616 DWORD bytesAvail =
channel->reader->bytesAvailable();
617#if defined QPROCESS_DEBUG
618 qDebug(
"QProcessPrivate::bytesAvailableInChannel(%d) == %lld",
631static BOOL QT_WIN_CALLBACK qt_terminateApp(HWND hwnd, LPARAM procId)
633 DWORD currentProcId = 0;
634 GetWindowThreadProcessId(hwnd, ¤tProcId);
635 if (currentProcId == (DWORD)procId)
636 PostMessage(hwnd, WM_CLOSE, 0, 0);
641void QProcessPrivate::terminateProcess()
644 EnumWindows(qt_terminateApp, (LPARAM)pid->dwProcessId);
645 PostThreadMessage(pid->dwThreadId, WM_CLOSE, 0, 0);
649void QProcessPrivate::killProcess()
657 if (processStarted())
660 if (processError == QProcess::FailedToStart)
667bool QProcessPrivate::drainOutputPipes()
669 bool readyReadEmitted =
false;
671 if (stdoutChannel.reader) {
672 stdoutChannel.reader->drainAndStop();
673 readyReadEmitted = _q_canReadStandardOutput();
675 if (stderrChannel.reader) {
676 stderrChannel.reader->drainAndStop();
677 readyReadEmitted |= _q_canReadStandardError();
680 return readyReadEmitted;
686 QProcessPoller poller(*
this);
693 if (stdinChannel.writer)
694 stdinChannel.writer->checkForWrite();
696 if ((stdoutChannel.reader && stdoutChannel.reader->checkForReadyRead())
697 || (stderrChannel.reader && stderrChannel.reader->checkForReadyRead()))
703 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
704 bool readyReadEmitted = drainOutputPipes();
707 return readyReadEmitted;
722 if (!stdinChannel.writer || !stdinChannel.writer->isWriteOperationActive())
725 QProcessPoller poller(*
this);
732 if (stdinChannel.writer->checkForWrite())
736 if (stdoutChannel.reader)
737 stdoutChannel.reader->checkForReadyRead();
740 if (stderrChannel.reader)
741 stderrChannel.reader->checkForReadyRead();
748 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
762#if defined QPROCESS_DEBUG
767 QProcessPoller poller(*
this);
774 if (stdinChannel.writer)
775 stdinChannel.writer->checkForWrite();
776 if (stdoutChannel.reader)
777 stdoutChannel.reader->checkForReadyRead();
778 if (stderrChannel.reader)
779 stderrChannel.reader->checkForReadyRead();
784 if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
796void QProcessPrivate::findExitCode()
800 if (GetExitCodeProcess(pid->hProcess, &theExitCode)) {
801 exitCode = theExitCode;
803 || (theExitCode >= 0x80000000 && theExitCode < 0xD0000000))
804 exitStatus = QProcess::CrashExit;
806 exitStatus = QProcess::NormalExit;
817 if (
d->stdinChannel.closed) {
818#if defined QPROCESS_DEBUG
819 qDebug(
"QProcess::writeData(%p \"%s\", %lld) == 0 (write channel closing)",
825 if (!
d->stdinChannel.writer) {
828 d, &QProcessPrivate::_q_bytesWritten);
830 d, &QProcessPrivate::_q_writeFailed);
833 if (
d->isWriteChunkCached(
data,
len))
834 d->stdinChannel.writer->write(*(
d->currentWriteChunk));
836 d->stdinChannel.writer->write(
data,
len);
838#if defined QPROCESS_DEBUG
839 qDebug(
"QProcess::writeData(%p \"%s\", %lld) == %lld (written to buffer)",
845qint64 QProcessPrivate::pipeWriterBytesToWrite()
const
847 return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() :
qint64(0);
850void QProcessPrivate::_q_bytesWritten(
qint64 bytes)
854 if (!emittedBytesWritten) {
855 QScopedValueRollback<bool> guard(emittedBytesWritten,
true);
856 emit q->bytesWritten(bytes);
858 if (stdinChannel.closed && pipeWriterBytesToWrite() == 0)
862void QProcessPrivate::_q_writeFailed()
865 setErrorAndEmit(QProcess::WriteError);
871 const QString &nativeArguments,
876 SHELLEXECUTEINFOW shellExecuteExInfo;
877 memset(&shellExecuteExInfo, 0,
sizeof(SHELLEXECUTEINFOW));
878 shellExecuteExInfo.cbSize =
sizeof(SHELLEXECUTEINFOW);
879 shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI | SEE_MASK_CLASSNAME;
880 shellExecuteExInfo.lpClass = L
"exefile";
881 shellExecuteExInfo.lpVerb = L
"runas";
883 shellExecuteExInfo.lpFile =
reinterpret_cast<LPCWSTR
>(
program.utf16());
885 shellExecuteExInfo.lpParameters =
reinterpret_cast<LPCWSTR
>(
args.utf16());
887 shellExecuteExInfo.lpDirectory =
reinterpret_cast<LPCWSTR
>(workingDir.
utf16());
888 shellExecuteExInfo.nShow = SW_SHOWNORMAL;
890 if (!ShellExecuteExW(&shellExecuteExInfo))
893 *pid =
qint64(GetProcessId(shellExecuteExInfo.hProcess));
894 CloseHandle(shellExecuteExInfo.hProcess);
898bool QProcessPrivate::startDetached(
qint64 *pid)
900 static const DWORD errorElevationRequired = 740;
902 if (!openChannelsForDetached()) {
909 bool success =
false;
910 PROCESS_INFORMATION pinfo;
912 void *envPtr =
nullptr;
914 if (!environment.inheritsFromParent()) {
915 envlist = qt_create_environment(environment.d.constData()->vars);
916 envPtr = envlist.data();
919 DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
920 dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
921 STARTUPINFOW startupInfo = createStartupInfo();
922 QProcess::CreateProcessArguments cpargs = {
923 nullptr,
reinterpret_cast<wchar_t *
>(
const_cast<ushort *
>(
args.utf16())),
924 nullptr,
nullptr,
true, dwCreationFlags, envPtr,
925 workingDirectory.isEmpty()
926 ?
nullptr :
reinterpret_cast<const wchar_t *
>(workingDirectory.utf16()),
929 success = callCreateProcess(&cpargs);
932 CloseHandle(pinfo.hThread);
933 CloseHandle(pinfo.hProcess);
935 *pid = pinfo.dwProcessId;
936 }
else if (GetLastError() == errorElevationRequired) {
938 qWarning(
"QProcess: custom environment will be ignored for detached elevated process.");
939 if (!stdinChannel.file.isEmpty() || !stdoutChannel.file.isEmpty()
940 || !stderrChannel.file.isEmpty()) {
941 qWarning(
"QProcess: file redirection is unsupported for detached elevated processes.");
944 workingDirectory, pid);
949 setErrorAndEmit(QProcess::FailedToStart);
IOBluetoothL2CAPChannel * channel
static qint64 applicationPid() Q_DECL_CONST_FUNCTION
qint64 remainingTime() const noexcept
Returns the remaining time in this QDeadlineTimer object in milliseconds.
static QString toNativeSeparators(const QString &pathName)
qsizetype size() const noexcept
const_pointer constData() const noexcept
bool isEmpty() const noexcept
const_reference at(qsizetype i) const noexcept
iterator insert(const Key &key, const T &value)
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
static QProcessEnvironment systemEnvironment()
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
\macro QT_RESTRICTED_CAST_FROM_ASCII
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
QString & replace(qsizetype i, qsizetype len, QChar after)
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
QString & insert(qsizetype i, QChar c)
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
QString & prepend(QChar c)
qsizetype length() const noexcept
Returns the number of characters in this string.
void bytesWritten(qint64 bytes)
QSet< QString >::iterator it
QList< QVariant > arguments
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
Q_CORE_EXPORT QByteArray toPrintable(const char *data, qint64 len, qsizetype maxSize)
static jboolean copy(JNIEnv *, jobject)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLsizei GLsizei GLchar * source
GLdouble GLdouble GLdouble GLdouble q
GLsizei const GLchar *const * path
GLsizei GLenum GLboolean sink
QT_BEGIN_NAMESPACE constexpr UINT KillProcessExitCode
#define PIPE_REJECT_REMOTE_CLIENTS
static void setError(QJsonObject *response, const QString &msg)
#define Q_ASSERT_X(cond, x, msg)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
static bool equal(const QChar *a, int l, const char *b)
QDeadlineTimer deadline(30s)