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 | void execChild(int workingDirectory, char **argv, char **envp) const noexcept; |
308 | #endif |
309 | bool processStarted(QString *errorMessage = nullptr); |
310 | void processFinished(); |
311 | void terminateProcess(); |
312 | void killProcess(); |
313 | #ifdef Q_OS_UNIX |
314 | void waitForDeadChild(); |
315 | #else |
316 | void findExitCode(); |
317 | #endif |
318 | #ifdef Q_OS_WIN |
319 | STARTUPINFOW createStartupInfo(); |
320 | bool callCreateProcess(QProcess::CreateProcessArguments *cpargs); |
321 | bool drainOutputPipes(); |
322 | #endif |
323 | |
324 | bool startDetached(qint64 *pPid); |
325 | |
326 | bool waitForStarted(const QDeadlineTimer &deadline); |
327 | bool waitForReadyRead(const QDeadlineTimer &deadline); |
328 | bool waitForBytesWritten(const QDeadlineTimer &deadline); |
329 | bool waitForFinished(const QDeadlineTimer &deadline); |
330 | |
331 | qint64 bytesAvailableInChannel(const Channel *channel) const; |
332 | qint64 readFromChannel(const Channel *channel, char *data, qint64 maxlen); |
333 | |
334 | void destroyPipe(Q_PIPE pipe[2]); |
335 | void cleanup(); |
336 | void setError(QProcess::ProcessError error, const QString &description = QString()); |
337 | void setErrorAndEmit(QProcess::ProcessError error, const QString &description = QString()); |
338 | }; |
339 | |
340 | #endif // QT_CONFIG(process) |
341 | |
342 | QT_END_NAMESPACE |
343 | |
344 | #endif // QPROCESS_P_H |
345 | |