summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qprocess_p.h
blob: 77ebe5ac2b06a124541dc50569660dc7bdad6640 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
// Copyright (C) 2016 The Qt Company Ltd.// Copyright (C) 2016 Intel Corporation.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#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 <QtCore/private/qorderedmutexlocker_p.h>#endif#ifdef Q_OS_WIN#include"QtCore/qt_windows.h"typedef HANDLE Q_PIPE;#define INVALID_Q_PIPE INVALID_HANDLE_VALUE#elsetypedefint Q_PIPE;#define INVALID_Q_PIPE -1#endif QT_BEGIN_NAMESPACE class QSocketNotifier;class QWindowsPipeReader;class QWindowsPipeWriter;class QWinEventNotifier;#ifdef Q_OS_WINclass QProcEnvKey :public QString {public:QProcEnvKey() {}explicitQProcEnvKey(const QString &other) :QString(other) {}QProcEnvKey(const QProcEnvKey &other) :QString(other) {}booloperator==(const QProcEnvKey &other)const{return!compare(other,Qt::CaseInsensitive); }};inlinebooloperator<(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;#elseusing QProcEnvKey = QByteArray;class QProcEnvValue {public:QProcEnvValue() =default;explicitQProcEnvValue(const QString &value) :stringValue(value) {}explicitQProcEnvValue(const QByteArray &value) :byteValue(value) {}booloperator==(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);#endifclass QProcessEnvironmentPrivate:public QSharedData {public:typedef QProcEnvKey Key;typedef QProcEnvValue Value;#ifdef Q_OS_WINinline Key prepareName(const QString &name)const{returnKey(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; }#elsestruct NameMapMutexLocker :public QMutexLocker<QMutex>{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{returnValue(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();}#endifusing Map = QMap<Key, Value>; Map vars;#ifdef Q_OS_UNIXtypedef QHash<QString, Key> NameHash;mutable NameHash nameMap;mutable QMutex nameMapMutex;#endifstatic QProcessEnvironment fromList(const QStringList &list); QStringList toList()const; QStringList keys()const;voidinsert(const QProcessEnvironmentPrivate &other);};template<> Q_INLINE_TEMPLATE void QSharedDataPointer<QProcessEnvironmentPrivate>::detach(){if(d && d->ref.loadRelaxed() ==1)return; QProcessEnvironmentPrivate *x = (d ?newQProcessEnvironmentPrivate(*d):new QProcessEnvironmentPrivate); x->ref.ref();if(d && !d->ref.deref())delete d.get(); d.reset(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};voidclear(); Channel &operator=(const QString &fileName){clear(); file = fileName; type = fileName.isEmpty() ? Normal : Redirect;return*this;}voidpipeTo(QProcessPrivate *other){clear(); process = other; type = PipeSource;}voidpipeFrom(QProcessPrivate *other){clear(); process = other; type = PipeSink;} QString file; QProcessPrivate *process =nullptr;#ifdef Q_OS_UNIX QSocketNotifier *notifier =nullptr;#elseunion{ 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 slotsbool_q_canReadStandardOutput();bool_q_canReadStandardError();#ifdef Q_OS_WIN qint64 pipeWriterBytesToWrite()const;void_q_bytesWritten(qint64 bytes);void_q_writeFailed();#elsebool_q_canWrite();boolwriteToStdin();#endifbool_q_startupNotification();void_q_processDied(); Channel stdinChannel; Channel stdoutChannel; Channel stderrChannel;boolopenChannels();boolopenChannelsForDetached();boolopenChannel(Channel &channel);voidcloseChannel(Channel *channel);voidcloseWriteChannel();voidcloseChannels();booltryReadFromChannel(Channel *channel);// obviously, only stdout and stderr QString program; QStringList arguments; QString workingDirectory; QProcessEnvironment environment =QProcessEnvironment::InheritFromParent;#if defined(Q_OS_WIN) QString nativeArguments;QProcess::CreateProcessArgumentModifier modifyCreateProcessArgs; QWinEventNotifier *processFinishedNotifier =nullptr; Q_PROCESS_INFORMATION *pid =nullptr;#elsestruct UnixExtras {std::function<void(void)> childProcessModifier;QProcess::UnixProcessParameters processParameters;};std::unique_ptr<UnixExtras> unixExtras; QSocketNotifier *stateNotifier =nullptr; Q_PIPE childStartedPipe[2] = {INVALID_Q_PIPE, INVALID_Q_PIPE}; pid_t pid =0;int forkfd = -1;#endifint exitCode =0; quint8 processState =QProcess::NotRunning; quint8 exitStatus =QProcess::NormalExit; quint8 processError =QProcess::UnknownError; quint8 processChannelMode =QProcess::SeparateChannels; quint8 inputChannelMode =QProcess::ManagedInputChannel;bool emittedReadyRead =false;bool emittedBytesWritten =false;voidstart(QIODevice::OpenMode mode);voidstartProcess();#if defined(Q_OS_UNIX)voidcommitChannels()const;#endifboolprocessStarted(QString *errorMessage =nullptr);voidprocessFinished();voidterminateProcess();voidkillProcess();#ifdef Q_OS_UNIXvoidwaitForDeadChild();#elsevoidfindExitCode();#endif#ifdef Q_OS_WIN STARTUPINFOW createStartupInfo();boolcallCreateProcess(QProcess::CreateProcessArguments *cpargs);booldrainOutputPipes();#endifboolstartDetached(qint64 *pPid);boolwaitForStarted(const QDeadlineTimer &deadline);boolwaitForReadyRead(const QDeadlineTimer &deadline);boolwaitForBytesWritten(const QDeadlineTimer &deadline);boolwaitForFinished(const QDeadlineTimer &deadline); qint64 bytesAvailableInChannel(const Channel *channel)const; qint64 readFromChannel(const Channel *channel,char*data, qint64 maxlen);voiddestroyPipe(Q_PIPE pipe[2]);voidcleanup();voidsetError(QProcess::ProcessError error,const QString &description =QString());voidsetErrorAndEmit(QProcess::ProcessError error,const QString &description =QString());const QProcessEnvironmentPrivate *environmentPrivate()const{return environment.d.constData(); }};#endif// QT_CONFIG(process) QT_END_NAMESPACE #endif// QPROCESS_P_H
close