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
62 QT_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"
70 typedef HANDLE Q_PIPE;
71 #define INVALID_Q_PIPE INVALID_HANDLE_VALUE
72 #else
73 typedef int Q_PIPE;
74 #define INVALID_Q_PIPE -1
75 #endif
76
77 QT_BEGIN_NAMESPACE
78
79 class QSocketNotifier;
80 class QWindowsPipeReader;
81 class QWindowsPipeWriter;
82 class QWinEventNotifier;
83 class QTimer;
84
85 #ifdef Q_OS_WIN
86 class QProcEnvKey : public QString
87 {
88 public:
QProcEnvKey()89 QProcEnvKey() {}
QProcEnvKey(const QString & other)90 explicit QProcEnvKey(const QString &other) : QString(other) {}
QProcEnvKey(const QProcEnvKey & other)91 QProcEnvKey(const QProcEnvKey &other) : QString(other) {}
92 bool operator==(const QProcEnvKey &other) const { return !compare(other, Qt::CaseInsensitive); }
93 };
94
95 inline 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
102 Q_DECLARE_TYPEINFO(QProcEnvKey, Q_MOVABLE_TYPE);
103
104 typedef QString QProcEnvValue;
105 #else
106 using QProcEnvKey = QByteArray;
107
108 class QProcEnvValue
109 {
110 public:
111 QProcEnvValue() = default;
QProcEnvValue(const QString & value)112 explicit QProcEnvValue(const QString &value) : stringValue(value) {}
QProcEnvValue(const QByteArray & 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 }
bytes()120 QByteArray bytes() const
121 {
122 if (byteValue.isEmpty() && !stringValue.isEmpty())
123 byteValue = stringValue.toLocal8Bit();
124 return byteValue;
125 }
string()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 };
136 Q_DECLARE_TYPEINFO(QProcEnvValue, Q_MOVABLE_TYPE);
137 #endif
138
139 class QProcessEnvironmentPrivate: public QSharedData
140 {
141 public:
142 typedef QProcEnvKey Key;
143 typedef QProcEnvValue Value;
144 #ifdef Q_OS_WIN
prepareName(const QString & name)145 inline Key prepareName(const QString &name) const { return Key(name); }
nameToString(const Key & name)146 inline QString nameToString(const Key &name) const { return name; }
prepareValue(const QString & value)147 inline Value prepareValue(const QString &value) const { return value; }
valueToString(const Value & value)148 inline QString valueToString(const Value &value) const { return value; }
149 #else
150 struct NameMapMutexLocker : public QMutexLocker
151 {
NameMapMutexLockerNameMapMutexLocker152 NameMapMutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->nameMapMutex) {}
153 };
154 struct OrderedNameMapMutexLocker : public QOrderedMutexLocker
155 {
OrderedNameMapMutexLockerOrderedNameMapMutexLocker156 OrderedNameMapMutexLocker(const QProcessEnvironmentPrivate *d1,
157 const QProcessEnvironmentPrivate *d2)
158 : QOrderedMutexLocker(&d1->nameMapMutex, &d2->nameMapMutex)
159 {}
160 };
161
prepareName(const QString & name)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 }
nameToString(const Key & name)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 }
prepareValue(const QString & value)179 inline Value prepareValue(const QString &value) const { return Value(value); }
valueToString(const Value & value)180 inline QString valueToString(const Value &value) const { return value.string(); }
181
QProcessEnvironmentPrivate()182 QProcessEnvironmentPrivate() : QSharedData() {}
QProcessEnvironmentPrivate(const QProcessEnvironmentPrivate & other)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
detach()213 template<> 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
227 class QProcessPrivate : public QIODevicePrivate
228 {
229 public:
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
ChannelChannel241 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
pipeToChannel260 void pipeTo(QProcessPrivate *other)
261 {
262 clear();
263 process = other;
264 type = PipeSource;
265 }
266
pipeFromChannel267 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
382 QT_END_NAMESPACE
383
384 #endif // QPROCESS_P_H
385