Devices: Unify Port Gathering method

All devices that support it use the same mechanism to gather ports
so this patch removes the individual implementations in favor
of a single one in IDevice.cpp.

This patch also removes:
* canAutodetectPorts() as it was not used.
* Port::parseFrom...Output as they are not used anymore.

Change-Id: I8ecedec2d71e60985402387982c64311c5a651e6
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Marcus Tillmanns
2023-04-05 11:15:02 +02:00
parent f5c41a7f83
commit 522de9bfd7
17 changed files with 23 additions and 182 deletions

View File

@@ -33,56 +33,6 @@ quint16 Port::number() const
QTC_ASSERT(isValid(), return -1); return quint16(m_port);
}
QList<Port> Port::parseFromSedOutput(const QByteArray &output)
{
QList<Port> ports;
const QList<QByteArray> portStrings = output.split('\n');
for (const QByteArray &portString : portStrings) {
if (portString.size() != 4)
continue;
bool ok;
const 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;
}
QList<Port> Port::parseFromCatOutput(const QByteArray &output)
{
// Parse something like
// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
// : 00000000:2717 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1001 0 3995881 1 0000000000000000 100 0 0 10 0
// : 00000000:2716 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1001 0 3594482 1 0000000000000000 100 0 0 10 0
const QRegularExpression re(".*: [[:xdigit:]]*:([[:xdigit:]]{4}).*");
QList<Port> ports;
const QStringList lines = QString::fromLocal8Bit(output).split('\n');
for (const QString &line : lines) {
const QRegularExpressionMatch match = re.match(line);
if (!match.hasMatch())
continue;
const QString portString = match.captured(1);
if (portString.size() != 4)
continue;
bool ok;
const 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, qPrintable(portString));
}
}
return ports;
}
QList<Port> Port::parseFromNetstatOutput(const QByteArray &output)
{
QList<Port> ports;

View File

@@ -24,8 +24,6 @@ public:
QString toString() const { return QString::number(m_port); }
static QList<Port> parseFromSedOutput(const QByteArray &output);
static QList<Port> parseFromCatOutput(const QByteArray &output);
static QList<Port> parseFromNetstatOutput(const QByteArray &output);
friend bool operator<(const Port &p1, const Port &p2) { return p1.number() < p2.number(); }

View File

@@ -388,11 +388,6 @@ IDeviceWidget *AndroidDevice::createWidget()
return new AndroidDeviceWidget(sharedFromThis());
}
bool AndroidDevice::canAutoDetectPorts() const
{
return true;
}
DeviceProcessSignalOperation::Ptr AndroidDevice::signalOperation() const
{
return DeviceProcessSignalOperation::Ptr(new AndroidSignalOperation());

View File

@@ -61,7 +61,6 @@ private:
void addActionsIfNotFound();
ProjectExplorer::IDevice::DeviceInfo deviceInformation() const override;
ProjectExplorer::IDeviceWidget *createWidget() override;
bool canAutoDetectPorts() const override;
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override;

View File

@@ -419,7 +419,7 @@ DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &dat
{
setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Docker"));
setOsType(OsTypeOtherUnix);
setOsType(OsTypeLinux);
setDefaultDisplayName(Tr::tr("Docker Image"));
setupId(IDevice::ManuallyAdded);
setType(Constants::DOCKER_DEVICE_TYPE);
@@ -832,33 +832,6 @@ ProcessInterface *DockerDevice::createProcessInterface() const
return new DockerProcessImpl(this->sharedFromThis(), d);
}
bool DockerDevice::canAutoDetectPorts() const
{
return true;
}
PortsGatheringMethod DockerDevice::portsGatheringMethod() const
{
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};
},
&Port::parseFromSedOutput};
};
DeviceProcessList *DockerDevice::createProcessListModel(QObject *parent) const
{
return new ProcessList(sharedFromThis(), parent);

View File

@@ -73,8 +73,6 @@ public:
Utils::ProcessInterface *createProcessInterface() const override;
bool canAutoDetectPorts() 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 false; }

View File

@@ -185,12 +185,6 @@ Utils::Port IosDevice::nextPort() const
return Utils::Port(m_lastPort);
}
bool IosDevice::canAutoDetectPorts() const
{
return true;
}
// IosDeviceManager
IosDeviceManager::TranslationMap IosDeviceManager::translationMap()

