From 7f2288d9cce54558425f68cf50422d273fa2efe5 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 10 May 2022 12:53:27 +0200 Subject: [PATCH] ProjectExplorer: Avoid need to fix up portsgatherer command lines ... by creating them with the right device to start with. Change-Id: Ib2f0f10b67322fe66a609287a629d3d720d83c93 Reviewed-by: Jarek Kobus --- src/plugins/docker/dockerdevice.cpp | 82 +++++++++---------- src/plugins/docker/dockerdevice.h | 2 +- .../devicesupport/desktopdevice.cpp | 59 ++++++------- .../devicesupport/desktopdevice.h | 2 +- .../devicesupport/deviceusedportsgatherer.cpp | 14 ++-- .../projectexplorer/devicesupport/idevice.cpp | 5 -- .../projectexplorer/devicesupport/idevice.h | 11 +-- src/plugins/qnx/qnxdevice.cpp | 45 +++++----- src/plugins/qnx/qnxdevice.h | 2 +- src/plugins/remotelinux/linuxdevice.cpp | 79 +++++++++--------- src/plugins/remotelinux/linuxdevice.h | 2 +- 11 files changed, 139 insertions(+), 164 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index f4757d31ce0..4b8adc44638 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -105,46 +105,6 @@ const QString s_pidMarker = "__qtc$$qtc__"; static Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg); #define LOG(x) qCDebug(dockerDeviceLog) << this << x << '\n' -class DockerPortsGatheringMethod : public PortsGatheringMethod -{ - CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const override - { - // We might encounter the situation that protocol is given IPv6 - // but the consumer of the free port information decides to open - // an IPv4(only) port. As a result the next IPv6 scan will - // report the port again as open (in IPv6 namespace), while the - // same port in IPv4 namespace might still be blocked, and - // re-use of this port fails. - // GDBserver behaves exactly like this. - - Q_UNUSED(protocol) - - // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6 - return {"sed", "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*", - CommandLine::Raw}; - } - - QList usedPorts(const QByteArray &output) const override - { - QList ports; - QList portStrings = output.split('\n'); - foreach (const QByteArray &portString, portStrings) { - if (portString.size() != 4) - continue; - bool ok; - const Utils::Port port(portString.toInt(&ok, 16)); - if (ok) { - if (!ports.contains(port)) - ports << port; - } else { - qWarning("%s: Unexpected string '%s' is not a port.", - Q_FUNC_INFO, portString.data()); - } - } - return ports; - } -}; - class ContainerShell : public Utils::DeviceShell { public: @@ -607,10 +567,46 @@ bool DockerDevice::canAutoDetectPorts() const return true; } -PortsGatheringMethod::Ptr DockerDevice::portsGatheringMethod() const +PortsGatheringMethod DockerDevice::portsGatheringMethod() const { - return DockerPortsGatheringMethod::Ptr(new DockerPortsGatheringMethod); -} + return { + [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine { + // We might encounter the situation that protocol is given IPv6 + // but the consumer of the free port information decides to open + // an IPv4(only) port. As a result the next IPv6 scan will + // report the port again as open (in IPv6 namespace), while the + // same port in IPv4 namespace might still be blocked, and + // re-use of this port fails. + // GDBserver behaves exactly like this. + + Q_UNUSED(protocol) + + // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6 + return {filePath("sed"), + "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*", + CommandLine::Raw}; + }, + + [](const QByteArray &output) { + QList ports; + const QList portStrings = output.split('\n'); + for (const QByteArray &portString : portStrings) { + if (portString.size() != 4) + continue; + bool ok; + const Utils::Port port(portString.toInt(&ok, 16)); + if (ok) { + if (!ports.contains(port)) + ports << port; + } else { + qWarning("%s: Unexpected string '%s' is not a port.", + Q_FUNC_INFO, portString.data()); + } + } + return ports; + } + }; +}; DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const { diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index c14aea2be0e..4fb75dc198c 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -66,7 +66,7 @@ public: Utils::ProcessInterface *createProcessInterface() const override; bool canAutoDetectPorts() const override; - ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override; + ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override; bool canCreateProcessModel() const override { return false; } ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override; bool hasDeviceTester() const override { return false; } diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index 981b9d07799..50efbb2dffb 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -118,43 +118,38 @@ DeviceEnvironmentFetcher::Ptr DesktopDevice::environmentFetcher() const return DeviceEnvironmentFetcher::Ptr(new DesktopDeviceEnvironmentFetcher()); } -class DesktopPortsGatheringMethod : public PortsGatheringMethod +PortsGatheringMethod DesktopDevice::portsGatheringMethod() const { - CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const override - { - // We might encounter the situation that protocol is given IPv6 - // but the consumer of the free port information decides to open - // an IPv4(only) port. As a result the next IPv6 scan will - // report the port again as open (in IPv6 namespace), while the - // same port in IPv4 namespace might still be blocked, and - // re-use of this port fails. - // GDBserver behaves exactly like this. + return { + [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine { + // We might encounter the situation that protocol is given IPv6 + // but the consumer of the free port information decides to open + // an IPv4(only) port. As a result the next IPv6 scan will + // report the port again as open (in IPv6 namespace), while the + // same port in IPv4 namespace might still be blocked, and + // re-use of this port fails. + // GDBserver behaves exactly like this. - Q_UNUSED(protocol) + Q_UNUSED(protocol) - if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost()) - return {"netstat", {"-a", "-n"}}; - if (HostOsInfo::isLinuxHost()) - return {"/bin/sh", {"-c", "cat /proc/net/tcp*"}}; - return {}; - } + if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost()) + return {filePath("netstat"), {"-a", "-n"}}; + if (HostOsInfo::isLinuxHost()) + return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}}; + return {}; + }, - QList usedPorts(const QByteArray &output) const override - { - QList ports; - const QList lines = output.split('\n'); - for (const QByteArray &line : lines) { - const Port port(Utils::parseUsedPortFromNetstatOutput(line)); - if (port.isValid() && !ports.contains(port)) - ports.append(port); + [](const QByteArray &output) { + QList ports; + const QList lines = output.split('\n'); + for (const QByteArray &line : lines) { + const Port port(Utils::parseUsedPortFromNetstatOutput(line)); + if (port.isValid() && !ports.contains(port)) + ports.append(port); + } + return ports; } - return ports; - } -}; - -PortsGatheringMethod::Ptr DesktopDevice::portsGatheringMethod() const -{ - return DesktopPortsGatheringMethod::Ptr(new DesktopPortsGatheringMethod); + }; } QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index ee8aa70e191..391bf53997f 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -48,7 +48,7 @@ public: bool canAutoDetectPorts() const override; bool canCreateProcessModel() const override; DeviceProcessList *createProcessListModel(QObject *parent) const override; - ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override; + ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override; DeviceProcessSignalOperation::Ptr signalOperation() const override; DeviceEnvironmentFetcher::Ptr environmentFetcher() const override; QUrl toolControlChannel(const ControlChannelHint &) const override; diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp index d2fd04125e3..fcba838e972 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp @@ -48,7 +48,7 @@ class DeviceUsedPortsGathererPrivate QByteArray remoteStdout; QByteArray remoteStderr; IDevice::ConstPtr device; - PortsGatheringMethod::Ptr portsGatheringMethod; + PortsGatheringMethod portsGatheringMethod; }; } // namespace Internal @@ -71,11 +71,14 @@ void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device) QTC_ASSERT(d->device, emit error("No device given"); return); d->portsGatheringMethod = d->device->portsGatheringMethod(); - QTC_ASSERT(d->portsGatheringMethod, emit error("Not implemented"); return); + QTC_ASSERT(d->portsGatheringMethod.commandLine, emit error("Not implemented"); return); + QTC_ASSERT(d->portsGatheringMethod.parsePorts, emit error("Not implemented"); return); const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol; d->process.reset(new QtcProcess); + d->process->setCommand(d->portsGatheringMethod.commandLine(protocol)); + connect(d->process.get(), &QtcProcess::done, this, &DeviceUsedPortsGatherer::handleProcessDone); connect(d->process.get(), &QtcProcess::readyReadStandardOutput, @@ -83,10 +86,7 @@ void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device) connect(d->process.get(), &QtcProcess::readyReadStandardError, this, [this] { d->remoteStderr += d->process->readAllStandardError(); }); - CommandLine command = d->portsGatheringMethod->commandLine(protocol); - const FilePath executable = d->device->mapToGlobalPath(command.executable()); - command.setExecutable(executable); - d->process->setCommand(command); + d->process->start(); } @@ -118,7 +118,7 @@ QList DeviceUsedPortsGatherer::usedPorts() const void DeviceUsedPortsGatherer::setupUsedPorts() { d->usedPorts.clear(); - const QList usedPorts = d->portsGatheringMethod->usedPorts(d->remoteStdout); + const QList usedPorts = d->portsGatheringMethod.parsePorts(d->remoteStdout); for (const Port port : usedPorts) { if (d->device->freePorts().contains(port)) d->usedPorts << port; diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 067f6a244a2..1bd6c10e331 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -565,11 +565,6 @@ const QList IDevice::deviceActions() const return d->deviceActions; } -PortsGatheringMethod::Ptr IDevice::portsGatheringMethod() const -{ - return PortsGatheringMethod::Ptr(); -} - DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const { Q_UNUSED(parent) diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index f5424c1ce00..8ba30b97e56 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -109,14 +109,11 @@ protected: explicit DeviceEnvironmentFetcher(); }; -class PROJECTEXPLORER_EXPORT PortsGatheringMethod +class PROJECTEXPLORER_EXPORT PortsGatheringMethod final { public: - using Ptr = QSharedPointer; - - virtual ~PortsGatheringMethod() = default; - virtual Utils::CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const = 0; - virtual QList usedPorts(const QByteArray &commandOutput) const = 0; + std::function commandLine; + std::function(const QByteArray &commandOutput)> parsePorts; }; // See cpp file for documentation. @@ -175,7 +172,7 @@ public: // Devices that can auto detect ports need not return a ports gathering method. Such devices can // obtain a free port on demand. eg: Desktop device. virtual bool canAutoDetectPorts() const { return false; } - virtual PortsGatheringMethod::Ptr portsGatheringMethod() const; + virtual PortsGatheringMethod portsGatheringMethod() const { return {}; } virtual bool canCreateProcessModel() const { return false; } virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const; virtual bool hasDeviceTester() const { return false; } diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp index 6af7a3f4a19..b28262d501a 100644 --- a/src/plugins/qnx/qnxdevice.cpp +++ b/src/plugins/qnx/qnxdevice.cpp @@ -106,29 +106,6 @@ void QnxProcessImpl::sendControlSignal(Utils::ControlSignal controlSignal) const char QnxVersionKey[] = "QnxVersion"; -class QnxPortsGatheringMethod : public PortsGatheringMethod -{ - // TODO: The command is probably needlessly complicated because the parsing method - // used to be fixed. These two can now be matched to each other. - CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const override - { - Q_UNUSED(protocol) - return {"netstat", {"-na"}}; - } - - QList usedPorts(const QByteArray &output) const override - { - QList ports; - const QList lines = output.split('\n'); - for (const QByteArray &line : lines) { - const Port port(Utils::parseUsedPortFromNetstatOutput(line)); - if (port.isValid() && !ports.contains(port)) - ports.append(port); - } - return ports; - } -}; - QnxDevice::QnxDevice() { setDisplayType(tr("QNX")); @@ -181,9 +158,27 @@ QVariantMap QnxDevice::toMap() const return map; } -PortsGatheringMethod::Ptr QnxDevice::portsGatheringMethod() const +PortsGatheringMethod QnxDevice::portsGatheringMethod() const { - return PortsGatheringMethod::Ptr(new QnxPortsGatheringMethod); + return { + // TODO: The command is probably needlessly complicated because the parsing method + // used to be fixed. These two can now be matched to each other. + [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine { + Q_UNUSED(protocol) + return {filePath("netstat"), {"-na"}}; + }, + + [](const QByteArray &output) { + QList ports; + const QList lines = output.split('\n'); + for (const QByteArray &line : lines) { + const Port port(Utils::parseUsedPortFromNetstatOutput(line)); + if (port.isValid() && !ports.contains(port)) + ports.append(port); + } + return ports; + } + }; } DeviceProcessList *QnxDevice::createProcessListModel(QObject *parent) const diff --git a/src/plugins/qnx/qnxdevice.h b/src/plugins/qnx/qnxdevice.h index 07266e2ff91..af72214d5ea 100644 --- a/src/plugins/qnx/qnxdevice.h +++ b/src/plugins/qnx/qnxdevice.h @@ -42,7 +42,7 @@ public: static Ptr create() { return Ptr(new QnxDevice); } - ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override; + ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override; ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override; ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override; diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index f49c6c968b2..599e696ce03 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -366,45 +366,6 @@ private: } }; -class LinuxPortsGatheringMethod : public PortsGatheringMethod -{ - CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const override - { - // We might encounter the situation that protocol is given IPv6 - // but the consumer of the free port information decides to open - // an IPv4(only) port. As a result the next IPv6 scan will - // report the port again as open (in IPv6 namespace), while the - // same port in IPv4 namespace might still be blocked, and - // re-use of this port fails. - // GDBserver behaves exactly like this. - - Q_UNUSED(protocol) - - // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6 - return {"sed", "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*", - CommandLine::Raw}; - } - - QList usedPorts(const QByteArray &output) const override - { - QList ports; - QList portStrings = output.split('\n'); - foreach (const QByteArray &portString, portStrings) { - if (portString.size() != 4) - continue; - bool ok; - const Utils::Port port(portString.toInt(&ok, 16)); - if (ok) { - if (!ports.contains(port)) - ports << port; - } else { - qWarning("%s: Unexpected string '%s' is not a port.", - Q_FUNC_INFO, portString.data()); - } - } - return ports; - } -}; // LinuxDevicePrivate @@ -1054,9 +1015,45 @@ bool LinuxDevice::canAutoDetectPorts() const return true; } -PortsGatheringMethod::Ptr LinuxDevice::portsGatheringMethod() const +PortsGatheringMethod LinuxDevice::portsGatheringMethod() const { - return LinuxPortsGatheringMethod::Ptr(new LinuxPortsGatheringMethod); + return { + [this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine { + // We might encounter the situation that protocol is given IPv6 + // but the consumer of the free port information decides to open + // an IPv4(only) port. As a result the next IPv6 scan will + // report the port again as open (in IPv6 namespace), while the + // same port in IPv4 namespace might still be blocked, and + // re-use of this port fails. + // GDBserver behaves exactly like this. + + Q_UNUSED(protocol) + + // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6 + return {filePath("sed"), + "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*", + CommandLine::Raw}; + }, + + [](const QByteArray &output) { + QList ports; + const QList portStrings = output.split('\n'); + for (const QByteArray &portString : portStrings) { + if (portString.size() != 4) + continue; + bool ok; + const Utils::Port port(portString.toInt(&ok, 16)); + if (ok) { + if (!ports.contains(port)) + ports << port; + } else { + qWarning("%s: Unexpected string '%s' is not a port.", + Q_FUNC_INFO, portString.data()); + } + } + return ports; + } + }; } DeviceProcessList *LinuxDevice::createProcessListModel(QObject *parent) const diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index 60c97b07e9c..3272e46e575 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -47,7 +47,7 @@ public: ProjectExplorer::IDeviceWidget *createWidget() override; bool canAutoDetectPorts() const override; - ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override; + ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override; bool canCreateProcessModel() const override { return true; } ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override; bool hasDeviceTester() const override { return true; }