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
This commit is contained in:
Klaus Basan
2015-11-17 01:22:29 +01:00
committed by Mathew Sutcliffe
parent b24cd2e9c7
commit 6dd8fb333e
7 changed files with 271 additions and 62 deletions

View File

@@ -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

View File

@@ -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<QDBusServer> 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);

View File

@@ -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)

View File

@@ -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();

View File

@@ -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<CUrl> &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

View File

@@ -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<CUrl> &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

View File

@@ -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))