Utils: Improve DeviceShell error handling

In case mktemp or mkfifo fails, the device shell script would
incorrectly print both success and failure messages.

Also cleaning State enum, removing unnecessary values.

Fixes possible crash if creation of master process fails.

Fixes: QTCREATORBUG-28928
Change-Id: I75fef54dc791b2b0a403bab19dab6813b62643ac
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-03-21 11:28:54 +01:00
parent 5633533e0d
commit 9ddd1e1d36
5 changed files with 21 additions and 15 deletions

View File

@@ -62,7 +62,6 @@ DeviceShell::~DeviceShell()
*/ */
RunResult DeviceShell::runInShell(const CommandLine &cmd, const QByteArray &stdInData) RunResult DeviceShell::runInShell(const CommandLine &cmd, const QByteArray &stdInData)
{ {
QTC_ASSERT(m_shellProcess, return {});
Q_ASSERT(QThread::currentThread() != &m_thread); Q_ASSERT(QThread::currentThread() != &m_thread);
return run(cmd, stdInData); return run(cmd, stdInData);
@@ -75,7 +74,7 @@ QStringList DeviceShell::missingFeatures() const { return m_missingFeatures; }
RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData) RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData)
{ {
// If the script failed to install, use QtcProcess directly instead. // If the script failed to install, use QtcProcess directly instead.
bool useProcess = m_shellScriptState == State::NoScript; bool useProcess = m_shellScriptState == State::Failed;
// Transferring large amounts of stdInData is slow via the shell script. // Transferring large amounts of stdInData is slow via the shell script.
// Use QtcProcess directly if the size exceeds 100kb. // Use QtcProcess directly if the size exceeds 100kb.
@@ -197,10 +196,7 @@ bool DeviceShell::start()
return false; return false;
} }
if (!installShellScript()) { if (installShellScript()) {
if (m_shellScriptState == State::FailedToStart)
closeShellProcess();
} else {
connect(m_shellProcess.get(), connect(m_shellProcess.get(),
&QtcProcess::readyReadStandardOutput, &QtcProcess::readyReadStandardOutput,
m_shellProcess.get(), m_shellProcess.get(),
@@ -213,6 +209,10 @@ bool DeviceShell::start()
qCWarning(deviceShellLog) qCWarning(deviceShellLog)
<< "Received unexpected output on stderr:" << stdErr; << "Received unexpected output on stderr:" << stdErr;
}); });
} else if (m_shellProcess->isRunning()) {
m_shellProcess->kill();
m_shellProcess.reset();
return false;
} }
connect(m_shellProcess.get(), &QtcProcess::done, m_shellProcess.get(), [this] { connect(m_shellProcess.get(), &QtcProcess::done, m_shellProcess.get(), [this] {
@@ -247,7 +247,7 @@ bool DeviceShell::checkCommand(const QByteArray &command)
} }
QByteArray out = m_shellProcess->readAllRawStandardOutput(); QByteArray out = m_shellProcess->readAllRawStandardOutput();
if (out.contains("<missing>")) { if (out.contains("<missing>")) {
m_shellScriptState = State::NoScript; m_shellScriptState = State::Failed;
qCWarning(deviceShellLog) << "Command" << command << "was not found"; qCWarning(deviceShellLog) << "Command" << command << "was not found";
m_missingFeatures.append(QString::fromUtf8(command)); m_missingFeatures.append(QString::fromUtf8(command));
return false; return false;
@@ -259,7 +259,7 @@ bool DeviceShell::checkCommand(const QByteArray &command)
bool DeviceShell::installShellScript() bool DeviceShell::installShellScript()
{ {
if (m_forceFailScriptInstallation) { if (m_forceFailScriptInstallation) {
m_shellScriptState = State::NoScript; m_shellScriptState = State::Failed;
return false; return false;
} }
@@ -290,15 +290,19 @@ bool DeviceShell::installShellScript()
} }
QByteArray out = m_shellProcess->readAllRawStandardError(); QByteArray out = m_shellProcess->readAllRawStandardError();
if (out.contains("SCRIPT_INSTALLED")) { if (out.contains("SCRIPT_INSTALLED") && !out.contains("ERROR_INSTALL_SCRIPT")) {
m_shellScriptState = State::Succeeded; m_shellScriptState = State::Succeeded;
return true; return true;
} }
if (out.contains("ERROR_INSTALL_SCRIPT")) { if (out.contains("ERROR_INSTALL_SCRIPT")) {
m_shellScriptState = State::NoScript; m_shellScriptState = State::Failed;
qCWarning(deviceShellLog) << "Failed installing device shell script"; qCWarning(deviceShellLog) << "Failed installing device shell script";
return false; return false;
} }
if (!out.isEmpty()) {
qCWarning(deviceShellLog)
<< "Unexpected output while installing device shell script:" << out;
}
} }
return true; return true;

View File

@@ -28,7 +28,7 @@ class QTCREATOR_UTILS_EXPORT DeviceShell : public QObject
Q_OBJECT Q_OBJECT
public: public:
enum class State { FailedToStart = -1, Unknown = 0, Succeeded = 1, NoScript = 2 }; enum class State { Failed = -1, Unknown = 0, Succeeded = 1 };
enum class ParseType { enum class ParseType {
StdOut, StdOut,

View File

@@ -1,7 +1,7 @@
# Copyright (C) 2022 The Qt Company Ltd. # Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
FINAL_OUT=$(mktemp -u) FINAL_OUT=$(mktemp -u) || exit 2
mkfifo "$FINAL_OUT" mkfifo "$FINAL_OUT" || exit 3
finalOutput() { finalOutput() {
local fileInputBuffer local fileInputBuffer

View File

@@ -222,7 +222,9 @@ void SshSharedConnection::emitConnected()
void SshSharedConnection::emitError(QProcess::ProcessError error, const QString &errorString) void SshSharedConnection::emitError(QProcess::ProcessError error, const QString &errorString)
{ {
m_state = QProcess::NotRunning; m_state = QProcess::NotRunning;
ProcessResultData resultData = m_masterProcess->resultData(); ProcessResultData resultData{-1, QProcess::CrashExit, QProcess::UnknownError, {}};
if (m_masterProcess)
resultData = m_masterProcess->resultData();
resultData.m_error = error; resultData.m_error = error;
resultData.m_errorString = errorString; resultData.m_errorString = errorString;
emit disconnected(resultData); emit disconnected(resultData);

View File

@@ -353,7 +353,7 @@ private slots:
QFETCH(CommandLine, cmdLine); QFETCH(CommandLine, cmdLine);
TestShell shell(cmdLine, true); TestShell shell(cmdLine, true);
QCOMPARE(shell.state(), DeviceShell::State::NoScript); QCOMPARE(shell.state(), DeviceShell::State::Failed);
const RunResult result = shell.runInShell({"echo", {"Hello"}}); const RunResult result = shell.runInShell({"echo", {"Hello"}});
QCOMPARE(result.exitCode, 0); QCOMPARE(result.exitCode, 0);