View File

@@ -36,7 +36,6 @@ public:
QString osVersion() const;
QString cpuArchitecture() const;
Utils::Port nextPort() const;
bool canAutoDetectPorts() const override;
static QString name();

View File

@@ -66,11 +66,6 @@ Utils::Port IosSimulator::nextPort() const
return Utils::Port(m_lastPort);
}
bool IosSimulator::canAutoDetectPorts() const
{
return true;
}
// IosDeviceType
IosDeviceType::IosDeviceType(IosDeviceType::Type type, const QString &identifier, const QString &displayName) :

View File

@@ -48,7 +48,6 @@ public:
ProjectExplorer::IDeviceWidget *createWidget() override;
Utils::Port nextPort() const;
bool canAutoDetectPorts() const override;
protected:
friend class IosSimulatorFactory;

View File

@@ -87,11 +87,6 @@ IDeviceWidget *DesktopDevice::createWidget()
// range can be confusing to the user. Hence, disabling the widget for now.
}
bool DesktopDevice::canAutoDetectPorts() const
{
return true;
}
bool DesktopDevice::canCreateProcessModel() const
{
return true;
@@ -107,31 +102,6 @@ DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const
return DeviceProcessSignalOperation::Ptr(new DesktopProcessSignalOperation());
}
PortsGatheringMethod DesktopDevice::portsGatheringMethod() const
{
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)
if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
return {filePath("netstat"), {"-a", "-n"}};
if (HostOsInfo::isLinuxHost())
return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
return {};
},
&Port::parseFromNetstatOutput
};
}
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
{
QUrl url;

View File

@@ -25,10 +25,8 @@ public:
IDevice::DeviceInfo deviceInformation() const override;
IDeviceWidget *createWidget() override;
bool canAutoDetectPorts() const override;
bool canCreateProcessModel() const override;
DeviceProcessList *createProcessListModel(QObject *parent) const override;
ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
DeviceProcessSignalOperation::Ptr signalOperation() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override;
bool usableAsBuildDevice() const override;

View File

@@ -371,6 +371,27 @@ const QList<IDevice::DeviceAction> IDevice::deviceActions() const
return d->deviceActions;
}
PortsGatheringMethod IDevice::portsGatheringMethod() const
{
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)
if (filePath("/proc/net").isReadableDir())
return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
return {filePath("netstat"), {"-a", "-n"}};
},
&Port::parseFromNetstatOutput};
};
DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const
{
Q_UNUSED(parent)

View File

@@ -139,10 +139,7 @@ public:
void addDeviceAction(const DeviceAction &deviceAction);
const QList<DeviceAction> deviceActions() const;
// 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 portsGatheringMethod() const { return {}; }
virtual PortsGatheringMethod portsGatheringMethod() const;
virtual bool canCreateProcessModel() const { return false; }
virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const;
virtual bool hasDeviceTester() const { return false; }

View File

@@ -78,16 +78,6 @@ public:
}});
}
PortsGatheringMethod portsGatheringMethod() const final
{
return {
[this](QAbstractSocket::NetworkLayerProtocol) {
return CommandLine(filePath("netstat"), {"-na"});
},
&Port::parseFromNetstatOutput
};
}
DeviceProcessSignalOperation::Ptr signalOperation() const final
{
return DeviceProcessSignalOperation::Ptr(new QnxDeviceProcessSignalOperation(sharedFromThis()));

View File

@@ -993,39 +993,6 @@ IDeviceWidget *LinuxDevice::createWidget()
return new Internal::GenericLinuxDeviceConfigurationWidget(sharedFromThis());
}
bool LinuxDevice::canAutoDetectPorts() const
{
return true;
}
PortsGatheringMethod LinuxDevice::portsGatheringMethod() const
{
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)
// We used to have
// // /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
// return {filePath("sed"),
// "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
//
// here, but that doesn't pass quoting on double-remote setups.
// Chicken out by using a simpler command.
return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
},
&Port::parseFromCatOutput
};
}
DeviceProcessList *LinuxDevice::createProcessListModel(QObject *parent) const
{
return new ProcessList(sharedFromThis(), parent);

View File

@@ -22,8 +22,6 @@ public:
ProjectExplorer::IDeviceWidget *createWidget() override;
bool canAutoDetectPorts() 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; }