| 1 | // Copyright (C) 2016 The Qt Company Ltd. | 
| 2 | // Copyright (C) 2016 Intel Corporation. | 
| 3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 4 |  | 
| 5 | #ifndef QPROCESS_P_H | 
| 6 | #define QPROCESS_P_H | 
| 7 |  | 
| 8 | // | 
| 9 | //  W A R N I N G | 
| 10 | //  ------------- | 
| 11 | // | 
| 12 | // This file is not part of the Qt API.  It exists purely as an | 
| 13 | // implementation detail.  This header file may change from version to | 
| 14 | // version without notice, or even be removed. | 
| 15 | // | 
| 16 | // We mean it. | 
| 17 | // | 
| 18 |  | 
| 19 | #include "QtCore/qprocess.h" | 
| 20 | #include "QtCore/qstringlist.h" | 
| 21 | #include "QtCore/qhash.h" | 
| 22 | #include "QtCore/qmap.h" | 
| 23 | #include "QtCore/qshareddata.h" | 
| 24 | #include "QtCore/qdeadlinetimer.h" | 
| 25 | #include "private/qiodevice_p.h" | 
| 26 |  | 
| 27 | QT_REQUIRE_CONFIG(processenvironment); | 
| 28 |  | 
| 29 | #ifdef Q_OS_UNIX | 
| 30 | #include <QtCore/private/qorderedmutexlocker_p.h> | 
| 31 | #endif | 
| 32 |  | 
| 33 | #ifdef Q_OS_WIN | 
| 34 | #include "QtCore/qt_windows.h" | 
| 35 | typedef HANDLE Q_PIPE; | 
| 36 | #define INVALID_Q_PIPE INVALID_HANDLE_VALUE | 
| 37 | #else | 
| 38 | typedef int Q_PIPE; | 
| 39 | #define INVALID_Q_PIPE -1 | 
| 40 | #endif | 
| 41 |  | 
| 42 | QT_BEGIN_NAMESPACE | 
| 43 |  | 
| 44 | class QSocketNotifier; | 
| 45 | class QWindowsPipeReader; | 
| 46 | class QWindowsPipeWriter; | 
| 47 | class QWinEventNotifier; | 
| 48 |  | 
| 49 | #ifdef Q_OS_WIN | 
| 50 | class QProcEnvKey : public QString | 
| 51 | { | 
| 52 | public: | 
| 53 |     QProcEnvKey() {} | 
| 54 |     explicit QProcEnvKey(const QString &other) : QString(other) {} | 
| 55 |     QProcEnvKey(const QProcEnvKey &other) : QString(other) {} | 
| 56 |     bool operator==(const QProcEnvKey &other) const { return !compare(other, Qt::CaseInsensitive); } | 
| 57 | }; | 
| 58 |  | 
| 59 | inline bool operator<(const QProcEnvKey &a, const QProcEnvKey &b) | 
| 60 | { | 
| 61 |     // On windows use case-insensitive ordering because that is how Windows needs the environment | 
| 62 |     // block sorted (https://msdn.microsoft.com/en-us/library/windows/desktop/ms682009(v=vs.85).aspx) | 
| 63 |     return a.compare(b, Qt::CaseInsensitive) < 0; | 
| 64 | } | 
| 65 |  | 
| 66 | Q_DECLARE_TYPEINFO(QProcEnvKey, Q_RELOCATABLE_TYPE); | 
| 67 |  | 
| 68 | typedef QString QProcEnvValue; | 
| 69 | #else | 
| 70 | using QProcEnvKey = QByteArray; | 
| 71 |  | 
| 72 | class QProcEnvValue | 
| 73 | { | 
| 74 | public: | 
| 75 |     QProcEnvValue() = default; | 
| 76 |     explicit QProcEnvValue(const QString &value) : stringValue(value) {} | 
| 77 |     explicit QProcEnvValue(const QByteArray &value) : byteValue(value) {} | 
| 78 |     bool operator==(const QProcEnvValue &other) const | 
| 79 |     { | 
| 80 |         return byteValue.isEmpty() && other.byteValue.isEmpty() | 
| 81 |                 ? stringValue == other.stringValue | 
| 82 |                 : bytes() == other.bytes(); | 
| 83 |     } | 
| 84 |     QByteArray bytes() const | 
| 85 |     { | 
| 86 |         if (byteValue.isEmpty() && !stringValue.isEmpty()) | 
| 87 |             byteValue = stringValue.toLocal8Bit(); | 
| 88 |         return byteValue; | 
| 89 |     } | 
| 90 |     QString string() const | 
| 91 |     { | 
| 92 |         if (stringValue.isEmpty() && !byteValue.isEmpty()) | 
| 93 |             stringValue = QString::fromLocal8Bit(ba: byteValue); | 
| 94 |         return stringValue; | 
| 95 |     } | 
| 96 |  | 
| 97 |     mutable QByteArray byteValue; | 
| 98 |     mutable QString stringValue; | 
| 99 | }; | 
| 100 | Q_DECLARE_TYPEINFO(QProcEnvValue, Q_RELOCATABLE_TYPE); | 
| 101 | #endif | 
| 102 |  | 
| 103 | class QProcessEnvironmentPrivate: public QSharedData | 
| 104 | { | 
| 105 | public: | 
| 106 |     typedef QProcEnvKey Key; | 
| 107 |     typedef QProcEnvValue Value; | 
| 108 | #ifdef Q_OS_WIN | 
| 109 |     inline Key prepareName(const QString &name) const { return Key(name); } | 
| 110 |     inline QString nameToString(const Key &name) const { return name; } | 
| 111 |     inline Value prepareValue(const QString &value) const { return value; } | 
| 112 |     inline QString valueToString(const Value &value) const { return value; } | 
| 113 | #else | 
| 114 |     struct NameMapMutexLocker : public QMutexLocker<QMutex> | 
| 115 |     { | 
| 116 |         NameMapMutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->nameMapMutex) {} | 
| 117 |     }; | 
| 118 |     struct OrderedNameMapMutexLocker : public QOrderedMutexLocker | 
| 119 |     { | 
| 120 |         OrderedNameMapMutexLocker(const QProcessEnvironmentPrivate *d1, | 
| 121 |                                   const QProcessEnvironmentPrivate *d2) | 
| 122 |             : QOrderedMutexLocker(&d1->nameMapMutex, &d2->nameMapMutex) | 
| 123 |         {} | 
| 124 |     }; | 
| 125 |  | 
| 126 |     inline Key prepareName(const QString &name) const | 
| 127 |     { | 
| 128 |         const NameMapMutexLocker locker(this); | 
| 129 |         Key &ent = nameMap[name]; | 
| 130 |         if (ent.isEmpty()) | 
| 131 |             ent = name.toLocal8Bit(); | 
| 132 |         return ent; | 
| 133 |     } | 
| 134 |     inline QString nameToString(const Key &name) const | 
| 135 |     { | 
| 136 |         const QString sname = QString::fromLocal8Bit(ba: name); | 
| 137 |         { | 
| 138 |             const NameMapMutexLocker locker(this); | 
| 139 |             nameMap[sname] = name; | 
| 140 |         } | 
| 141 |         return sname; | 
| 142 |     } | 
| 143 |     inline Value prepareValue(const QString &value) const { return Value(value); } | 
| 144 |     inline QString valueToString(const Value &value) const { return value.string(); } | 
| 145 |  | 
| 146 |     QProcessEnvironmentPrivate() : QSharedData() {} | 
| 147 |     QProcessEnvironmentPrivate(const QProcessEnvironmentPrivate &other) : | 
| 148 |         QSharedData(), vars(other.vars) | 
| 149 |     { | 
| 150 |         // We don't need to lock our own mutex, as this object is new and | 
| 151 |         // consequently not shared. For the same reason, non-const methods | 
| 152 |         // do not need a lock, as they detach objects (however, we need to | 
| 153 |         // ensure that they really detach before using prepareName()). | 
| 154 |         NameMapMutexLocker locker(&other); | 
| 155 |         nameMap = other.nameMap; | 
| 156 |         // We need to detach our nameMap, so that our mutex can protect it. | 
| 157 |         // As we are being detached, it likely would be detached a moment later anyway. | 
| 158 |         nameMap.detach(); | 
| 159 |     } | 
| 160 | #endif | 
| 161 |  | 
| 162 |     using Map = QMap<Key, Value>; | 
| 163 |     Map vars; | 
| 164 |  | 
| 165 | #ifdef Q_OS_UNIX | 
| 166 |     typedef QHash<QString, Key> NameHash; | 
| 167 |     mutable NameHash nameMap; | 
| 168 |     mutable QMutex nameMapMutex; | 
| 169 | #endif | 
| 170 |  | 
| 171 |     static QProcessEnvironment fromList(const QStringList &list); | 
| 172 |     QStringList toList() const; | 
| 173 |     QStringList keys() const; | 
| 174 |     void insert(const QProcessEnvironmentPrivate &other); | 
| 175 | }; | 
| 176 |  | 
| 177 | template<> Q_INLINE_TEMPLATE void QSharedDataPointer<QProcessEnvironmentPrivate>::detach() | 
| 178 | { | 
| 179 |     if (d && d->ref.loadRelaxed() == 1) | 
| 180 |         return; | 
| 181 |     QProcessEnvironmentPrivate *x = (d ? new QProcessEnvironmentPrivate(*d) | 
| 182 |                                      : new QProcessEnvironmentPrivate); | 
| 183 |     x->ref.ref(); | 
| 184 |     if (d && !d->ref.deref()) | 
| 185 |         delete d; | 
| 186 |     d = x; | 
| 187 | } | 
| 188 |  | 
| 189 | #if QT_CONFIG(process) | 
| 190 |  | 
| 191 | class QProcessPrivate : public QIODevicePrivate | 
| 192 | { | 
| 193 | public: | 
| 194 |     Q_DECLARE_PUBLIC(QProcess) | 
| 195 |  | 
| 196 |     struct Channel { | 
| 197 |         enum ProcessChannelType : char { | 
| 198 |             Normal = 0, | 
| 199 |             PipeSource = 1, | 
| 200 |             PipeSink = 2, | 
| 201 |             Redirect = 3 | 
| 202 |         }; | 
| 203 |  | 
| 204 |         void clear(); | 
| 205 |  | 
| 206 |         Channel &operator=(const QString &fileName) | 
| 207 |         { | 
| 208 |             clear(); | 
| 209 |             file = fileName; | 
| 210 |             type = fileName.isEmpty() ? Normal : Redirect; | 
| 211 |             return *this; | 
| 212 |         } | 
| 213 |  | 
| 214 |         void pipeTo(QProcessPrivate *other) | 
| 215 |         { | 
| 216 |             clear(); | 
| 217 |             process = other; | 
| 218 |             type = PipeSource; | 
| 219 |         } | 
| 220 |  | 
| 221 |         void pipeFrom(QProcessPrivate *other) | 
| 222 |         { | 
| 223 |             clear(); | 
| 224 |             process = other; | 
| 225 |             type = PipeSink; | 
| 226 |         } | 
| 227 |  | 
| 228 |         QString file; | 
| 229 |         QProcessPrivate *process = nullptr; | 
| 230 | #ifdef Q_OS_UNIX | 
| 231 |         QSocketNotifier *notifier = nullptr; | 
| 232 | #else | 
| 233 |         union { | 
| 234 |             QWindowsPipeReader *reader = nullptr; | 
| 235 |             QWindowsPipeWriter *writer; | 
| 236 |         }; | 
| 237 | #endif | 
| 238 |         Q_PIPE pipe[2] = {INVALID_Q_PIPE, INVALID_Q_PIPE}; | 
| 239 |  | 
| 240 |         ProcessChannelType type = Normal; | 
| 241 |         bool closed = false; | 
| 242 |         bool append = false; | 
| 243 |     }; | 
| 244 |  | 
| 245 |     QProcessPrivate(); | 
| 246 |     virtual ~QProcessPrivate(); | 
| 247 |  | 
| 248 |     // private slots | 
| 249 |     bool _q_canReadStandardOutput(); | 
| 250 |     bool _q_canReadStandardError(); | 
| 251 | #ifdef Q_OS_WIN | 
| 252 |     qint64 pipeWriterBytesToWrite() const; | 
| 253 |     void _q_bytesWritten(qint64 bytes); | 
| 254 |     void _q_writeFailed(); | 
| 255 | #else | 
| 256 |     bool _q_canWrite(); | 
| 257 |     bool writeToStdin(); | 
| 258 | #endif | 
| 259 |     bool _q_startupNotification(); | 
| 260 |     void _q_processDied(); | 
| 261 |  | 
| 262 |     Channel stdinChannel; | 
| 263 |     Channel stdoutChannel; | 
| 264 |     Channel stderrChannel; | 
| 265 |     bool openChannels(); | 
| 266 |     bool openChannelsForDetached(); | 
| 267 |     bool openChannel(Channel &channel); | 
| 268 |     void closeChannel(Channel *channel); | 
| 269 |     void closeWriteChannel(); | 
| 270 |     void closeChannels(); | 
| 271 |     bool tryReadFromChannel(Channel *channel); // obviously, only stdout and stderr | 
| 272 |  | 
| 273 |     QString program; | 
| 274 |     QStringList arguments; | 
| 275 |     QString workingDirectory; | 
| 276 |     QProcessEnvironment environment = QProcessEnvironment::InheritFromParent; | 
| 277 | #if defined(Q_OS_WIN) | 
| 278 |     QString nativeArguments; | 
| 279 |     QProcess::CreateProcessArgumentModifier modifyCreateProcessArgs; | 
| 280 |     QWinEventNotifier *processFinishedNotifier = nullptr; | 
| 281 |     Q_PROCESS_INFORMATION *pid = nullptr; | 
| 282 | #else | 
| 283 |     struct  { | 
| 284 |         std::function<void(void)> ; | 
| 285 |         QProcess::UnixProcessParameters ; | 
| 286 |     }; | 
| 287 |     std::unique_ptr<UnixExtras> ; | 
| 288 |     QSocketNotifier *stateNotifier = nullptr; | 
| 289 |     Q_PIPE childStartedPipe[2] = {INVALID_Q_PIPE, INVALID_Q_PIPE}; | 
| 290 |     pid_t pid = 0; | 
| 291 |     int forkfd = -1; | 
| 292 | #endif | 
| 293 |  | 
| 294 |     int exitCode = 0; | 
| 295 |     quint8 processState = QProcess::NotRunning; | 
| 296 |     quint8 exitStatus = QProcess::NormalExit; | 
| 297 |     quint8 processError = QProcess::UnknownError; | 
| 298 |     quint8 processChannelMode = QProcess::SeparateChannels; | 
| 299 |     quint8 inputChannelMode = QProcess::ManagedInputChannel; | 
| 300 |     bool emittedReadyRead = false; | 
| 301 |     bool emittedBytesWritten = false; | 
| 302 |  | 
| 303 |     void start(QIODevice::OpenMode mode); | 
| 304 |     void startProcess(); | 
| 305 | #if defined(Q_OS_UNIX) | 
| 306 |     void commitChannels() const; | 
| 307 | #endif | 
| 308 |     bool processStarted(QString *errorMessage = nullptr); | 
| 309 |     void processFinished(); | 
| 310 |     void terminateProcess(); | 
| 311 |     void killProcess(); | 
| 312 | #ifdef Q_OS_UNIX | 
| 313 |     void waitForDeadChild(); | 
| 314 | #else | 
| 315 |     void findExitCode(); | 
| 316 | #endif | 
| 317 | #ifdef Q_OS_WIN | 
| 318 |     STARTUPINFOW createStartupInfo(); | 
| 319 |     bool callCreateProcess(QProcess::CreateProcessArguments *cpargs); | 
| 320 |     bool drainOutputPipes(); | 
| 321 | #endif | 
| 322 |  | 
| 323 |     bool startDetached(qint64 *pPid); | 
| 324 |  | 
| 325 |     bool waitForStarted(const QDeadlineTimer &deadline); | 
| 326 |     bool waitForReadyRead(const QDeadlineTimer &deadline); | 
| 327 |     bool waitForBytesWritten(const QDeadlineTimer &deadline); | 
| 328 |     bool waitForFinished(const QDeadlineTimer &deadline); | 
| 329 |  | 
| 330 |     qint64 bytesAvailableInChannel(const Channel *channel) const; | 
| 331 |     qint64 readFromChannel(const Channel *channel, char *data, qint64 maxlen); | 
| 332 |  | 
| 333 |     void destroyPipe(Q_PIPE pipe[2]); | 
| 334 |     void cleanup(); | 
| 335 |     void setError(QProcess::ProcessError error, const QString &description = QString()); | 
| 336 |     void setErrorAndEmit(QProcess::ProcessError error, const QString &description = QString()); | 
| 337 |  | 
| 338 |     const QProcessEnvironmentPrivate *environmentPrivate() const | 
| 339 |     { return environment.d.constData(); } | 
| 340 | }; | 
| 341 |  | 
| 342 | #endif // QT_CONFIG(process) | 
| 343 |  | 
| 344 | QT_END_NAMESPACE | 
| 345 |  | 
| 346 | #endif // QPROCESS_P_H | 
| 347 |  |