LinuxDevice: Restart shell when ssh parameters changed

Change-Id: I96fa7de4f8314ccd3c78bee165b722fdbba6e8d0
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2022-04-22 16:02:03 +02:00
parent a06721fe71
commit d1d2477605

View File

@@ -114,8 +114,9 @@ signals:
void autoDestructRequested(); void autoDestructRequested();
private: private:
void emitError(QProcess::ProcessError processError, const QString &errorString);
void emitConnected(); void emitConnected();
void emitError(QProcess::ProcessError processError, const QString &errorString);
void emitDisconnected();
QString fullProcessError(const QString &sshErrorPrefix); QString fullProcessError(const QString &sshErrorPrefix);
QStringList connectionArgs(const FilePath &binary) const QStringList connectionArgs(const FilePath &binary) const
{ return connectionOptions(binary) << m_sshParameters.host(); } { return connectionOptions(binary) << m_sshParameters.host(); }
@@ -127,6 +128,7 @@ private:
QTimer m_timer; QTimer m_timer;
int m_ref = 0; int m_ref = 0;
bool m_stale = false; bool m_stale = false;
QProcess::ProcessState m_state = QProcess::NotRunning;
}; };
SshSharedConnection::SshSharedConnection(const SshConnectionParameters &sshParameters, QObject *parent) SshSharedConnection::SshSharedConnection(const SshConnectionParameters &sshParameters, QObject *parent)
@@ -194,7 +196,10 @@ void SshSharedConnection::connectToHost()
const QByteArray reply = m_masterProcess->readAllStandardOutput(); const QByteArray reply = m_masterProcess->readAllStandardOutput();
if (reply == "\n") if (reply == "\n")
emitConnected(); emitConnected();
// TODO: otherwise emitError and finish master process?
}); });
// TODO: in case of refused connection we are getting the following on stdErr:
// ssh: connect to host 127.0.0.1 port 22: Connection refused\r\n
connect(m_masterProcess.get(), &QtcProcess::done, [this] { connect(m_masterProcess.get(), &QtcProcess::done, [this] {
const QProcess::ProcessError error = m_masterProcess->error(); const QProcess::ProcessError error = m_masterProcess->error();
if (error == QProcess::FailedToStart) { if (error == QProcess::FailedToStart) {
@@ -232,7 +237,7 @@ void SshSharedConnection::disconnectFromHost()
QProcess::ProcessState SshSharedConnection::state() const QProcess::ProcessState SshSharedConnection::state() const
{ {
return m_masterProcess ? m_masterProcess->state() : QProcess::NotRunning; return m_state;
} }
SshConnectionInfo SshSharedConnection::connectionInfo() const SshConnectionInfo SshSharedConnection::connectionInfo() const
@@ -267,14 +272,22 @@ SshConnectionInfo SshSharedConnection::connectionInfo() const
return m_connInfo; return m_connInfo;
} }
void SshSharedConnection::emitConnected()
{
m_state = QProcess::Running;
emit connected(socketFilePath());
}
void SshSharedConnection::emitError(QProcess::ProcessError error, const QString &errorString) void SshSharedConnection::emitError(QProcess::ProcessError error, const QString &errorString)
{ {
m_state = QProcess::NotRunning;
emit disconnected({ 0, QProcess::NormalExit, error, errorString }); emit disconnected({ 0, QProcess::NormalExit, error, errorString });
} }
void SshSharedConnection::emitConnected() void SshSharedConnection::emitDisconnected()
{ {
emit connected(socketFilePath()); m_state = QProcess::NotRunning;
emit disconnected(m_masterProcess->resultData());
} }
QString SshSharedConnection::fullProcessError(const QString &sshErrorPrefix) QString SshSharedConnection::fullProcessError(const QString &sshErrorPrefix)
@@ -450,7 +463,6 @@ public:
QThread m_shellThread; QThread m_shellThread;
ShellThreadHandler *m_handler = nullptr; ShellThreadHandler *m_handler = nullptr;
mutable QMutex m_shellMutex; mutable QMutex m_shellMutex;
mutable QMutex m_sharedConnectionMutex;
}; };
// SshProcessImpl // SshProcessImpl
@@ -533,9 +545,7 @@ void SshProcessInterface::start()
qint64 SshProcessInterface::write(const QByteArray &data) qint64 SshProcessInterface::write(const QByteArray &data)
{ {
Q_UNUSED(data) return d->m_process.writeRaw(data);
QTC_CHECK(false);
return -1;
} }
void SshProcessInterface::sendControlSignal(ControlSignal controlSignal) void SshProcessInterface::sendControlSignal(ControlSignal controlSignal)
@@ -625,12 +635,13 @@ void LinuxProcessImpl::handleStarted(qint64 processId)
if (m_setup.m_terminalMode == TerminalMode::Off) if (m_setup.m_terminalMode == TerminalMode::Off)
return; return;
m_pidParsed = true;
emitStarted(processId); emitStarted(processId);
} }
void LinuxProcessImpl::handleReadyReadStandardOutput(const QByteArray &outputData) void LinuxProcessImpl::handleReadyReadStandardOutput(const QByteArray &outputData)
{ {
if (m_pidParsed || m_setup.m_terminalMode != TerminalMode::Off) { if (m_pidParsed) {
emit readyRead(outputData, {}); emit readyRead(outputData, {});
return; return;
} }
@@ -777,6 +788,7 @@ void SshProcessInterfacePrivate::doStart()
m_process.setProcessImpl(q->m_setup.m_processImpl); m_process.setProcessImpl(q->m_setup.m_processImpl);
m_process.setProcessMode(q->m_setup.m_processMode); m_process.setProcessMode(q->m_setup.m_processMode);
m_process.setTerminalMode(q->m_setup.m_terminalMode); m_process.setTerminalMode(q->m_setup.m_terminalMode);
m_process.setWriteData(q->m_setup.m_writeData);
// TODO: what about other fields from m_setup? // TODO: what about other fields from m_setup?
SshRemoteProcess::setupSshEnvironment(&m_process); SshRemoteProcess::setupSshEnvironment(&m_process);
if (!m_sshParameters.x11DisplayName.isEmpty()) { if (!m_sshParameters.x11DisplayName.isEmpty()) {
@@ -830,42 +842,41 @@ class ShellThreadHandler : public QObject
public: public:
~ShellThreadHandler() ~ShellThreadHandler()
{ {
if (m_shell && m_shell->isRunning()) { closeShell();
m_shell->write("exit\n");
m_shell->waitForFinished();
}
qDeleteAll(m_connections); qDeleteAll(m_connections);
} }
bool startFailed(const SshConnectionParameters &parameters) void closeShell()
{ {
if (m_shell && m_shell->isRunning()) {
m_shell->write("exit\n");
m_shell->waitForFinished(-1);
}
m_shell.reset(); m_shell.reset();
qCDebug(linuxDeviceLog) << "Failed to connect to" << parameters.host();
return false;
} }
// Call me with shell mutex locked
bool start(const SshConnectionParameters &parameters) bool start(const SshConnectionParameters &parameters)
{ {
closeShell();
setSshParameters(parameters);
m_shell.reset(new SshRemoteProcess("/bin/sh", m_shell.reset(new SshRemoteProcess("/bin/sh",
parameters.connectionOptions(SshSettings::sshFilePath()) << parameters.host())); m_displaylessSshParameters.connectionOptions(SshSettings::sshFilePath())
<< m_displaylessSshParameters.host()));
m_shell->setProcessMode(ProcessMode::Writer); m_shell->setProcessMode(ProcessMode::Writer);
m_shell->setWriteData("echo\n");
m_shell->start(); m_shell->start();
const bool startOK = m_shell->waitForStarted();
if (!startOK)
return startFailed(parameters);
m_shell->write("echo\n");
const bool readOK = m_shell->waitForReadyRead();
if (!readOK)
return startFailed(parameters);
const QByteArray output = m_shell->readAllStandardOutput();
if (output != "\n")
return startFailed(parameters);
if (!m_shell->waitForStarted() || !m_shell->waitForReadyRead()
|| m_shell->readAllStandardOutput() != "\n") {
closeShell();
qCDebug(linuxDeviceLog) << "Failed to connect to" << m_displaylessSshParameters.host();
return false;
}
return true; return true;
} }
// Call me with shell mutex locked
bool runInShell(const CommandLine &cmd, const QByteArray &data = {}) bool runInShell(const CommandLine &cmd, const QByteArray &data = {})
{ {
QTC_ASSERT(m_shell, return false); QTC_ASSERT(m_shell, return false);
@@ -890,6 +901,7 @@ public:
return !result; return !result;
} }
// Call me with shell mutex locked
QByteArray outputForRunInShell(const QString &cmd) QByteArray outputForRunInShell(const QString &cmd)
{ {
QTC_ASSERT(m_shell, return {}); QTC_ASSERT(m_shell, return {});
@@ -922,6 +934,7 @@ public:
void setSshParameters(const SshConnectionParameters &sshParameters) void setSshParameters(const SshConnectionParameters &sshParameters)
{ {
QMutexLocker locker(&m_mutex);
const SshConnectionParameters displaylessSshParameters = displayless(sshParameters); const SshConnectionParameters displaylessSshParameters = displayless(sshParameters);
if (m_displaylessSshParameters == displaylessSshParameters) if (m_displaylessSshParameters == displaylessSshParameters)
@@ -985,11 +998,21 @@ public:
return {}; return {};
} }
bool isRunning() const { return m_shell.get(); } // Call me with shell mutex locked, called from other thread
bool isRunning(const SshConnectionParameters &sshParameters) const
{
if (!m_shell)
return false;
QMutexLocker locker(&m_mutex);
if (m_displaylessSshParameters != displayless(sshParameters))
return false;
return true;
}
private: private:
mutable QMutex m_mutex;
SshConnectionParameters m_displaylessSshParameters; SshConnectionParameters m_displaylessSshParameters;
QList<SshSharedConnection *> m_connections; QList<SshSharedConnection *> m_connections;
std::unique_ptr<SshRemoteProcess> m_shell; std::unique_ptr<QtcProcess> m_shell;
}; };
// LinuxDevice // LinuxDevice
@@ -1160,11 +1183,16 @@ LinuxDevicePrivate::~LinuxDevicePrivate()
QMetaObject::invokeMethod(&m_shellThread, closeShell, Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(&m_shellThread, closeShell, Qt::BlockingQueuedConnection);
} }
// Call me with shell mutex locked
bool LinuxDevicePrivate::setupShell() bool LinuxDevicePrivate::setupShell()
{ {
const SshConnectionParameters sshParameters = q->sshParameters();
if (m_handler->isRunning(sshParameters))
return true;
bool ok = false; bool ok = false;
QMetaObject::invokeMethod(m_handler, [this, parameters = q->sshParameters()] { QMetaObject::invokeMethod(m_handler, [this, sshParameters] {
return m_handler->start(parameters); return m_handler->start(sshParameters);
}, Qt::BlockingQueuedConnection, &ok); }, Qt::BlockingQueuedConnection, &ok);
return ok; return ok;
} }
@@ -1173,10 +1201,7 @@ bool LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &da
{ {
QMutexLocker locker(&m_shellMutex); QMutexLocker locker(&m_shellMutex);
DEBUG(cmd.toUserOutput()); DEBUG(cmd.toUserOutput());
if (!m_handler->isRunning()) { QTC_ASSERT(setupShell(), return false);
const bool ok = setupShell();
QTC_ASSERT(ok, return false);
}
bool ret = false; bool ret = false;
QMetaObject::invokeMethod(m_handler, [this, &cmd, &data] { QMetaObject::invokeMethod(m_handler, [this, &cmd, &data] {
@@ -1189,10 +1214,7 @@ QByteArray LinuxDevicePrivate::outputForRunInShell(const QString &cmd)
{ {
QMutexLocker locker(&m_shellMutex); QMutexLocker locker(&m_shellMutex);
DEBUG(cmd); DEBUG(cmd);
if (!m_handler->isRunning()) { QTC_ASSERT(setupShell(), return {});
const bool ok = setupShell();
QTC_ASSERT(ok, return {});
}
QByteArray ret; QByteArray ret;
QMetaObject::invokeMethod(m_handler, [this, &cmd] { QMetaObject::invokeMethod(m_handler, [this, &cmd] {
@@ -1210,12 +1232,9 @@ void LinuxDevicePrivate::attachToSharedConnection(SshConnectionHandle *connectio
const SshConnectionParameters &sshParameters) const SshConnectionParameters &sshParameters)
{ {
QString socketFilePath; QString socketFilePath;
{ QMetaObject::invokeMethod(m_handler, [this, connectionHandle, sshParameters] {
QMutexLocker locker(&m_sharedConnectionMutex); return m_handler->attachToSharedConnection(connectionHandle, sshParameters);
QMetaObject::invokeMethod(m_handler, [this, connectionHandle, sshParameters] { }, Qt::BlockingQueuedConnection, &socketFilePath);
return m_handler->attachToSharedConnection(connectionHandle, sshParameters);
}, Qt::BlockingQueuedConnection, &socketFilePath);
}
if (!socketFilePath.isEmpty()) if (!socketFilePath.isEmpty())
emit connectionHandle->connected(socketFilePath); emit connectionHandle->connected(socketFilePath);
} }