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(str: 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
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(str: 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;
301 QProcess::InputChannelMode inputChannelMode;
302 QProcess::ProcessError processError;
303 QProcess::ProcessState processState;
304 QString workingDirectory;
305 Q_PID pid;
306 int sequenceNumber;
307
308 bool dying;
309 bool emittedReadyRead;
310 bool emittedBytesWritten;
311
312 Channel stdinChannel;
313 Channel stdoutChannel;
314 Channel stderrChannel;
315 bool openChannel(Channel &channel);
316 void closeChannel(Channel *channel);
317 void closeWriteChannel();
318 bool tryReadFromChannel(Channel *channel); // obviously, only stdout and stderr
319
320 QString program;
321 QStringList arguments;
322#if defined(Q_OS_WIN)
323 QString nativeArguments;
324 QProcess::CreateProcessArgumentModifier modifyCreateProcessArgs;
325#endif
326 QProcessEnvironment environment;
327
328 Q_PIPE childStartedPipe[2];
329 void destroyPipe(Q_PIPE pipe[2]);
330
331 QSocketNotifier *startupSocketNotifier;
332 QSocketNotifier *deathNotifier;
333
334 int forkfd;
335
336#ifdef Q_OS_WIN
337 QTimer *stdinWriteTrigger;
338 QWinEventNotifier *processFinishedNotifier;
339#endif
340
341 void start(QIODevice::OpenMode mode);
342 void startProcess();
343#if defined(Q_OS_UNIX)
344 void execChild(const char *workingDirectory, char **argv, char **envp);
345#endif
346 bool processStarted(QString *errorMessage = nullptr);
347 void terminateProcess();
348 void killProcess();
349 void findExitCode();
350#ifdef Q_OS_UNIX
351 bool waitForDeadChild();
352#endif
353#ifdef Q_OS_WIN
354 bool callCreateProcess(QProcess::CreateProcessArguments *cpargs);
355 bool drainOutputPipes();
356 void flushPipeWriter();
357 qint64 pipeWriterBytesToWrite() const;
358#endif
359
360 bool startDetached(qint64 *pPid);
361
362 int exitCode;
363 QProcess::ExitStatus exitStatus;
364 bool crashed;
365
366 bool waitForStarted(int msecs = 30000);
367 bool waitForReadyRead(int msecs = 30000);
368 bool waitForBytesWritten(int msecs = 30000);
369 bool waitForFinished(int msecs = 30000);
370
371 qint64 bytesAvailableInChannel(const Channel *channel) const;
372 qint64 readFromChannel(const Channel *channel, char *data, qint64 maxlen);
373 bool writeToStdin();
374
375 void cleanup();
376 void setError(QProcess::ProcessError error, const QString &description = QString());
377 void setErrorAndEmit(QProcess::ProcessError error, const QString &description = QString());
378};
379
380#endif // QT_CONFIG(process)
381
382QT_END_NAMESPACE
383
384#endif // QPROCESS_P_H
385

source code of qtbase/src/corelib/io/qprocess_p.h