Commit 6434a697 authored by Christian Kandeler's avatar Christian Kandeler
Browse files

SSH: Act on failing SFTP server.



So far we ignored crashes and unexpected exits of the remote SFTP
service under the assumption that the SSH server would catch these
itself and act accordingly. This is not the case, however: OpenSSH,
for instance, does not even realize if its sftp-server binary is
not present at all. As a result, Qt Creator waits indefinitely for
an SFTP operation to finish. Now we emit an error and close the
offending channel.

Task-number: QTCREATORBUG-10339
Change-Id: I132ed4a0098434a4cfce6056b964bd6363951fd7
Reviewed-by: default avatarVolker Vogelhuber <wiendl@web.de>
Reviewed-by: default avatarChristian Kandeler <christian.kandeler@digia.com>
parent 8b99e553
...@@ -88,8 +88,8 @@ SftpChannel::SftpChannel(quint32 channelId, ...@@ -88,8 +88,8 @@ SftpChannel::SftpChannel(quint32 channelId,
{ {
connect(d, SIGNAL(initialized()), this, SIGNAL(initialized()), connect(d, SIGNAL(initialized()), this, SIGNAL(initialized()),
Qt::QueuedConnection); Qt::QueuedConnection);
connect(d, SIGNAL(initializationFailed(QString)), this, connect(d, SIGNAL(channelError(QString)), this,
SIGNAL(initializationFailed(QString)), Qt::QueuedConnection); SIGNAL(channelError(QString)), Qt::QueuedConnection);
connect(d, SIGNAL(dataAvailable(QSsh::SftpJobId,QString)), this, connect(d, SIGNAL(dataAvailable(QSsh::SftpJobId,QString)), this,
SIGNAL(dataAvailable(QSsh::SftpJobId,QString)), Qt::QueuedConnection); SIGNAL(dataAvailable(QSsh::SftpJobId,QString)), Qt::QueuedConnection);
connect(d, SIGNAL(fileInfoAvailable(QSsh::SftpJobId,QList<QSsh::SftpFileInfo>)), this, connect(d, SIGNAL(fileInfoAvailable(QSsh::SftpJobId,QList<QSsh::SftpFileInfo>)), this,
...@@ -271,7 +271,7 @@ void SftpChannelPrivate::handleChannelFailure() ...@@ -271,7 +271,7 @@ void SftpChannelPrivate::handleChannelFailure()
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
"Unexpected SSH_MSG_CHANNEL_FAILURE packet."); "Unexpected SSH_MSG_CHANNEL_FAILURE packet.");
} }
emit initializationFailed(tr("Server could not start SFTP subsystem.")); emit channelError(tr("Server could not start SFTP subsystem."));
closeChannel(); closeChannel();
} }
...@@ -298,18 +298,22 @@ void SftpChannelPrivate::handleChannelExtendedDataInternal(quint32 type, ...@@ -298,18 +298,22 @@ void SftpChannelPrivate::handleChannelExtendedDataInternal(quint32 type,
void SftpChannelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus) void SftpChannelPrivate::handleExitStatus(const SshChannelExitStatus &exitStatus)
{ {
const char * const message = "Remote SFTP service exited with exit code %d";
#ifdef CREATOR_SSH_DEBUG #ifdef CREATOR_SSH_DEBUG
qDebug(message, exitStatus.exitStatus); qDebug("Remote SFTP service exited with exit code %d", exitStatus.exitStatus);
#else
if (exitStatus.exitStatus != 0)
qWarning(message, exitStatus.exitStatus);
#endif #endif
emit channelError(tr("The SFTP server finished unexpectedly with exit code %1.")
.arg(exitStatus.exitStatus));
// Note: According to the specs, the server must close the channel after this happens,
// but OpenSSH doesn't do that, so we need to initiate the closing procedure ourselves.
closeChannel();
} }
void SftpChannelPrivate::handleExitSignal(const SshChannelExitSignal &signal) void SftpChannelPrivate::handleExitSignal(const SshChannelExitSignal &signal)
{ {
qWarning("Remote SFTP service killed; signal was %s", signal.signal.data()); emit channelError(tr("The SFTP server crashed: %1.").arg(signal.error));
closeChannel(); // See above.
} }
void SftpChannelPrivate::handleCurrentPacket() void SftpChannelPrivate::handleCurrentPacket()
...@@ -356,7 +360,7 @@ void SftpChannelPrivate::handleServerVersion() ...@@ -356,7 +360,7 @@ void SftpChannelPrivate::handleServerVersion()
#endif #endif
const quint32 serverVersion = m_incomingPacket.extractServerVersion(); const quint32 serverVersion = m_incomingPacket.extractServerVersion();
if (serverVersion != ProtocolVersion) { if (serverVersion != ProtocolVersion) {
emit initializationFailed(tr("Protocol version mismatch: Expected %1, got %2") emit channelError(tr("Protocol version mismatch: Expected %1, got %2")
.arg(serverVersion).arg(ProtocolVersion)); .arg(serverVersion).arg(ProtocolVersion));
closeChannel(); closeChannel();
} else { } else {
...@@ -854,7 +858,7 @@ void SftpChannelPrivate::handleOpenFailureInternal(const QString &reason) ...@@ -854,7 +858,7 @@ void SftpChannelPrivate::handleOpenFailureInternal(const QString &reason)
throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR, throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
"Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet."); "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
} }
emit initializationFailed(tr("Server could not start session: %1").arg(reason)); emit channelError(tr("Server could not start session: %1").arg(reason));
} }
void SftpChannelPrivate::sendReadRequest(const SftpDownload::Ptr &job, void SftpChannelPrivate::sendReadRequest(const SftpDownload::Ptr &job,
......
...@@ -83,7 +83,7 @@ public: ...@@ -83,7 +83,7 @@ public:
signals: signals:
void initialized(); void initialized();
void initializationFailed(const QString &reason); void channelError(const QString &reason);
void closed(); void closed();
// error.isEmpty <=> finished successfully // error.isEmpty <=> finished successfully
......
...@@ -52,7 +52,7 @@ public: ...@@ -52,7 +52,7 @@ public:
signals: signals:
void initialized(); void initialized();
void initializationFailed(const QString &reason); void channelError(const QString &reason);
void closed(); void closed();
void finished(QSsh::SftpJobId job, const QString &error = QString()); void finished(QSsh::SftpJobId job, const QString &error = QString());
void dataAvailable(QSsh::SftpJobId job, const QString &data); void dataAvailable(QSsh::SftpJobId job, const QString &data);
......
...@@ -296,12 +296,12 @@ void SftpFileSystemModel::handleSshConnectionEstablished() ...@@ -296,12 +296,12 @@ void SftpFileSystemModel::handleSshConnectionEstablished()
{ {
d->sftpChannel = d->sshConnection->createSftpChannel(); d->sftpChannel = d->sshConnection->createSftpChannel();
connect(d->sftpChannel.data(), SIGNAL(initialized()), SLOT(handleSftpChannelInitialized())); connect(d->sftpChannel.data(), SIGNAL(initialized()), SLOT(handleSftpChannelInitialized()));
connect(d->sftpChannel.data(), SIGNAL(initializationFailed(QString)), connect(d->sftpChannel.data(), SIGNAL(channelError(QString)),
SLOT(handleSftpChannelInitializationFailed(QString))); SLOT(handleSftpChannelError(QString)));
d->sftpChannel->initialize(); d->sftpChannel->initialize();
} }
void SftpFileSystemModel::handleSftpChannelInitializationFailed(const QString &reason) void SftpFileSystemModel::handleSftpChannelError(const QString &reason)
{ {
emit connectionError(reason); emit connectionError(reason);
beginResetModel(); beginResetModel();
......
...@@ -84,7 +84,7 @@ private slots: ...@@ -84,7 +84,7 @@ private slots:
void handleSshConnectionEstablished(); void handleSshConnectionEstablished();
void handleSshConnectionFailure(); void handleSshConnectionFailure();
void handleSftpChannelInitialized(); void handleSftpChannelInitialized();
void handleSftpChannelInitializationFailed(const QString &reason); void handleSftpChannelError(const QString &reason);
void handleFileInfo(QSsh::SftpJobId jobId, const QList<QSsh::SftpFileInfo> &fileInfoList); void handleFileInfo(QSsh::SftpJobId jobId, const QList<QSsh::SftpFileInfo> &fileInfoList);
void handleSftpJobFinished(QSsh::SftpJobId jobId, const QString &errorMessage); void handleSftpJobFinished(QSsh::SftpJobId jobId, const QString &errorMessage);
......
...@@ -123,8 +123,8 @@ void GenericDirectUploadService::doDeploy() ...@@ -123,8 +123,8 @@ void GenericDirectUploadService::doDeploy()
d->uploader = connection()->createSftpChannel(); d->uploader = connection()->createSftpChannel();
connect(d->uploader.data(), SIGNAL(initialized()), SLOT(handleSftpInitialized())); connect(d->uploader.data(), SIGNAL(initialized()), SLOT(handleSftpInitialized()));
connect(d->uploader.data(), SIGNAL(initializationFailed(QString)), connect(d->uploader.data(), SIGNAL(channelError(QString)),
SLOT(handleSftpInitializationFailed(QString))); SLOT(handleSftpChannelError(QString)));
d->uploader->initialize(); d->uploader->initialize();
d->state = InitializingSftp; d->state = InitializingSftp;
} }
...@@ -146,7 +146,7 @@ void GenericDirectUploadService::handleSftpInitialized() ...@@ -146,7 +146,7 @@ void GenericDirectUploadService::handleSftpInitialized()
uploadNextFile(); uploadNextFile();
} }
void GenericDirectUploadService::handleSftpInitializationFailed(const QString &message) void GenericDirectUploadService::handleSftpChannelError(const QString &message)
{ {
QTC_ASSERT(d->state == InitializingSftp, setFinished(); return); QTC_ASSERT(d->state == InitializingSftp, setFinished(); return);
......
...@@ -65,7 +65,7 @@ public: ...@@ -65,7 +65,7 @@ public:
private slots: private slots:
void handleSftpInitialized(); void handleSftpInitialized();
void handleSftpInitializationFailed(const QString &errorMessage); void handleSftpChannelError(const QString &errorMessage);
void handleUploadFinished(QSsh::SftpJobId jobId, const QString &errorMsg); void handleUploadFinished(QSsh::SftpJobId jobId, const QString &errorMsg);
void handleMkdirFinished(int exitStatus); void handleMkdirFinished(int exitStatus);
void handleLnFinished(int exitStatus); void handleLnFinished(int exitStatus);
......
...@@ -62,8 +62,8 @@ void PackageUploader::uploadPackage(SshConnection *connection, ...@@ -62,8 +62,8 @@ void PackageUploader::uploadPackage(SshConnection *connection,
m_uploader = m_connection->createSftpChannel(); m_uploader = m_connection->createSftpChannel();
connect(m_uploader.data(), SIGNAL(initialized()), this, connect(m_uploader.data(), SIGNAL(initialized()), this,
SLOT(handleSftpChannelInitialized())); SLOT(handleSftpChannelInitialized()));
connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this, connect(m_uploader.data(), SIGNAL(channelError(QString)), this,
SLOT(handleSftpChannelInitializationFailed(QString))); SLOT(handleSftpChannelError(QString)));
connect(m_uploader.data(), SIGNAL(finished(QSsh::SftpJobId,QString)), connect(m_uploader.data(), SIGNAL(finished(QSsh::SftpJobId,QString)),
this, SLOT(handleSftpJobFinished(QSsh::SftpJobId,QString))); this, SLOT(handleSftpJobFinished(QSsh::SftpJobId,QString)));
m_uploader->initialize(); m_uploader->initialize();
...@@ -86,7 +86,7 @@ void PackageUploader::handleConnectionFailure() ...@@ -86,7 +86,7 @@ void PackageUploader::handleConnectionFailure()
emit uploadFinished(tr("Connection failed: %1").arg(errorMsg)); emit uploadFinished(tr("Connection failed: %1").arg(errorMsg));
} }
void PackageUploader::handleSftpChannelInitializationFailed(const QString &errorMsg) void PackageUploader::handleSftpChannelError(const QString &errorMsg)
{ {
QTC_ASSERT(m_state == InitializingSftp || m_state == Inactive, return); QTC_ASSERT(m_state == InitializingSftp || m_state == Inactive, return);
......
...@@ -63,7 +63,7 @@ signals: ...@@ -63,7 +63,7 @@ signals:
private slots: private slots:
void handleConnectionFailure(); void handleConnectionFailure();
void handleSftpChannelInitialized(); void handleSftpChannelInitialized();
void handleSftpChannelInitializationFailed(const QString &error); void handleSftpChannelError(const QString &error);
void handleSftpJobFinished(QSsh::SftpJobId job, const QString &error); void handleSftpJobFinished(QSsh::SftpJobId job, const QString &error);
private: private:
......
Subproject commit 3b6b1b7fbc50bca101ad89a8acd80774bc668dde Subproject commit 2c1a305295f05f35527b0cceb500d013e12752e0
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment