diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp index fb4ef53579f..b7890bb610b 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp @@ -7,6 +7,7 @@ #include "remotelinuxtr.h" #include "sshkeycreationdialog.h" +#include #include #include @@ -17,6 +18,7 @@ #include #include +#include #include #include #include @@ -84,6 +86,16 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget( m_sourceProfileCheckBox = new QCheckBox(Tr::tr("Source %1 and %2").arg("/etc/profile").arg("$HOME/.profile")); + m_linkDeviceComboBox = new QComboBox; + m_linkDeviceComboBox->addItem(Tr::tr("Direct"), QVariant()); + + auto dm = DeviceManager::instance(); + const int dmCount = dm->deviceCount(); + for (int i = 0; i < dmCount; ++i) { + IDevice::ConstPtr dev = dm->deviceAt(i); + m_linkDeviceComboBox->addItem(dev->displayName(), dev->id().toSetting()); + } + auto sshPortLabel = new QLabel(Tr::tr("&SSH port:")); sshPortLabel->setBuddy(m_sshPortSpinBox); @@ -98,7 +110,8 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget( m_keyLabel, m_keyFileLineEdit, createKeyButton, br, Tr::tr("GDB server executable:"), m_gdbServerLineEdit, br, Tr::tr("QML runtime executable:"), m_qmlRuntimeLineEdit, br, - QString(), m_sourceProfileCheckBox, br + QString(), m_sourceProfileCheckBox, br, + Tr::tr("Access via:"), m_linkDeviceComboBox }.attachTo(this); connect(m_hostLineEdit, &QLineEdit::editingFinished, @@ -131,6 +144,8 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget( this, &GenericLinuxDeviceConfigurationWidget::hostKeyCheckingChanged); connect(m_sourceProfileCheckBox, &QCheckBox::toggled, this, &GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged); + connect(m_linkDeviceComboBox, &QComboBox::currentIndexChanged, + this, &GenericLinuxDeviceConfigurationWidget::linkDeviceChanged); initGui(); } @@ -226,6 +241,12 @@ void GenericLinuxDeviceConfigurationWidget::sourceProfileCheckingChanged(bool do device()->setExtraData(Constants::SourceProfile, doCheck); } +void GenericLinuxDeviceConfigurationWidget::linkDeviceChanged(int index) +{ + const QVariant deviceId = m_linkDeviceComboBox->itemData(index); + device()->setExtraData(Constants::LinkDevice, deviceId); +} + void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi() { hostNameEditingFinished(); @@ -235,6 +256,10 @@ void GenericLinuxDeviceConfigurationWidget::updateDeviceFromUi() keyFileEditingFinished(); handleFreePortsChanged(); gdbServerEditingFinished(); + sshPortEditingFinished(); + timeoutEditingFinished(); + sourceProfileCheckingChanged(m_sourceProfileCheckBox->isChecked()); + linkDeviceChanged(m_linkDeviceComboBox->currentIndex()); } void GenericLinuxDeviceConfigurationWidget::updatePortsWarningLabel() @@ -273,6 +298,16 @@ void GenericLinuxDeviceConfigurationWidget::initGui() m_sshPortSpinBox->setEnabled(!device()->isAutoDetected()); m_hostKeyCheckBox->setChecked(sshParams.hostKeyCheckingMode != SshHostKeyCheckingNone); m_sourceProfileCheckBox->setChecked(device()->extraData(Constants::SourceProfile).toBool()); + Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice)); + auto dm = DeviceManager::instance(); + int found = -1; + for (int i = 0, n = dm->deviceCount(); i < n; ++i) { + if (dm->deviceAt(i)->id() == linkDeviceId) { + found = i; + break; + } + } + m_linkDeviceComboBox->setCurrentIndex(found + 1); // There's the "Direct" entry first. m_hostLineEdit->setText(sshParams.host()); m_sshPortSpinBox->setValue(sshParams.port()); diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h index 024656d1fa0..e9840c9a436 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.h @@ -7,6 +7,7 @@ QT_BEGIN_NAMESPACE class QCheckBox; +class QComboBox; class QLabel; class QLineEdit; class QRadioButton; @@ -43,6 +44,7 @@ private: void createNewKey(); void hostKeyCheckingChanged(bool doCheck); void sourceProfileCheckingChanged(bool doCheck); + void linkDeviceChanged(int index); void updateDeviceFromUi() override; void updatePortsWarningLabel(); @@ -63,6 +65,7 @@ private: Utils::PathChooser *m_gdbServerLineEdit; Utils::PathChooser *m_qmlRuntimeLineEdit; QCheckBox *m_sourceProfileCheckBox; + QComboBox *m_linkDeviceComboBox; }; } // RemoteLinux::Internal diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index da7b7aa2d18..4ef2c7340a1 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -389,6 +390,8 @@ public: QString m_socketFilePath; SshParameters m_sshParameters; + IDevice::ConstPtr m_linkDevice; + bool m_connecting = false; bool m_killed = false; @@ -397,6 +400,7 @@ public: QByteArray m_output; QByteArray m_error; bool m_pidParsed = false; + bool m_useConnectionSharing = false; }; SshProcessInterface::SshProcessInterface(const IDevice::ConstPtr &device) @@ -591,11 +595,15 @@ void SshProcessInterfacePrivate::start() { clearForStart(); + const Id linkDeviceId = Id::fromSetting(m_device->extraData(Constants::LinkDevice)); + m_linkDevice = DeviceManager::instance()->find(linkDeviceId); + m_useConnectionSharing = !m_linkDevice && SshSettings::connectionSharingEnabled(); + m_sshParameters = m_device->sshParameters(); // TODO: Do we really need it for master process? m_sshParameters.x11DisplayName = q->m_setup.m_extraData.value("Ssh.X11ForwardToDisplay").toString(); - if (SshSettings::connectionSharingEnabled()) { + if (m_useConnectionSharing) { m_connecting = true; m_connectionHandle.reset(new SshConnectionHandle(m_device)); m_connectionHandle->setParent(this); @@ -662,30 +670,35 @@ void SshProcessInterfacePrivate::doStart() m_process.start(); } -CommandLine SshProcessInterfacePrivate::fullLocalCommandLine() const +static CommandLine getCommandLine( + const FilePath sshBinary, + const CommandLine commandLine0, + const FilePath &workingDirectory, + const Environment &env, + const QStringList &options, + bool useX, + bool useTerminal, + bool usePidMarker, + bool sourceProfile) { - CommandLine cmd{SshSettings::sshFilePath()}; + CommandLine cmd{sshBinary}; - if (!m_sshParameters.x11DisplayName.isEmpty()) + if (useX) cmd.addArg("-X"); - if (q->m_setup.m_terminalMode != TerminalMode::Off || q->m_setup.m_ptyData) + if (useTerminal) cmd.addArg("-tt"); cmd.addArg("-q"); - QStringList options = m_sshParameters.connectionOptions(SshSettings::sshFilePath()); - if (!m_socketFilePath.isEmpty()) - options << "-o" << ("ControlPath=" + m_socketFilePath); - options << m_sshParameters.host(); cmd.addArgs(options); - CommandLine commandLine = q->m_setup.m_commandLine; + CommandLine commandLine = commandLine0; FilePath executable = FilePath::fromParts({}, {}, commandLine.executable().path()); commandLine.setExecutable(executable); CommandLine inner; - if (!commandLine.isEmpty() && m_device->extraData(Constants::SourceProfile).toBool()) { + if (!commandLine.isEmpty() && sourceProfile) { const QStringList rcFilesToSource = {"/etc/profile", "$HOME/.profile"}; for (const QString &filePath : rcFilesToSource) { inner.addArgs({"test", "-f", filePath}); @@ -695,31 +708,87 @@ CommandLine SshProcessInterfacePrivate::fullLocalCommandLine() const } } - if (!q->m_setup.m_workingDirectory.isEmpty()) { - inner.addArgs({"cd", q->m_setup.m_workingDirectory.path()}); + if (!workingDirectory.isEmpty()) { + inner.addArgs({"cd", workingDirectory.path()}); inner.addArgs("&&", CommandLine::Raw); } - if (q->m_setup.m_terminalMode == TerminalMode::Off && !q->m_setup.m_ptyData) + if (usePidMarker) inner.addArgs(QString("echo ") + s_pidMarker + "$$" + s_pidMarker + " && ", CommandLine::Raw); - const Environment &env = q->m_setup.m_environment; env.forEachEntry([&](const QString &key, const QString &value, bool) { inner.addArgs(key + "='" + env.expandVariables(value) + '\'', CommandLine::Raw); }); - if (q->m_setup.m_terminalMode == TerminalMode::Off && !q->m_setup.m_ptyData) + if (!useTerminal && !commandLine.isEmpty()) inner.addArg("exec"); if (!commandLine.isEmpty()) inner.addCommandLineAsArgs(commandLine, CommandLine::Raw); - cmd.addArg(inner.arguments()); return cmd; } +CommandLine SshProcessInterfacePrivate::fullLocalCommandLine() const +{ + const FilePath sshBinary = SshSettings::sshFilePath(); + const bool useTerminal = q->m_setup.m_terminalMode != TerminalMode::Off || q->m_setup.m_ptyData; + const bool usePidMarker = !useTerminal; + const bool sourceProfile = m_device->extraData(Constants::SourceProfile).toBool(); + const bool useX = !m_sshParameters.x11DisplayName.isEmpty(); + + CommandLine cmd; + if (m_linkDevice) { + QStringList farOptions = m_sshParameters.connectionOptions("ssh"); + farOptions << m_sshParameters.host(); + + const SshParameters nearParameters = m_linkDevice->sshParameters(); + QStringList nearOptions = nearParameters.connectionOptions(sshBinary); +// if (!m_socketFilePath.isEmpty()) +// options << "-o" << ("ControlPath=" + m_socketFilePath); + nearOptions << nearParameters.host(); + + cmd = getCommandLine("ssh", + q->m_setup.m_commandLine, + {}, + {}, + farOptions, + false, + false, + false, + false); + + cmd = getCommandLine(sshBinary, + cmd, + {}, + {}, + nearOptions, + false, + false, + usePidMarker, + false); + } else { + QStringList options = m_sshParameters.connectionOptions(sshBinary); + if (!m_socketFilePath.isEmpty()) + options << "-o" << ("ControlPath=" + m_socketFilePath); + options << m_sshParameters.host(); + + cmd = getCommandLine(sshBinary, + q->m_setup.m_commandLine, + q->m_setup.m_workingDirectory, + q->m_setup.m_environment, + options, + useX, + useTerminal, + usePidMarker, + sourceProfile); + } + + return cmd; +} + // ShellThreadHandler static SshParameters displayless(const SshParameters &sshParameters) @@ -1203,7 +1272,11 @@ private: void start() final { m_sshParameters = displayless(m_device->sshParameters()); - if (SshSettings::connectionSharingEnabled()) { + const Id linkDeviceId = Id::fromSetting(m_device->extraData(Constants::LinkDevice)); + const auto linkDevice = DeviceManager::instance()->find(linkDeviceId); + const bool useConnectionSharing = !linkDevice && SshSettings::connectionSharingEnabled(); + + if (useConnectionSharing) { m_connecting = true; m_connectionHandle.reset(new SshConnectionHandle(m_device)); m_connectionHandle->setParent(this); diff --git a/src/plugins/remotelinux/remotelinux_constants.h b/src/plugins/remotelinux/remotelinux_constants.h index a7ad8de6989..40cc1d1a8b1 100644 --- a/src/plugins/remotelinux/remotelinux_constants.h +++ b/src/plugins/remotelinux/remotelinux_constants.h @@ -19,8 +19,9 @@ const char RsyncDeployStepId[] = "RemoteLinux.RsyncDeployStep"; const char CustomCommandDeployStepId[] = "RemoteLinux.GenericRemoteLinuxCustomCommandDeploymentStep"; const char KillAppStepId[] = "RemoteLinux.KillAppStep"; -const char SupportsRSync[] = "RemoteLinux.SupportsRSync"; -const char SourceProfile[] = "RemoteLinux.SourceProfile"; +const char SupportsRSync[] = "RemoteLinux.SupportsRSync"; +const char SourceProfile[] = "RemoteLinux.SourceProfile"; +const char LinkDevice[] = "RemoteLinux.LinkDevice"; const char RunConfigId[] = "RemoteLinuxRunConfiguration:"; const char CustomRunConfigId[] = "RemoteLinux.CustomRunConfig";