From 6dd8fb333eeb13536c69ad75cbab70c0fbcc8e20 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Tue, 17 Nov 2015 01:22:29 +0100 Subject: [PATCH] refs #507, checks if URLs / DBus can be connected / reached * new class CFailoverUrlList * improved utility methods * CDBusServer: utility function to check server / DBus can be connected * check in swift GUI if DBus is available --- src/blackcore/dbus_server.cpp | 159 ++++++++++++++++++------- src/blackcore/dbus_server.h | 42 ++++--- src/blackmisc/network/networkutils.cpp | 16 ++- src/blackmisc/network/networkutils.h | 8 +- src/blackmisc/network/urllist.cpp | 63 ++++++++++ src/blackmisc/network/urllist.h | 40 +++++++ src/swiftguistandard/main.cpp | 5 + 7 files changed, 271 insertions(+), 62 deletions(-) diff --git a/src/blackcore/dbus_server.cpp b/src/blackcore/dbus_server.cpp index 9bb053d3f..2f6c94f47 100644 --- a/src/blackcore/dbus_server.cpp +++ b/src/blackcore/dbus_server.cpp @@ -18,7 +18,6 @@ using namespace BlackMisc::Network; namespace BlackCore { - /* * Constructor * Remark, without the default "unix:tmpdir=/tmp" any refereal to address crashes @@ -71,10 +70,13 @@ namespace BlackCore case SERVERMODE_P2P: default: { + QString dbusAddress(CDBusServer::isQtDBusAddress(address) ? address : "tcp:host=127.0.0.1,port=45000"); + dbusAddress = dbusAddress.toLower().trimmed().replace(' ', ""); + if (!dbusAddress.contains("bind=")) { dbusAddress = dbusAddress.append(",bind=*"); } this->m_serverMode = CDBusServer::SERVERMODE_P2P; this->m_busServer.reset( new QDBusServer( - CDBusServer::isQtDBusAddress(address) ? address : "tcp:host=127.0.0.1,port=45000", // "unix:tmpdir=/tmp" + dbusAddress, // "unix:tmpdir=/tmp" parent) ); m_busServer->setAnonymousAuthenticationAllowed(true); @@ -94,9 +96,6 @@ namespace BlackCore } // switch } - /* - * Name of service - */ const QString &CDBusServer::ServiceName() { static const QString sn(BLACKCORE_RUNTIME_SERVICENAME); @@ -111,28 +110,58 @@ namespace BlackCore if (!success) { CLogMessage(this).warning("Failed to launch dbus-daemon!"); } } - /* - * Check for P2P address - */ - bool CDBusServer::isP2P(const QString &address) + bool CDBusServer::isP2PAddress(const QString &address) { return CDBusServer::addressToDBusMode(address) == SERVERMODE_P2P; } - /* - * Is Qt DBus address - */ + bool CDBusServer::splitDBusAddressIntoHostAndPort(const QString &dbusAddress, QString &host, int &port) + { + bool ok = false; + QString dbus(dbusAddress.trimmed().toLower().replace(' ', "")); + if (dbus.contains("host=") || dbus.contains("port=")) + { + // "tcp:host=foo.com,port=123" + QStringList parts(dbus.split(',')); + for (const QString &p : parts) + { + if (p.contains("host=")) + { + host = p.mid(p.lastIndexOf("=") + 1).trimmed(); + } + else if (p.contains("port=")) + { + bool ok; + port = p.mid(p.lastIndexOf("=") + 1).trimmed().toInt(&ok); + if (!ok) { port = -1; } + } + } + if (port < 0) { port = 45000; } + if (host.isEmpty()) { host = "127.0.0.1"; } + ok = true; + } + + if (!ok) + { + host = ""; + port = -1; + } + return ok; + } + bool CDBusServer::isQtDBusAddress(const QString &address) { return - (address.startsWith("tcp:") || - address.startsWith("unix:") + (address.contains("tcp:") || + address.contains("unix:") ); } - /* - * Class info - */ + bool CDBusServer::isSessionOrSystemAddress(const QString &address) + { + return address == sessionDBusServer() || address == systemDBusServer(); + } + const QString CDBusServer::getClassInfo(QObject *object) { if (!object) return ""; @@ -147,9 +176,6 @@ namespace BlackCore return ""; } - /* - * Connection established - */ bool CDBusServer::ps_registerObjectsWithP2PConnection(const QDBusConnection &connection) { Q_ASSERT(!this->m_objects.isEmpty()); @@ -170,9 +196,6 @@ namespace BlackCore return success; } - /* - * Add the objects - */ void CDBusServer::addObject(const QString &path, QObject *object) { if (!object) { return; } @@ -217,9 +240,6 @@ namespace BlackCore } } - /* - * Last error - */ QDBusError CDBusServer::lastQDBusServerError() const { if (!hasQDBusServer()) { return QDBusError(); } @@ -231,17 +251,11 @@ namespace BlackCore return this->m_busServer.data(); } - /* - * Real server? - */ bool CDBusServer::hasQDBusServer() const { return !this->m_busServer.isNull(); } - /* - * Unregister all objects - */ void CDBusServer::unregisterAllObjects() { if (this->m_objects.isEmpty()) return; @@ -267,13 +281,31 @@ namespace BlackCore } // all paths } - /* - * p2pDBusServer - */ + const QDBusConnection &CDBusServer::defaultConnection() + { + static QDBusConnection defaultConnection("default"); + return defaultConnection; + } + + const QString &CDBusServer::sessionDBusServer() + { + static QString session("session"); + return session; + } + + const QString &CDBusServer::systemDBusServer() + { + static QString system("system"); + return system; + } + QString CDBusServer::p2pAddress(const QString &host, const QString &port) { QString h = host.isEmpty() ? "127.0.0.1" : host.trimmed(); QString p = port; + + // can handle host and port separately or combined + // such as "myHost::1234" if (port.isEmpty()) { if (h.contains(":")) @@ -296,9 +328,6 @@ namespace BlackCore return p2p; } - /* - * Fix address - */ QString CDBusServer::fixAddressToDBusAddress(const QString &address) { if (address.isEmpty() || address == sessionDBusServer() || address == systemDBusServer()) { return address; } @@ -306,9 +335,6 @@ namespace BlackCore return p2pAddress(address); } - /* - * Convert address to mode - */ CDBusServer::ServerMode CDBusServer::addressToDBusMode(const QString &address) { QString a = address.toLower(); @@ -317,4 +343,55 @@ namespace BlackCore else { return SERVERMODE_P2P; } } + bool CDBusServer::isDBusAvailable(const QString &address, int port, int timeoutMs) + { + QString m; + return CNetworkUtils::canConnect(address, port, m, timeoutMs); + } + + bool CDBusServer::isDBusAvailable(const QString &address, int port, QString &message, int timeoutMs) + { + return CNetworkUtils::canConnect(address, port, message, timeoutMs); + } + + bool CDBusServer::isDBusAvailable(const QString &dbusAddress, QString &message, int timeoutMs) + { + if (dbusAddress.isEmpty()) { message = "no address"; return false; } + if (isP2PAddress(dbusAddress)) + { + QString host; + int port = -1; + if (splitDBusAddressIntoHostAndPort(dbusAddress, host, port)) + { + return isDBusAvailable(host, port, message, timeoutMs); + } + else + { + return false; + } + } + else + { + QDBusConnection connection( + (dbusAddress == systemDBusServer()) ? + QDBusConnection::systemBus() : + QDBusConnection::connectToBus(QDBusConnection::SessionBus, ServiceName()) + ); + bool success = connection.isConnected(); + + // further checks would need to go here + // failing session bus not detected yet + + message = connection.lastError().message(); + connection.disconnectFromBus(ServiceName()); + return success; + } + } + + bool CDBusServer::isDBusAvailable(const QString &dbusAddress, int timeoutMs) + { + QString m; + return isDBusAvailable(dbusAddress, m, timeoutMs); + } + } // namespace diff --git a/src/blackcore/dbus_server.h b/src/blackcore/dbus_server.h index 25c51af34..ddb4059ef 100644 --- a/src/blackcore/dbus_server.h +++ b/src/blackcore/dbus_server.h @@ -74,25 +74,13 @@ namespace BlackCore void unregisterAllObjects(); //! Default connection - static const QDBusConnection &defaultConnection() - { - static QDBusConnection defaultConnection("default"); - return defaultConnection; - } + static const QDBusConnection &defaultConnection(); //! Denotes a session DBus server - static const QString &sessionDBusServer() - { - static QString session("session"); - return session; - } + static const QString &sessionDBusServer(); //! Denotes a session DBus server - static const QString &systemDBusServer() - { - static QString system("system"); - return system; - } + static const QString &systemDBusServer(); //! Denotes a P2P DBus server, e.g. "tcp:host=192.168.3.3,port=45000" //! \remarks it is valid to pass only one string as host:port @@ -107,6 +95,27 @@ namespace BlackCore //! Qt DBus address, e.g. "unix:tmpdir=/tmp", "tcp:host=127.0.0.1,port=45000" static bool isQtDBusAddress(const QString &address); + //! Session or system DBus address + static bool isSessionOrSystemAddress(const QString &address); + + //! Check if address means a real server with P2P connection + static bool isP2PAddress(const QString &address); + + //! Split DBus address into host and port + static bool splitDBusAddressIntoHostAndPort(const QString &dbusAddress, QString &host, int &port); + + //! Is DBus available? + static bool isDBusAvailable(const QString &address, int port, int timeoutMs = 1500); + + //! Is DBus available? + static bool isDBusAvailable(const QString &address, int port, QString &message, int timeoutMs = 1500); + + //! Is DBus available? + static bool isDBusAvailable(const QString &dbusAddress, QString &message, int timeoutMs = 1500); + + //! Is DBus available? + static bool isDBusAvailable(const QString &dbusAddress, int timeoutMs = 1500); + private: ServerMode m_serverMode = SERVERMODE_P2P; QScopedPointer m_busServer; //!< QDBusServer implementation @@ -116,9 +125,6 @@ namespace BlackCore //! Manually launch our shipped dbus daemon void launchDbusDaemon(); - //! Check if address means a real server with P2P connection - static bool isP2P(const QString &address); - //! Get the class info static const QString getClassInfo(QObject *object); diff --git a/src/blackmisc/network/networkutils.cpp b/src/blackmisc/network/networkutils.cpp index 8d9b4d98c..54c3957ad 100644 --- a/src/blackmisc/network/networkutils.cpp +++ b/src/blackmisc/network/networkutils.cpp @@ -134,9 +134,21 @@ namespace BlackMisc return canConnect(host, p, message, timeoutMs); } - bool CNetworkUtils::canConnect(const CUrl &location, QString &message, int timeoutMs) + bool CNetworkUtils::canConnect(const QUrl &url, int timeoutMs) { - return canConnect(location.getHost(), location.getPort(), message, timeoutMs); + QString m; + return canConnect(url, m, timeoutMs); + } + + bool CNetworkUtils::canConnect(const CUrl &url, QString &message, int timeoutMs) + { + return canConnect(url.getHost(), url.getPort(), message, timeoutMs); + } + + bool CNetworkUtils::canConnect(const CUrl &url, int timeoutMs) + { + QString m; + return canConnect(url, m, timeoutMs); } bool CNetworkUtils::isValidIPv4Address(const QString &candidate) diff --git a/src/blackmisc/network/networkutils.h b/src/blackmisc/network/networkutils.h index 37024d937..83167b42a 100644 --- a/src/blackmisc/network/networkutils.h +++ b/src/blackmisc/network/networkutils.h @@ -66,7 +66,13 @@ namespace BlackMisc static bool canConnect(const QUrl &url, QString &message, int timeoutMs = 1500); //! Can connect to URL? - static bool canConnect(const BlackMisc::Network::CUrl &location, QString &message, int timeoutMs = 1500); + static bool canConnect(const QUrl &url, int timeoutMs = 1500); + + //! Can connect to URL? + static bool canConnect(const BlackMisc::Network::CUrl &url, QString &message, int timeoutMs = 1500); + + //! Can connect to URL? + static bool canConnect(const BlackMisc::Network::CUrl &url, int timeoutMs = 1500); //! Find out my IPv4 address, empty if not possible static QStringList getKnownIpAddresses(); diff --git a/src/blackmisc/network/urllist.cpp b/src/blackmisc/network/urllist.cpp index ffa8e89ef..929e72cda 100644 --- a/src/blackmisc/network/urllist.cpp +++ b/src/blackmisc/network/urllist.cpp @@ -8,6 +8,7 @@ */ #include "blackmisc/network/urllist.h" +#include "blackmisc/network/networkutils.h" #include "blackmisc/math/mathutils.h" using namespace BlackMisc::Math; @@ -38,6 +39,22 @@ namespace BlackMisc return (*this)[i]; } + CUrl CUrlList::getRandomWorkingUrl(int maxTrials) const + { + if (this->isEmpty()) { return CUrl(); } + if (maxTrials < 1) { return CUrl();} + CUrlList trials; + + for (int t = 0; t < maxTrials && t < this->size(); t++) + { + CUrl url(getRandomWithout(trials)); + trials.push_back(url); + QString message; + if (CNetworkUtils::canConnect(url, message)) { return url; } + } + return CUrl(); + } + CUrl CUrlList::getRandomWithout(const CUrlList &exclude) const { CUrlList copy(*this); @@ -87,5 +104,51 @@ namespace BlackMisc return urls; } + QString CUrlList::convertToQString(const QString &separator, bool i18n) const + { + const QStringList sl(toStringList(i18n)); + return sl.join(separator); + } + + CFailoverUrlList::CFailoverUrlList(int maxTrials) : + m_maxTrials(maxTrials) + { } + + CFailoverUrlList::CFailoverUrlList(const QStringList &listOfUrls, int maxTrials) : + CUrlList(listOfUrls), m_maxTrials(maxTrials) + { } + + CFailoverUrlList::CFailoverUrlList(const CSequence &other, int maxTrials) : + CUrlList(other), m_maxTrials(maxTrials) + { } + + CUrlList CFailoverUrlList::getWithoutFailed() const + { + CUrlList urls(*this); + urls.removeIfIn(m_failedUrls); + return urls; + } + + bool CFailoverUrlList::addFailedUrl(const CUrl &failedUrl) + { + this->m_failedUrls.push_back(failedUrl); + return hasMoreUrlsToTry(); + } + + bool CFailoverUrlList::hasMoreUrlsToTry() const + { + if (this->isEmpty()) { return false; } + return (m_failedUrls.size() < this->size() && m_failedUrls.size() < m_maxTrials); + } + + CUrl CFailoverUrlList::getNextWorkingUrl(const CUrl &failedUrl, bool random) + { + if (!failedUrl.isEmpty()) { this->addFailedUrl(failedUrl); } + if (!hasMoreUrlsToTry()) { return CUrl(); } + CUrl url(this->getNextUrl(random)); + if (CNetworkUtils::canConnect(url)) { return url; } + if (addFailedUrl(url)) { return getNextUrl(); } + return CUrl(); + } } // namespace } // namespace diff --git a/src/blackmisc/network/urllist.h b/src/blackmisc/network/urllist.h index 15076e26c..14476aeb8 100644 --- a/src/blackmisc/network/urllist.h +++ b/src/blackmisc/network/urllist.h @@ -41,6 +41,9 @@ namespace BlackMisc //! Random location for distributed load CUrl getRandomUrl() const; + //! Random location for distributed load, tested + CUrl getRandomWorkingUrl(int maxTrials = 2) const; + //! Random location for distributed load CUrl getRandomWithout(const CUrlList &exclude) const; @@ -53,9 +56,46 @@ namespace BlackMisc //! Append path to all URLs CUrlList appendPath(const QString &path) const; + //! To formatted String + QString convertToQString(const QString &separator, bool i18n = false) const; + private: mutable int m_currentIndexDistributedLoad = -1; }; + + //! URL list with fail support + class BLACKMISC_EXPORT CFailoverUrlList : public CUrlList + { + public: + //! Default constructor. + CFailoverUrlList(int maxTrials = 2); + + //! By list of URLs + explicit CFailoverUrlList(const QStringList &listOfUrls, int maxTrials = 2); + + //! Construct from a base class object. + CFailoverUrlList(const CSequence &other, int maxTrials = 2); + + //! All failed URLs + const CUrlList &getFailedUrls() const { return m_failedUrls; } + + //! Get without the failed URLs + CUrlList getWithoutFailed() const; + + //! Failed URL + bool addFailedUrl(const CUrl &failedUrl); + + //! More URLs to try + bool hasMoreUrlsToTry() const; + + //! Next working URL, test if it can be connected + CUrl getNextWorkingUrl(const CUrl &failedUrl = CUrl(), bool random = true); + + private: + int m_maxTrials = 2; //!< number of max trials + CUrlList m_failedUrls; + }; + } //namespace } // namespace diff --git a/src/swiftguistandard/main.cpp b/src/swiftguistandard/main.cpp index 520c974c7..bbfbc2f6f 100644 --- a/src/swiftguistandard/main.cpp +++ b/src/swiftguistandard/main.cpp @@ -81,6 +81,11 @@ CommandLineParseResult parseCommandLine(QCommandLineParser &parser, { QString v(parser.value(dBusOption).trimmed()); dBusAddress = CDBusServer::fixAddressToDBusAddress(v); + if (!CDBusServer::isDBusAvailable(dBusAddress)) + { + errorMessage = "DBus server at " + dBusAddress + " can not be reached"; + return CommandLineError; + } } if (parser.isSet(windowOption))