summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhjk <hjk@qt.io>2017-08-07 18:54:01 +0200
committerhjk <hjk@qt.io>2017-08-10 15:23:07 +0000
commit7f87e2af3c21343a0a7fd0a74b63b748584d60c6 (patch)
tree4baacf5f7a2c0c7f2ac372035805dab17da92241
parent4048629d1a5940032267f80e44c6032e881abd4e (diff)
downloadqt-creator-7f87e2af3c21343a0a7fd0a74b63b748584d60c6.tar.gz
DeviceSupport: Implement DesktopDevice::portsGatheringMethod()
The feature is useful in a QtApplicationManager debugging context. Internally, DeviceUsedPortsGatherer uses a DeviceProcess now, not an SshRemoteProcess, to cover cases where the (Windows Desktop) device not have ssh available. Change-Id: I9d33ceac65a135123a376ebd2727dcb540563179 Reviewed-by: Wolfgang Bremer <wolfgang.bremer@pelagicore.com> Reviewed-by: Dan Cape <dcape@qnx.com> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.cpp100
-rw-r--r--src/plugins/projectexplorer/devicesupport/desktopdevice.h1
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp78
-rw-r--r--src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h8
-rw-r--r--src/plugins/projectexplorer/devicesupport/idevice.h3
-rw-r--r--src/plugins/qnx/qnxdevice.cpp14
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp8
7 files changed, 152 insertions, 60 deletions
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
index dde4edce5d..7f243e8e43 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp
@@ -37,11 +37,13 @@
#include <ssh/sshconnection.h>
#include <utils/environment.h>
+#include <utils/hostosinfo.h>
#include <utils/portlist.h>
#include <QCoreApplication>
using namespace ProjectExplorer::Constants;
+using namespace Utils;
namespace ProjectExplorer {
@@ -137,6 +139,104 @@ DeviceEnvironmentFetcher::Ptr DesktopDevice::environmentFetcher() const
return DeviceEnvironmentFetcher::Ptr(new DesktopDeviceEnvironmentFetcher());
}
+class DesktopPortsGatheringMethod : public PortsGatheringMethod
+{
+ Runnable runnable(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)
+
+ StandardRunnable runnable;
+ if (HostOsInfo::isWindowsHost()) {
+ runnable.executable = "netstat";
+ runnable.commandLineArguments = "-a -n";
+ } else if (HostOsInfo::isLinuxHost()) {
+ runnable.executable = "/bin/sh";
+ runnable.commandLineArguments = "-c 'cat /proc/net/tcp*'";
+ }
+ return runnable;
+ }
+
+ QList<Utils::Port> usedPorts(const QByteArray &output) const override
+ {
+ QList<Utils::Port> ports;
+ const QList<QByteArray> lines = output.split('\n');
+ if (HostOsInfo::isWindowsHost()) {
+ // Expected output is something like
+ //
+ // Active Connections
+ //
+ // Proto Local Address Foreign Address State
+ // TCP 0.0.0.0:80 0.0.0.0:0 LISTENING
+ // TCP 0.0.0.0:113 0.0.0.0:0 LISTENING
+ // [...]
+ // TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING
+ // TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED
+ for (const QByteArray &line : lines) {
+ const QByteArray trimmed = line.trimmed();
+ if (!trimmed.startsWith("TCP"))
+ continue;
+ int colonPos = trimmed.indexOf(':');
+ if (colonPos < 0)
+ continue;
+ int spacePos = trimmed.indexOf(':', colonPos + 1);
+ if (spacePos < 0)
+ continue;
+ bool ok;
+ int len = spacePos - colonPos - 1;
+ const Utils::Port port(line.mid(colonPos + 1, len).toInt(&ok, 16));
+ if (ok) {
+ if (!ports.contains(port))
+ ports << port;
+ } else {
+ qWarning("%s: Unexpected string '%s' is not a port.",
+ Q_FUNC_INFO, line.data());
+ }
+ }
+ } else if (HostOsInfo::isLinuxHost()) {
+ // Expected outpit is something like
+ //
+ // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ...
+ // 0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ...
+ //
+ for (const QByteArray &line : lines) {
+ int firstColonPos = line.indexOf(':');
+ if (firstColonPos < 0)
+ continue;
+ int secondColonPos = line.indexOf(':', firstColonPos + 1);
+ if (secondColonPos < 0)
+ continue;
+ int spacePos = line.indexOf(':', secondColonPos + 1);
+ if (spacePos < 0)
+ continue;
+ bool ok;
+ int len = spacePos - secondColonPos - 1;
+ const Utils::Port port(line.mid(secondColonPos + 1, len).toInt(&ok, 16));
+ if (ok) {
+ if (!ports.contains(port))
+ ports << port;
+ } else {
+ qWarning("%s: Unexpected string '%s' is not a port.",
+ Q_FUNC_INFO, line.data());
+ }
+ }
+ }
+ return ports;
+ }
+};
+
+PortsGatheringMethod::Ptr DesktopDevice::portsGatheringMethod() const
+{
+ return DesktopPortsGatheringMethod::Ptr(new DesktopPortsGatheringMethod);
+}
+
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
{
QUrl url;
diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
index 588281c1d2..203dd8fe49 100644
--- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h
+++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h
@@ -49,6 +49,7 @@ public:
bool canCreateProcessModel() const override;
DeviceProcessList *createProcessListModel(QObject *parent) const override;
bool canCreateProcess() const override { return true; }
+ ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override;
DeviceProcess *createProcess(QObject *parent) const override;
DeviceProcessSignalOperation::Ptr signalOperation() const override;
DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
index 7115bb0de3..2167f9ddbb 100644
--- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
+++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp
@@ -23,14 +23,14 @@
**
****************************************************************************/
+#include "deviceprocess.h"
#include "deviceusedportsgatherer.h"
+#include <projectexplorer/runnables.h>
+
#include <utils/port.h>
#include <utils/portlist.h>
#include <utils/qtcassert.h>
-#include <ssh/sshconnection.h>
-#include <ssh/sshconnectionmanager.h>
-#include <ssh/sshremoteprocess.h>
using namespace QSsh;
using namespace Utils;
@@ -41,12 +41,12 @@ namespace Internal {
class DeviceUsedPortsGathererPrivate
{
public:
- SshConnection *connection;
- SshRemoteProcess::Ptr process;
+ QPointer<DeviceProcess> process;
QList<Port> usedPorts;
QByteArray remoteStdout;
QByteArray remoteStderr;
IDevice::ConstPtr device;
+ PortsGatheringMethod::Ptr portsGatheringMethod;
};
} // namespace Internal
@@ -54,7 +54,6 @@ class DeviceUsedPortsGathererPrivate
DeviceUsedPortsGatherer::DeviceUsedPortsGatherer(QObject *parent) :
QObject(parent), d(new Internal::DeviceUsedPortsGathererPrivate)
{
- d->connection = 0;
}
DeviceUsedPortsGatherer::~DeviceUsedPortsGatherer()
@@ -65,51 +64,36 @@ DeviceUsedPortsGatherer::~DeviceUsedPortsGatherer()
void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device)
{
- QTC_ASSERT(!d->connection, emit error("No connection"); return);
- QTC_ASSERT(device && device->portsGatheringMethod(),
- emit error("Not implemented"); return);
-
d->device = device;
- d->connection = QSsh::acquireConnection(device->sshParameters());
- connect(d->connection, &SshConnection::error,
- this, &DeviceUsedPortsGatherer::handleConnectionError);
- if (d->connection->state() == SshConnection::Connected) {
- handleConnectionEstablished();
- return;
- }
- connect(d->connection, &SshConnection::connected,
- this, &DeviceUsedPortsGatherer::handleConnectionEstablished);
- if (d->connection->state() == SshConnection::Unconnected)
- d->connection->connectToHost();
-}
+ QTC_ASSERT(d->device, emit error("No device given"); return);
-void DeviceUsedPortsGatherer::handleConnectionEstablished()
-{
- const QAbstractSocket::NetworkLayerProtocol protocol
- = d->connection->connectionInfo().localAddress.protocol();
- const QByteArray commandLine = d->device->portsGatheringMethod()->commandLine(protocol);
- d->process = d->connection->createRemoteProcess(commandLine);
+ d->portsGatheringMethod = d->device->portsGatheringMethod();
+ QTC_ASSERT(d->portsGatheringMethod, emit error("Not implemented"); return);
+
+ const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol;
+ d->process = d->device->createProcess(this);
- connect(d->process.data(), &SshRemoteProcess::closed, this, &DeviceUsedPortsGatherer::handleProcessClosed);
- connect(d->process.data(), &SshRemoteProcess::readyReadStandardOutput, this, &DeviceUsedPortsGatherer::handleRemoteStdOut);
- connect(d->process.data(), &SshRemoteProcess::readyReadStandardError, this, &DeviceUsedPortsGatherer::handleRemoteStdErr);
+ connect(d->process.data(), &DeviceProcess::finished,
+ this, &DeviceUsedPortsGatherer::handleProcessFinished);
+ connect(d->process.data(), &DeviceProcess::error,
+ this, &DeviceUsedPortsGatherer::handleProcessError);
+ connect(d->process.data(), &DeviceProcess::readyReadStandardOutput,
+ this, &DeviceUsedPortsGatherer::handleRemoteStdOut);
+ connect(d->process.data(), &DeviceProcess::readyReadStandardError,
+ this, &DeviceUsedPortsGatherer::handleRemoteStdErr);
- d->process->start();
+ const Runnable runnable = d->portsGatheringMethod->runnable(protocol);
+ d->process->start(runnable);
}
void DeviceUsedPortsGatherer::stop()
{
- if (!d->connection)
- return;
d->usedPorts.clear();
d->remoteStdout.clear();
d->remoteStderr.clear();
if (d->process)
disconnect(d->process.data(), 0, this, 0);
d->process.clear();
- disconnect(d->connection, 0, this, 0);
- QSsh::releaseConnection(d->connection);
- d->connection = 0;
}
Port DeviceUsedPortsGatherer::getNextFreePort(PortList *freePorts) const
@@ -130,7 +114,7 @@ QList<Port> DeviceUsedPortsGatherer::usedPorts() const
void DeviceUsedPortsGatherer::setupUsedPorts()
{
d->usedPorts.clear();
- const QList<Port> usedPorts = d->device->portsGatheringMethod()->usedPorts(d->remoteStdout);
+ const QList<Port> usedPorts = d->portsGatheringMethod->usedPorts(d->remoteStdout);
foreach (const Port port, usedPorts) {
if (d->device->freePorts().contains(port))
d->usedPorts << port;
@@ -138,27 +122,23 @@ void DeviceUsedPortsGatherer::setupUsedPorts()
emit portListReady();
}
-void DeviceUsedPortsGatherer::handleConnectionError()
+void DeviceUsedPortsGatherer::handleProcessError()
{
- if (!d->connection)
- return;
- emit error(tr("Connection error: %1").arg(d->connection->errorString()));
+ emit error(tr("Connection error: %1").arg(d->process->errorString()));
stop();
}
-void DeviceUsedPortsGatherer::handleProcessClosed(int exitStatus)
+void DeviceUsedPortsGatherer::handleProcessFinished()
{
- if (!d->connection)
+ if (!d->process)
return;
QString errMsg;
+ QProcess::ExitStatus exitStatus = d->process->exitStatus();
switch (exitStatus) {
- case SshRemoteProcess::FailedToStart:
- errMsg = tr("Could not start remote process: %1").arg(d->process->errorString());
- break;
- case SshRemoteProcess::CrashExit:
+ case QProcess::CrashExit:
errMsg = tr("Remote process crashed: %1").arg(d->process->errorString());
break;
- case SshRemoteProcess::NormalExit:
+ case QProcess::NormalExit:
if (d->process->exitCode() == 0)
setupUsedPorts();
else
diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
index c298b6fbd0..cb1508aae7 100644
--- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
+++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h
@@ -33,6 +33,7 @@
namespace ProjectExplorer {
namespace Internal { class DeviceUsedPortsGathererPrivate; }
+class StandardRunnable;
class PROJECTEXPLORER_EXPORT DeviceUsedPortsGatherer : public QObject
{
@@ -42,7 +43,7 @@ public:
DeviceUsedPortsGatherer(QObject *parent = 0);
~DeviceUsedPortsGatherer() override;
- void start(const ProjectExplorer::IDevice::ConstPtr &device);
+ void start(const IDevice::ConstPtr &device);
void stop();
Utils::Port getNextFreePort(Utils::PortList *freePorts) const; // returns -1 if no more are left
QList<Utils::Port> usedPorts() const;
@@ -52,11 +53,10 @@ signals:
void portListReady();
private:
- void handleConnectionEstablished();
- void handleConnectionError();
- void handleProcessClosed(int exitStatus);
void handleRemoteStdOut();
void handleRemoteStdErr();
+ void handleProcessError();
+ void handleProcessFinished();
void setupUsedPorts();
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index 4bf9ed09c2..7fa91b0f0e 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -57,6 +57,7 @@ class Connection;
class DeviceProcess;
class DeviceProcessList;
class Kit;
+class Runnable;
class RunControl;
class RunWorker;
@@ -110,7 +111,7 @@ public:
typedef QSharedPointer<const PortsGatheringMethod> Ptr;
virtual ~PortsGatheringMethod() = default;
- virtual QByteArray commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const = 0;
+ virtual Runnable runnable(QAbstractSocket::NetworkLayerProtocol protocol) const = 0;
virtual QList<Utils::Port> usedPorts(const QByteArray &commandOutput) const = 0;
};
diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp
index 994f74a839..31a8d9064d 100644
--- a/src/plugins/qnx/qnxdevice.cpp
+++ b/src/plugins/qnx/qnxdevice.cpp
@@ -55,19 +55,25 @@ 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.
- QByteArray commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const
+ Runnable runnable(QAbstractSocket::NetworkLayerProtocol protocol) const override
{
Q_UNUSED(protocol);
- return "netstat -na "
+ StandardRunnable runnable;
+ // FIXME: Is this extra shell needed?
+ runnable.executable = "/bin/sh";
+ runnable.commandLineArguments = "-c \""
+ "netstat -na "
"| sed 's/[a-z]\\+\\s\\+[0-9]\\+\\s\\+[0-9]\\+\\s\\+\\(\\*\\|[0-9\\.]\\+\\)\\.\\([0-9]\\+\\).*/\\2/g' "
"| while read line; do "
"if [[ $line != udp* ]] && [[ $line != Active* ]]; then "
"printf '%x\n' $line; "
"fi; "
- "done";
+ "done"
+ "\"";
+ return runnable;
}
- QList<Port> usedPorts(const QByteArray &output) const
+ QList<Port> usedPorts(const QByteArray &output) const override
{
QList<Port> ports;
QList<QByteArray> portStrings = output.split('\n');
diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp
index 1e6fe89993..0e783a1a19 100644
--- a/src/plugins/remotelinux/linuxdevice.cpp
+++ b/src/plugins/remotelinux/linuxdevice.cpp
@@ -35,6 +35,7 @@
#include <coreplugin/id.h>
#include <projectexplorer/devicesupport/sshdeviceprocesslist.h>
+#include <projectexplorer/runnables.h>
#include <ssh/sshremoteprocessrunner.h>
#include <utils/algorithm.h>
#include <utils/port.h>
@@ -122,7 +123,7 @@ private:
class LinuxPortsGatheringMethod : public PortsGatheringMethod
{
- QByteArray commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const
+ Runnable runnable(QAbstractSocket::NetworkLayerProtocol protocol) const
{
// We might encounter the situation that protocol is given IPv6
// but the consumer of the free port information decides to open
@@ -135,7 +136,10 @@ class LinuxPortsGatheringMethod : public PortsGatheringMethod
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*";
+ StandardRunnable runnable;
+ runnable.executable = "sed";
+ runnable.commandLineArguments = "-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*";
+ return runnable;
}
QList<Utils::Port> usedPorts(const QByteArray &output) const