1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "maemoremotemounter.h"
35 #include "maemoglobal.h"
36 #include "maemousedportsgatherer.h"
37 #include "qt4maemotarget.h"
39 #include <utils/ssh/sshconnection.h>
40 #include <utils/ssh/sshremoteprocess.h>
41 #include <qt4projectmanager/qt4buildconfiguration.h>
42 #include <utils/qtcassert.h>
44 #include <QtCore/QTimer>
46 #define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state)
48 using namespace Utils;
50 namespace Qt4ProjectManager {
53 MaemoRemoteMounter::MaemoRemoteMounter(QObject *parent)
54 : QObject(parent), m_utfsServerTimer(new QTimer(this)), m_state(Inactive)
56 connect(m_utfsServerTimer, SIGNAL(timeout()), this,
57 SLOT(handleUtfsServerTimeout()));
58 m_utfsServerTimer->setSingleShot(true);
61 MaemoRemoteMounter::~MaemoRemoteMounter()
66 void MaemoRemoteMounter::setConnection(const SshConnection::Ptr &connection)
68 ASSERT_STATE(Inactive);
69 m_connection = connection;
72 void MaemoRemoteMounter::setBuildConfiguration(const Qt4BuildConfiguration *bc)
74 ASSERT_STATE(Inactive);
75 const QtVersion * const qtVersion = bc->qtVersion();
77 = qobject_cast<AbstractQt4MaemoTarget *>(bc->target())->allowsRemoteMounts();
78 m_maddeRoot = MaemoGlobal::maddeRoot(qtVersion);
81 void MaemoRemoteMounter::addMountSpecification(const MaemoMountSpecification &mountSpec,
84 ASSERT_STATE(Inactive);
86 if (m_remoteMountsAllowed && mountSpec.isValid())
87 m_mountSpecs << MountInfo(mountSpec, mountAsRoot);
90 bool MaemoRemoteMounter::hasValidMountSpecifications() const
92 return !m_mountSpecs.isEmpty();
95 void MaemoRemoteMounter::mount(MaemoPortList *freePorts,
96 const MaemoUsedPortsGatherer *portsGatherer)
98 ASSERT_STATE(Inactive);
99 Q_ASSERT(m_utfsServers.isEmpty());
100 Q_ASSERT(m_connection);
102 if (m_mountSpecs.isEmpty()) {
104 emit reportProgress(tr("No directories to mount"));
107 m_freePorts = freePorts;
108 m_portsGatherer = portsGatherer;
113 void MaemoRemoteMounter::unmount()
115 ASSERT_STATE(Inactive);
117 if (m_mountSpecs.isEmpty()) {
118 emit reportProgress(tr("No directories to unmount"));
124 for (int i = 0; i < m_mountSpecs.count(); ++i) {
125 remoteCall += QString::fromLocal8Bit("%1 umount %2 && %1 rmdir %2;")
126 .arg(MaemoGlobal::remoteSudo(),
127 m_mountSpecs.at(i).mountSpec.remoteMountPoint);
130 m_umountStderr.clear();
131 m_unmountProcess = m_connection->createRemoteProcess(remoteCall.toUtf8());
132 connect(m_unmountProcess.data(), SIGNAL(closed(int)), this,
133 SLOT(handleUnmountProcessFinished(int)));
134 connect(m_unmountProcess.data(), SIGNAL(errorOutputAvailable(QByteArray)),
135 this, SLOT(handleUmountStderr(QByteArray)));
136 setState(Unmounting);
137 m_unmountProcess->start();
140 void MaemoRemoteMounter::handleUnmountProcessFinished(int exitStatus)
142 ASSERT_STATE(QList<State>() << Unmounting << Inactive);
144 if (m_state == Inactive)
149 switch (exitStatus) {
150 case SshRemoteProcess::FailedToStart:
151 errorMsg = tr("Could not execute unmount request.");
153 case SshRemoteProcess::KilledBySignal:
154 errorMsg = tr("Failure unmounting: %1")
155 .arg(m_unmountProcess->errorString());
157 case SshRemoteProcess::ExitedNormally:
160 Q_ASSERT_X(false, Q_FUNC_INFO,
161 "Impossible SshRemoteProcess exit status.");
164 killAllUtfsServers();
166 if (errorMsg.isEmpty()) {
167 emit reportProgress(tr("Finished unmounting."));
170 if (!m_umountStderr.isEmpty()) {
171 errorMsg += tr("\nstderr was: '%1'")
172 .arg(QString::fromUtf8(m_umountStderr));
174 emit error(errorMsg);
178 void MaemoRemoteMounter::stop()
183 void MaemoRemoteMounter::startUtfsClients()
185 const QString chmodFuse
186 = MaemoGlobal::remoteSudo() + QLatin1String(" chmod a+r+w /dev/fuse");
187 const QString chmodUtfsClient
188 = QLatin1String("chmod a+x ") + utfsClientOnDevice();
189 const QLatin1String andOp(" && ");
190 QString remoteCall = chmodFuse + andOp + chmodUtfsClient;
191 for (int i = 0; i < m_mountSpecs.count(); ++i) {
192 MountInfo &mountInfo = m_mountSpecs[i];
194 = m_portsGatherer->getNextFreePort(m_freePorts);
195 if (mountInfo.remotePort == -1) {
197 emit error(tr("Error: Not enough free ports on device to fulfill all mount requests."));
201 const MaemoMountSpecification &mountSpec = mountInfo.mountSpec;
202 const QString mkdir = QString::fromLocal8Bit("%1 mkdir -p %2")
203 .arg(MaemoGlobal::remoteSudo(), mountSpec.remoteMountPoint);
204 const QString chmod = QString::fromLocal8Bit("%1 chmod a+r+w+x %2")
205 .arg(MaemoGlobal::remoteSudo(), mountSpec.remoteMountPoint);
207 = QString::fromLocal8Bit("%1 -l %2 -r %2 -b %2 %4 -o nonempty")
208 .arg(utfsClientOnDevice()).arg(mountInfo.remotePort)
209 .arg(mountSpec.remoteMountPoint);
210 if (mountInfo.mountAsRoot)
211 utfsClient.prepend(MaemoGlobal::remoteSudo() + QLatin1Char(' '));
212 QLatin1String seqOp("; ");
213 remoteCall += seqOp + MaemoGlobal::remoteSourceProfilesCommand()
214 + seqOp + mkdir + andOp + chmod + andOp + utfsClient;
217 emit reportProgress(tr("Starting remote UTFS clients..."));
218 m_utfsClientStderr.clear();
219 m_mountProcess = m_connection->createRemoteProcess(remoteCall.toUtf8());
220 connect(m_mountProcess.data(), SIGNAL(started()), this,
221 SLOT(handleUtfsClientsStarted()));
222 connect(m_mountProcess.data(), SIGNAL(closed(int)), this,
223 SLOT(handleUtfsClientsFinished(int)));
224 connect(m_mountProcess.data(), SIGNAL(errorOutputAvailable(QByteArray)),
225 this, SLOT(handleUtfsClientStderr(QByteArray)));
226 m_mountProcess->start();
228 setState(UtfsClientsStarting);
231 void MaemoRemoteMounter::handleUtfsClientsStarted()
233 ASSERT_STATE(QList<State>() << UtfsClientsStarting << Inactive);
234 if (m_state == UtfsClientsStarting) {
235 setState(UtfsClientsStarted);
236 QTimer::singleShot(250, this, SLOT(startUtfsServers()));
240 void MaemoRemoteMounter::handleUtfsClientsFinished(int exitStatus)
242 ASSERT_STATE(QList<State>() << UtfsClientsStarting << UtfsClientsStarted
243 << UtfsServersStarted << Inactive);
245 if (m_state == Inactive)
249 if (exitStatus == SshRemoteProcess::ExitedNormally
250 && m_mountProcess->exitCode() == 0) {
251 emit reportProgress(tr("Mount operation succeeded."));
254 QString errMsg = tr("Failure running UTFS client: %1")
255 .arg(m_mountProcess->errorString());
256 if (!m_utfsClientStderr.isEmpty())
257 errMsg += tr("\nstderr was: '%1'")
258 .arg(QString::fromUtf8(m_utfsClientStderr));
263 void MaemoRemoteMounter::startUtfsServers()
265 ASSERT_STATE(QList<State>() << UtfsClientsStarted << Inactive);
267 if (m_state == Inactive)
270 emit reportProgress(tr("Starting UTFS servers..."));
271 m_utfsServerTimer->start(30000);
272 for (int i = 0; i < m_mountSpecs.count(); ++i) {
273 const MountInfo &mountInfo = m_mountSpecs.at(i);
274 const MaemoMountSpecification &mountSpec = mountInfo.mountSpec;
275 const ProcPtr utfsServerProc(new QProcess);
276 const QString port = QString::number(mountInfo.remotePort);
277 const QString localSecretOpt = QLatin1String("-l");
278 const QString remoteSecretOpt = QLatin1String("-r");
279 const QStringList utfsServerArgs = QStringList() << localSecretOpt
280 << port << remoteSecretOpt << port << QLatin1String("-c")
281 << (m_connection->connectionParameters().host + QLatin1Char(':') + port)
282 << mountSpec.localDir;
283 connect(utfsServerProc.data(),
284 SIGNAL(finished(int,QProcess::ExitStatus)), this,
285 SLOT(handleUtfsServerFinished(int,QProcess::ExitStatus)));
286 connect(utfsServerProc.data(), SIGNAL(error(QProcess::ProcessError)),
287 this, SLOT(handleUtfsServerError(QProcess::ProcessError)));
288 connect(utfsServerProc.data(), SIGNAL(readyReadStandardError()), this,
289 SLOT(handleUtfsServerStderr()));
290 m_utfsServers << utfsServerProc;
291 utfsServerProc->start(utfsServer(), utfsServerArgs);
294 setState(UtfsServersStarted);
297 void MaemoRemoteMounter::handleUtfsServerStderr()
299 if (m_state != Inactive) {
300 QProcess * const proc = static_cast<QProcess *>(sender());
301 const QByteArray &output = proc->readAllStandardError();
302 emit debugOutput(QString::fromLocal8Bit(output));
306 void MaemoRemoteMounter::handleUtfsServerError(QProcess::ProcessError)
308 if (m_state == Inactive || m_utfsServers.isEmpty())
311 QProcess * const proc = static_cast<QProcess *>(sender());
312 QString errorString = proc->errorString();
313 const QByteArray &errorOutput = proc->readAllStandardError();
314 if (!errorOutput.isEmpty()) {
315 errorString += tr("\nstderr was: %1")
316 .arg(QString::fromLocal8Bit(errorOutput));
318 killAllUtfsServers();
319 emit error(tr("Error running UTFS server: %1").arg(errorString));
324 void MaemoRemoteMounter::handleUtfsServerFinished(int /* exitCode */,
325 QProcess::ExitStatus exitStatus)
327 if (m_state != Inactive && exitStatus != QProcess::NormalExit)
328 handleUtfsServerError(static_cast<QProcess *>(sender())->error());
331 void MaemoRemoteMounter::handleUtfsClientStderr(const QByteArray &output)
333 if (m_state != Inactive)
334 m_utfsClientStderr += output;
337 void MaemoRemoteMounter::handleUmountStderr(const QByteArray &output)
339 if (m_state != Inactive)
340 m_umountStderr += output;
343 QString MaemoRemoteMounter::utfsClientOnDevice() const
345 return QLatin1String("/usr/lib/mad-developer/utfs-client");
348 QString MaemoRemoteMounter::utfsServer() const
350 return m_maddeRoot + QLatin1String("/madlib/utfs-server");
353 void MaemoRemoteMounter::killAllUtfsServers()
355 foreach (const ProcPtr &proc, m_utfsServers)
356 killUtfsServer(proc.data());
357 m_utfsServers.clear();
360 void MaemoRemoteMounter::killUtfsServer(QProcess *proc)
362 disconnect(proc, 0, this, 0);
364 proc->waitForFinished(1000);
368 void MaemoRemoteMounter::handleUtfsServerTimeout()
370 ASSERT_STATE(QList<State>() << UtfsServersStarted << Inactive);
371 if (m_state == Inactive)
374 killAllUtfsServers();
375 emit error(tr("Timeout waiting for UTFS servers to connect."));
380 void MaemoRemoteMounter::setState(State newState)
382 if (newState == Inactive) {
383 m_utfsServerTimer->stop();
384 if (m_mountProcess) {
385 disconnect(m_mountProcess.data(), 0, this, 0);
386 m_mountProcess->closeChannel();
388 if (m_unmountProcess) {
389 disconnect(m_unmountProcess.data(), 0, this, 0);
390 m_unmountProcess->closeChannel();
396 } // namespace Internal
397 } // namespace Qt4ProjectManager