Commit 0b5e36d6 authored by ck's avatar ck
Browse files

Maemo: Encapsulate remote mount functionality in its own class.

parent 823f0fd1
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "maemoglobal.h"
#include "maemoremotemounter.h"
#include "maemotoolchain.h"
#include <coreplugin/ssh/sftpchannel.h>
#include <coreplugin/ssh/sshconnection.h>
#include <coreplugin/ssh/sshremoteprocess.h>
#include <QtCore/QProcess>
using namespace Core;
namespace Qt4ProjectManager {
namespace Internal {
MaemoRemoteMounter::MaemoRemoteMounter(QObject *parent,
const MaemoToolChain *toolchain)
: QObject(parent), m_toolChain(toolchain), m_uploadJobId(SftpInvalidJob),
m_stop(false)
{
}
MaemoRemoteMounter::~MaemoRemoteMounter() {}
void MaemoRemoteMounter::setConnection(const Core::SshConnection::Ptr &connection)
{
m_connection = connection;
}
void MaemoRemoteMounter::addMountSpecification(const MaemoMountSpecification &mountSpec)
{
if (mountSpec.isValid())
m_mountSpecs << mountSpec;
}
void MaemoRemoteMounter::mount(const MaemoDeviceConfig &devConfig)
{
m_devConfig = devConfig;
m_stop = false;
Q_ASSERT(m_utfsServers.isEmpty());
if (m_mountSpecs.isEmpty())
emit mounted();
else
deployUtfsClient();
}
void MaemoRemoteMounter::unmount()
{
m_stop = false;
if (m_mountSpecs.isEmpty()) {
emit unmounted();
return;
}
QString remoteCall;
for (int i = 0; i < m_mountSpecs.count(); ++i) {
remoteCall += QString::fromLocal8Bit("%1 umount %2;")
.arg(MaemoGlobal::remoteSudo(),
m_mountSpecs.at(i).remoteMountPoint);
}
m_unmountProcess = m_connection->createRemoteProcess(remoteCall.toUtf8());
connect(m_unmountProcess.data(), SIGNAL(closed(int)), this,
SLOT(handleUnmountProcessFinished(int)));
connect(m_unmountProcess.data(), SIGNAL(errorOutputAvailable(QByteArray)),
this, SIGNAL(remoteErrorOutput(QByteArray)));
m_unmountProcess->start();
}
void MaemoRemoteMounter::handleUnmountProcessFinished(int exitStatus)
{
if (m_stop)
return;
QString errorMsg;
switch (exitStatus) {
case SshRemoteProcess::FailedToStart:
errorMsg = tr("Could not execute unmount request.");
break;
case SshRemoteProcess::KilledBySignal:
errorMsg = tr("Failure unmounting: %1")
.arg(m_unmountProcess->errorString());
break;
case SshRemoteProcess::ExitedNormally:
break;
default:
Q_ASSERT_X(false, Q_FUNC_INFO,
"Impossible SshRemoteProcess exit status.");
}
foreach (const ProcPtr &utfsServer, m_utfsServers) {
utfsServer->terminate();
utfsServer->waitForFinished(1000);
utfsServer->kill();
}
m_mountSpecs.clear();
if (errorMsg.isEmpty())
emit unmounted();
else
emit error(errorMsg);
}
void MaemoRemoteMounter::stop()
{
m_stop = true;
if (m_utfsClientUploader) {
disconnect(m_utfsClientUploader.data(), 0, this, 0);
m_utfsClientUploader->closeChannel();
}
if (m_mountProcess) {
disconnect(m_mountProcess.data(), 0, this, 0);
m_mountProcess->closeChannel();
}
if (m_unmountProcess) {
disconnect(m_unmountProcess.data(), 0, this, 0);
m_unmountProcess->closeChannel();
}
}
void MaemoRemoteMounter::deployUtfsClient()
{
m_utfsClientUploader = m_connection->createSftpChannel();
connect(m_utfsClientUploader.data(), SIGNAL(initialized()), this,
SLOT(handleUploaderInitialized()));
connect(m_utfsClientUploader.data(), SIGNAL(initializationFailed(QString)),
this, SLOT(handleUploaderInitializationFailed(QString)));
m_utfsClientUploader->initialize();
}
void MaemoRemoteMounter::handleUploaderInitializationFailed(const QString &reason)
{
if (m_stop)
return;
emit error(tr("Failed to establish SFTP connection: %1").arg(reason));
}
void MaemoRemoteMounter::handleUploaderInitialized()
{
if (m_stop)
return;
connect(m_utfsClientUploader.data(),
SIGNAL(finished(Core::SftpJobId, QString)), this,
SLOT(handleUploadFinished(Core::SftpJobId, QString)));
const QString localFile
= m_toolChain->maddeRoot() + QLatin1String("/madlib/armel/utfs-client");
m_uploadJobId
= m_utfsClientUploader->uploadFile(localFile, utfsClientOnDevice(),
SftpOverwriteExisting);
if (m_uploadJobId == SftpInvalidJob)
emit error(tr("Could not upload UTFS client (%1).").arg(localFile));
}
void MaemoRemoteMounter::handleUploadFinished(Core::SftpJobId jobId,
const QString &errorMsg)
{
if (m_stop)
return;
if (jobId != m_uploadJobId) {
qWarning("Warning: unknown upload job %d finished.", jobId);
return;
}
m_uploadJobId = SftpInvalidJob;
if (!errorMsg.isEmpty()) {
emit error(tr("Could not upload UTFS client: %1").arg(errorMsg));
return;
}
startUtfsClients();
}
void MaemoRemoteMounter::startUtfsClients()
{
const QString chmodFuse
= MaemoGlobal::remoteSudo() + QLatin1String(" chmod a+r+w /dev/fuse");
const QString chmodUtfsClient
= QLatin1String("chmod a+x ") + utfsClientOnDevice();
const QLatin1String andOp(" && ");
QString remoteCall = chmodFuse + andOp + chmodUtfsClient;
for (int i = 0; i < m_mountSpecs.count(); ++i) {
const MaemoMountSpecification &mountSpec = m_mountSpecs.at(i);
const QString mkdir = QString::fromLocal8Bit("%1 mkdir -p %2")
.arg(MaemoGlobal::remoteSudo(), mountSpec.remoteMountPoint);
const QString chmod = QString::fromLocal8Bit("%1 chmod a+r+w+x %2")
.arg(MaemoGlobal::remoteSudo(), mountSpec.remoteMountPoint);
const QString utfsClient
= QString::fromLocal8Bit("%1 -l %2 -r %2 -b %2 %4")
.arg(utfsClientOnDevice()).arg(mountSpec.remotePort)
.arg(mountSpec.remoteMountPoint);
remoteCall += andOp + mkdir + andOp + chmod + andOp + utfsClient;
}
m_mountProcess = m_connection->createRemoteProcess(remoteCall.toUtf8());
connect(m_mountProcess.data(), SIGNAL(started()), this,
SLOT(handleUtfsClientsStarted()));
connect(m_mountProcess.data(), SIGNAL(closed(int)), this,
SLOT(handleUtfsClientsFinished(int)));
connect(m_mountProcess.data(), SIGNAL(errorOutputAvailable(QByteArray)),
this, SIGNAL(remoteErrorOutput(QByteArray)));
m_mountProcess->start();
}
void MaemoRemoteMounter::handleUtfsClientsStarted()
{
if (!m_stop)
startUtfsServers();
}
void MaemoRemoteMounter::handleUtfsClientsFinished(int exitStatus)
{
if (m_stop)
return;
switch (exitStatus) {
case SshRemoteProcess::FailedToStart:
emit error(tr("Could not execute mount request."));
break;
case SshRemoteProcess::KilledBySignal:
emit error(tr("Failure running UTFS client: %1")
.arg(m_mountProcess->errorString()));
break;
case SshRemoteProcess::ExitedNormally:
if (m_mountProcess->exitCode() != 0)
emit error(tr("Could not execute mount request."));
break;
default:
Q_ASSERT_X(false, Q_FUNC_INFO,
"Impossible SshRemoteProcess exit status.");
}
}
void MaemoRemoteMounter::startUtfsServers()
{
for (int i = 0; i < m_mountSpecs.count(); ++i) {
const MaemoMountSpecification &mountSpec = m_mountSpecs.at(i);
const ProcPtr utfsServerProc(new QProcess);
connect(utfsServerProc.data(), SIGNAL(readyReadStandardError()), this,
SLOT(handleUtfsServerErrorOutput()));
const QString port = QString::number(mountSpec.remotePort);
const QString localSecretOpt = QLatin1String("-l");
const QString remoteSecretOpt = QLatin1String("-r");
const QStringList utfsServerArgs = QStringList() << localSecretOpt
<< port << remoteSecretOpt << port << QLatin1String("-c")
<< (m_devConfig.server.host + QLatin1Char(':') + port)
<< mountSpec.localDir;
utfsServerProc->start(utfsServer(), utfsServerArgs);
if (!utfsServerProc->waitForStarted()) {
emit error(tr("Could not start UTFS server: %1")
.arg(utfsServerProc->errorString()));
return;
}
m_utfsServers << utfsServerProc;
}
emit mounted();
}
void MaemoRemoteMounter::handleUtfsServerErrorOutput()
{
emit remoteErrorOutput(qobject_cast<QProcess *>(sender())->readAllStandardError());
}
QString MaemoRemoteMounter::utfsClientOnDevice() const
{
return MaemoGlobal::homeDirOnDevice(QLatin1String("developer"))
+ QLatin1String("/utfs-client");
}
QString MaemoRemoteMounter::utfsServer() const
{
return m_toolChain->maddeRoot() + QLatin1String("/madlib/utfs-server");
}
} // namespace Internal
} // namespace Qt4ProjectManager
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef MAEMOREMOTEMOUNTER_H
#define MAEMOREMOTEMOUNTER_H
#include "maemodeviceconfigurations.h"
#include "maemomountspecification.h"
#include <coreplugin/ssh/sftpdefs.h>
#include <QtCore/QList>
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <QtCore/QString>
QT_FORWARD_DECLARE_CLASS(QProcess);
namespace Core {
class SftpChannel;
class SshConnection;
class SshRemoteProcess;
}
namespace Qt4ProjectManager {
namespace Internal {
class MaemoToolChain;
class MaemoRemoteMounter : public QObject
{
Q_OBJECT
public:
MaemoRemoteMounter(QObject *parent, const MaemoToolChain *toolchain);
~MaemoRemoteMounter();
void addMountSpecification(const MaemoMountSpecification &mountSpec);
void mount(const MaemoDeviceConfig &devConfig);
void unmount();
void stop();
// Must be connected already.
void setConnection(const QSharedPointer<Core::SshConnection> &connection);
signals:
void mounted();
void unmounted();
void error(const QString &reason);
void remoteErrorOutput(const QByteArray &output);
private slots:
void handleUploaderInitialized();
void handleUploaderInitializationFailed(const QString &reason);
void handleUploadFinished(Core::SftpJobId jobId, const QString &error);
void handleUtfsClientsStarted();
void handleUtfsClientsFinished(int exitStatus);
void handleUtfsServerErrorOutput();
void handleUnmountProcessFinished(int exitStatus);
private:
void deployUtfsClient();
void startUtfsClients();
void startUtfsServers();
QString utfsClientOnDevice() const;
QString utfsServer() const;
const MaemoToolChain * const m_toolChain;
MaemoDeviceConfig m_devConfig;
QSharedPointer<Core::SshConnection> m_connection;
QList<MaemoMountSpecification> m_mountSpecs;
QSharedPointer<Core::SftpChannel> m_utfsClientUploader;
QSharedPointer<Core::SshRemoteProcess> m_mountProcess;
QSharedPointer<Core::SshRemoteProcess> m_unmountProcess;
Core::SftpJobId m_uploadJobId;
typedef QSharedPointer<QProcess> ProcPtr;
QList<ProcPtr> m_utfsServers;
bool m_stop;
};
} // namespace Internal
} // namespace Qt4ProjectManager
#endif // MAEMOREMOTEMOUNTER_H
......@@ -74,7 +74,6 @@ void MaemoRunControl::start()
} else {
emit appendMessage(this, tr("Preparing remote side ..."), false);
m_running = true;
m_stopped = false;
emit started();
disconnect(m_runner, 0, this, 0);
connect(m_runner, SIGNAL(error(QString)), this,
......@@ -95,12 +94,7 @@ void MaemoRunControl::start()
void MaemoRunControl::stop()
{
m_stopped = true;
if (m_runner) {
disconnect(m_runner, 0, this, 0);
m_runner->stop();
}
setFinished();
m_runner->stop();
}
void MaemoRunControl::handleSshError(const QString &error)
......@@ -122,9 +116,6 @@ void MaemoRunControl::startExecution()
void MaemoRunControl::handleRemoteProcessFinished(int exitCode)
{
if (m_stopped)
return;
emit appendMessage(this,
tr("Finished running remote process. Exit code was %1.").arg(exitCode),
false);
......@@ -155,6 +146,7 @@ void MaemoRunControl::handleError(const QString &errString)
void MaemoRunControl::setFinished()
{
disconnect(m_runner, 0, this, 0);
m_running = false;
emit finished();
}
......
......@@ -72,7 +72,6 @@ private:
MaemoRunConfiguration *m_runConfig; // TODO this pointer can be invalid
const MaemoDeviceConfig m_devConfig;
MaemoSshRunner * const m_runner;
bool m_stopped;
bool m_running;
};
......
......@@ -36,15 +36,14 @@
#include "maemodeviceconfigurations.h"
#include "maemoglobal.h"
#include "maemoremotemounter.h"
#include "maemoremotemountsmodel.h"
#include "maemorunconfiguration.h"
#include "maemotoolchain.h"
#include <coreplugin/ssh/sftpchannel.h>
#include <coreplugin/ssh/sshconnection.h>
#include <coreplugin/ssh/sshremoteprocess.h>
#include <QtCore/QFileInfo>
#include <QtCore/QProcess>
using namespace Core;
......@@ -54,8 +53,8 @@ namespace Internal {
MaemoSshRunner::MaemoSshRunner(QObject *parent,
MaemoRunConfiguration *runConfig, bool debugging)
: QObject(parent), m_runConfig(runConfig),
m_devConfig(runConfig->deviceConfig()),
m_uploadJobId(SftpInvalidJob),
m_mounter(new MaemoRemoteMounter(this, runConfig->toolchain())),
m_devConfig(runConfig->deviceConfig()), m_shuttingDown(false),
m_debugging(debugging)
{
m_procsToKill
......@@ -63,6 +62,12 @@ MaemoSshRunner::MaemoSshRunner(QObject *parent,
<< QLatin1String("utfs-client");
if (debugging)
m_procsToKill << QLatin1String("gdbserver");
connect(m_mounter, SIGNAL(mounted()), this, SLOT(handleMounted()));
connect(m_mounter, SIGNAL(unmounted()), this, SLOT(handleUnmounted()));
connect(m_mounter, SIGNAL(error(QString)), this,
SLOT(handleMounterError(QString)));
connect(m_mounter, SIGNAL(remoteErrorOutput(QByteArray)), this,
SIGNAL(remoteErrorOutput(QByteArray)));
}
MaemoSshRunner::~MaemoSshRunner() {}
......@@ -74,23 +79,14 @@ void MaemoSshRunner::setConnection(const QSharedPointer<Core::SshConnection> &co
void MaemoSshRunner::start()
{
m_mountSpecs.clear();
const MaemoRemoteMountsModel * const remoteMounts
= m_runConfig->remoteMounts();
for (int i = 0; i < remoteMounts->mountSpecificationCount(); ++i) {
const MaemoMountSpecification &mountSpec
= remoteMounts->mountSpecificationAt(i);
if (mountSpec.isValid())
m_mountSpecs << mountSpec;
}
if (m_debugging && m_runConfig->useRemoteGdb()) {
m_mountSpecs << MaemoMountSpecification(
m_runConfig->localDirToMountForRemoteGdb(),
MaemoGlobal::remoteProjectSourcesMountPoint(),
m_devConfig.debuggingPort);
// Should not happen.
if (m_shuttingDown) {
emit error(tr("Can't restart yet, haven't shut down properly."));
return;
}
m_stop = false;
m_exitStatus = -1;
if (m_connection)
disconnect(m_connection.data(), 0, this, 0);
const bool reUse = m_connection
......@@ -110,25 +106,15 @@ void MaemoSshRunner::start()
void MaemoSshRunner::stop()
{
if (m_shuttingDown)
return;
m_stop = true;
disconnect(m_connection.data(), 0, this, 0);
if (m_initialCleaner)
disconnect(m_initialCleaner.data(), 0, this, 0);
if (m_utfsClientUploader) {
disconnect(m_utfsClientUploader.data(), 0, this, 0);
m_utfsClientUploader->closeChannel();
}
if (m_mountProcess) {
disconnect(m_mountProcess.data(), 0, this, 0);
m_mountProcess->closeChannel();
}
if (m_debugging || m_runner) {
if (m_runner) {
disconnect(m_runner.data(), 0, this, 0);
m_runner->closeChannel();
}
cleanup(false);
}
m_mounter->stop();
if (m_cleaner)
disconnect(m_cleaner.data(), 0, this, 0);
cleanup(false);
}
void MaemoSshRunner::handleConnected()
......@@ -147,6 +133,7 @@ void MaemoSshRunner::handleConnectionFailure()
void MaemoSshRunner::cleanup(bool initialCleanup)
{
m_shuttingDown = !initialCleanup;
QString niceKill;
QString brutalKill;
foreach (const QString &proc, m_procsToKill) {
......@@ -154,192 +141,80 @@ void MaemoSshRunner::cleanup(bool initialCleanup)
brutalKill += QString::fromLocal8Bit("pkill -x -9 %1;").arg(proc);
}
QString remoteCall = niceKill + QLatin1String("sleep 1; ") + brutalKill;
for (int i = 0; i < m_mountSpecs.count(); ++i) {
remoteCall += QString::fromLocal8Bit("%1 umount %2;")
.arg(MaemoGlobal::remoteSudo(), m_mountSpecs.at(i).remoteMountPoint);
}
remoteCall.remove(remoteCall.count() - 1, 1); // Get rid of trailing semicolon.
SshRemoteProcess::Ptr proc
= m_connection->createRemoteProcess(remoteCall.toUtf8());
if (initialCleanup) {
m_initialCleaner = proc;
connect(m_initialCleaner.data(), SIGNAL(closed(int)), this,
SLOT(handleInitialCleanupFinished(int)));
}