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