/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QPROCESS_P_H #define QPROCESS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "QtCore/qprocess.h" #include "QtCore/qstringlist.h" #include "QtCore/qhash.h" #include "QtCore/qmap.h" #include "QtCore/qshareddata.h" #include "QtCore/qdeadlinetimer.h" #include "private/qiodevice_p.h" QT_REQUIRE_CONFIG(processenvironment); #ifdef Q_OS_UNIX #include #endif #ifdef Q_OS_WIN #include "QtCore/qt_windows.h" typedef HANDLE Q_PIPE; #define INVALID_Q_PIPE INVALID_HANDLE_VALUE #else typedef int Q_PIPE; #define INVALID_Q_PIPE -1 #endif QT_BEGIN_NAMESPACE class QSocketNotifier; class QWindowsPipeReader; class QWindowsPipeWriter; class QWinEventNotifier; #ifdef Q_OS_WIN class QProcEnvKey : public QString { public: QProcEnvKey() {} explicit QProcEnvKey(const QString &other) : QString(other) {} QProcEnvKey(const QProcEnvKey &other) : QString(other) {} bool operator==(const QProcEnvKey &other) const { return !compare(other, Qt::CaseInsensitive); } }; inline bool operator<(const QProcEnvKey &a, const QProcEnvKey &b) { // On windows use case-insensitive ordering because that is how Windows needs the environment // block sorted (https://msdn.microsoft.com/en-us/library/windows/desktop/ms682009(v=vs.85).aspx) return a.compare(b, Qt::CaseInsensitive) < 0; } Q_DECLARE_TYPEINFO(QProcEnvKey, Q_RELOCATABLE_TYPE); typedef QString QProcEnvValue; #else using QProcEnvKey = QByteArray; class QProcEnvValue { public: QProcEnvValue() = default; explicit QProcEnvValue(const QString &value) : stringValue(value) {} explicit QProcEnvValue(const QByteArray &value) : byteValue(value) {} bool operator==(const QProcEnvValue &other) const { return byteValue.isEmpty() && other.byteValue.isEmpty() ? stringValue == other.stringValue : bytes() == other.bytes(); } QByteArray bytes() const { if (byteValue.isEmpty() && !stringValue.isEmpty()) byteValue = stringValue.toLocal8Bit(); return byteValue; } QString string() const { if (stringValue.isEmpty() && !byteValue.isEmpty()) stringValue = QString::fromLocal8Bit(byteValue); return stringValue; } mutable QByteArray byteValue; mutable QString stringValue; }; Q_DECLARE_TYPEINFO(QProcEnvValue, Q_RELOCATABLE_TYPE); #endif class QProcessEnvironmentPrivate: public QSharedData { public: typedef QProcEnvKey Key; typedef QProcEnvValue Value; #ifdef Q_OS_WIN inline Key prepareName(const QString &name) const { return Key(name); } inline QString nameToString(const Key &name) const { return name; } inline Value prepareValue(const QString &value) const { return value; } inline QString valueToString(const Value &value) const { return value; } #else struct NameMapMutexLocker : public QMutexLocker { NameMapMutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->nameMapMutex) {} }; struct OrderedNameMapMutexLocker : public QOrderedMutexLocker { OrderedNameMapMutexLocker(const QProcessEnvironmentPrivate *d1, const QProcessEnvironmentPrivate *d2) : QOrderedMutexLocker(&d1->nameMapMutex, &d2->nameMapMutex) {} }; inline Key prepareName(const QString &name) const { const NameMapMutexLocker locker(this); Key &ent = nameMap[name]; if (ent.isEmpty()) ent = name.toLocal8Bit(); return ent; } inline QString nameToString(const Key &name) const { const QString sname = QString::fromLocal8Bit(name); { const NameMapMutexLocker locker(this); nameMap[sname] = name; } return sname; } inline Value prepareValue(const QString &value) const { return Value(value); } inline QString valueToString(const Value &value) const { return value.string(); } QProcessEnvironmentPrivate() : QSharedData() {} QProcessEnvironmentPrivate(const QProcessEnvironmentPrivate &other) : QSharedData(), vars(other.vars) { // We don't need to lock our own mutex, as this object is new and // consequently not shared. For the same reason, non-const methods // do not need a lock, as they detach objects (however, we need to // ensure that they really detach before using prepareName()). NameMapMutexLocker locker(&other); nameMap = other.nameMap; // We need to detach our nameMap, so that our mutex can protect it. // As we are being detached, it likely would be detached a moment later anyway. nameMap.detach(); } #endif using Map = QMap; Map vars; #ifdef Q_OS_UNIX typedef QHash NameHash; mutable NameHash nameMap; mutable QMutex nameMapMutex; #endif static QProcessEnvironment fromList(const QStringList &list); QStringList toList() const; QStringList keys() const; void insert(const QProcessEnvironmentPrivate &other); }; template<> Q_INLINE_TEMPLATE void QSharedDataPointer::detach() { if (d && d->ref.loadRelaxed() == 1) return; QProcessEnvironmentPrivate *x = (d ? new QProcessEnvironmentPrivate(*d) : new QProcessEnvironmentPrivate); x->ref.ref(); if (d && !d->ref.deref()) delete d; d = x; } #if QT_CONFIG(process) class QProcessPrivate : public QIODevicePrivate { public: Q_DECLARE_PUBLIC(QProcess) struct Channel { enum ProcessChannelType : char { Normal = 0, PipeSource = 1, PipeSink = 2, Redirect = 3 }; void clear(); Channel &operator=(const QString &fileName) { clear(); file = fileName; type = fileName.isEmpty() ? Normal : Redirect; return *this; } void pipeTo(QProcessPrivate *other) { clear(); process = other; type = PipeSource; } void pipeFrom(QProcessPrivate *other) { clear(); process = other; type = PipeSink; } QString file; QProcessPrivate *process = nullptr; #ifdef Q_OS_UNIX QSocketNotifier *notifier = nullptr; #else union { QWindowsPipeReader *reader = nullptr; QWindowsPipeWriter *writer; }; #endif Q_PIPE pipe[2] = {INVALID_Q_PIPE, INVALID_Q_PIPE}; ProcessChannelType type = Normal; bool closed = false; bool append = false; }; QProcessPrivate(); virtual ~QProcessPrivate(); // private slots bool _q_canReadStandardOutput(); bool _q_canReadStandardError(); #ifdef Q_OS_WIN qint64 pipeWriterBytesToWrite() const; void _q_bytesWritten(qint64 bytes); void _q_writeFailed(); #else bool _q_canWrite(); bool writeToStdin(); #endif bool _q_startupNotification(); void _q_processDied(); QProcess::ProcessChannelMode processChannelMode = QProcess::SeparateChannels; QProcess::InputChannelMode inputChannelMode = QProcess::ManagedInputChannel; QProcess::ProcessError processError = QProcess::UnknownError; QProcess::ProcessState processState = QProcess::NotRunning; QString workingDirectory; #ifdef Q_OS_WIN Q_PROCESS_INFORMATION *pid = nullptr; #else qint64 pid = 0; #endif bool emittedReadyRead = false; bool emittedBytesWritten = false; Channel stdinChannel; Channel stdoutChannel; Channel stderrChannel; bool openChannels(); bool openChannelsForDetached(); bool openChannel(Channel &channel); #if defined(Q_OS_UNIX) void commitChannels(); #endif void closeChannel(Channel *channel); void closeWriteChannel(); void closeChannels(); bool tryReadFromChannel(Channel *channel); // obviously, only stdout and stderr QString program; QStringList arguments; #if defined(Q_OS_WIN) QString nativeArguments; QProcess::CreateProcessArgumentModifier modifyCreateProcessArgs; #else std::function childProcessModifier; #endif QProcessEnvironment environment = QProcessEnvironment::InheritFromParent; #ifdef Q_OS_UNIX Q_PIPE childStartedPipe[2] = {INVALID_Q_PIPE, INVALID_Q_PIPE}; QSocketNotifier *stateNotifier = nullptr; int forkfd = -1; #else QWinEventNotifier *processFinishedNotifier = nullptr; #endif void start(QIODevice::OpenMode mode); void startProcess(); #if defined(Q_OS_UNIX) void execChild(const char *workingDirectory, char **argv, char **envp); #endif bool processStarted(QString *errorMessage = nullptr); void processFinished(); void terminateProcess(); void killProcess(); #ifdef Q_OS_UNIX void waitForDeadChild(); #else void findExitCode(); #endif #ifdef Q_OS_WIN STARTUPINFOW createStartupInfo(); bool callCreateProcess(QProcess::CreateProcessArguments *cpargs); bool drainOutputPipes(); #endif bool startDetached(qint64 *pPid); int exitCode = 0; QProcess::ExitStatus exitStatus = QProcess::NormalExit; bool crashed = false; bool waitForStarted(const QDeadlineTimer &deadline); bool waitForReadyRead(const QDeadlineTimer &deadline); bool waitForBytesWritten(const QDeadlineTimer &deadline); bool waitForFinished(const QDeadlineTimer &deadline); qint64 bytesAvailableInChannel(const Channel *channel) const; qint64 readFromChannel(const Channel *channel, char *data, qint64 maxlen); void destroyPipe(Q_PIPE pipe[2]); void cleanup(); void setError(QProcess::ProcessError error, const QString &description = QString()); void setErrorAndEmit(QProcess::ProcessError error, const QString &description = QString()); }; #endif // QT_CONFIG(process) QT_END_NAMESPACE #endif // QPROCESS_P_H