SSH: Add ssh-askpass implementation

There is no standard implementation for the ssh-askpass tool on any
platform, so we currently can't find one on our own. In particular, this
means that the RemoteLinux device test will always fail for a newly
added device, which is not acceptable.
So we just add our own askpass implementation and point the SSH settings
to it.

Change-Id: I7ca18725e63f591ef9defdf13768a21cc3de4a54
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2018-12-18 17:47:51 +01:00
parent 8331b98dbb
commit b958b9b669
8 changed files with 99 additions and 19 deletions

View File

@@ -40,8 +40,13 @@ namespace Internal {
SshProcess::SshProcess() SshProcess::SshProcess()
{ {
Utils::Environment env = Utils::Environment::systemEnvironment(); Utils::Environment env = Utils::Environment::systemEnvironment();
if (SshSettings::askpassFilePath().exists()) if (SshSettings::askpassFilePath().exists()) {
env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput()); env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput());
// OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform.
if (!env.hasKey("DISPLAY"))
env.set("DISPLAY", ":0");
}
setProcessEnvironment(env.toProcessEnvironment()); setProcessEnvironment(env.toProcessEnvironment());
} }

View File

@@ -113,12 +113,23 @@ int SshSettings::connectionSharingTimeout()
return sshSettings->connectionSharingTimeOutInMinutes; return sshSettings->connectionSharingTimeOutInMinutes;
} }
static FileName filePathValue(const FileName &value, const QString &defaultFileName) static FileName filePathValue(const FileName &value, const QStringList &candidateFileNames)
{ {
return !value.isEmpty() if (!value.isEmpty())
? value return value;
: Environment::systemEnvironment().searchInPath(defaultFileName, const QList<FileName> additionalSearchPaths = sshSettings->searchPathRetriever();
sshSettings->searchPathRetriever()); for (const QString &candidate : candidateFileNames) {
const FileName filePath = Environment::systemEnvironment()
.searchInPath(candidate, additionalSearchPaths);
if (!filePath.isEmpty())
return filePath;
}
return FileName();
}
static FileName filePathValue(const FileName &value, const QString &candidateFileName)
{
return filePathValue(value, QStringList(candidateFileName));
} }
void SshSettings::setSshFilePath(const FileName &ssh) { sshSettings->sshFilePath = ssh; } void SshSettings::setSshFilePath(const FileName &ssh) { sshSettings->sshFilePath = ssh; }
@@ -138,7 +149,7 @@ FileName SshSettings::askpassFilePath()
candidate = sshSettings->askpassFilePath; candidate = sshSettings->askpassFilePath;
if (candidate.isEmpty()) if (candidate.isEmpty())
candidate = FileName::fromString(Environment::systemEnvironment().value("SSH_ASKPASS")); candidate = FileName::fromString(Environment::systemEnvironment().value("SSH_ASKPASS"));
return filePathValue(candidate, "ssh-askpass"); return filePathValue(candidate, QStringList{"qtc-askpass", "ssh-askpass"});
} }
void SshSettings::setKeygenFilePath(const FileName &keygen) void SshSettings::setKeygenFilePath(const FileName &keygen)

View File

