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

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