Newer
Older
/**************************************************************************
**
** 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 "maemoremotemounter.h"
#include "maemotoolchain.h"
#include <coreplugin/ssh/sftpchannel.h>
#include <coreplugin/ssh/sshconnection.h>
#include <coreplugin/ssh/sshremoteprocess.h>
#include <utils/qtcassert.h>
#include <QtCore/QTimer>
#define ASSERT_STATE(state) assertState(state, Q_FUNC_INFO)
using namespace Core;
namespace Qt4ProjectManager {
namespace Internal {
MaemoRemoteMounter::MaemoRemoteMounter(QObject *parent)
: QObject(parent), m_utfsServerTimer(new QTimer(this)),
m_uploadJobId(SftpInvalidJob), m_state(Inactive)
connect(m_utfsServerTimer, SIGNAL(timeout()), this,
SLOT(handleUtfsServerTimeout()));
MaemoRemoteMounter::~MaemoRemoteMounter()
{
killAllUtfsServers();
}
void MaemoRemoteMounter::setConnection(const Core::SshConnection::Ptr &connection)
{
ASSERT_STATE(Inactive);
m_connection = connection;
}
bool MaemoRemoteMounter::addMountSpecification(const MaemoMountSpecification &mountSpec,
ASSERT_STATE(Inactive);
if (mountSpec.isValid()) {
if (!m_portList.hasMore())
return false;
else
m_mountSpecs << MountInfo(mountSpec, m_portList.getNext(), mountAsRoot);
}
return true;
ASSERT_STATE(Inactive);
Q_ASSERT(m_utfsServers.isEmpty());
Q_ASSERT(m_connection);
if (!m_toolChain->allowsRemoteMounts())
m_mountSpecs.clear();
if (m_mountSpecs.isEmpty()) {
setState(Inactive);
emit reportProgress(tr("No directories to mount"));
}
void MaemoRemoteMounter::unmount()
{
ASSERT_STATE(Inactive);
if (m_mountSpecs.isEmpty()) {
emit reportProgress(tr("No directories to unmount"));
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).mountSpec.remoteMountPoint);
emit reportProgress(tr("Unmounting remote mount points..."));
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, SLOT(handleUmountStderr(QByteArray)));
setState(Unmounting);
m_unmountProcess->start();
}
void MaemoRemoteMounter::handleUnmountProcessFinished(int exitStatus)
{
ASSERT_STATE(QList<State>() << Unmounting << Inactive);
if (m_state == Inactive)
setState(Inactive);
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.");
}
emit reportProgress(tr("Finished unmounting."));
} else {
if (!m_umountStderr.isEmpty()) {
errorMsg += tr("\nstderr was: '%1'")
.arg(QString::fromUtf8(m_umountStderr));
}
emit error(errorMsg);
}
void MaemoRemoteMounter::stop()
{
setState(Inactive);
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()
{
emit reportProgress(tr("Setting up SFTP connection..."));
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();
setState(UploaderInitializing);
}
void MaemoRemoteMounter::handleUploaderInitializationFailed(const QString &reason)
{
ASSERT_STATE(QList<State>() << UploaderInitializing << Inactive);
if (m_state == UploaderInitializing) {
emit error(tr("Failed to establish SFTP connection: %1").arg(reason));
setState(Inactive);
}
void MaemoRemoteMounter::handleUploaderInitialized()
{
ASSERT_STATE(QList<State>() << UploaderInitializing << Inactive);
if (m_state == Inactive)
emit reportProgress(tr("Uploading UTFS client..."));
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) {
setState(Inactive);
emit error(tr("Could not upload UTFS client (%1).").arg(localFile));
} else {
setState(UploadRunning);
}
void MaemoRemoteMounter::handleUploadFinished(Core::SftpJobId jobId,
const QString &errorMsg)
{
ASSERT_STATE(QList<State>() << UploadRunning << Inactive);
if (m_state == Inactive)
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));
setState(Inactive);
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 MountInfo &mountInfo = m_mountSpecs.at(i);
const MaemoMountSpecification &mountSpec = mountInfo.mountSpec;
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);
= QString::fromLocal8Bit("%1 -l %2 -r %2 -b %2 %4 -o nonempty")
.arg(utfsClientOnDevice()).arg(mountInfo.remotePort)
.arg(mountSpec.remoteMountPoint);
if (mountInfo.mountAsRoot)
utfsClient.prepend(MaemoGlobal::remoteSudo() + QLatin1Char(' '));
QLatin1String seqOp("; ");
remoteCall += seqOp + MaemoGlobal::remoteSourceProfilesCommand()
+ seqOp + mkdir + andOp + chmod + andOp + utfsClient;
emit reportProgress(tr("Starting remote UTFS clients..."));
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, SLOT(handleUtfsClientStderr(QByteArray)));
m_mountProcess->start();
setState(UtfsClientsStarting);
void MaemoRemoteMounter::handleUtfsClientsStarted()
{
ASSERT_STATE(QList<State>() << UtfsClientsStarting << Inactive);
if (m_state == UtfsClientsStarting) {
setState(UtfsClientsStarted);
QTimer::singleShot(250, this, SLOT(startUtfsServers()));
void MaemoRemoteMounter::handleUtfsClientsFinished(int exitStatus)
{
ASSERT_STATE(QList<State>() << UtfsClientsStarting << UtfsClientsStarted
<< UtfsServersStarted << Inactive);
if (m_state == Inactive)
setState(Inactive);
if (exitStatus == SshRemoteProcess::ExitedNormally
&& m_mountProcess->exitCode() == 0) {
emit reportProgress(tr("Mount operation succeeded."));
} else {
QString errMsg = tr("Failure running UTFS client: %1")
.arg(m_mountProcess->errorString());
if (!m_utfsClientStderr.isEmpty())
errMsg += tr("\nstderr was: '%1'")
.arg(QString::fromUtf8(m_utfsClientStderr));
emit error(errMsg);
}
}
void MaemoRemoteMounter::startUtfsServers()
{
ASSERT_STATE(QList<State>() << UtfsClientsStarted << Inactive);
if (m_state == Inactive)
emit reportProgress(tr("Starting UTFS servers..."));
m_utfsServerTimer->start(30000);
for (int i = 0; i < m_mountSpecs.count(); ++i) {
const MountInfo &mountInfo = m_mountSpecs.at(i);
const MaemoMountSpecification &mountSpec = mountInfo.mountSpec;
const ProcPtr utfsServerProc(new QProcess);
const QString port = QString::number(mountInfo.remotePort);
const QString localSecretOpt = QLatin1String("-l");
const QString remoteSecretOpt = QLatin1String("-r");
const QStringList utfsServerArgs = QStringList() << localSecretOpt
<< port << remoteSecretOpt << port << QLatin1String("-c")
<< (m_connection->connectionParameters().host + QLatin1Char(':') + port)
<< mountSpec.localDir;
connect(utfsServerProc.data(),
SIGNAL(finished(int,QProcess::ExitStatus)), this,
SLOT(handleUtfsServerFinished(int,QProcess::ExitStatus)));
connect(utfsServerProc.data(), SIGNAL(error(QProcess::ProcessError)),
this, SLOT(handleUtfsServerError(QProcess::ProcessError)));
connect(utfsServerProc.data(), SIGNAL(readyReadStandardError()), this,
SLOT(handleUtfsServerStderr()));
m_utfsServers << utfsServerProc;
utfsServerProc->start(utfsServer(), utfsServerArgs);
setState(UtfsServersStarted);
void MaemoRemoteMounter::handleUtfsServerStderr()
{
if (m_state != Inactive) {
QProcess * const proc = static_cast<QProcess *>(sender());
const QByteArray &output = proc->readAllStandardError();
emit debugOutput(QString::fromLocal8Bit(output));
}
}
void MaemoRemoteMounter::handleUtfsServerError(QProcess::ProcessError)
if (m_state == Inactive || m_utfsServers.isEmpty())
QProcess * const proc = static_cast<QProcess *>(sender());
QString errorString = proc->errorString();
const QByteArray &errorOutput = proc->readAllStandardError();
if (!errorOutput.isEmpty()) {
errorString += tr("\nstderr was: %1")
.arg(QString::fromLocal8Bit(errorOutput));
killUtfsClients();
emit error(tr("Error running UTFS server: %1").arg(errorString));
setState(Inactive);
void MaemoRemoteMounter::handleUtfsServerFinished(int /* exitCode */,
QProcess::ExitStatus exitStatus)
{
if (m_state != Inactive && exitStatus != QProcess::NormalExit)
handleUtfsServerError(static_cast<QProcess *>(sender())->error());
void MaemoRemoteMounter::handleUtfsClientStderr(const QByteArray &output)
{
if (m_state != Inactive)
m_utfsClientStderr += output;
}
void MaemoRemoteMounter::handleUmountStderr(const QByteArray &output)
if (m_state != Inactive)
m_umountStderr += output;
}
QString MaemoRemoteMounter::utfsClientOnDevice() const
{
return MaemoGlobal::homeDirOnDevice(m_connection->connectionParameters().uname)
+ QLatin1String("/utfs-client");
}
QString MaemoRemoteMounter::utfsServer() const
{
return m_toolChain->maddeRoot() + QLatin1String("/madlib/utfs-server");
}
void MaemoRemoteMounter::killAllUtfsServers()
{
foreach (const ProcPtr &proc, m_utfsServers)
killUtfsServer(proc.data());
m_utfsServers.clear();
}
void MaemoRemoteMounter::killUtfsServer(QProcess *proc)
{
disconnect(proc, 0, this, 0);
proc->terminate();
proc->waitForFinished(1000);
proc->kill();
}
void MaemoRemoteMounter::handleUtfsServerTimeout()
{
ASSERT_STATE(QList<State>() << UtfsServersStarted << Inactive);
if (m_state == Inactive)
return;
killAllUtfsServers();
killUtfsClients();
emit error(tr("Timeout waiting for UTFS servers to connect."));
setState(Inactive);
}
void MaemoRemoteMounter::assertState(State expectedState, const char *func)
{
assertState(QList<State>() << expectedState, func);
}
void MaemoRemoteMounter::assertState(const QList<State> &expectedStates,
const char *func)
{
QTC_ASSERT(expectedStates.contains(m_state),
qDebug("Unexpected state %d at %s.", m_state, func))
void MaemoRemoteMounter::setState(State newState)
{
if (newState == Inactive)
m_utfsServerTimer->stop();
m_state = newState;
}
void MaemoRemoteMounter::killUtfsClients()
{
const SshRemoteProcess::Ptr utfsClientKiller
= m_connection->createRemoteProcess("pkill utfs-client; sleep 1; "
"pkill -9 utfs-client");
utfsClientKiller->start();
}
} // namespace Internal
} // namespace Qt4ProjectManager