1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #ifndef QPROCESS_P_H
43 #define QPROCESS_P_H
44
45 //
46 // W A R N I N G
47 // -------------
48 //
49 // This file is not part of the Qt API. It exists purely as an
50 // implementation detail. This header file may change from version to
51 // version without notice, or even be removed.
52 //
53 // We mean it.
54 //
55
56 #include "QtCore/qprocess.h"
57 #include "QtCore/qstringlist.h"
58 #include "QtCore/qhash.h"
59 #include "QtCore/qshareddata.h"
60 #include "private/qringbuffer_p.h"
61 #include "private/qiodevice_p.h"
62 #ifdef Q_OS_UNIX
63 #include <QtCore/private/qorderedmutexlocker_p.h>
64 #endif
65
66 #ifdef Q_OS_WIN
67 #include "QtCore/qt_windows.h"
68 typedef HANDLE Q_PIPE;
69 #define INVALID_Q_PIPE INVALID_HANDLE_VALUE
70 #else
71 typedef int Q_PIPE;
72 #define INVALID_Q_PIPE -1
73 #endif
74
75 #ifndef QT_NO_PROCESS
76
77 QT_BEGIN_NAMESPACE
78
79 class QSocketNotifier;
80 class QWindowsPipeWriter;
81 class QWinEventNotifier;
82 class QTimer;
83 #if defined(Q_OS_SYMBIAN)
84 class RProcess;
85 #endif
86
87 #ifdef Q_OS_WIN
88 class QProcEnvKey : public QString
89 {
90 public:
QProcEnvKey()91 QProcEnvKey() {}
QProcEnvKey(const QString & other)92 explicit QProcEnvKey(const QString &other) : QString(other) {}
QProcEnvKey(const QProcEnvKey & other)93 QProcEnvKey(const QProcEnvKey &other) : QString(other) {}
94 bool operator==(const QProcEnvKey &other) const { return !compare(other, Qt::CaseInsensitive); }
95 };
qHash(const QProcEnvKey & key)96 inline uint qHash(const QProcEnvKey &key) { return qHash(key.toCaseFolded()); }
97
98 typedef QString QProcEnvValue;
99 #else
100 class QProcEnvKey
101 {
102 public:
QProcEnvKey()103 QProcEnvKey() : hash(0) {}
QProcEnvKey(const QByteArray & other)104 explicit QProcEnvKey(const QByteArray &other) : key(other), hash(qHash(key)) {}
QProcEnvKey(const QProcEnvKey & other)105 QProcEnvKey(const QProcEnvKey &other) { *this = other; }
106 bool operator==(const QProcEnvKey &other) const { return key == other.key; }
107
108 QByteArray key;
109 uint hash;
110 };
qHash(const QProcEnvKey & key)111 inline uint qHash(const QProcEnvKey &key) { return key.hash; }
112
113 class QProcEnvValue
114 {
115 public:
QProcEnvValue()116 QProcEnvValue() {}
QProcEnvValue(const QProcEnvValue & other)117 QProcEnvValue(const QProcEnvValue &other) { *this = other; }
QProcEnvValue(const QString & value)118 explicit QProcEnvValue(const QString &value) : stringValue(value) {}
QProcEnvValue(const QByteArray & value)119 explicit QProcEnvValue(const QByteArray &value) : byteValue(value) {}
120 bool operator==(const QProcEnvValue &other) const
121 {
122 return byteValue.isEmpty() && other.byteValue.isEmpty()
123 ? stringValue == other.stringValue
124 : bytes() == other.bytes();
125 }
bytes()126 QByteArray bytes() const
127 {
128 if (byteValue.isEmpty() && !stringValue.isEmpty())
129 byteValue = stringValue.toLocal8Bit();
130 return byteValue;
131 }
string()132 QString string() const
133 {
134 if (stringValue.isEmpty() && !byteValue.isEmpty())
135 stringValue = QString::fromLocal8Bit(byteValue);
136 return stringValue;
137 }
138
139 mutable QByteArray byteValue;
140 mutable QString stringValue;
141 };
142 Q_DECLARE_TYPEINFO(QProcEnvValue, Q_MOVABLE_TYPE);
143 #endif
144 Q_DECLARE_TYPEINFO(QProcEnvKey, Q_MOVABLE_TYPE);
145
146 class QProcessEnvironmentPrivate: public QSharedData
147 {
148 public:
149 typedef QProcEnvKey Key;
150 typedef QProcEnvValue Value;
151 #ifdef Q_OS_WIN
prepareName(const QString & name)152 inline Key prepareName(const QString &name) const { return Key(name); }
nameToString(const Key & name)153 inline QString nameToString(const Key &name) const { return name; }
prepareValue(const QString & value)154 inline Value prepareValue(const QString &value) const { return value; }
valueToString(const Value & value)155 inline QString valueToString(const Value &value) const { return value; }
156 struct MutexLocker {
MutexLockerMutexLocker157 MutexLocker(const QProcessEnvironmentPrivate *) {}
158 };
159 struct OrderedMutexLocker {
OrderedMutexLockerOrderedMutexLocker160 OrderedMutexLocker(const QProcessEnvironmentPrivate *,
161 const QProcessEnvironmentPrivate *) {}
162 };
163 #else
prepareName(const QString & name)164 inline Key prepareName(const QString &name) const
165 {
166 Key &ent = nameMap[name];
167 if (ent.key.isEmpty())
168 ent = Key(name.toLocal8Bit());
169 return ent;
170 }
nameToString(const Key & name)171 inline QString nameToString(const Key &name) const
172 {
173 const QString sname = QString::fromLocal8Bit(name.key);
174 nameMap[sname] = name;
175 return sname;
176 }
prepareValue(const QString & value)177 inline Value prepareValue(const QString &value) const { return Value(value); }
valueToString(const Value & value)178 inline QString valueToString(const Value &value) const { return value.string(); }
179
180 struct MutexLocker : public QMutexLocker
181 {
MutexLockerMutexLocker182 MutexLocker(const QProcessEnvironmentPrivate *d) : QMutexLocker(&d->mutex) {}
183 };
184 struct OrderedMutexLocker : public QOrderedMutexLocker
185 {
OrderedMutexLockerOrderedMutexLocker186 OrderedMutexLocker(const QProcessEnvironmentPrivate *d1,
187 const QProcessEnvironmentPrivate *d2) :
188 QOrderedMutexLocker(&d1->mutex, &d2->mutex)
189 {}
190 };
191
QProcessEnvironmentPrivate()192 QProcessEnvironmentPrivate() : QSharedData() {}
QProcessEnvironmentPrivate(const QProcessEnvironmentPrivate & other)193 QProcessEnvironmentPrivate(const QProcessEnvironmentPrivate &other) :
194 QSharedData()
195 {
196 // This being locked ensures that the functions that only assign
197 // d pointers don't need explicit locking.
198 // We don't need to lock our own mutex, as this object is new and
199 // consequently not shared. For the same reason, non-const methods
200 // do not need a lock, as they detach objects (however, we need to
201 // ensure that they really detach before using prepareName()).
202 MutexLocker locker(&other);
203 hash = other.hash;
204 nameMap = other.nameMap;
205 // We need to detach our members, so that our mutex can protect them.
206 // As we are being detached, they likely would be detached a moment later anyway.
207 hash.detach();
208 nameMap.detach();
209 }
210 #endif
211
212 typedef QHash<Key, Value> Hash;
213 Hash hash;
214
215 #ifdef Q_OS_UNIX
216 typedef QHash<QString, Key> NameHash;
217 mutable NameHash nameMap;
218
219 mutable QMutex mutex;
220 #endif
221
222 static QProcessEnvironment fromList(const QStringList &list);
223 QStringList toList() const;
224 QStringList keys() const;
225 void insert(const QProcessEnvironmentPrivate &other);
226 };
227
detach()228 template<> Q_INLINE_TEMPLATE void QSharedDataPointer<QProcessEnvironmentPrivate>::detach()
229 {
230 if (d && d->ref == 1)
231 return;
232 QProcessEnvironmentPrivate *x = (d ? new QProcessEnvironmentPrivate(*d)
233 : new QProcessEnvironmentPrivate);
234 x->ref.ref();
235 if (d && !d->ref.deref())
236 delete d;
237 d = x;
238 }
239
240 class QProcessPrivate : public QIODevicePrivate
241 {
242 public:
243 Q_DECLARE_PUBLIC(QProcess)
244
245 struct Channel {
246 enum ProcessChannelType {
247 Normal = 0,
248 PipeSource = 1,
249 PipeSink = 2,
250 Redirect = 3
251 // if you add "= 4" here, increase the number of bits below
252 };
253
ChannelChannel254 Channel() : process(0), notifier(0), type(Normal), closed(false), append(false)
255 {
256 pipe[0] = INVALID_Q_PIPE;
257 pipe[1] = INVALID_Q_PIPE;
258 }
259
260 void clear();
261
262 Channel &operator=(const QString &fileName)
263 {
264 clear();
265 file = fileName;
266 type = fileName.isEmpty() ? Normal : Redirect;
267 return *this;
268 }
269
pipeToChannel270 void pipeTo(QProcessPrivate *other)
271 {
272 clear();
273 process = other;
274 type = PipeSource;
275 }
276
pipeFromChannel277 void pipeFrom(QProcessPrivate *other)
278 {
279 clear();
280 process = other;
281 type = PipeSink;
282 }
283
284 QString file;
285 QProcessPrivate *process;
286 QSocketNotifier *notifier;
287 Q_PIPE pipe[2];
288
289 unsigned type : 2;
290 bool closed : 1;
291 bool append : 1;
292 };
293
294 QProcessPrivate();
295 virtual ~QProcessPrivate();
296
297 // private slots
298 bool _q_canReadStandardOutput();
299 bool _q_canReadStandardError();
300 bool _q_canWrite();
301 bool _q_startupNotification();
302 bool _q_processDied();
303 void _q_notified();
304
305 QProcess::ProcessChannel processChannel;
306 QProcess::ProcessChannelMode processChannelMode;
307 QProcess::ProcessError processError;
308 QProcess::ProcessState processState;
309 QString workingDirectory;
310 Q_PID pid;
311 int sequenceNumber;
312
313 bool dying;
314 bool emittedReadyRead;
315 bool emittedBytesWritten;
316
317 Channel stdinChannel;
318 Channel stdoutChannel;
319 Channel stderrChannel;
320 bool createChannel(Channel &channel);
321 void closeWriteChannel();
322
323 QString program;
324 QStringList arguments;
325 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
326 QString nativeArguments;
327 #endif
328 QProcessEnvironment environment;
329
330 QRingBuffer outputReadBuffer;
331 QRingBuffer errorReadBuffer;
332 QRingBuffer writeBuffer;
333
334 Q_PIPE childStartedPipe[2];
335 Q_PIPE deathPipe[2];
336 void destroyPipe(Q_PIPE pipe[2]);
337
338 QSocketNotifier *startupSocketNotifier;
339 QSocketNotifier *deathNotifier;
340
341 // the wonderful windows notifier
342 QTimer *notifier;
343 QWindowsPipeWriter *pipeWriter;
344 QWinEventNotifier *processFinishedNotifier;
345
346 void startProcess();
347 #if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(Q_OS_QNX)
348 void execChild(const char *workingDirectory, char **path, char **argv, char **envp);
349 #elif defined(Q_OS_QNX)
350 pid_t spawnChild(const char *workingDirectory, char **argv, char **envp);
351 #endif
352 bool processStarted();
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 void flushPipeWriter();
361 qint64 pipeWriterBytesToWrite() const;
362 #endif
363
364 static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(),
365 qint64 *pid = 0);
366
367 int exitCode;
368 QProcess::ExitStatus exitStatus;
369 bool crashed;
370 #ifdef Q_OS_UNIX
371 int serial;
372 #endif
373
374 bool waitForStarted(int msecs = 30000);
375 bool waitForReadyRead(int msecs = 30000);
376 bool waitForBytesWritten(int msecs = 30000);
377 bool waitForFinished(int msecs = 30000);
378 bool waitForWrite(int msecs = 30000);
379
380 qint64 bytesAvailableFromStdout() const;
381 qint64 bytesAvailableFromStderr() const;
382 qint64 readFromStdout(char *data, qint64 maxlen);
383 qint64 readFromStderr(char *data, qint64 maxlen);
384 qint64 writeToStdin(const char *data, qint64 maxlen);
385
386 void cleanup();
387 #ifdef Q_OS_UNIX
388 static void initializeProcessManager();
389 #endif
390
391 #ifdef Q_OS_SYMBIAN
392 bool processLaunched;
393 RProcess* symbianProcess;
394 #endif
395 };
396
397 QT_END_NAMESPACE
398
399 #endif // QT_NO_PROCESS
400
401 #endif // QPROCESS_P_H
402