forked from qt-creator/qt-creator
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 <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -105,46 +105,6 @@ const QString s_pidMarker = "__qtc$$qtc__";
|
|||||||
static Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg);
|
static Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg);
|
||||||
#define LOG(x) qCDebug(dockerDeviceLog) << this << x << '\n'
|
#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<Utils::Port> usedPorts(const QByteArray &output) const override
|
|
||||||
{
|
|
||||||
QList<Utils::Port> ports;
|
|
||||||
QList<QByteArray> 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
|
class ContainerShell : public Utils::DeviceShell
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -607,10 +567,46 @@ bool DockerDevice::canAutoDetectPorts() const
|
|||||||
return true;
|
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<Utils::Port> ports;
|
||||||
|
const QList<QByteArray> 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
|
DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const
|
||||||
{
|
{
|
||||||
|
@@ -66,7 +66,7 @@ public:
|
|||||||
Utils::ProcessInterface *createProcessInterface() const override;
|
Utils::ProcessInterface *createProcessInterface() const override;
|
||||||
|
|
||||||
bool canAutoDetectPorts() const override;
|
bool canAutoDetectPorts() const override;
|
||||||
ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override;
|
ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
|
||||||
bool canCreateProcessModel() const override { return false; }
|
bool canCreateProcessModel() const override { return false; }
|
||||||
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||||
bool hasDeviceTester() const override { return false; }
|
bool hasDeviceTester() const override { return false; }
|
||||||
|
@@ -118,43 +118,38 @@ DeviceEnvironmentFetcher::Ptr DesktopDevice::environmentFetcher() const
|
|||||||
return DeviceEnvironmentFetcher::Ptr(new DesktopDeviceEnvironmentFetcher());
|
return DeviceEnvironmentFetcher::Ptr(new DesktopDeviceEnvironmentFetcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
class DesktopPortsGatheringMethod : public PortsGatheringMethod
|
PortsGatheringMethod DesktopDevice::portsGatheringMethod() const
|
||||||
{
|
{
|
||||||
CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const override
|
return {
|
||||||
{
|
[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
|
||||||
// We might encounter the situation that protocol is given IPv6
|
// We might encounter the situation that protocol is given IPv6
|
||||||
// but the consumer of the free port information decides to open
|
// but the consumer of the free port information decides to open
|
||||||
// an IPv4(only) port. As a result the next IPv6 scan will
|
// an IPv4(only) port. As a result the next IPv6 scan will
|
||||||
// report the port again as open (in IPv6 namespace), while the
|
// report the port again as open (in IPv6 namespace), while the
|
||||||
// same port in IPv4 namespace might still be blocked, and
|
// same port in IPv4 namespace might still be blocked, and
|
||||||
// re-use of this port fails.
|
// re-use of this port fails.
|
||||||
// GDBserver behaves exactly like this.
|
// GDBserver behaves exactly like this.
|
||||||
|
|
||||||
Q_UNUSED(protocol)
|
Q_UNUSED(protocol)
|
||||||
|
|
||||||
if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
|
if (HostOsInfo::isWindowsHost() || HostOsInfo::isMacHost())
|
||||||
return {"netstat", {"-a", "-n"}};
|
return {filePath("netstat"), {"-a", "-n"}};
|
||||||
if (HostOsInfo::isLinuxHost())
|
if (HostOsInfo::isLinuxHost())
|
||||||
return {"/bin/sh", {"-c", "cat /proc/net/tcp*"}};
|
return {filePath("/bin/sh"), {"-c", "cat /proc/net/tcp*"}};
|
||||||
return {};
|
return {};
|
||||||
}
|
},
|
||||||
|
|
||||||
QList<Utils::Port> usedPorts(const QByteArray &output) const override
|
[](const QByteArray &output) {
|
||||||
{
|
QList<Utils::Port> ports;
|
||||||
QList<Utils::Port> ports;
|
const QList<QByteArray> lines = output.split('\n');
|
||||||
const QList<QByteArray> lines = output.split('\n');
|
for (const QByteArray &line : lines) {
|
||||||
for (const QByteArray &line : lines) {
|
const Port port(Utils::parseUsedPortFromNetstatOutput(line));
|
||||||
const Port port(Utils::parseUsedPortFromNetstatOutput(line));
|
if (port.isValid() && !ports.contains(port))
|
||||||
if (port.isValid() && !ports.contains(port))
|
ports.append(port);
|
||||||
ports.append(port);
|
}
|
||||||
|
return ports;
|
||||||
}
|
}
|
||||||
return ports;
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
PortsGatheringMethod::Ptr DesktopDevice::portsGatheringMethod() const
|
|
||||||
{
|
|
||||||
return DesktopPortsGatheringMethod::Ptr(new DesktopPortsGatheringMethod);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
|
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
|
||||||
|
@@ -48,7 +48,7 @@ public:
|
|||||||
bool canAutoDetectPorts() const override;
|
bool canAutoDetectPorts() const override;
|
||||||
bool canCreateProcessModel() const override;
|
bool canCreateProcessModel() const override;
|
||||||
DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||||
ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override;
|
ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
|
||||||
DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
||||||
DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
||||||
QUrl toolControlChannel(const ControlChannelHint &) const override;
|
QUrl toolControlChannel(const ControlChannelHint &) const override;
|
||||||
|
@@ -48,7 +48,7 @@ class DeviceUsedPortsGathererPrivate
|
|||||||
QByteArray remoteStdout;
|
QByteArray remoteStdout;
|
||||||
QByteArray remoteStderr;
|
QByteArray remoteStderr;
|
||||||
IDevice::ConstPtr device;
|
IDevice::ConstPtr device;
|
||||||
PortsGatheringMethod::Ptr portsGatheringMethod;
|
PortsGatheringMethod portsGatheringMethod;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
@@ -71,11 +71,14 @@ void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device)
|
|||||||
QTC_ASSERT(d->device, emit error("No device given"); return);
|
QTC_ASSERT(d->device, emit error("No device given"); return);
|
||||||
|
|
||||||
d->portsGatheringMethod = d->device->portsGatheringMethod();
|
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;
|
const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol;
|
||||||
|
|
||||||
d->process.reset(new QtcProcess);
|
d->process.reset(new QtcProcess);
|
||||||
|
d->process->setCommand(d->portsGatheringMethod.commandLine(protocol));
|
||||||
|
|
||||||
connect(d->process.get(), &QtcProcess::done,
|
connect(d->process.get(), &QtcProcess::done,
|
||||||
this, &DeviceUsedPortsGatherer::handleProcessDone);
|
this, &DeviceUsedPortsGatherer::handleProcessDone);
|
||||||
connect(d->process.get(), &QtcProcess::readyReadStandardOutput,
|
connect(d->process.get(), &QtcProcess::readyReadStandardOutput,
|
||||||
@@ -83,10 +86,7 @@ void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device)
|
|||||||
connect(d->process.get(), &QtcProcess::readyReadStandardError,
|
connect(d->process.get(), &QtcProcess::readyReadStandardError,
|
||||||
this, [this] { d->remoteStderr += d->process->readAllStandardError(); });
|
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();
|
d->process->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ QList<Port> DeviceUsedPortsGatherer::usedPorts() const
|
|||||||
void DeviceUsedPortsGatherer::setupUsedPorts()
|
void DeviceUsedPortsGatherer::setupUsedPorts()
|
||||||
{
|
{
|
||||||
d->usedPorts.clear();
|
d->usedPorts.clear();
|
||||||
const QList<Port> usedPorts = d->portsGatheringMethod->usedPorts(d->remoteStdout);
|
const QList<Port> usedPorts = d->portsGatheringMethod.parsePorts(d->remoteStdout);
|
||||||
for (const Port port : usedPorts) {
|
for (const Port port : usedPorts) {
|
||||||
if (d->device->freePorts().contains(port))
|
if (d->device->freePorts().contains(port))
|
||||||
d->usedPorts << port;
|
d->usedPorts << port;
|
||||||
|
@@ -565,11 +565,6 @@ const QList<IDevice::DeviceAction> IDevice::deviceActions() const
|
|||||||
return d->deviceActions;
|
return d->deviceActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
PortsGatheringMethod::Ptr IDevice::portsGatheringMethod() const
|
|
||||||
{
|
|
||||||
return PortsGatheringMethod::Ptr();
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const
|
DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
|
@@ -109,14 +109,11 @@ protected:
|
|||||||
explicit DeviceEnvironmentFetcher();
|
explicit DeviceEnvironmentFetcher();
|
||||||
};
|
};
|
||||||
|
|
||||||
class PROJECTEXPLORER_EXPORT PortsGatheringMethod
|
class PROJECTEXPLORER_EXPORT PortsGatheringMethod final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Ptr = QSharedPointer<const PortsGatheringMethod>;
|
std::function<Utils::CommandLine(QAbstractSocket::NetworkLayerProtocol protocol)> commandLine;
|
||||||
|
std::function<QList<Utils::Port>(const QByteArray &commandOutput)> parsePorts;
|
||||||
virtual ~PortsGatheringMethod() = default;
|
|
||||||
virtual Utils::CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const = 0;
|
|
||||||
virtual QList<Utils::Port> usedPorts(const QByteArray &commandOutput) const = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// See cpp file for documentation.
|
// 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
|
// 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.
|
// obtain a free port on demand. eg: Desktop device.
|
||||||
virtual bool canAutoDetectPorts() const { return false; }
|
virtual bool canAutoDetectPorts() const { return false; }
|
||||||
virtual PortsGatheringMethod::Ptr portsGatheringMethod() const;
|
virtual PortsGatheringMethod portsGatheringMethod() const { return {}; }
|
||||||
virtual bool canCreateProcessModel() const { return false; }
|
virtual bool canCreateProcessModel() const { return false; }
|
||||||
virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const;
|
virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const;
|
||||||
virtual bool hasDeviceTester() const { return false; }
|
virtual bool hasDeviceTester() const { return false; }
|
||||||
|
@@ -106,29 +106,6 @@ void QnxProcessImpl::sendControlSignal(Utils::ControlSignal controlSignal)
|
|||||||
|
|
||||||
const char QnxVersionKey[] = "QnxVersion";
|
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<Port> usedPorts(const QByteArray &output) const override
|
|
||||||
{
|
|
||||||
QList<Utils::Port> ports;
|
|
||||||
const QList<QByteArray> 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()
|
QnxDevice::QnxDevice()
|
||||||
{
|
{
|
||||||
setDisplayType(tr("QNX"));
|
setDisplayType(tr("QNX"));
|
||||||
@@ -181,9 +158,27 @@ QVariantMap QnxDevice::toMap() const
|
|||||||
return map;
|
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<Utils::Port> ports;
|
||||||
|
const QList<QByteArray> 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
|
DeviceProcessList *QnxDevice::createProcessListModel(QObject *parent) const
|
||||||
|
@@ -42,7 +42,7 @@ public:
|
|||||||
|
|
||||||
static Ptr create() { return Ptr(new QnxDevice); }
|
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::DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||||
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
||||||
|
|
||||||
|
@@ -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<Utils::Port> usedPorts(const QByteArray &output) const override
|
|
||||||
{
|
|
||||||
QList<Utils::Port> ports;
|
|
||||||
QList<QByteArray> 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
|
// LinuxDevicePrivate
|
||||||
|
|
||||||
@@ -1054,9 +1015,45 @@ bool LinuxDevice::canAutoDetectPorts() const
|
|||||||
return true;
|
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<Utils::Port> ports;
|
||||||
|
const QList<QByteArray> 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
|
DeviceProcessList *LinuxDevice::createProcessListModel(QObject *parent) const
|
||||||
|
@@ -47,7 +47,7 @@ public:
|
|||||||
ProjectExplorer::IDeviceWidget *createWidget() override;
|
ProjectExplorer::IDeviceWidget *createWidget() override;
|
||||||
|
|
||||||
bool canAutoDetectPorts() const override;
|
bool canAutoDetectPorts() const override;
|
||||||
ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override;
|
ProjectExplorer::PortsGatheringMethod portsGatheringMethod() const override;
|
||||||
bool canCreateProcessModel() const override { return true; }
|
bool canCreateProcessModel() const override { return true; }
|
||||||
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||||
bool hasDeviceTester() const override { return true; }
|
bool hasDeviceTester() const override { return true; }
|
||||||
|
Reference in New Issue
Block a user