OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / qt-maemo / maemoremotemounter.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "maemoremotemounter.h"
34
35 #include "maemoglobal.h"
36 #include "maemousedportsgatherer.h"
37 #include "qt4maemotarget.h"
38
39 #include <utils/ssh/sshconnection.h>
40 #include <utils/ssh/sshremoteprocess.h>
41 #include <qt4projectmanager/qt4buildconfiguration.h>
42 #include <utils/qtcassert.h>
43
44 #include <QtCore/QTimer>
45
46 #define ASSERT_STATE(state) ASSERT_STATE_GENERIC(State, state, m_state)
47
48 using namespace Utils;
49
50 namespace Qt4ProjectManager {
51 namespace Internal {
52
53 MaemoRemoteMounter::MaemoRemoteMounter(QObject *parent)
54     : QObject(parent), m_utfsServerTimer(new QTimer(this)), m_state(Inactive)
55 {
56     connect(m_utfsServerTimer, SIGNAL(timeout()), this,
57         SLOT(handleUtfsServerTimeout()));
58     m_utfsServerTimer->setSingleShot(true);
59 }
60
61 MaemoRemoteMounter::~MaemoRemoteMounter()
62 {
63     killAllUtfsServers();
64 }
65
66 void MaemoRemoteMounter::setConnection(const SshConnection::Ptr &connection)
67 {
68     ASSERT_STATE(Inactive);
69     m_connection = connection;
70 }
71
72 void MaemoRemoteMounter::setBuildConfiguration(const Qt4BuildConfiguration *bc)
73 {
74     ASSERT_STATE(Inactive);
75     const QtVersion * const qtVersion = bc->qtVersion();
76     m_remoteMountsAllowed
77         = qobject_cast<AbstractQt4MaemoTarget *>(bc->target())->allowsRemoteMounts();
78     m_maddeRoot = MaemoGlobal::maddeRoot(qtVersion);
79 }
80
81 void MaemoRemoteMounter::addMountSpecification(const MaemoMountSpecification &mountSpec,
82     bool mountAsRoot)
83 {
84     ASSERT_STATE(Inactive);
85
86     if (m_remoteMountsAllowed && mountSpec.isValid())
87         m_mountSpecs << MountInfo(mountSpec, mountAsRoot);
88 }
89
90 bool MaemoRemoteMounter::hasValidMountSpecifications() const
91 {
92     return !m_mountSpecs.isEmpty();
93 }
94
95 void MaemoRemoteMounter::mount(MaemoPortList *freePorts,
96     const MaemoUsedPortsGatherer *portsGatherer)
97 {
98     ASSERT_STATE(Inactive);
99     Q_ASSERT(m_utfsServers.isEmpty());
100     Q_ASSERT(m_connection);
101
102     if (m_mountSpecs.isEmpty()) {
103         setState(Inactive);
104         emit reportProgress(tr("No directories to mount"));
105         emit mounted();
106     } else {
107         m_freePorts = freePorts;
108         m_portsGatherer = portsGatherer;
109         startUtfsClients();
110     }
111 }
112
113 void MaemoRemoteMounter::unmount()
114 {
115     ASSERT_STATE(Inactive);
116
117     if (m_mountSpecs.isEmpty()) {
118         emit reportProgress(tr("No directories to unmount"));
119         emit unmounted();
120         return;
121     }
122
123     QString remoteCall;
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);
128     }
129
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();
138 }
139
140 void MaemoRemoteMounter::handleUnmountProcessFinished(int exitStatus)
141 {
142     ASSERT_STATE(QList<State>() << Unmounting << Inactive);
143
144     if (m_state == Inactive)
145         return;
146     setState(Inactive);
147
148     QString errorMsg;
149     switch (exitStatus) {
150     case SshRemoteProcess::FailedToStart:
151         errorMsg = tr("Could not execute unmount request.");
152         break;
153     case SshRemoteProcess::KilledBySignal:
154         errorMsg = tr("Failure unmounting: %1")
155             .arg(m_unmountProcess->errorString());
156         break;
157     case SshRemoteProcess::ExitedNormally:
158         break;
159     default:
160         Q_ASSERT_X(false, Q_FUNC_INFO,
161             "Impossible SshRemoteProcess exit status.");
162     }
163
164     killAllUtfsServers();
165
166     if (errorMsg.isEmpty()) {
167         emit reportProgress(tr("Finished unmounting."));
168         emit unmounted();
169     } else {
170         if (!m_umountStderr.isEmpty()) {
171             errorMsg += tr("\nstderr was: '%1'")
172                 .arg(QString::fromUtf8(m_umountStderr));
173         }
174         emit error(errorMsg);
175     }
176 }
177
178 void MaemoRemoteMounter::stop()
179 {
180     setState(Inactive);
181 }
182
183 void MaemoRemoteMounter::startUtfsClients()
184 {
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];
193         mountInfo.remotePort
194             = m_portsGatherer->getNextFreePort(m_freePorts);
195         if (mountInfo.remotePort == -1) {
196             setState(Inactive);
197             emit error(tr("Error: Not enough free ports on device to fulfill all mount requests."));
198             return;
199         }
200
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);
206         QString utfsClient
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;
215     }
216
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();
227
228     setState(UtfsClientsStarting);
229 }
230
231 void MaemoRemoteMounter::handleUtfsClientsStarted()
232 {
233     ASSERT_STATE(QList<State>() << UtfsClientsStarting << Inactive);
234     if (m_state == UtfsClientsStarting) {
235         setState(UtfsClientsStarted);
236         QTimer::singleShot(250, this, SLOT(startUtfsServers()));
237     }
238 }
239
240 void MaemoRemoteMounter::handleUtfsClientsFinished(int exitStatus)
241 {
242     ASSERT_STATE(QList<State>() << UtfsClientsStarting << UtfsClientsStarted
243         << UtfsServersStarted << Inactive);
244
245     if (m_state == Inactive)
246         return;
247
248     setState(Inactive);
249     if (exitStatus == SshRemoteProcess::ExitedNormally
250             && m_mountProcess->exitCode() == 0) {
251         emit reportProgress(tr("Mount operation succeeded."));
252         emit mounted();
253     } else {
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));
259         emit error(errMsg);
260     }
261 }
262
263 void MaemoRemoteMounter::startUtfsServers()
264 {
265     ASSERT_STATE(QList<State>() << UtfsClientsStarted << Inactive);
266
267     if (m_state == Inactive)
268         return;
269
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);
292     }
293
294     setState(UtfsServersStarted);
295 }
296
297 void MaemoRemoteMounter::handleUtfsServerStderr()
298 {
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));
303     }
304 }
305
306 void MaemoRemoteMounter::handleUtfsServerError(QProcess::ProcessError)
307 {
308     if (m_state == Inactive || m_utfsServers.isEmpty())
309         return;
310
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));
317     }
318     killAllUtfsServers();
319     emit error(tr("Error running UTFS server: %1").arg(errorString));
320
321     setState(Inactive);
322 }
323
324 void MaemoRemoteMounter::handleUtfsServerFinished(int /* exitCode */,
325     QProcess::ExitStatus exitStatus)
326 {
327     if (m_state != Inactive && exitStatus != QProcess::NormalExit)
328         handleUtfsServerError(static_cast<QProcess *>(sender())->error());
329 }
330
331 void MaemoRemoteMounter::handleUtfsClientStderr(const QByteArray &output)
332 {
333     if (m_state != Inactive)
334         m_utfsClientStderr += output;
335 }
336
337 void MaemoRemoteMounter::handleUmountStderr(const QByteArray &output)
338 {
339     if (m_state != Inactive)
340         m_umountStderr += output;
341 }
342
343 QString MaemoRemoteMounter::utfsClientOnDevice() const
344 {
345     return QLatin1String("/usr/lib/mad-developer/utfs-client");
346 }
347
348 QString MaemoRemoteMounter::utfsServer() const
349 {
350     return m_maddeRoot + QLatin1String("/madlib/utfs-server");
351 }
352
353 void MaemoRemoteMounter::killAllUtfsServers()
354 {
355     foreach (const ProcPtr &proc, m_utfsServers)
356         killUtfsServer(proc.data());
357     m_utfsServers.clear();
358 }
359
360 void MaemoRemoteMounter::killUtfsServer(QProcess *proc)
361 {
362     disconnect(proc, 0, this, 0);
363     proc->terminate();
364     proc->waitForFinished(1000);
365     proc->kill();
366 }
367
368 void MaemoRemoteMounter::handleUtfsServerTimeout()
369 {
370     ASSERT_STATE(QList<State>() << UtfsServersStarted << Inactive);
371     if (m_state == Inactive)
372         return;
373
374     killAllUtfsServers();
375     emit error(tr("Timeout waiting for UTFS servers to connect."));
376
377     setState(Inactive);
378 }
379
380 void MaemoRemoteMounter::setState(State newState)
381 {
382     if (newState == Inactive) {
383         m_utfsServerTimer->stop();
384         if (m_mountProcess) {
385             disconnect(m_mountProcess.data(), 0, this, 0);
386             m_mountProcess->closeChannel();
387         }
388         if (m_unmountProcess) {
389             disconnect(m_unmountProcess.data(), 0, this, 0);
390             m_unmountProcess->closeChannel();
391         }
392     }
393     m_state = newState;
394 }
395
396 } // namespace Internal
397 } // namespace Qt4ProjectManager