installer-framework/src/libs/installer/qprocesswrapper.cpp

431 lines
14 KiB
C++
Raw Normal View History

/**************************************************************************
**
** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
**************************************************************************/
#include "qprocesswrapper.h"
#include "fsengineclient.h"
#include "templates.cpp"
#include <QtCore/QThread>
#include <QtNetwork/QTcpSocket>
// -- QProcessWrapper::Private
class QProcessWrapper::Private
{
public:
2011-05-16 21:15:04 +02:00
Private(QProcessWrapper *qq)
: q(qq)
, ignoreTimer(false)
, socket(0)
{}
bool createSocket()
{
if (!FSEngineClientHandler::instance().isActive())
return false;
if (socket != 0 && socket->state() == static_cast<int>(QAbstractSocket::ConnectedState))
return true;
2011-05-16 21:15:04 +02:00
if (socket != 0)
delete socket;
socket = new QTcpSocket;
if (!FSEngineClientHandler::instance().connect(socket))
return false;
2011-05-16 21:15:04 +02:00
stream.setDevice(socket);
stream.setVersion(QDataStream::Qt_4_2);
2011-05-16 21:15:04 +02:00
stream << QString::fromLatin1("createQProcess");
socket->flush();
2011-05-16 21:15:04 +02:00
stream.device()->waitForReadyRead(-1);
quint32 test;
stream >> test;
stream.device()->readAll();
2011-05-16 21:15:04 +02:00
q->startTimer(250);
return true;
}
class TimerBlocker
{
public:
2011-05-16 21:15:04 +02:00
explicit TimerBlocker(const QProcessWrapper *wrapper)
: w(const_cast<QProcessWrapper *>(wrapper))
{
w->d->ignoreTimer = true;
}
2011-05-16 21:15:04 +02:00
~TimerBlocker()
{
w->d->ignoreTimer = false;
}
private:
2011-05-16 21:15:04 +02:00
QProcessWrapper *const w;
};
private:
2011-05-16 21:15:04 +02:00
QProcessWrapper *const q;
public:
bool ignoreTimer;
QProcess process;
2011-05-16 21:15:04 +02:00
mutable QTcpSocket *socket;
mutable QDataStream stream;
};
// -- QProcessWrapper
2011-05-16 21:15:04 +02:00
QProcessWrapper::QProcessWrapper(QObject *parent)
: QObject(parent)
, d(new Private(this))
{
connect(&d->process, SIGNAL(bytesWritten(qint64)), SIGNAL(bytesWritten(qint64)));
connect(&d->process, SIGNAL(aboutToClose()), SIGNAL(aboutToClose()));
connect(&d->process, SIGNAL(readChannelFinished()), SIGNAL(readChannelFinished()));
connect(&d->process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError)));
connect(&d->process, SIGNAL(readyReadStandardOutput()), SIGNAL(readyReadStandardOutput()));
connect(&d->process, SIGNAL(readyReadStandardError()), SIGNAL(readyReadStandardError()));
connect(&d->process, SIGNAL(finished(int)), SIGNAL(finished(int)));
connect(&d->process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
connect(&d->process, SIGNAL(readyRead()), SIGNAL(readyRead()));
connect(&d->process, SIGNAL(started()), SIGNAL(started()));
connect(&d->process, SIGNAL(stateChanged(QProcess::ProcessState)), SIGNAL(stateChanged(QProcess::ProcessState)));
}
QProcessWrapper::~QProcessWrapper()
{
2011-05-16 21:15:04 +02:00
if (d->socket != 0) {
d->stream << QString::fromLatin1("destroyQProcess");
d->socket->flush();
quint32 result;
d->stream >> result;
2011-05-16 21:15:04 +02:00
if (QThread::currentThread() == d->socket->thread()) {
d->socket->close();
delete d->socket;
2011-05-16 21:15:04 +02:00
} else {
d->socket->deleteLater();
}
}
delete d;
}
2011-05-16 21:15:04 +02:00
void QProcessWrapper::timerEvent(QTimerEvent *event)
{
2011-05-16 21:15:04 +02:00
Q_UNUSED(event)
2011-05-16 21:15:04 +02:00
if (d->ignoreTimer)
return;
2011-05-16 21:15:04 +02:00
QList<QVariant> receivedSignals;
{
2011-05-16 21:15:04 +02:00
const Private::TimerBlocker blocker(this);
2011-05-16 21:15:04 +02:00
d->stream << QString::fromLatin1("getQProcessSignals");
d->socket->flush();
2011-05-16 21:15:04 +02:00
d->stream.device()->waitForReadyRead(-1);
quint32 test;
d->stream >> test;
d->stream >> receivedSignals;
d->stream.device()->readAll();
}
2011-05-16 21:15:04 +02:00
while (!receivedSignals.isEmpty()) {
const QString name = receivedSignals.takeFirst().toString();
2011-05-16 21:15:04 +02:00
if (name == QLatin1String("started")) {
emit started();
2011-05-16 21:15:04 +02:00
} else if (name == QLatin1String("readyRead")) {
emit readyRead();
2011-05-16 21:15:04 +02:00
} else if (name == QLatin1String("stateChanged")) {
const QProcess::ProcessState newState =
static_cast<QProcess::ProcessState> (receivedSignals.takeFirst().toInt());
emit stateChanged(newState);
} else if (name == QLatin1String("finished")) {
const int exitCode = receivedSignals.takeFirst().toInt();
2011-05-16 21:15:04 +02:00
const QProcess::ExitStatus exitStatus =
static_cast<QProcess::ExitStatus> (receivedSignals.takeFirst().toInt());
emit finished(exitCode);
emit finished(exitCode, exitStatus);
}
}
}
2011-05-16 21:15:04 +02:00
bool startDetached(const QString &program, const QStringList &args, const QString &workingDirectory,
qint64 *pid);
2011-05-16 21:15:04 +02:00
bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments,
const QString &workingDirectory, qint64 *pid)
{
QProcessWrapper w;
2011-05-16 21:15:04 +02:00
if (w.d->createSocket()) {
const QPair<bool, qint64> result = callRemoteMethod<QPair<bool, qint64> >(w.d->stream,
QLatin1String("QProcess::startDetached"), program, arguments, workingDirectory);
if (pid != 0)
*pid = result.second;
return result.first;
}
2011-05-16 21:15:04 +02:00
return ::startDetached(program, arguments, workingDirectory, pid);
}
2011-05-16 21:15:04 +02:00
bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments)
{
2011-05-16 21:15:04 +02:00
return startDetached(program, arguments, QDir::currentPath());
}
2011-05-16 21:15:04 +02:00
bool QProcessWrapper::startDetached(const QString &program)
{
2011-05-16 21:15:04 +02:00
return startDetached(program, QStringList());
}
2011-05-16 21:15:04 +02:00
void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode mode)
{
const Private::TimerBlocker blocker(this);
if (d->createSocket()) {
callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setProcessChannelMode"),
static_cast<QProcess::ProcessChannelMode>(mode));
} else {
d->process.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(mode));
}
}
/*!
Cancels the process. This methods tries to terminate the process
gracefully by calling QProcess::terminate. After 10 seconds, the process gets killed.
*/
void QProcessWrapper::cancel()
{
2011-05-16 21:15:04 +02:00
if (state() == QProcessWrapper::Running)
terminate();
2011-05-16 21:15:04 +02:00
if (!waitForFinished(10000))
kill();
}
2011-05-16 21:15:04 +02:00
void QProcessWrapper::setReadChannel(QProcessWrapper::ProcessChannel chan)
{
2011-05-16 21:15:04 +02:00
const Private::TimerBlocker blocker(this);
if (d->createSocket()) {
callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setReadChannel"),
static_cast<QProcess::ProcessChannel>(chan));
} else {
d->process.setReadChannel(static_cast<QProcess::ProcessChannel>(chan));
}
}
2011-05-16 21:15:04 +02:00
bool QProcessWrapper::waitForFinished(int msecs)
{
2011-05-16 21:15:04 +02:00
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<bool>(d->stream, QLatin1String("QProcess::waitForFinished"), msecs);
return d->process.waitForFinished(msecs);
}
2011-05-16 21:15:04 +02:00
bool QProcessWrapper::waitForStarted(int msecs)
{
2011-05-16 21:15:04 +02:00
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<bool>(d->stream, QLatin1String("QProcess::waitForStarted"), msecs);
return d->process.waitForStarted(msecs);
}
2011-05-16 21:15:04 +02:00
qint64 QProcessWrapper::write(const QByteArray &data)
{
2011-05-16 21:15:04 +02:00
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<qint64>(d->stream, QLatin1String("QProcess::write"), data);
return d->process.write(data);
}
void QProcessWrapper::closeWriteChannel()
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
callRemoteVoidMethod<void>(d->stream, QLatin1String("QProcess::closeWriteChannel"));
else
d->process.closeWriteChannel();
}
int QProcessWrapper::exitCode() const
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<int>(d->stream, QLatin1String("QProcess::exitCode"));
return static_cast<int>(d->process.exitCode());
}
QProcessWrapper::ExitStatus QProcessWrapper::exitStatus() const
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<QProcessWrapper::ExitStatus>(d->stream, QLatin1String("QProcess::exitStatus"));
return static_cast<QProcessWrapper::ExitStatus>(d->process.exitStatus());
}
void QProcessWrapper::kill()
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
callRemoteVoidMethod<void>(d->stream, QLatin1String("QProcess::kill"));
else
d->process.kill();
}
QByteArray QProcessWrapper::readAll()
2011-05-17 09:55:48 +02:00
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<QByteArray>(d->stream, QLatin1String("QProcess::readAll"));
return d->process.readAll();
}
QByteArray QProcessWrapper::readAllStandardOutput()
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<QByteArray>(d->stream, QLatin1String("QProcess::readAllStandardOutput"));
return d->process.readAllStandardOutput();
}
void QProcessWrapper::start(const QString &param1, const QStringList &param2, QIODevice::OpenMode param3)
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
callRemoteVoidMethod(d->stream, QLatin1String("QProcess::start"), param1, param2, param3);
else
d->process.start(param1, param2, param3);
}
void QProcessWrapper::start(const QString &param1)
{
const Private::TimerBlocker blocker(this);
2011-05-17 09:55:48 +02:00
if (d->createSocket())
callRemoteVoidMethod(d->stream, QLatin1String("QProcess::start"), param1);
else
d->process.start(param1);
}
QProcessWrapper::ProcessState QProcessWrapper::state() const
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<QProcessWrapper::ProcessState>(d->stream, QLatin1String("QProcess::state"));
return static_cast<QProcessWrapper::ProcessState>(d->process.state());
}
void QProcessWrapper::terminate()
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
callRemoteVoidMethod<void>(d->stream, QLatin1String("QProcess::terminate"));
else
d->process.terminate();
}
QProcessWrapper::ProcessChannel QProcessWrapper::readChannel() const
2011-05-17 09:55:48 +02:00
{
const Private::TimerBlocker blocker(this);
if (d->createSocket()) {
return callRemoteMethod<QProcessWrapper::ProcessChannel>(d->stream,
QLatin1String("QProcess::readChannel"));
}
return static_cast<QProcessWrapper::ProcessChannel>(d->process.readChannel());
}
QProcessWrapper::ProcessChannelMode QProcessWrapper::processChannelMode() const
{
const Private::TimerBlocker blocker(this);
if (d->createSocket()) {
return callRemoteMethod<QProcessWrapper::ProcessChannelMode>(d->stream,
QLatin1String("QProcess::processChannelMode"));
}
return static_cast<QProcessWrapper::ProcessChannelMode>(d->process.processChannelMode());
}
QString QProcessWrapper::workingDirectory() const
2011-05-17 09:55:48 +02:00
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<QString>(d->stream, QLatin1String("QProcess::workingDirectory"));
return static_cast<QString>(d->process.workingDirectory());
}
QString QProcessWrapper::errorString() const
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
return callRemoteMethod<QString>(d->stream, QLatin1String("QProcess::errorString"));
return static_cast<QString>(d->process.errorString());
}
void QProcessWrapper::setEnvironment(const QStringList &param1)
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setEnvironment"), param1);
else
d->process.setEnvironment(param1);
2011-05-16 21:15:04 +02:00
}
#ifdef Q_OS_WIN
void QProcessWrapper::setNativeArguments(const QString &param1)
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setNativeArguments"), param1);
else
d->process.setNativeArguments(param1);
}
#endif
void QProcessWrapper::setWorkingDirectory(const QString &param1)
2011-05-17 09:55:48 +02:00
{
const Private::TimerBlocker blocker(this);
if (d->createSocket())
callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setWorkingDirectory"), param1);
else
d->process.setWorkingDirectory(param1);
}