@@ -1723,8 +1723,10 @@ void ProjectExplorerPlugin::extensionsInitialized()
DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice)); DeviceManager::instance()->addDevice(IDevice::Ptr(new DesktopDevice));
QSsh::SshSettings::loadSettings(Core::ICore::settings()); QSsh::SshSettings::loadSettings(Core::ICore::settings());
if (Utils::HostOsInfo::isWindowsHost()) {
const auto searchPathRetriever = [] { const auto searchPathRetriever = [] {
Utils::FileNameList searchPaths;
searchPaths << Utils::FileName::fromString(Core::ICore::libexecPath());
if (Utils::HostOsInfo::isWindowsHost()) {
const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git") const QString gitBinary = Core::ICore::settings()->value("Git/BinaryPath", "git")
.toString(); .toString();
const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path") const QStringList rawGitSearchPaths = Core::ICore::settings()->value("Git/Path")
@@ -1733,15 +1735,14 @@ void ProjectExplorerPlugin::extensionsInitialized()
[](const QString &rawPath) { return Utils::FileName::fromString(rawPath); }); [](const QString &rawPath) { return Utils::FileName::fromString(rawPath); });
const Utils::FileName fullGitPath = Utils::Environment::systemEnvironment() const Utils::FileName fullGitPath = Utils::Environment::systemEnvironment()
.searchInPath(gitBinary, gitSearchPaths); .searchInPath(gitBinary, gitSearchPaths);
if (fullGitPath.isEmpty()) if (!fullGitPath.isEmpty()) {
return Utils::FileNameList(); searchPaths << fullGitPath.parentDir()
return Utils::FileNameList{ << fullGitPath.parentDir().parentDir() + "/usr/bin";
fullGitPath.parentDir(), }
fullGitPath.parentDir().parentDir() + "/usr/bin" }
}; return searchPaths;
}; };
QSsh::SshSettings::setExtraSearchPathRetriever(searchPathRetriever); QSsh::SshSettings::setExtraSearchPathRetriever(searchPathRetriever);
}
// delay restoring kits until UI is shown for improved perceived startup performance // delay restoring kits until UI is shown for improved perceived startup performance
QTimer::singleShot(0, this, &ProjectExplorerPlugin::restoreKits); QTimer::singleShot(0, this, &ProjectExplorerPlugin::restoreKits);

View File

@@ -0,0 +1,47 @@
/****************************************************************************
**
** 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 <QApplication>
#include <QInputDialog>
#include <QTimer>
#include <iostream>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTimer::singleShot(0, [] {
QInputDialog dlg;
const QStringList appArgs = qApp->arguments();
QString labelText = QCoreApplication::translate("qtc-askpass",
"Password required for SSH login.");
if (appArgs.count() > 1)
labelText.append('\n').append(appArgs.at(1));
dlg.setLabelText(labelText);
dlg.setTextEchoMode(QLineEdit::Password);
if (dlg.exec() == QDialog::Accepted)
std::cout << qPrintable(dlg.textValue()) << std::endl;
});
return app.exec();
}

View File

@@ -0,0 +1,5 @@
include(../../qtcreatortool.pri)
TARGET = qtc-askpass
SOURCES = qtc-askpass-main.cpp

View File

@@ -0,0 +1,9 @@
import qbs
QtcTool {
name: "qtc-askpass"
Depends { name: "Qt.widgets" }
files: [
"qtc-askpass-main.cpp",
]
}

View File

@@ -6,7 +6,8 @@ SUBDIRS = qtpromaker \
../plugins/cpaster/frontend \ ../plugins/cpaster/frontend \
valgrindfake \ valgrindfake \
3rdparty \ 3rdparty \
buildoutputparser buildoutputparser \
qtc-askpass
isEmpty(QTC_SKIP_SDKTOOL): SUBDIRS += sdktool isEmpty(QTC_SKIP_SDKTOOL): SUBDIRS += sdktool

View File

@@ -12,6 +12,7 @@ Project {
"qml2puppet/qml2puppet.qbs", "qml2puppet/qml2puppet.qbs",
"qtcdebugger/qtcdebugger.qbs", "qtcdebugger/qtcdebugger.qbs",
"qtcreatorcrashhandler/qtcreatorcrashhandler.qbs", "qtcreatorcrashhandler/qtcreatorcrashhandler.qbs",
"qtc-askpass/qtc-askpass.qbs",
"qtpromaker/qtpromaker.qbs", "qtpromaker/qtpromaker.qbs",
"sdktool/sdktool.qbs", "sdktool/sdktool.qbs",
"valgrindfake/valgrindfake.qbs", "valgrindfake/valgrindfake.qbs",