diff options
author | Christian Kandeler <[email protected]> | 2018-11-23 11:07:57 +0100 |
---|---|---|
committer | Christian Kandeler <[email protected]> | 2018-12-13 15:10:11 +0000 |
commit | d7178b88c4b2572fb83b28f8178940766216deed (patch) | |
tree | 861eb8069fb97c8e8e79f56cb8f88f05126639fc /src/plugins | |
parent | 030d4d01084b04af361f07dd6360dfad8e2cc19c (diff) |
SSH: Use OpenSSH tools
... instead of our own SSH library.
Advantages:
- Full compatibility with OpenSSH behavior guaranteed.
- Minimal maintenance effort.
- Less code to build.
- Big chunk of 3rd party sources can be removed from our repository.
One the downside, Windows users now need to install OpenSSH for
RemoteLinux support. Hoewever, people doing embedded development
probably have it installed anyway.
[ChangeLog] Switched SSH backend to OpenSSH
Fixes: QTCREATORBUG-15744
Fixes: QTCREATORBUG-15807
Fixes: QTCREATORBUG-19306
Fixes: QTCREATORBUG-20210
Change-Id: Ifcfefdd39401e45ba1f4aca35d2c5bf7046c7aab
Reviewed-by: Eike Ziller <[email protected]>
Reviewed-by: Ulf Hermann <[email protected]>
Diffstat (limited to 'src/plugins')
39 files changed, 623 insertions, 755 deletions
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 939d4d3884b..e5bcc779a92 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -31,7 +31,6 @@ #include <coreplugin/messagemanager.h> #include <projectexplorer/projectexplorerconstants.h> -#include <ssh/sshhostkeydatabase.h> #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/persistentsettings.h> @@ -72,7 +71,6 @@ public: static DeviceManager *clonedInstance; QList<IDevice::Ptr> devices; QHash<Core::Id, Core::Id> defaultDevices; - QSsh::SshHostKeyDatabasePtr hostKeyDatabase; Utils::PersistentSettingsWriter *writer = nullptr; }; @@ -134,7 +132,6 @@ void DeviceManager::save() QVariantMap data; data.insert(QLatin1String(DeviceManagerKey), toMap()); d->writer->save(data, Core::ICore::mainWindow()); - d->hostKeyDatabase->store(hostKeysFilePath()); } void DeviceManager::load() @@ -315,11 +312,6 @@ bool DeviceManager::isLoaded() const return d->writer; } -QSsh::SshHostKeyDatabasePtr DeviceManager::hostKeyDatabase() const -{ - return d->hostKeyDatabase; -} - void DeviceManager::setDefaultDevice(Core::Id id) { QTC_ASSERT(this != instance(), return); @@ -356,13 +348,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager if (isInstance) { QTC_ASSERT(!m_instance, return); m_instance = this; - d->hostKeyDatabase = QSsh::SshHostKeyDatabasePtr::create(); - const QString keyFilePath = hostKeysFilePath(); - if (QFileInfo::exists(keyFilePath)) { - QString error; - if (!d->hostKeyDatabase->load(keyFilePath, &error)) - Core::MessageManager::write(error); - } connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, &DeviceManager::save); } } @@ -406,11 +391,6 @@ IDevice::ConstPtr DeviceManager::defaultDevice(Core::Id deviceType) const return id.isValid() ? find(id) : IDevice::ConstPtr(); } -QString DeviceManager::hostKeysFilePath() -{ - return settingsFilePath(QLatin1String("/ssh-hostkeys")).toString(); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.h b/src/plugins/projectexplorer/devicesupport/devicemanager.h index 2b086f06836..bd9c2b606ed 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.h +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.h @@ -33,7 +33,6 @@ #include <memory> -namespace QSsh { class SshHostKeyDatabase; } namespace Utils { class FileName; } namespace ProjectExplorer { @@ -97,8 +96,6 @@ private: static void replaceInstance(); static void removeClonedInstance(); - static QString hostKeysFilePath(); - QSharedPointer<QSsh::SshHostKeyDatabase> hostKeyDatabase() const; static Utils::FileName settingsFilePath(const QString &extension); static Utils::FileName systemSettingsFilePath(const QString &deviceFileRelativePath); static void copy(const DeviceManager *source, DeviceManager *target, bool deep); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 2ff4090ffd5..0487954312d 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -120,16 +120,14 @@ const char PortsSpecKey[] = "FreePortsSpec"; const char UserNameKey[] = "Uname"; const char AuthKey[] = "Authentication"; const char KeyFileKey[] = "KeyFile"; -const char PasswordKey[] = "Password"; const char TimeoutKey[] = "Timeout"; const char HostKeyCheckingKey[] = "HostKeyChecking"; -const char SshOptionsKey[] = "SshOptions"; const char DebugServerKey[] = "DebugServerKey"; const char QmlsceneKey[] = "QmlsceneKey"; using AuthType = QSsh::SshConnectionParameters::AuthenticationType; -const AuthType DefaultAuthType = QSsh::SshConnectionParameters::AuthenticationTypePublicKey; +const AuthType DefaultAuthType = QSsh::SshConnectionParameters::AuthenticationTypeAll; const IDevice::MachineType DefaultMachineType = IDevice::Hardware; const int DefaultTimeout = 10; @@ -161,7 +159,6 @@ DeviceTester::DeviceTester(QObject *parent) : QObject(parent) { } IDevice::IDevice() : d(new Internal::IDevicePrivate) { - d->sshParameters.hostKeyDatabase = DeviceManager::instance()->hostKeyDatabase(); } IDevice::IDevice(Core::Id type, Origin origin, MachineType machineType, Core::Id id) @@ -172,7 +169,6 @@ IDevice::IDevice(Core::Id type, Origin origin, MachineType machineType, Core::Id d->machineType = machineType; QTC_CHECK(origin == ManuallyAdded || id.isValid()); d->id = id.isValid() ? id : newId(); - d->sshParameters.hostKeyDatabase = DeviceManager::instance()->hostKeyDatabase(); } IDevice::IDevice(const IDevice &other) @@ -328,16 +324,19 @@ void IDevice::fromMap(const QVariantMap &map) d->sshParameters.setHost(map.value(QLatin1String(HostKey)).toString()); d->sshParameters.setPort(map.value(QLatin1String(SshPortKey), 22).toInt()); d->sshParameters.setUserName(map.value(QLatin1String(UserNameKey)).toString()); - d->sshParameters.authenticationType - = static_cast<AuthType>(map.value(QLatin1String(AuthKey), DefaultAuthType).toInt()); - d->sshParameters.setPassword(map.value(QLatin1String(PasswordKey)).toString()); + + // Pre-4.9, the authentication enum used to have more values + const int storedAuthType = map.value(QLatin1String(AuthKey), DefaultAuthType).toInt(); + const bool outdatedAuthType = storedAuthType + > QSsh::SshConnectionParameters::AuthenticationTypeSpecificKey; + d->sshParameters.authenticationType = outdatedAuthType + ? QSsh::SshConnectionParameters::AuthenticationTypeAll + : static_cast<AuthType>(storedAuthType); + d->sshParameters.privateKeyFile = map.value(QLatin1String(KeyFileKey), defaultPrivateKeyFilePath()).toString(); d->sshParameters.timeout = map.value(QLatin1String(TimeoutKey), DefaultTimeout).toInt(); d->sshParameters.hostKeyCheckingMode = static_cast<QSsh::SshHostKeyCheckingMode> (map.value(QLatin1String(HostKeyCheckingKey), QSsh::SshHostKeyCheckingNone).toInt()); - const QVariant optionsVariant = map.value(QLatin1String(SshOptionsKey)); - if (optionsVariant.isValid()) // false for QtC < 3.4 - d->sshParameters.options = QSsh::SshConnectionOptions(optionsVariant.toInt()); QString portsSpec = map.value(PortsSpecKey).toString(); if (portsSpec.isEmpty()) @@ -369,11 +368,9 @@ QVariantMap IDevice::toMap() const map.insert(QLatin1String(SshPortKey), d->sshParameters.port()); map.insert(QLatin1String(UserNameKey), d->sshParameters.userName()); map.insert(QLatin1String(AuthKey), d->sshParameters.authenticationType); - map.insert(QLatin1String(PasswordKey), d->sshParameters.password()); map.insert(QLatin1String(KeyFileKey), d->sshParameters.privateKeyFile); map.insert(QLatin1String(TimeoutKey), d->sshParameters.timeout); map.insert(QLatin1String(HostKeyCheckingKey), d->sshParameters.hostKeyCheckingMode); - map.insert(QLatin1String(SshOptionsKey), static_cast<int>(d->sshParameters.options)); map.insert(QLatin1String(PortsSpecKey), d->freePorts.toString()); map.insert(QLatin1String(VersionKey), d->version); @@ -404,7 +401,6 @@ QSsh::SshConnectionParameters IDevice::sshParameters() const void IDevice::setSshParameters(const QSsh::SshConnectionParameters &sshParameters) { d->sshParameters = sshParameters; - d->sshParameters.hostKeyDatabase = DeviceManager::instance()->hostKeyDatabase(); } QUrl IDevice::toolControlChannel(const ControlChannelHint &) const diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp index 9feb7e71663..e3cc959a5d1 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp @@ -39,18 +39,19 @@ namespace ProjectExplorer { +enum class Signal { Interrupt, Terminate, Kill }; + class SshDeviceProcess::SshDeviceProcessPrivate { public: SshDeviceProcessPrivate(SshDeviceProcess *q) : q(q) {} SshDeviceProcess * const q; - bool serverSupportsSignals = false; QSsh::SshConnection *connection = nullptr; - QSsh::SshRemoteProcess::Ptr process; + QSsh::SshRemoteProcessPtr process; Runnable runnable; QString errorMessage; - QSsh::SshRemoteProcess::ExitStatus exitStatus; + QProcess::ExitStatus exitStatus = QProcess::NormalExit; DeviceProcessSignalOperation::Ptr killOperation; QTimer killTimer; QByteArray stdOut; @@ -59,7 +60,7 @@ public: enum State { Inactive, Connecting, Connected, ProcessRunning } state = Inactive; void setState(State newState); - void doSignal(QSsh::SshRemoteProcess::Signal signal); + void doSignal(Signal signal); QString displayName() const { @@ -85,15 +86,12 @@ void SshDeviceProcess::start(const Runnable &runnable) d->errorMessage.clear(); d->exitCode = -1; + d->exitStatus = QProcess::NormalExit; d->runnable = runnable; - d->connection = QSsh::acquireConnection(device()->sshParameters()); - const QString displayName = d->displayName(); - const QString connDisplayName = d->connection->x11DisplayName(); - if (!displayName.isEmpty() && !connDisplayName.isEmpty() && connDisplayName != displayName) { - QSsh::releaseConnection(d->connection); - d->connection = new QSsh::SshConnection(device()->sshParameters(), this); - } - connect(d->connection, &QSsh::SshConnection::error, + QSsh::SshConnectionParameters params = device()->sshParameters(); + params.x11DisplayName = d->displayName(); + d->connection = QSsh::acquireConnection(params); + connect(d->connection, &QSsh::SshConnection::errorOccurred, this, &SshDeviceProcess::handleConnectionError); connect(d->connection, &QSsh::SshConnection::disconnected, this, &SshDeviceProcess::handleDisconnected); @@ -110,17 +108,17 @@ void SshDeviceProcess::start(const Runnable &runnable) void SshDeviceProcess::interrupt() { QTC_ASSERT(d->state == SshDeviceProcessPrivate::ProcessRunning, return); - d->doSignal(QSsh::SshRemoteProcess::IntSignal); + d->doSignal(Signal::Interrupt); } void SshDeviceProcess::terminate() { - d->doSignal(QSsh::SshRemoteProcess::TermSignal); + d->doSignal(Signal::Terminate); } void SshDeviceProcess::kill() { - d->doSignal(QSsh::SshRemoteProcess::KillSignal); + d->doSignal(Signal::Kill); } QProcess::ProcessState SshDeviceProcess::state() const @@ -169,11 +167,6 @@ QByteArray SshDeviceProcess::readAllStandardError() return data; } -void SshDeviceProcess::setSshServerSupportsSignals(bool signalsSupported) -{ - d->serverSupportsSignals = signalsSupported; -} - qint64 SshDeviceProcess::processId() const { return 0; @@ -185,15 +178,11 @@ void SshDeviceProcess::handleConnected() d->setState(SshDeviceProcessPrivate::Connected); d->process = d->connection->createRemoteProcess(fullCommandLine(d->runnable).toUtf8()); - connect(d->process.data(), &QSsh::SshRemoteProcess::started, this, &SshDeviceProcess::handleProcessStarted); - connect(d->process.data(), &QSsh::SshRemoteProcess::closed, this, &SshDeviceProcess::handleProcessFinished); - connect(d->process.data(), &QSsh::SshRemoteProcess::readyReadStandardOutput, this, &SshDeviceProcess::handleStdout); - connect(d->process.data(), &QSsh::SshRemoteProcess::readyReadStandardError, this, &SshDeviceProcess::handleStderr); - - d->process->clearEnvironment(); - const Utils::Environment env = d->runnable.environment; - for (Utils::Environment::const_iterator it = env.constBegin(); it != env.constEnd(); ++it) - d->process->addToEnvironment(env.key(it).toUtf8(), env.value(it).toUtf8()); + connect(d->process.get(), &QSsh::SshRemoteProcess::started, this, &SshDeviceProcess::handleProcessStarted); + connect(d->process.get(), &QSsh::SshRemoteProcess::done, this, &SshDeviceProcess::handleProcessFinished); + connect(d->process.get(), &QSsh::SshRemoteProcess::readyReadStandardOutput, this, &SshDeviceProcess::handleStdout); + connect(d->process.get(), &QSsh::SshRemoteProcess::readyReadStandardError, this, &SshDeviceProcess::handleStderr); + const QString display = d->displayName(); if (!display.isEmpty()) d->process->requestX11Forwarding(display); @@ -234,24 +223,10 @@ void SshDeviceProcess::handleProcessStarted() emit started(); } -void SshDeviceProcess::handleProcessFinished(int exitStatus) +void SshDeviceProcess::handleProcessFinished() { - d->exitStatus = static_cast<QSsh::SshRemoteProcess::ExitStatus>(exitStatus); - switch (d->exitStatus) { - case QSsh::SshRemoteProcess::FailedToStart: - QTC_ASSERT(d->state == SshDeviceProcessPrivate::Connected, return); - break; - case QSsh::SshRemoteProcess::CrashExit: - QTC_ASSERT(d->state == SshDeviceProcessPrivate::ProcessRunning, return); - break; - case QSsh::SshRemoteProcess::NormalExit: - QTC_ASSERT(d->state == SshDeviceProcessPrivate::ProcessRunning, return); - d->exitCode = d->process->exitCode(); - break; - default: - QTC_ASSERT(false, return); - } d->errorMessage = d->process->errorString(); + d->exitCode = d->process->exitCode(); d->setState(SshDeviceProcessPrivate::Inactive); emit finished(); } @@ -302,7 +277,7 @@ QString SshDeviceProcess::fullCommandLine(const Runnable &runnable) const return cmdLine; } -void SshDeviceProcess::SshDeviceProcessPrivate::doSignal(QSsh::SshRemoteProcess::Signal signal) +void SshDeviceProcess::SshDeviceProcessPrivate::doSignal(Signal signal) { switch (state) { case SshDeviceProcessPrivate::Inactive: @@ -315,28 +290,24 @@ void SshDeviceProcess::SshDeviceProcessPrivate::doSignal(QSsh::SshRemoteProcess: break; case SshDeviceProcessPrivate::Connected: case SshDeviceProcessPrivate::ProcessRunning: - if (serverSupportsSignals) { - process->sendSignal(signal); + DeviceProcessSignalOperation::Ptr signalOperation = q->device()->signalOperation(); + quint64 processId = q->processId(); + if (signal == Signal::Interrupt) { + if (processId != 0) + signalOperation->interruptProcess(processId); + else + signalOperation->interruptProcess(runnable.executable); } else { - DeviceProcessSignalOperation::Ptr signalOperation = q->device()->signalOperation(); - quint64 processId = q->processId(); - if (signal == QSsh::SshRemoteProcess::IntSignal) { - if (processId != 0) - signalOperation->interruptProcess(processId); - else - signalOperation->interruptProcess(runnable.executable); - } else { - if (killOperation) // We are already in the process of killing the app. - return; - killOperation = signalOperation; - connect(signalOperation.data(), &DeviceProcessSignalOperation::finished, q, - &SshDeviceProcess::handleKillOperationFinished); - killTimer.start(5000); - if (processId != 0) - signalOperation->killProcess(processId); - else - signalOperation->killProcess(runnable.executable); - } + if (killOperation) // We are already in the process of killing the app. + return; + killOperation = signalOperation; + connect(signalOperation.data(), &DeviceProcessSignalOperation::finished, q, + &SshDeviceProcess::handleKillOperationFinished); + killTimer.start(5000); + if (processId != 0) + signalOperation->killProcess(processId); + else + signalOperation->killProcess(runnable.executable); } break; } @@ -360,10 +331,7 @@ void SshDeviceProcess::SshDeviceProcessPrivate::setState(SshDeviceProcess::SshDe process->disconnect(q); if (connection) { connection->disconnect(q); - if (connection->parent() == q) - connection->deleteLater(); - else - QSsh::releaseConnection(connection); + QSsh::releaseConnection(connection); connection = nullptr; } } diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h index 19acc317698..7bbe942354e 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h @@ -55,15 +55,12 @@ public: qint64 write(const QByteArray &data) override; - // Default is "false" due to OpenSSH not implementing this feature for some reason. - void setSshServerSupportsSignals(bool signalsSupported); - private: void handleConnected(); void handleConnectionError(); void handleDisconnected(); void handleProcessStarted(); - void handleProcessFinished(int exitStatus); + void handleProcessFinished(); void handleStdout(); void handleStderr(); void handleKillOperationFinished(const QString &errorMessage); diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp index 589b3d17218..c1c7b0a45f7 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp @@ -72,31 +72,21 @@ void SshDeviceProcessList::handleConnectionError() reportError(tr("Connection failure: %1").arg(d->process.lastConnectionErrorString())); } -void SshDeviceProcessList::handleListProcessFinished(int exitStatus) +void SshDeviceProcessList::handleListProcessFinished(const QString &error) { setFinished(); - switch (exitStatus) { - case SshRemoteProcess::FailedToStart: - handleProcessError(tr("Error: Process listing command failed to start: %1") - .arg(d->process.processErrorString())); - break; - case SshRemoteProcess::CrashExit: - handleProcessError(tr("Error: Process listing command crashed: %1") - .arg(d->process.processErrorString())); - break; - case SshRemoteProcess::NormalExit: - if (d->process.processExitCode() == 0) { - const QByteArray remoteStdout = d->process.readAllStandardOutput(); - const QString stdoutString - = QString::fromUtf8(remoteStdout.data(), remoteStdout.count()); - reportProcessListUpdated(buildProcessList(stdoutString)); - } else { - handleProcessError(tr("Process listing command failed with exit code %1.") - .arg(d->process.processExitCode())); - } - break; - default: - Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid exit status"); + if (!error.isEmpty()) { + handleProcessError(error); + return; + } + if (d->process.processExitCode() == 0) { + const QByteArray remoteStdout = d->process.readAllStandardOutput(); + const QString stdoutString + = QString::fromUtf8(remoteStdout.data(), remoteStdout.count()); + reportProcessListUpdated(buildProcessList(stdoutString)); + } else { + handleProcessError(tr("Process listing command failed with exit code %1.") + .arg(d->process.processExitCode())); } } diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h index 8ecefce9b93..60937e65cc0 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h @@ -40,7 +40,7 @@ public: private: void handleConnectionError(); - void handleListProcessFinished(int exitStatus); + void handleListProcessFinished(const QString &error); void handleKillProcessFinished(const QString &errorString); virtual QString listProcessesCommandLine() const = 0; diff --git a/src/plugins/projectexplorer/devicesupport/sshsettingspage.cpp b/src/plugins/projectexplorer/devicesupport/sshsettingspage.cpp new file mode 100644 index 00000000000..c87d27d9784 --- /dev/null +++ b/src/plugins/projectexplorer/devicesupport/sshsettingspage.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "sshsettingspage.h" + +#include <coreplugin/icore.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <ssh/sshsettings.h> +#include <utils/pathchooser.h> + +#include <QCheckBox> +#include <QCoreApplication> +#include <QFormLayout> +#include <QLabel> +#include <QSpinBox> +#include <QString> + +using namespace QSsh; +using namespace Utils; + +namespace ProjectExplorer { +namespace Internal { + +class SshSettingsWidget : public QWidget +{ + Q_OBJECT +public: + SshSettingsWidget(); + void saveSettings(); + +private: + void setupConnectionSharingCheckBox(); + void setupConnectionSharingSpinBox(); + void setupSshPathChooser(); + void setupSftpPathChooser(); + void setupAskpassPathChooser(); + void setupKeygenPathChooser(); + void setupPathChooser(PathChooser &chooser, const FileName &initialPath, bool &changedFlag); + void updateSpinboxEnabled(); + + QCheckBox m_connectionSharingCheckBox; + QSpinBox m_connectionSharingSpinBox; + PathChooser m_sshChooser; + PathChooser m_sftpChooser; + PathChooser m_askpassChooser; + PathChooser m_keygenChooser; + bool m_sshPathChanged = false; + bool m_sftpPathChanged = false; + bool m_askpassPathChanged = false; + bool m_keygenPathChanged = false; +}; + +SshSettingsPage::SshSettingsPage(QObject *parent) : Core::IOptionsPage(parent) +{ + setId(Constants::SSH_SETTINGS_PAGE_ID); + setDisplayName(tr("SSH")); + setCategory(Constants::DEVICE_SETTINGS_CATEGORY); + setDisplayCategory(QCoreApplication::translate("ProjectExplorer", "SSH")); + setCategoryIcon(Utils::Icon({{":/projectexplorer/images/settingscategory_devices.png", + Utils::Theme::PanelTextColorDark}}, Utils::Icon::Tint)); +} + +QWidget *SshSettingsPage::widget() +{ + if (!m_widget) + m_widget = new SshSettingsWidget; + return m_widget; +} + +void SshSettingsPage::apply() +{ + m_widget->saveSettings(); +} + +void SshSettingsPage::finish() +{ + delete m_widget; +} + + +SshSettingsWidget::SshSettingsWidget() +{ + setupConnectionSharingCheckBox(); + setupConnectionSharingSpinBox(); + setupSshPathChooser(); + setupSftpPathChooser(); + setupAskpassPathChooser(); + setupKeygenPathChooser(); + auto * const layout = new QFormLayout(this); + layout->addRow(tr("Enable connection sharing:"), &m_connectionSharingCheckBox); + layout->addRow(tr("Connection sharing timeout:"), &m_connectionSharingSpinBox); + layout->addRow(tr("Path to ssh executable:"), &m_sshChooser); + layout->addRow(tr("Path to sftp executable:"), &m_sftpChooser); + layout->addRow(tr("Path to ssh-askpass executable:"), &m_askpassChooser); + layout->addRow(tr("Path to ssh-keygen executable:"), &m_keygenChooser); + updateSpinboxEnabled(); +} + +void SshSettingsWidget::saveSettings() +{ + SshSettings::setConnectionSharingEnabled(m_connectionSharingCheckBox.isChecked()); + SshSettings::setConnectionSharingTimeout(m_connectionSharingSpinBox.value()); + if (m_sshPathChanged) + SshSettings::setSshFilePath(m_sshChooser.fileName()); + if (m_sftpPathChanged) + SshSettings::setSftpFilePath(m_sftpChooser.fileName()); + if (m_askpassPathChanged) + SshSettings::setAskpassFilePath(m_askpassChooser.fileName()); + if (m_keygenPathChanged) + SshSettings::setKeygenFilePath(m_keygenChooser.fileName()); + SshSettings::storeSettings(Core::ICore::settings()); +} + +void SshSettingsWidget::setupConnectionSharingCheckBox() +{ + m_connectionSharingCheckBox.setChecked(SshSettings::connectionSharingEnabled()); + connect(&m_connectionSharingCheckBox, &QCheckBox::toggled, + this, &SshSettingsWidget::updateSpinboxEnabled); +} + +void SshSettingsWidget::setupConnectionSharingSpinBox() +{ + m_connectionSharingSpinBox.setMinimum(1); + m_connectionSharingSpinBox.setValue(SshSettings::connectionSharingTimeout()); + m_connectionSharingSpinBox.setSuffix(tr(" minutes")); +} + +void SshSettingsWidget::setupSshPathChooser() +{ + setupPathChooser(m_sshChooser, SshSettings::sshFilePath(), m_sshPathChanged); +} + +void SshSettingsWidget::setupSftpPathChooser() +{ + setupPathChooser(m_sftpChooser, SshSettings::sftpFilePath(), m_sftpPathChanged); +} + +void SshSettingsWidget::setupAskpassPathChooser() +{ + setupPathChooser(m_askpassChooser, SshSettings::askpassFilePath(), m_askpassPathChanged); +} + +void SshSettingsWidget::setupKeygenPathChooser() +{ + setupPathChooser(m_keygenChooser, SshSettings::keygenFilePath(), m_keygenPathChanged); +} + +void SshSettingsWidget::setupPathChooser(PathChooser &chooser, const FileName &initialPath, + bool &changedFlag) +{ + chooser.setExpectedKind(PathChooser::ExistingCommand); + chooser.setFileName(initialPath); + connect(&chooser, &PathChooser::pathChanged, [&changedFlag] { changedFlag = true; }); +} + +void SshSettingsWidget::updateSpinboxEnabled() +{ + m_connectionSharingSpinBox.setEnabled(m_connectionSharingCheckBox.isChecked()); + static_cast<QFormLayout *>(layout())->labelForField(&m_connectionSharingSpinBox) + ->setEnabled(m_connectionSharingCheckBox.isChecked()); +} + +} // namespace Internal +} // namespace ProjectExplorer + +#include <sshsettingspage.moc> diff --git a/src/plugins/projectexplorer/devicesupport/sshsettingspage.h b/src/plugins/projectexplorer/devicesupport/sshsettingspage.h new file mode 100644 index 00000000000..e43ef93826a --- /dev/null +++ b/src/plugins/projectexplorer/devicesupport/sshsettingspage.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <coreplugin/dialogs/ioptionspage.h> + +#include <QPointer> + +namespace ProjectExplorer { +namespace Internal { + +class SshSettingsWidget; + +class SshSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + SshSettingsPage(QObject *parent = 0); + +private: + QWidget *widget() override; + void apply() override; + void finish() override; + + QPointer<SshSettingsWidget> m_widget; +}; + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 5048c2d0ca1..068b4f720b8 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -84,6 +84,7 @@ #include "devicesupport/desktopdevicefactory.h" #include "devicesupport/devicemanager.h" #include "devicesupport/devicesettingspage.h" +#include "devicesupport/sshsettingspage.h" #include "targetsettingspanel.h" #include "projectpanelfactory.h" #include "waitforstopdialog.h" @@ -119,6 +120,7 @@ #include <texteditor/textdocument.h> #include <texteditor/texteditorconstants.h> #include <ssh/sshconnection.h> +#include <ssh/sshsettings.h> #include <utils/algorithm.h> #include <utils/fileutils.h> @@ -536,6 +538,7 @@ public: // Settings pages ProjectExplorerSettingsPage m_projectExplorerSettingsPage; DeviceSettingsPage m_deviceSettingsPage; + SshSettingsPage m_sshSettingsPage; ProjectTreeWidgetFactory m_projectTreeFactory; FolderNavigationWidgetFactory m_folderNavigationWidgetFactory; @@ -1715,6 +1718,28 @@ void ProjectExplorerPlugin::extensionsInitialized() BuildManager::extensionsInitialized(); DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice)); + + QSsh::SshSettings::loadSettings(Core::ICore::settings()); + if (Utils::HostOsInfo::isWindowsHost()) { + const auto searchPathRetriever = [] { + const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git") + .toString(); + const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path") + .toString().split(':', QString::SkipEmptyParts); + const Utils::FileNameList gitSearchPaths = Utils::transform(rawGitSearchPaths, + [](const QString &rawPath) { return Utils::FileName::fromString(rawPath); }); + const Utils::FileName fullGitPath = Utils::Environment::systemEnvironment() + .searchInPath(gitBinary, gitSearchPaths); + if (fullGitPath.isEmpty()) + return Utils::FileNameList(); + return Utils::FileNameList{ + fullGitPath.parentDir(), + fullGitPath.parentDir().parentDir() + "/usr/bin" + }; + }; + QSsh::SshSettings::setExtraSearchPathRetriever(searchPathRetriever); + } + // delay restoring kits until UI is shown for improved perceived startup performance QTimer::singleShot(0, this, &ProjectExplorerPlugin::restoreKits); } diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index 21f70f48e26..8ef32ae9bad 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -130,6 +130,7 @@ HEADERS += projectexplorer.h \ devicesupport/localprocesslist.h \ devicesupport/sshdeviceprocess.h \ devicesupport/sshdeviceprocesslist.h \ + devicesupport/sshsettingspage.h \ devicesupport/desktopdeviceconfigurationwidget.h \ devicesupport/desktopprocesssignaloperation.h \ deploymentdata.h \ @@ -271,6 +272,7 @@ SOURCES += projectexplorer.cpp \ devicesupport/localprocesslist.cpp \ devicesupport/sshdeviceprocess.cpp \ devicesupport/sshdeviceprocesslist.cpp \ + devicesupport/sshsettingspage.cpp \ devicesupport/desktopdeviceconfigurationwidget.cpp \ devicesupport/desktopprocesssignaloperation.cpp \ deployablefile.cpp \ diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index d25b52c7d42..5836c06c458 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -220,6 +220,7 @@ Project { "localprocesslist.cpp", "localprocesslist.h", "sshdeviceprocess.cpp", "sshdeviceprocess.h", "sshdeviceprocesslist.cpp", "sshdeviceprocesslist.h", + "sshsettingspage.cpp", "sshsettingspage.h", "desktopprocesssignaloperation.cpp", "desktopprocesssignaloperation.h", "desktopdeviceconfigurationwidget.cpp", "desktopdeviceconfigurationwidget.h", "desktopdeviceconfigurationwidget.ui" ] diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 61f1429fbe9..de81a428336 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -107,6 +107,7 @@ const char KITS_SETTINGS_CATEGORY[] = "A.Kits"; // Kits pages const char KITS_SETTINGS_PAGE_ID[] = "D.ProjectExplorer.KitsOptions"; const char DEVICE_SETTINGS_PAGE_ID[] = "E.ProjectExplorer.DeviceOptions"; +const char SSH_SETTINGS_PAGE_ID[] = "F.ProjectExplorer.SshOptions"; const char TOOLCHAIN_SETTINGS_PAGE_ID[] = "M.ProjectExplorer.ToolChainOptions"; const char DEBUGGER_SETTINGS_PAGE_ID[] = "N.ProjectExplorer.DebuggerOptions"; diff --git a/src/plugins/qnx/qnxdevicetester.cpp b/src/plugins/qnx/qnxdevicetester.cpp index 3c8c37e98a9..004de59acbe 100644 --- a/src/plugins/qnx/qnxdevicetester.cpp +++ b/src/plugins/qnx/qnxdevicetester.cpp @@ -113,12 +113,12 @@ void QnxDeviceTester::handleGenericTestFinished(TestResult result) testNextCommand(); } -void QnxDeviceTester::handleProcessFinished(int exitStatus) +void QnxDeviceTester::handleProcessFinished(const QString &error) { QTC_ASSERT(m_state == CommandsTest, return); const QString command = m_commandsToTest[m_currentCommandIndex]; - if (exitStatus == QSsh::SshRemoteProcess::NormalExit) { + if (error.isEmpty()) { if (m_processRunner->processExitCode() == 0) { emit progressMessage(tr("%1 found.").arg(command) + QLatin1Char('\n')); } else { diff --git a/src/plugins/qnx/qnxdevicetester.h b/src/plugins/qnx/qnxdevicetester.h index 67acf1c776e..91d5548bda4 100644 --- a/src/plugins/qnx/qnxdevicetester.h +++ b/src/plugins/qnx/qnxdevicetester.h @@ -46,7 +46,7 @@ public: private slots: void handleGenericTestFinished(ProjectExplorer::DeviceTester::TestResult result); - void handleProcessFinished(int exitStatus); + void handleProcessFinished(const QString &error); void handleConnectionError(); private: diff --git a/src/plugins/qnx/qnxdevicewizard.cpp b/src/plugins/qnx/qnxdevicewizard.cpp index 58b71f95863..2ca8968388b 100644 --- a/src/plugins/qnx/qnxdevicewizard.cpp +++ b/src/plugins/qnx/qnxdevicewizard.cpp @@ -63,12 +63,10 @@ QnxDeviceWizard::QnxDeviceWizard(QWidget *parent) : IDevice::Ptr QnxDeviceWizard::device() { QSsh::SshConnectionParameters sshParams; - sshParams.options = QSsh::SshIgnoreDefaultProxy; sshParams.url = m_setupPage->url(); sshParams.timeout = 10; sshParams.authenticationType = m_setupPage->authenticationType(); - if (sshParams.authenticationType != QSsh::SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods - && sshParams.authenticationType != QSsh::SshConnectionParameters::AuthenticationTypePassword) + if (sshParams.authenticationType == QSsh::SshConnectionParameters::AuthenticationTypeSpecificKey) sshParams.privateKeyFile = m_setupPage->privateKeyFilePath(); QnxDevice::Ptr device = QnxDevice::create(m_setupPage->configurationName(), diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp index c63e16ce5ab..12e47ca0d20 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp @@ -196,7 +196,7 @@ void AbstractRemoteLinuxDeployService::handleDeviceSetupDone(bool success) d->state = Connecting; d->connection = QSsh::acquireConnection(deviceConfiguration()->sshParameters()); - connect(d->connection, &SshConnection::error, + connect(d->connection, &SshConnection::errorOccurred, this, &AbstractRemoteLinuxDeployService::handleConnectionFailure); if (d->connection->state() == SshConnection::Connected) { handleConnected(); diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp index 3112d519c8a..100cf040201 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.cpp +++ b/src/plugins/remotelinux/genericdirectuploadservice.cpp @@ -26,9 +26,10 @@ #include "genericdirectuploadservice.h" #include <projectexplorer/deployablefile.h> +#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> -#include <ssh/sftpchannel.h> +#include <ssh/sftptransfer.h> #include <ssh/sshconnection.h> #include <ssh/sshremoteprocess.h> @@ -44,46 +45,27 @@ using namespace QSsh; namespace RemoteLinux { namespace Internal { -namespace { -enum State { Inactive, InitializingSftp, Uploading }; -} // anonymous namespace - -enum class JobType { - PreQuery, - Upload, - Mkdir, - Ln, - Chmod, - PostQuery, - None -}; -struct Job -{ - DeployableFile file; - JobType type; - QDateTime result; - explicit Job(const DeployableFile &file = DeployableFile(), JobType type = JobType::None, - const QDateTime &result = QDateTime()) - : file(file), type(type), result(result) {} -}; +enum State { Inactive, PreChecking, Uploading, PostProcessing }; class GenericDirectUploadServicePrivate { public: + DeployableFile getFileForProcess(SshRemoteProcess *proc) + { + const auto it = remoteProcs.find(proc); + QTC_ASSERT(it != remoteProcs.end(), return DeployableFile()); + const DeployableFile file = *it; + remoteProcs.erase(it); + return file; + } + bool incremental = false; bool ignoreMissingFiles = false; - bool uploadJobRunning = false; + QHash<SshRemoteProcess *, DeployableFile> remoteProcs; State state = Inactive; - QList<DeployableFile> filesToUpload; - - QHash<SftpJobId, Job> runningJobs; - - SshRemoteProcess::Ptr runningProc; - DeployableFile runningProcFile; - - SftpChannel::Ptr uploader; + SftpTransferPtr uploader; QList<DeployableFile> deployableFiles; }; @@ -145,253 +127,62 @@ void GenericDirectUploadService::stopDeviceSetup() void GenericDirectUploadService::doDeploy() { QTC_ASSERT(d->state == Inactive, setFinished(); return); - - d->uploader = connection()->createSftpChannel(); - connect(d->uploader.data(), &SftpChannel::initialized, - this, &GenericDirectUploadService::handleSftpInitialized); - connect(d->uploader.data(), &SftpChannel::channelError, - this, &GenericDirectUploadService::handleSftpChannelError); - d->uploader->initialize(); - d->state = InitializingSftp; -} - -void GenericDirectUploadService::handleSftpInitialized() -{ - QTC_ASSERT(d->state == InitializingSftp, setFinished(); return); - QTC_ASSERT(!d->deployableFiles.isEmpty(), setFinished(); return); - connect(d->uploader.data(), &SftpChannel::finished, - this, &GenericDirectUploadService::handleJobFinished); - connect(d->uploader.data(), &SftpChannel::fileInfoAvailable, - this, &GenericDirectUploadService::handleFileInfoAvailable); - d->state = Uploading; + d->state = PreChecking; queryFiles(); } -void GenericDirectUploadService::handleSftpChannelError(const QString &message) +QDateTime GenericDirectUploadService::timestampFromStat(const DeployableFile &file, + SshRemoteProcess *statProc) { - QTC_ASSERT(d->state == InitializingSftp, setFinished(); return); - - emit errorMessage(tr("SFTP initialization failed: %1").arg(message)); - setFinished(); - handleDeploymentDone(); -} - -void GenericDirectUploadService::handleFileInfoAvailable(SftpJobId jobId, - const QList<SftpFileInfo> &fileInfos) -{ - QTC_ASSERT(d->state == Uploading, return); - QTC_ASSERT(fileInfos.length() == 1, return); - auto it = d->runningJobs.find(jobId); - QTC_ASSERT(it != d->runningJobs.end(), return); - it->result = QDateTime::fromSecsSinceEpoch(fileInfos.at(0).mtime); -} - -void GenericDirectUploadService::handleJobFinished(SftpJobId jobId, const QString &errorMsg) -{ - auto it = d->runningJobs.find(jobId); - QTC_ASSERT(it != d->runningJobs.end(), return); - - Job job = *it; - d->runningJobs.erase(it); - - switch (job.type) { - case JobType::PreQuery: - if (hasRemoteFileChanged(job.file, job.result)) { - d->filesToUpload.append(job.file); - if (!d->uploadJobRunning) - uploadNextFile(); - } else { - tryFinish(); - } - break; - case JobType::Upload: - QTC_CHECK(d->uploadJobRunning); - - if (!errorMsg.isEmpty()) { - QString errorString = tr("Upload of file \"%1\" failed. The server said: \"%2\".") - .arg(job.file.localFilePath().toUserOutput(), errorMsg); - if (errorMsg == QLatin1String("Failure") - && job.file.remoteDirectory().contains(QLatin1String("/bin"))) { - errorString += QLatin1Char(' ') - + tr("If \"%1\" is currently running on the remote host, " - "you might need to stop it first.").arg(job.file.remoteFilePath()); - } - emit errorMessage(errorString); - setFinished(); - handleDeploymentDone(); - return; - } - - // This is done for Windows. - if (job.file.isExecutable()) { - const QString command = QLatin1String("chmod a+x ") - + Utils::QtcProcess::quoteArgUnix(job.file.remoteFilePath()); - d->runningProc = connection()->createRemoteProcess(command.toUtf8()); - d->runningProcFile = job.file; - connect(d->runningProc.data(), &SshRemoteProcess::closed, - this, &GenericDirectUploadService::handleUploadProcFinished); - connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardOutput, - this, &GenericDirectUploadService::handleStdOutData); - connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardError, - this, &GenericDirectUploadService::handleStdErrData); - connect(d->runningProc.data(), &SshRemoteProcess::readChannelFinished, - this, &GenericDirectUploadService::handleReadChannelFinished); - d->runningProc->start(); - } else { - d->uploadJobRunning = false; - const SftpJobId jobId = d->uploader->statFile(job.file.remoteFilePath()); - if (jobId == SftpInvalidJob) { - emit errorMessage(tr("SFTP stat query for %1 failed.") - .arg(job.file.remoteFilePath())); - saveDeploymentTimeStamp(job.file, QDateTime()); - } else { - d->runningJobs.insert(jobId, Job(job.file, JobType::PostQuery)); - } - uploadNextFile(); - } - break; - case JobType::PostQuery: - if (!errorMsg.isEmpty()) { - emit warningMessage(tr("Could not determine remote timestamp of %1: %2") - .arg(job.file.remoteFilePath()).arg(errorMsg)); - } - saveDeploymentTimeStamp(job.file, job.result); - tryFinish(); - break; - default: - QTC_CHECK(false); - break; + QString errorDetails; + if (statProc->exitStatus() != QProcess::NormalExit) + errorDetails = statProc->errorString(); + else if (statProc->exitCode() != 0) + errorDetails = QString::fromUtf8(statProc->readAllStandardError()); + if (!errorDetails.isEmpty()) { + emit warningMessage(tr("Failed to retrieve remote timestamp for file \"%1\". " + "Incremental deployment will not work. Error message was: %2") + .arg(file.remoteFilePath(), errorDetails)); + return QDateTime(); } -} - -void GenericDirectUploadService::clearRunningProc() -{ - d->runningProc.clear(); - d->runningProcFile = DeployableFile(); - d->uploadJobRunning = false; -} - -void GenericDirectUploadService::handleUploadProcFinished(int exitStatus) -{ - QTC_ASSERT(d->state == Uploading, setFinished(); return); - QTC_ASSERT(d->uploadJobRunning, return); - - if (exitStatus != SshRemoteProcess::NormalExit || d->runningProc->exitCode() != 0) - handleProcFailure(); - else - runPostQueryOnProcResult(); -} - -void GenericDirectUploadService::handleProcFailure() -{ - emit errorMessage(tr("Failed to upload file \"%1\".") - .arg(d->runningProcFile.localFilePath().toUserOutput())); - clearRunningProc(); - setFinished(); - handleDeploymentDone(); -} - -void GenericDirectUploadService::runPostQueryOnProcResult() -{ - const SftpJobId jobId = d->uploader->statFile(d->runningProcFile.remoteFilePath()); - if (jobId == SftpInvalidJob) { - emit errorMessage(tr("SFTP stat query for %1 failed.") - .arg(d->runningProcFile.remoteFilePath())); - saveDeploymentTimeStamp(d->runningProcFile, QDateTime()); - } else { - d->runningJobs.insert(jobId, Job(d->runningProcFile, JobType::PostQuery)); + QByteArray output = statProc->readAllStandardOutput().trimmed(); + const QString warningString(tr("Unexpected stat output for remote file \"%1\": %2") + .arg(file.remoteFilePath()).arg(QString::fromUtf8(output))); + if (!output.startsWith(file.remoteFilePath().toUtf8())) { + emit warningMessage(warningString); + return QDateTime(); } - clearRunningProc(); - uploadNextFile(); -} - -void GenericDirectUploadService::tryFinish() -{ - if (d->filesToUpload.isEmpty() && d->runningJobs.isEmpty() && d->runningProc.isNull()) { - emit progressMessage(tr("All files successfully deployed.")); - setFinished(); - handleDeploymentDone(); + const QByteArrayList columns = output.mid(file.remoteFilePath().toUtf8().size() + 1).split(' '); + if (columns.size() < 15) { // Normal Linux stat: 16 columns, busybox stat: 15 columns + emit warningMessage(warningString); + return QDateTime(); } -} - -void GenericDirectUploadService::handleMkdirFinished(int exitStatus) -{ - QTC_ASSERT(d->state == Uploading, setFinished(); return); - - QFileInfo fi = d->runningProcFile.localFilePath().toFileInfo(); - if (exitStatus != SshRemoteProcess::NormalExit || d->runningProc->exitCode() != 0) { - handleProcFailure(); - } else if (fi.isDir()) { - runPostQueryOnProcResult(); - } else { - const QString remoteFilePath = d->runningProcFile.remoteFilePath(); - if (fi.isSymLink()) { - const QString target = fi.dir().relativeFilePath(fi.symLinkTarget()); // see QTBUG-5817. - const QStringList args = QStringList() << QLatin1String("ln") << QLatin1String("-sf") - << target << remoteFilePath; - const QString command = Utils::QtcProcess::joinArgs(args, Utils::OsTypeLinux); - - // See comment in SftpChannel::createLink as to why we can't use it. - d->runningProc = connection()->createRemoteProcess(command.toUtf8()); - connect(d->runningProc.data(), &SshRemoteProcess::closed, - this, &GenericDirectUploadService::handleUploadProcFinished); - connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardOutput, - this, &GenericDirectUploadService::handleStdOutData); - connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardError, - this, &GenericDirectUploadService::handleStdErrData); - connect(d->runningProc.data(), &SshRemoteProcess::readChannelFinished, - this, &GenericDirectUploadService::handleReadChannelFinished); - d->runningProc->start(); - } else { - const SftpJobId job = d->uploader->uploadFile( - d->runningProcFile.localFilePath().toString(), remoteFilePath, - SftpOverwriteExisting); - if (job == SftpInvalidJob) { - const QString message = tr("Failed to upload file \"%1\": " - "Could not open for reading.") - .arg(d->runningProcFile.localFilePath().toUserOutput()); - clearRunningProc(); - if (d->ignoreMissingFiles) { - emit warningMessage(message); - uploadNextFile(); - } else { - emit errorMessage(message); - setFinished(); - handleDeploymentDone(); - } - } else { - d->runningJobs[job] = Job(d->runningProcFile, JobType::Upload); - clearRunningProc(); - d->uploadJobRunning = true; - } - } + bool isNumber; + const qint64 secsSinceEpoch = columns.at(12).toLongLong(&isNumber); + if (!isNumber) { + emit warningMessage(warningString); + return QDateTime(); } + return QDateTime::fromSecsSinceEpoch(secsSinceEpoch); } -void GenericDirectUploadService::handleStdOutData() -{ - auto const process = qobject_cast<SshRemoteProcess *>(sender()); - if (process) - emit stdOutData(QString::fromUtf8(process->readAllStandardOutput())); -} - -void GenericDirectUploadService::handleStdErrData() -{ - auto const process = qobject_cast<SshRemoteProcess *>(sender()); - if (process) - emit stdErrData(QString::fromUtf8(process->readAllStandardError())); -} - -void GenericDirectUploadService::handleReadChannelFinished() +void GenericDirectUploadService::checkForStateChangeOnRemoteProcFinished() { - auto const process = qobject_cast<SshRemoteProcess *>(sender()); - if (process && process->atEnd()) - process->close(); + if (!d->remoteProcs.isEmpty()) + return; + if (d->state == PreChecking) { + uploadFiles(); + return; + } + QTC_ASSERT(d->state == PostProcessing, return); + emit progressMessage(tr("All files successfully deployed.")); + setFinished(); + handleDeploymentDone(); } void GenericDirectUploadService::stopDeployment() { - QTC_ASSERT(d->state == InitializingSftp || d->state == Uploading, setFinished(); return); + QTC_ASSERT(d->state != Inactive, return); setFinished(); handleDeploymentDone(); @@ -419,80 +210,143 @@ QList<DeployableFile> GenericDirectUploadService::collectFilesToUpload( void GenericDirectUploadService::setFinished() { d->state = Inactive; - if (d->runningProc) - disconnect(d->runningProc.data(), nullptr, this, nullptr); + for (auto it = d->remoteProcs.begin(); it != d->remoteProcs.end(); ++it) { + it.key()->disconnect(); + it.key()->terminate(); + } + d->remoteProcs.clear(); if (d->uploader) { - disconnect(d->uploader.data(), nullptr, this, nullptr); - d->uploader->closeChannel(); + d->uploader->disconnect(); + d->uploader->stop(); + d->uploader.release()->deleteLater(); } - clearRunningProc(); - d->uploadJobRunning = false; - d->runningJobs.clear(); d->filesToUpload.clear(); } -void GenericDirectUploadService::uploadNextFile() +void GenericDirectUploadService::queryFiles() { - QTC_ASSERT(!d->uploadJobRunning, return); + QTC_ASSERT(d->state == PreChecking || d->state == PostProcessing, return); + QTC_ASSERT(d->state == PostProcessing || d->remoteProcs.isEmpty(), return); - if (d->filesToUpload.isEmpty()) { - tryFinish(); - return; + const QList<DeployableFile> &filesToCheck = d->state == PreChecking + ? d->deployableFiles : d->filesToUpload; + for (const DeployableFile &file : filesToCheck) { + if (d->state == PreChecking && (!d->incremental || hasLocalFileChanged(file))) { + d->filesToUpload.append(file); + continue; + } + // We'd like to use --format=%Y, but it's not supported by busybox. + const QByteArray statCmd = "stat -t " + + Utils::QtcProcess::quoteArgUnix(file.remoteFilePath()).toUtf8(); + SshRemoteProcess * const statProc = connection()->createRemoteProcess(statCmd).release(); + statProc->setParent(this); + connect(statProc, &SshRemoteProcess::done, this, + [this, statProc, state = d->state] { + QTC_ASSERT(d->state == state, return); + const DeployableFile file = d->getFileForProcess(statProc); + QTC_ASSERT(file.isValid(), return); + const QDateTime timestamp = timestampFromStat(file, statProc); + statProc->deleteLater(); + switch (state) { + case PreChecking: + if (!timestamp.isValid() || hasRemoteFileChanged(file, timestamp)) + d->filesToUpload.append(file); + break; + case PostProcessing: + if (timestamp.isValid()) + saveDeploymentTimeStamp(file, timestamp); + break; + case Inactive: + case Uploading: + QTC_CHECK(false); + break; + } + checkForStateChangeOnRemoteProcFinished(); + }); + d->remoteProcs.insert(statProc, file); + statProc->start(); } + checkForStateChangeOnRemoteProcFinished(); +} - const DeployableFile df = d->filesToUpload.takeFirst(); - - QString dirToCreate = df.remoteDirectory(); - if (dirToCreate.isEmpty()) { - emit warningMessage(tr("Warning: No remote path set for local file \"%1\". " - "Skipping upload.").arg(df.localFilePath().toUserOutput())); - uploadNextFile(); +void GenericDirectUploadService::uploadFiles() +{ + QTC_ASSERT(d->state == PreChecking, return); + d->state = Uploading; + if (d->filesToUpload.empty()) { + emit progressMessage(tr("No files need to be uploaded.")); + setFinished(); + handleDeploymentDone(); return; } - - QFileInfo fi = df.localFilePath().toFileInfo(); - if (fi.isDir()) - dirToCreate += QLatin1Char('/') + fi.fileName(); - const QString command = QLatin1String("mkdir -p ") - + Utils::QtcProcess::quoteArgUnix(dirToCreate); - QTC_CHECK(d->runningProc.isNull()); - d->runningProc = connection()->createRemoteProcess(command.toUtf8()); - connect(d->runningProc.data(), &SshRemoteProcess::closed, - this, &GenericDirectUploadService::handleMkdirFinished); - connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardOutput, - this, &GenericDirectUploadService::handleStdOutData); - connect(d->runningProc.data(), &SshRemoteProcess::readyReadStandardError, - this, &GenericDirectUploadService::handleStdErrData); - connect(d->runningProc.data(), &SshRemoteProcess::readChannelFinished, - this, &GenericDirectUploadService::handleReadChannelFinished); - emit progressMessage(tr("Uploading file \"%1\"...") - .arg(df.localFilePath().toUserOutput())); - d->runningProcFile = df; - d->runningProc->start(); - d->uploadJobRunning = true; + emit progressMessage(tr("%n file(s) need to be uploaded.", "", d->filesToUpload.size())); + FilesToTransfer filesToTransfer; + for (const DeployableFile &f : d->filesToUpload) { + if (!f.localFilePath().exists()) { + const QString message = tr("Local file \"%1\" does not exist.") + .arg(f.localFilePath().toUserOutput()); + if (d->ignoreMissingFiles) { + emit warningMessage(message); + continue; + } else { + emit errorMessage(message); + setFinished(); + handleDeploymentDone(); + return; + } + } + filesToTransfer << FileToTransfer(f.localFilePath().toString(), f.remoteFilePath()); + } + d->uploader = connection()->createUpload(filesToTransfer, FileTransferErrorHandling::Abort); + connect(d->uploader.get(), &SftpTransfer::done, [this](const QString &error) { + QTC_ASSERT(d->state == Uploading, return); + if (!error.isEmpty()) { + emit errorMessage(error); + setFinished(); + handleDeploymentDone(); + return; + } + d->state = PostProcessing; + chmod(); + queryFiles(); + }); + connect(d->uploader.get(), &SftpTransfer::progress, + this, &GenericDirectUploadService::progressMessage); + d->uploader->start(); } -void GenericDirectUploadService::queryFiles() +void GenericDirectUploadService::chmod() { - QTC_ASSERT(d->state == Uploading, return); - - for (const DeployableFile &file : qAsConst(d->deployableFiles)) { - if (!d->incremental || hasLocalFileChanged(file)) { - d->filesToUpload.append(file); - continue; - } - - const SftpJobId jobId = d->uploader->statFile(file.remoteFilePath()); - if (jobId == SftpInvalidJob) { - emit warningMessage(tr("SFTP stat query for %1 failed.").arg(file.remoteFilePath())); - d->filesToUpload.append(file); + QTC_ASSERT(d->state == PostProcessing, return); + if (!Utils::HostOsInfo::isWindowsHost()) + return; + for (const DeployableFile &f : d->filesToUpload) { + if (!f.isExecutable()) continue; - } - - d->runningJobs.insert(jobId, Job(file, JobType::PreQuery)); + const QString command = QLatin1String("chmod a+x ") + + Utils::QtcProcess::quoteArgUnix(f.remoteFilePath()); + SshRemoteProcess * const chmodProc + = connection()->createRemoteProcess(command.toUtf8()).release(); + chmodProc->setParent(this); + connect(chmodProc, &SshRemoteProcess::done, this, + [this, chmodProc, state = d->state](const QString &error) { + QTC_ASSERT(state == d->state, return); + const DeployableFile file = d->getFileForProcess(chmodProc); + QTC_ASSERT(file.isValid(), return); + if (!error.isEmpty()) { + emit warningMessage(tr("Remote chmod failed for file \"%1\": %2") + .arg(file.remoteFilePath(), error)); + } else if (chmodProc->exitCode() != 0) { + emit warningMessage(tr("Remote chmod failed for file \"%1\": %2") + .arg(file.remoteFilePath(), + QString::fromUtf8(chmodProc->readAllStandardError()))); + } + chmodProc->deleteLater(); + checkForStateChangeOnRemoteProcFinished(); + }); + d->remoteProcs.insert(chmodProc, f); + chmodProc->start(); } - - uploadNextFile(); } } //namespace RemoteLinux diff --git a/src/plugins/remotelinux/genericdirectuploadservice.h b/src/plugins/remotelinux/genericdirectuploadservice.h index 4f5dbb71b21..c41f8f99cca 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.h +++ b/src/plugins/remotelinux/genericdirectuploadservice.h @@ -32,10 +32,11 @@ #include <QList> +QT_FORWARD_DECLARE_CLASS(QDateTime) QT_FORWARD_DECLARE_CLASS(QString) namespace ProjectExplorer { class DeployableFile; } - +namespace QSsh { class SshRemoteProcess; } namespace RemoteLinux { namespace Internal { class GenericDirectUploadServicePrivate; } @@ -60,27 +61,16 @@ public: void stopDeployment() override; private: - void handleSftpInitialized(); - void handleSftpChannelError(const QString &errorMessage); - void handleFileInfoAvailable(QSsh::SftpJobId jobId, const QList<QSsh::SftpFileInfo> &fileInfos); - void handleJobFinished(QSsh::SftpJobId jobId, const QString &errorMsg); - void handleUploadProcFinished(int exitStatus); - void handleMkdirFinished(int exitStatus); - void handleStdOutData(); - void handleStdErrData(); - void handleReadChannelFinished(); + QDateTime timestampFromStat(const ProjectExplorer::DeployableFile &file, + QSsh::SshRemoteProcess *statProc); + void checkForStateChangeOnRemoteProcFinished(); QList<ProjectExplorer::DeployableFile> collectFilesToUpload( const ProjectExplorer::DeployableFile &file) const; void setFinished(); - void uploadNextFile(); void queryFiles(); - void clearRunningProc(); - - void handleProcFailure(); - void runPostQueryOnProcResult(); - - void tryFinish(); + void uploadFiles(); + void chmod(); Internal::GenericDirectUploadServicePrivate * const d; }; diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp index 9ef6561a3e6..5957ac428b4 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp @@ -48,18 +48,12 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget( this, &GenericLinuxDeviceConfigurationWidget::hostNameEditingFinished); connect(m_ui->userLineEdit, &QLineEdit::editingFinished, this, &GenericLinuxDeviceConfigurationWidget::userNameEditingFinished); - connect(m_ui->pwdLineEdit, &QLineEdit::editingFinished, - this, &GenericLinuxDeviceConfigurationWidget::passwordEditingFinished); - connect(m_ui->passwordButton, &QAbstractButton::toggled, - this, &GenericLinuxDeviceConfigurationWidget::authenticationTypeChanged); connect(m_ui->keyFileLineEdit, &PathChooser::editingFinished, this, &GenericLinuxDeviceConfigurationWidget::keyFileEditingFinished); connect(m_ui->keyFileLineEdit, &PathChooser::browsingFinished, this, &GenericLinuxDeviceConfigurationWidget::keyFileEditingFinished); connect(m_ui->keyButton, &QAbstractButton::toggled, this, &GenericLinuxDeviceConfigurationWidget::authenticationTypeChanged); - connect(m_ui->agentButton, &QAbstractButton::toggled, - this, &GenericLinuxDeviceConfigurationWidget::authenticationTypeChanged); connect(m_ui->timeoutSpinBox, &QAbstractSpinBox::editingFinished, this, &GenericLinuxDeviceConfigurationWidget::timeoutEditingFinished); connect(m_ui->timeoutSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), @@ -68,8 +62,6 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget( this, &GenericLinuxDeviceConfigurationWidget::sshPortEditingFinished); connect(m_ui->sshPortSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &GenericLinuxDeviceConfigurationWidget::sshPortEditingFinished); - connect(m_ui->showPasswordCheckBox, &QAbstractButton::toggled, - this, &GenericLinuxDeviceConfigurationWidget::showPassword); connect(m_ui->portsLineEdit, &QLineEdit::editingFinished, this, &GenericLinuxDeviceConfigurationWidget::handleFreePortsChanged); connect(m_ui->createKeyButton, &QAbstractButton::clicked, @@ -91,15 +83,11 @@ GenericLinuxDeviceConfigurationWidget::~GenericLinuxDeviceConfigurationWidget() void GenericLinuxDeviceConfigurationWidget::authenticationTypeChanged() { SshConnectionParameters sshParams = device()->sshParameters(); - const bool usePassword = m_ui->passwordButton->isChecked(); const bool useKeyFile = m_ui->keyButton->isChecked(); - sshParams.authenticationType - = usePassword ? SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods - : useKeyFile ? SshConnectionParameters::AuthenticationTypePublicKey - : SshConnectionParameters::AuthenticationTypeAgent; + sshParams.authenticationType = useKeyFile + ? SshConnectionParameters::AuthenticationTypeSpecificKey + : SshConnectionParameters::AuthenticationTypeAll; device()->setSshParameters(sshParams); - m_ui->pwdLineEdit->setEnabled(usePassword); - m_ui->passwordLabel->setEnabled(usePassword); m_ui->keyFileLineEdit->setEnabled(useKeyFile); m_ui->keyLabel->setEnabled(useKeyFile); } @@ -132,13 +120,6 @@ void GenericLinuxDeviceConfigurationWidget::userNameEditingFinished() device()->setSshParameters(sshParams); } -void GenericLinuxDeviceConfigurationWidget::passwordEditingFinished() -{ - SshConnectionParameters sshParams = device()->sshParameters(); - sshParams.setPassword(m_ui->pwdLineEdit->text()); - device()->setSshParameters(sshParams); -} - void GenericLinuxDeviceConfigurationWidget::keyFileEditingFinished() { SshConnectionParameters sshParams = device()->sshParameters(); @@ -157,12 +138,6 @@ void GenericLinuxDeviceConfigurationWidget::handleFreePortsChanged() updatePortsWarningLabel(); } -void GenericLinuxDeviceConfigurationWidget::showPassword(bool showClearText) -{ - m_ui->pwdLineEdit->setEchoMode(showClearText - ? QLineEdit::Normal : QLineEdit::Password); -} - void GenericLinuxDeviceConfigurationWidget::setPrivateKey(const QString &path) { m_ui->keyFileLineEdit->setPath(path); @@ -190,7 +165,6 @@ void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi() sshPortEditingFinished(); timeoutEditingFinished(); userNameEditingFinished(); - passwordEditingFinished(); keyFileEditingFinished(); handleFreePortsChanged(); gdbServerEditingFinished(); @@ -220,16 +194,12 @@ void GenericLinuxDeviceConfigurationWidget::initGui() const SshConnectionParameters &sshParams = device()->sshParameters(); switch (sshParams.authenticationType) { - case SshConnectionParameters::AuthenticationTypePublicKey: + case SshConnectionParameters::AuthenticationTypeSpecificKey: m_ui->keyButton->setChecked(true); break; - case SshConnectionParameters::AuthenticationTypeAgent: - m_ui->agentButton->setChecked(true); + case SshConnectionParameters::AuthenticationTypeAll: + m_ui->defaultAuthButton->setChecked(true); break; - case SshConnectionParameters::AuthenticationTypePassword: - case SshConnectionParameters::AuthenticationTypeKeyboardInteractive: - case SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods: - m_ui->passwordButton->setChecked(true); } m_ui->timeoutSpinBox->setValue(sshParams.timeout); m_ui->hostLineEdit->setEnabled(!device()->isAutoDetected()); @@ -241,9 +211,7 @@ void GenericLinuxDeviceConfigurationWidget::initGui() m_ui->portsLineEdit->setText(device()->freePorts().toString()); m_ui->timeoutSpinBox->setValue(sshParams.timeout); m_ui->userLineEdit->setText(sshParams.userName()); - m_ui->pwdLineEdit->setText(sshParams.password()); m_ui->keyFileLineEdit->setPath(sshParams.privateKeyFile); - m_ui->showPasswordCheckBox->setChecked(false); m_ui->gdbServerLineEdit->setText(device()->debugServerPath()); updatePortsWarningLabel(); } diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h index 217e5b1379b..7faf3494d6c 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h @@ -49,10 +49,8 @@ private: void sshPortEditingFinished(); void timeoutEditingFinished(); void userNameEditingFinished(); - void passwordEditingFinished(); void keyFileEditingFinished(); void gdbServerEditingFinished(); - void showPassword(bool showClearText); void handleFreePortsChanged(); void setPrivateKey(const QString &path); void createNewKey(); diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui index 108ab2f344e..6e08ace732d 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.ui @@ -52,23 +52,16 @@ <number>0</number> </property> <item> - <widget class="QRadioButton" name="passwordButton"> + <widget class="QRadioButton" name="defaultAuthButton"> <property name="text"> - <string>Password</string> + <string>Default</string> </property> </widget> </item> <item> <widget class="QRadioButton" name="keyButton"> <property name="text"> - <string>&Key</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="agentButton"> - <property name="text"> - <string>Key via ssh-agent</string> + <string>Specific &key</string> </property> </widget> </item> @@ -206,33 +199,8 @@ <item row="4" column="1"> <widget class="QLineEdit" name="userLineEdit"/> </item> - <item row="5" column="0"> - <widget class="QLabel" name="passwordLabel"> - <property name="text"> - <string>&Password:</string> - </property> - <property name="buddy"> - <cstring>pwdLineEdit</cstring> - </property> - </widget> - </item> <item row="5" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <item> - <widget class="QLineEdit" name="pwdLineEdit"> - <property name="echoMode"> - <enum>QLineEdit::Password</enum> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="showPasswordCheckBox"> - <property name="text"> - <string>Show password</string> - </property> - </widget> - </item> - </layout> + <layout class="QHBoxLayout" name="horizontalLayout_6"/> </item> <item row="6" column="0"> <widget class="QLabel" name="keyLabel"> @@ -280,7 +248,6 @@ </widget> </item> </layout> - <zorder>passwordLabel</zorder> <zorder>authTypeButtonsWidget</zorder> <zorder>hostNameLabel</zorder> <zorder>userNameLabel</zorder> @@ -302,7 +269,7 @@ </customwidget> </customwidgets> <tabstops> - <tabstop>passwordButton</tabstop> + <tabstop>defaultAuthButton</tabstop> <tabstop>keyButton</tabstop> <tabstop>hostLineEdit</tabstop> <tabstop>sshPortSpinBox</tabstop> @@ -310,8 +277,6 @@ <tabstop>portsLineEdit</tabstop> <tabstop>timeoutSpinBox</tabstop> <tabstop>userLineEdit</tabstop> - <tabstop>pwdLineEdit</tabstop> - <tabstop>showPasswordCheckBox</tabstop> <tabstop>createKeyButton</tabstop> <tabstop>gdbServerLineEdit</tabstop> </tabstops> diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp index f2eb58a6130..30a26d66d79 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp @@ -71,11 +71,10 @@ GenericLinuxDeviceConfigurationWizard::~GenericLinuxDeviceConfigurationWizard() IDevice::Ptr GenericLinuxDeviceConfigurationWizard::device() { SshConnectionParameters sshParams; - sshParams.options &= ~SshConnectionOptions(SshEnableStrictConformanceChecks); // For older SSH servers. sshParams.url = d->setupPage.url(); sshParams.timeout = 10; sshParams.authenticationType = d->setupPage.authenticationType(); - if (sshParams.authenticationType == SshConnectionParameters::AuthenticationTypePublicKey) + if (sshParams.authenticationType == SshConnectionParameters::AuthenticationTypeSpecificKey) sshParams.privateKeyFile = d->setupPage.privateKeyFilePath(); IDevice::Ptr device = LinuxDevice::create(d->setupPage.configurationName(), Core::Id(Constants::GenericLinuxOsType), IDevice::Hardware); diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp index 88c62bcaf88..2ed7f6b1fa7 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp @@ -62,12 +62,10 @@ GenericLinuxDeviceConfigurationWizardSetupPage::GenericLinuxDeviceConfigurationW connect(d->ui.userNameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); connect(d->ui.privateKeyPathChooser, &PathChooser::validChanged, this, &QWizardPage::completeChanged); - connect(d->ui.passwordButton, &QAbstractButton::toggled, + connect(d->ui.defaultAuthButton, &QAbstractButton::toggled, this, &GenericLinuxDeviceConfigurationWizardSetupPage::handleAuthTypeChanged); connect(d->ui.keyButton, &QAbstractButton::toggled, this, &GenericLinuxDeviceConfigurationWizardSetupPage::handleAuthTypeChanged); - connect(d->ui.agentButton, &QAbstractButton::toggled, - this, &GenericLinuxDeviceConfigurationWizardSetupPage::handleAuthTypeChanged); } GenericLinuxDeviceConfigurationWizardSetupPage::~GenericLinuxDeviceConfigurationWizardSetupPage() @@ -80,8 +78,7 @@ void GenericLinuxDeviceConfigurationWizardSetupPage::initializePage() d->ui.nameLineEdit->setText(defaultConfigurationName()); d->ui.hostNameLineEdit->setText(defaultHostName()); d->ui.userNameLineEdit->setText(defaultUserName()); - d->ui.passwordButton->setChecked(true); - d->ui.passwordLineEdit->setText(defaultPassWord()); + d->ui.defaultAuthButton->setChecked(true); d->ui.privateKeyPathChooser->setPath(ProjectExplorer::IDevice::defaultPrivateKeyFilePath()); handleAuthTypeChanged(); } @@ -91,7 +88,7 @@ bool GenericLinuxDeviceConfigurationWizardSetupPage::isComplete() const return !configurationName().isEmpty() && !d->ui.hostNameLineEdit->text().trimmed().isEmpty() && !d->ui.userNameLineEdit->text().trimmed().isEmpty() - && (authenticationType() != SshConnectionParameters::AuthenticationTypePublicKey + && (authenticationType() != SshConnectionParameters::AuthenticationTypeSpecificKey || d->ui.privateKeyPathChooser->isValid()); } @@ -105,17 +102,14 @@ QUrl GenericLinuxDeviceConfigurationWizardSetupPage::url() const QUrl url; url.setHost(d->ui.hostNameLineEdit->text().trimmed()); url.setUserName(d->ui.userNameLineEdit->text().trimmed()); - url.setPassword(d->ui.passwordLineEdit->text()); url.setPort(22); return url; } SshConnectionParameters::AuthenticationType GenericLinuxDeviceConfigurationWizardSetupPage::authenticationType() const { - return d->ui.passwordButton->isChecked() - ? SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods - : d->ui.keyButton->isChecked() ? SshConnectionParameters::AuthenticationTypePublicKey - : SshConnectionParameters::AuthenticationTypeAgent; + return d->ui.keyButton->isChecked() ? SshConnectionParameters::AuthenticationTypeSpecificKey + : SshConnectionParameters::AuthenticationTypeAll; } QString GenericLinuxDeviceConfigurationWizardSetupPage::privateKeyFilePath() const @@ -145,10 +139,8 @@ QString GenericLinuxDeviceConfigurationWizardSetupPage::defaultPassWord() const void GenericLinuxDeviceConfigurationWizardSetupPage::handleAuthTypeChanged() { - d->ui.passwordLineEdit->setEnabled(authenticationType() - == SshConnectionParameters::AuthenticationTypeTryAllPasswordBasedMethods); d->ui.privateKeyPathChooser->setEnabled(authenticationType() - == SshConnectionParameters::AuthenticationTypePublicKey); + == SshConnectionParameters::AuthenticationTypeSpecificKey); emit completeChanged(); } diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui index 54ccf0d7b09..1c93a359b75 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardsetuppage.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>564</width> - <height>207</height> + <width>590</width> + <height>188</height> </rect> </property> <property name="windowTitle"> @@ -92,23 +92,16 @@ <item row="3" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_4"> <item> - <widget class="QRadioButton" name="passwordButton"> + <widget class="QRadioButton" name="defaultAuthButton"> <property name="text"> - <string>Password</string> + <string>Default</string> </property> </widget> </item> <item> <widget class="QRadioButton" name="keyButton"> <property name="text"> - <string>Key</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="agentButton"> - <property name="text"> - <string>Agent</string> + <string>Specific key</string> </property> </widget> </item> @@ -128,44 +121,13 @@ </layout> </item> <item row="4" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>The user's password:</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <item> - <widget class="QLineEdit" name="passwordLineEdit"> - <property name="echoMode"> - <enum>QLineEdit::Password</enum> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_4"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item row="5" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>The file containing the user's private key:</string> </property> </widget> </item> - <item row="5" column="1"> + <item row="4" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_6"> <item> <widget class="Utils::PathChooser" name="privateKeyPathChooser" native="true"/> diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index 913b3250e2b..0702c417385 100644 --- a/src/plugins/remotelinux/linuxdevicetester.cpp +++ b/src/plugins/remotelinux/linuxdevicetester.cpp @@ -28,7 +28,7 @@ #include <projectexplorer/devicesupport/deviceusedportsgatherer.h> #include <utils/port.h> #include <utils/qtcassert.h> -#include <ssh/sftpchannel.h> +#include <ssh/sftptransfer.h> #include <ssh/sshremoteprocess.h> #include <ssh/sshconnection.h> @@ -48,9 +48,9 @@ class GenericLinuxDeviceTesterPrivate public: IDevice::ConstPtr deviceConfiguration; SshConnection *connection = nullptr; - SshRemoteProcess::Ptr process; + SshRemoteProcessPtr process; DeviceUsedPortsGatherer portsGatherer; - SftpChannel::Ptr sftpChannel; + SftpTransferPtr sftpUpload; State state = Inactive; }; @@ -76,7 +76,7 @@ void GenericLinuxDeviceTester::testDevice(const IDevice::ConstPtr &deviceConfigu d->connection = new SshConnection(deviceConfiguration->sshParameters(), this); connect(d->connection, &SshConnection::connected, this, &GenericLinuxDeviceTester::handleConnected); - connect(d->connection, &SshConnection::error, + connect(d->connection, &SshConnection::errorOccurred, this, &GenericLinuxDeviceTester::handleConnectionFailure); emit progressMessage(tr("Connecting to host...")); @@ -99,7 +99,7 @@ void GenericLinuxDeviceTester::stopTest() d->process->close(); break; case TestingSftp: - d->sftpChannel->closeChannel(); + d->sftpUpload->stop(); break; case Inactive: break; @@ -113,7 +113,7 @@ void GenericLinuxDeviceTester::handleConnected() QTC_ASSERT(d->state == Connecting, return); d->process = d->connection->createRemoteProcess("uname -rsm"); - connect(d->process.data(), &SshRemoteProcess::closed, + connect(d->process.get(), &SshRemoteProcess::done, this, &GenericLinuxDeviceTester::handleProcessFinished); emit progressMessage(tr("Checking kernel version...")); @@ -125,15 +125,16 @@ void GenericLinuxDeviceTester::handleConnectionFailure() { QTC_ASSERT(d->state != Inactive, return); - emit errorMessage(tr("SSH connection failure: %1").arg(d->connection->errorString()) + QLatin1Char('\n')); + emit errorMessage(d->connection->errorString() + QLatin1Char('\n')); + setFinished(TestFailure); } -void GenericLinuxDeviceTester::handleProcessFinished(int exitStatus) +void GenericLinuxDeviceTester::handleProcessFinished(const QString &error) { QTC_ASSERT(d->state == RunningUname, return); - if (exitStatus != SshRemoteProcess::NormalExit || d->process->exitCode() != 0) { + if (!error.isEmpty() || d->process->exitCode() != 0) { const QByteArray stderrOutput = d->process->readAllStandardError(); if (!stderrOutput.isEmpty()) emit errorMessage(tr("uname failed: %1").arg(QString::fromUtf8(stderrOutput)) + QLatin1Char('\n')); @@ -176,34 +177,35 @@ void GenericLinuxDeviceTester::handlePortListReady() .arg(portList) + QLatin1Char('\n')); } - emit progressMessage(tr("Checking if an SFTP channel can be set up...")); - d->sftpChannel = d->connection->createSftpChannel(); - connect(d->sftpChannel.data(), &SftpChannel::initialized, - this, &GenericLinuxDeviceTester::handleSftpInitialized); - connect(d->sftpChannel.data(), &SftpChannel::channelError, - this, &GenericLinuxDeviceTester::handleSftpError); + emit progressMessage(tr("Checking whether an SFTP connection can be set up...")); + d->sftpUpload = d->connection->createUpload(FilesToTransfer(), + FileTransferErrorHandling::Abort); + connect(d->sftpUpload.get(), &SftpTransfer::done, + this, &GenericLinuxDeviceTester::handleSftpFinished); d->state = TestingSftp; - d->sftpChannel->initialize(); -} - -void GenericLinuxDeviceTester::handleSftpInitialized() -{ - QTC_ASSERT(d->state == TestingSftp, return); - emit progressMessage(tr("SFTP channel successfully initialized.\n")); - setFinished(TestSuccess); + d->sftpUpload->start(); } -void GenericLinuxDeviceTester::handleSftpError(const QString &message) +void GenericLinuxDeviceTester::handleSftpFinished(const QString &error) { QTC_ASSERT(d->state == TestingSftp, return); - emit errorMessage(tr("Error setting up SFTP channel: %1\n").arg(message)); - setFinished(TestFailure); + if (!error.isEmpty()) { + emit errorMessage(tr("Error setting up SFTP connection: %1\n").arg(error)); + setFinished(TestFailure); + } else { + emit progressMessage(tr("SFTP service available.\n")); + setFinished(TestSuccess); + } } void GenericLinuxDeviceTester::setFinished(TestResult result) { d->state = Inactive; disconnect(&d->portsGatherer, nullptr, this, nullptr); + if (d->sftpUpload) { + disconnect(d->sftpUpload.get(), nullptr, this, nullptr); + d->sftpUpload.release()->deleteLater(); + } if (d->connection) { disconnect(d->connection, nullptr, this, nullptr); d->connection->deleteLater(); diff --git a/src/plugins/remotelinux/linuxdevicetester.h b/src/plugins/remotelinux/linuxdevicetester.h index d845c2e26c9..a63ec51d04c 100644 --- a/src/plugins/remotelinux/linuxdevicetester.h +++ b/src/plugins/remotelinux/linuxdevicetester.h @@ -47,11 +47,10 @@ public: private: void handleConnected(); void handleConnectionFailure(); - void handleProcessFinished(int exitStatus); + void handleProcessFinished(const QString &error); void handlePortsGatheringError(const QString &message); void handlePortListReady(); - void handleSftpInitialized(); - void handleSftpError(const QString &message); + void handleSftpFinished(const QString &error); void setFinished(ProjectExplorer::DeviceTester::TestResult result); Internal::GenericLinuxDeviceTesterPrivate * const d; diff --git a/src/plugins/remotelinux/packageuploader.cpp b/src/plugins/remotelinux/packageuploader.cpp index 5de62edf075..cb5af237840 100644 --- a/src/plugins/remotelinux/packageuploader.cpp +++ b/src/plugins/remotelinux/packageuploader.cpp @@ -26,7 +26,7 @@ #include "packageuploader.h" #include <utils/qtcassert.h> -#include <ssh/sftpchannel.h> +#include <ssh/sftptransfer.h> #include <ssh/sshconnection.h> using namespace QSsh; @@ -46,29 +46,24 @@ void PackageUploader::uploadPackage(SshConnection *connection, { QTC_ASSERT(m_state == Inactive, return); - setState(InitializingSftp); + setState(Uploading); emit progress(tr("Preparing SFTP connection...")); - m_localFilePath = localFilePath; - m_remoteFilePath = remoteFilePath; m_connection = connection; - connect(m_connection, &SshConnection::error, + connect(m_connection, &SshConnection::errorOccurred, this, &PackageUploader::handleConnectionFailure); - m_uploader = m_connection->createSftpChannel(); - connect(m_uploader.data(), &SftpChannel::initialized, - this, &PackageUploader::handleSftpChannelInitialized); - connect(m_uploader.data(), &SftpChannel::channelError, - this, &PackageUploader::handleSftpChannelError); - connect(m_uploader.data(), &SftpChannel::finished, - this, &PackageUploader::handleSftpJobFinished); - m_uploader->initialize(); + m_uploader = m_connection->createUpload({FileToTransfer(localFilePath, remoteFilePath)}, + FileTransferErrorHandling::Abort); + connect(m_uploader.get(), &SftpTransfer::done, this, &PackageUploader::handleUploadDone); + m_uploader->start(); } void PackageUploader::cancelUpload() { - QTC_ASSERT(m_state == InitializingSftp || m_state == Uploading, return); + QTC_ASSERT(m_state == Uploading, return); - cleanup(); + setState(Inactive); + emit uploadFinished(tr("Package upload canceled.")); } void PackageUploader::handleConnectionFailure() @@ -81,53 +76,15 @@ void PackageUploader::handleConnectionFailure() emit uploadFinished(tr("Connection failed: %1").arg(errorMsg)); } -void PackageUploader::handleSftpChannelError(const QString &errorMsg) +void PackageUploader::handleUploadDone(const QString &errorMsg) { - QTC_ASSERT(m_state == InitializingSftp || m_state == Inactive, return); - - if (m_state == Inactive) - return; + QTC_ASSERT(m_state == Uploading, return); setState(Inactive); - emit uploadFinished(tr("SFTP error: %1").arg(errorMsg)); -} - -void PackageUploader::handleSftpChannelInitialized() -{ - QTC_ASSERT(m_state == InitializingSftp || m_state == Inactive, return); - - if (m_state == Inactive) - return; - - const SftpJobId job = m_uploader->uploadFile(m_localFilePath, - m_remoteFilePath, SftpOverwriteExisting); - if (job == SftpInvalidJob) { - setState(Inactive); - emit uploadFinished(tr("Package upload failed: Could not open file.")); - } else { - emit progress(tr("Starting upload...")); - setState(Uploading); - } -} - -void PackageUploader::handleSftpJobFinished(SftpJobId, const QString &errorMsg) -{ - QTC_ASSERT(m_state == Uploading || m_state == Inactive, return); - - if (m_state == Inactive) - return; - if (!errorMsg.isEmpty()) emit uploadFinished(tr("Failed to upload package: %2").arg(errorMsg)); else emit uploadFinished(); - cleanup(); -} - -void PackageUploader::cleanup() -{ - m_uploader->closeChannel(); - setState(Inactive); } void PackageUploader::setState(State newState) @@ -136,8 +93,9 @@ void PackageUploader::setState(State newState) return; if (newState == Inactive) { if (m_uploader) { - disconnect(m_uploader.data(), nullptr, this, nullptr); - m_uploader.clear(); + disconnect(m_uploader.get(), nullptr, this, nullptr); + m_uploader->stop(); + m_uploader.release()->deleteLater(); } if (m_connection) { disconnect(m_connection, nullptr, this, nullptr); diff --git a/src/plugins/remotelinux/packageuploader.h b/src/plugins/remotelinux/packageuploader.h index 7e61eefbe33..99499e9a16d 100644 --- a/src/plugins/remotelinux/packageuploader.h +++ b/src/plugins/remotelinux/packageuploader.h @@ -26,7 +26,6 @@ #pragma once #include <QObject> -#include <QSharedPointer> #include <QString> #include <ssh/sftpdefs.h> @@ -56,20 +55,15 @@ signals: void uploadFinished(const QString &errorMsg = QString()); private: - enum State { InitializingSftp, Uploading, Inactive }; + enum State { Uploading, Inactive }; void handleConnectionFailure(); - void handleSftpChannelInitialized(); - void handleSftpChannelError(const QString &error); - void handleSftpJobFinished(QSsh::SftpJobId job, const QString &error); - void cleanup(); + void handleUploadDone(const QString &error); void setState(State newState); State m_state; QSsh::SshConnection *m_connection; - QSharedPointer<QSsh::SftpChannel> m_uploader; - QString m_localFilePath; - QString m_remoteFilePath; + QSsh::SftpTransferPtr m_uploader; }; } // namespace Internal diff --git a/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp b/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp index 5a336125ed1..de2d43906cf 100644 --- a/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp +++ b/src/plugins/remotelinux/remotelinuxcheckforfreediskspaceservice.cpp @@ -69,17 +69,12 @@ void RemoteLinuxCheckForFreeDiskSpaceService::handleStdErr() void RemoteLinuxCheckForFreeDiskSpaceService::handleProcessFinished() { - switch (d->processRunner->processExitStatus()) { - case QSsh::SshRemoteProcess::FailedToStart: - emit errorMessage(tr("Remote process failed to start.")); + if (!d->processRunner->processErrorString().isEmpty()) { + emit errorMessage(tr("Remote process failed: %1") + .arg(d->processRunner->processErrorString())); stopDeployment(); return; - case QSsh::SshRemoteProcess::CrashExit: - emit errorMessage(tr("Remote process crashed.")); - stopDeployment(); - return; - case QSsh::SshRemoteProcess::NormalExit: - break; + } bool isNumber; diff --git a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp index eddcda0f195..ffa1dfe6af7 100644 --- a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp +++ b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp @@ -119,14 +119,12 @@ void RemoteLinuxCustomCommandDeployService::handleStderr() emit stdErrData(QString::fromUtf8(d->runner->readAllStandardError())); } -void RemoteLinuxCustomCommandDeployService::handleProcessClosed(int exitStatus) +void RemoteLinuxCustomCommandDeployService::handleProcessClosed(const QString &error) { QTC_ASSERT(d->state == Running, return); - if (exitStatus == SshRemoteProcess::FailedToStart) { - emit errorMessage(tr("Remote process failed to start.")); - } else if (exitStatus == SshRemoteProcess::CrashExit) { - emit errorMessage(tr("Remote process was killed by a signal.")); + if (!error.isEmpty()) { + emit errorMessage(tr("Remote process failed: %1").arg(error)); } else if (d->runner->processExitCode() != 0) { emit errorMessage(tr("Remote process finished with exit code %1.") .arg(d->runner->processExitCode())); diff --git a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h index 75352420f6d..33b0e67b1d8 100644 --- a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h +++ b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h @@ -52,7 +52,7 @@ protected: private: void handleStdout(); void handleStderr(); - void handleProcessClosed(int exitStatus); + void handleProcessClosed(const QString &error); Internal::RemoteLinuxCustomCommandDeployservicePrivate *d; }; diff --git a/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp b/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp index 3f84fa7c37a..4d3439931bb 100644 --- a/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp +++ b/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp @@ -98,12 +98,12 @@ void AbstractRemoteLinuxPackageInstaller::handleConnectionError() setFinished(); } -void AbstractRemoteLinuxPackageInstaller::handleInstallationFinished(int exitStatus) +void AbstractRemoteLinuxPackageInstaller::handleInstallationFinished(const QString &error) { if (!d->isRunning) return; - if (exitStatus != SshRemoteProcess::NormalExit || d->installer->processExitCode() != 0) + if (!error.isEmpty() || d->installer->processExitCode() != 0) emit finished(tr("Installing package failed.")); else if (!errorString().isEmpty()) emit finished(errorString()); diff --git a/src/plugins/remotelinux/remotelinuxpackageinstaller.h b/src/plugins/remotelinux/remotelinuxpackageinstaller.h index 25f1b34f77e..ddf713eda28 100644 --- a/src/plugins/remotelinux/remotelinuxpackageinstaller.h +++ b/src/plugins/remotelinux/remotelinuxpackageinstaller.h @@ -54,7 +54,7 @@ protected: private: void handleConnectionError(); - void handleInstallationFinished(int exitStatus); + void handleInstallationFinished(const QString &error); void handleInstallerOutput(); void handleInstallerErrorOutput(); diff --git a/src/plugins/remotelinux/sshkeydeployer.cpp b/src/plugins/remotelinux/sshkeydeployer.cpp index 5d7cccdc8f7..7581a720185 100644 --- a/src/plugins/remotelinux/sshkeydeployer.cpp +++ b/src/plugins/remotelinux/sshkeydeployer.cpp @@ -79,19 +79,18 @@ void SshKeyDeployer::handleConnectionFailure() emit error(tr("Connection failed: %1").arg(d->deployProcess.lastConnectionErrorString())); } -void SshKeyDeployer::handleKeyUploadFinished(int exitStatus) +void SshKeyDeployer::handleKeyUploadFinished() { - Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart - || exitStatus == SshRemoteProcess::CrashExit - || exitStatus == SshRemoteProcess::NormalExit); - const int exitCode = d->deployProcess.processExitCode(); const QString errorMsg = d->deployProcess.processErrorString(); cleanup(); - if (exitStatus == SshRemoteProcess::NormalExit && exitCode == 0) + if (errorMsg.isEmpty() && exitCode == 0) { emit finishedSuccessfully(); - else - emit error(tr("Key deployment failed: %1.").arg(errorMsg)); + } else { + emit error(tr("Key deployment failed: %1.").arg(errorMsg.isEmpty() + ? QString::fromUtf8(d->deployProcess.readAllStandardError()) + : errorMsg)); + } } void SshKeyDeployer::stopDeployment() diff --git a/src/plugins/remotelinux/sshkeydeployer.h b/src/plugins/remotelinux/sshkeydeployer.h index 8c5df26d8ab..664543cb3c3 100644 --- a/src/plugins/remotelinux/sshkeydeployer.h +++ b/src/plugins/remotelinux/sshkeydeployer.h @@ -52,7 +52,7 @@ signals: private: void handleConnectionFailure(); - void handleKeyUploadFinished(int exitStatus); + void handleKeyUploadFinished(); void cleanup(); Internal::SshKeyDeployerPrivate * const d; diff --git a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp b/src/plugins/valgrind/callgrind/callgrindcontroller.cpp index 40a025b5d94..1974ced68f5 100644 --- a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp +++ b/src/plugins/valgrind/callgrind/callgrindcontroller.cpp @@ -25,7 +25,7 @@ #include "callgrindcontroller.h" -#include <ssh/sftpchannel.h> +#include <ssh/sftpsession.h> #include <ssh/sshconnectionmanager.h> #include <utils/fileutils.h> @@ -237,12 +237,12 @@ void CallgrindController::foundRemoteFile() { m_remoteFile = m_findRemoteFile->readAllStandardOutput().trimmed(); - m_sftp = m_ssh->createSftpChannel(); - connect(m_sftp.data(), &QSsh::SftpChannel::finished, + m_sftp = m_ssh->createSftpSession(); + connect(m_sftp.get(), &QSsh::SftpSession::commandFinished, this, &CallgrindController::sftpJobFinished); - connect(m_sftp.data(), &QSsh::SftpChannel::initialized, + connect(m_sftp.get(), &QSsh::SftpSession::started, this, &CallgrindController::sftpInitialized); - m_sftp->initialize(); + m_sftp->start(); } void CallgrindController::sftpInitialized() @@ -254,14 +254,14 @@ void CallgrindController::sftpInitialized() dataFile.setAutoRemove(false); dataFile.close(); - m_downloadJob = m_sftp->downloadFile(QString::fromUtf8(m_remoteFile), m_tempDataFile, QSsh::SftpOverwriteExisting); + m_downloadJob = m_sftp->downloadFile(QString::fromUtf8(m_remoteFile), m_tempDataFile); } void CallgrindController::sftpJobFinished(QSsh::SftpJobId job, const QString &error) { QTC_ASSERT(job == m_downloadJob, return); - m_sftp->closeChannel(); + m_sftp->quit(); if (error.isEmpty()) emit localParseDataAvailable(m_tempDataFile); diff --git a/src/plugins/valgrind/callgrind/callgrindcontroller.h b/src/plugins/valgrind/callgrind/callgrindcontroller.h index 057ec794a83..d3a6639e4e2 100644 --- a/src/plugins/valgrind/callgrind/callgrindcontroller.h +++ b/src/plugins/valgrind/callgrind/callgrindcontroller.h @@ -26,7 +26,6 @@ #pragma once #include <ssh/sshremoteprocess.h> -#include <ssh/sftpchannel.h> #include <ssh/sshconnection.h> #include <projectexplorer/runconfiguration.h> @@ -91,8 +90,8 @@ private: // remote callgrind support QSsh::SshConnection *m_ssh = nullptr; QString m_tempDataFile; - QSsh::SshRemoteProcess::Ptr m_findRemoteFile; - QSsh::SftpChannel::Ptr m_sftp; + QSsh::SshRemoteProcessPtr m_findRemoteFile; + QSsh::SftpSessionPtr m_sftp; QSsh::SftpJobId m_downloadJob = 0; QByteArray m_remoteFile; }; diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 31fbab64cf9..a9e8fbd51cc 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -155,7 +155,7 @@ public: *localServerAddress = connection.connectionInfo().localAddress; reportStarted(); }); - connect(&connection, &QSsh::SshConnection::error, this, [this] { + connect(&connection, &QSsh::SshConnection::errorOccurred, this, [this] { reportFailure(); }); } |