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);
|
||||
#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
|
||||
{
|
||||
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<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
|
||||
{
|
||||
|
@@ -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; }
|
||||
|
@@ -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<Utils::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);
|
||||
[](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;
|
||||
}
|
||||
return ports;
|
||||
}
|
||||
};
|
||||
|
||||
PortsGatheringMethod::Ptr DesktopDevice::portsGatheringMethod() const
|
||||
{
|
||||
return DesktopPortsGatheringMethod::Ptr(new DesktopPortsGatheringMethod);
|
||||
};
|
||||
}
|
||||
|
||||
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
|
||||
|
@@ -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;
|
||||
|
@@ -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<Port> DeviceUsedPortsGatherer::usedPorts() const
|
||||
void DeviceUsedPortsGatherer::setupUsedPorts()
|
||||
{
|
||||
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) {
|
||||
if (d->device->freePorts().contains(port))
|
||||
d->usedPorts << port;
|
||||
|
@@ -565,11 +565,6 @@ const QList<IDevice::DeviceAction> IDevice::deviceActions() const
|
||||
return d->deviceActions;
|
||||
}
|
||||
|
||||
PortsGatheringMethod::Ptr IDevice::portsGatheringMethod() const
|
||||
{
|
||||
return PortsGatheringMethod::Ptr();
|
||||
}
|
||||
|
||||
DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
@@ -109,14 +109,11 @@ protected:
|
||||
explicit DeviceEnvironmentFetcher();
|
||||
};
|
||||
|
||||
class PROJECTEXPLORER_EXPORT PortsGatheringMethod
|
||||
class PROJECTEXPLORER_EXPORT PortsGatheringMethod final
|
||||
{
|
||||
public:
|
||||
using Ptr = QSharedPointer<const PortsGatheringMethod>;
|
||||
|
||||
virtual ~PortsGatheringMethod() = default;
|
||||
virtual Utils::CommandLine commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const = 0;
|
||||
virtual QList<Utils::Port> usedPorts(const QByteArray &commandOutput) const = 0;
|
||||
std::function<Utils::CommandLine(QAbstractSocket::NetworkLayerProtocol protocol)> commandLine;
|
||||
std::function<QList<Utils::Port>(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; }
|
||||
|
@@ -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<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()
|
||||
{
|
||||
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<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
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
||||
@@ -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<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
|
||||
|
@@ -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; }
|
||||
|
Reference in New Issue
Block a user