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