Files
pilotclient/src/blackmisc/dbusserver.cpp
Mathew Sutcliffe 50b8fbe8fe [bug][Windows][Installer][DBus] Install session.conf in the correct location where dbus-daemon will look for it.
Summary:
On Windows, libdbus will spawn dbus-daemon when swift tries to connect to the session bus.

The daemon looks for session.conf in the share directory.
This patch ensures session.conf is installed in that directory.

Fixes T79.

Reviewers: rwinklmeier

Reviewed By: rwinklmeier

Subscribers: jenkins

Tags: #swift_pilot_client

Maniphest Tasks: T79

Differential Revision: https://dev.swift-project.org/D22
2017-05-14 17:21:37 +01:00

454 lines
17 KiB
C++

/* Copyright (C) 2014
* swift project Community / Contributors
*
* This file is part of swift Project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
#include "blackmisc/dbusserver.h"
#include "blackmisc/logmessage.h"
#include "blackmisc/network/networkutils.h"
#include "blackmisc/processctrl.h"
#include "blackmisc/statusmessage.h"
#include <QDBusServer>
#include <QMetaClassInfo>
#include <QMetaObject>
#include <QStringList>
#include <QThread>
#include <QtGlobal>
using namespace BlackMisc::Network;
namespace BlackMisc
{
CDBusServer::CDBusServer(const QString &service, const QString &address, QObject *parent) : QObject(parent)
{
m_serverMode = modeOfAddress(address);
switch (m_serverMode)
{
case SERVERMODE_SESSIONBUS:
{
QDBusConnection testConnection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, coreServiceName());
if (! testConnection.isConnected()) { launchDBusDaemon(); }
testConnection.disconnectFromBus(coreServiceName());
// Sleep for 200 ms in order for dbus-daemon to finish loading.
// FIXME: Dirty workaround. Instead polling the server up to x times every 50 ms until its connection is accepted.
QThread::msleep(200);
QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, coreServiceName());
if (! connection.isConnected() || ! connection.registerService(service))
{
// registration fails can either mean something wrong with DBus or service already exists
CLogMessage(this).warning("DBus registration: %1") << connection.lastError().message();
CLogMessage(this).warning("Cannot register DBus service, check server running: dbus-daemon.exe --session --address=tcp:host=192.168.0.133,port=45000");
}
}
break;
case SERVERMODE_SYSTEMBUS:
{
QDBusConnection testConnection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, coreServiceName());
if (! testConnection.isConnected()) { launchDBusDaemon(); }
testConnection.disconnectFromBus(coreServiceName());
// Sleep for 200 ms in order for dbus-daemon to finish loading.
QThread::msleep(200);
QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, coreServiceName());
if (! connection.isConnected() || ! connection.registerService(service))
{
// registration fails can either mean something wrong with DBus or service already exists
CLogMessage(this).warning("DBus registration: %1") << connection.lastError().message();
CLogMessage(this).warning("Cannot register DBus service, check server running: dbus-daemon.exe --system --address=tcp:host=192.168.0.133,port=45000");
}
}
break;
case SERVERMODE_P2P:
default:
{
QString dbusAddress = isQtDBusAddress(address) ? address : "tcp:host=127.0.0.1,port=45000";
dbusAddress = dbusAddress.toLower().trimmed().replace(' ', "");
if (! dbusAddress.contains("bind=")) { dbusAddress = dbusAddress.append(",bind=*"); } // bind to all network interfaces
m_busServer.reset(new QDBusServer(dbusAddress, parent));
m_busServer->setAnonymousAuthenticationAllowed(true);
// Note: P2P has no service name
if (m_busServer->isConnected())
{
CLogMessage(this).debug() << "Server listening on address:" << m_busServer->address();
}
else
{
CLogMessage(this).warning("DBus P2P connection failed: %1") << lastQDBusServerError().message();
}
connect(m_busServer.data(), &QDBusServer::newConnection, this, &CDBusServer::ps_registerObjectsWithP2PConnection);
}
break;
}
}
CDBusServer::~CDBusServer()
{
this->removeAllObjects();
QDBusConnection::disconnectFromBus(coreServiceName());
}
const QString &CDBusServer::coreServiceName()
{
static const QString sn = SWIFT_SERVICENAME;
return sn;
}
void CDBusServer::launchDBusDaemon()
{
static const QString program = QStringLiteral("dbus-daemon");
static const QStringList arguments = { QStringLiteral("--session") };
const bool success = CProcessCtrl::startDetached(program, arguments, false);
if (!success) { CLogMessage(this).error("Failed to launch dbus-daemon!"); }
}
bool CDBusServer::isP2PAddress(const QString &address)
{
return modeOfAddress(address) == SERVERMODE_P2P;
}
bool CDBusServer::dBusAddressToHostAndPort(QString address, QString &host, int &port)
{
address = address.trimmed().toLower().replace(' ', "");
if (address.contains("host=") || address.contains("port="))
{
// "tcp:host=foo.com,port=123"
const QStringList parts(address.split(','));
for (const QString &part : parts)
{
if (part.startsWith("host="))
{
host = part.midRef(part.lastIndexOf("=") + 1).trimmed().toString();
}
else if (part.startsWith("port="))
{
bool ok;
port = part.midRef(part.lastIndexOf("=") + 1).trimmed().toInt(&ok);
if (! ok) { port = -1; }
}
}
if (port < 0) { port = 45000; }
if (host.isEmpty()) { host = "127.0.0.1"; }
return true;
}
else
{
host = "";
port = -1;
return false;
}
}
bool CDBusServer::isQtDefaultConnection(const QDBusConnection &connection)
{
return connection.name() == QDBusConnection::sessionBus().name() ||
connection.name() == QDBusConnection::systemBus().name();
}
bool CDBusServer::isQtDBusAddress(const QString &address)
{
return address.startsWith("tcp:") || address.startsWith("unix:");
}
bool CDBusServer::isSessionOrSystemAddress(const QString &address)
{
return address == sessionBusAddress() || address == systemBusAddress();
}
QString CDBusServer::getDBusInterfaceFromClassInfo(QObject *object)
{
if (! object) { return ""; }
const QMetaObject *mo = object->metaObject();
for (int i = 0; i < mo->classInfoCount(); i++)
{
QMetaClassInfo ci = mo->classInfo(i);
QString name(ci.name());
if (name == "D-Bus Interface") { return QString(ci.value()); }
}
return "";
}
bool CDBusServer::ps_registerObjectsWithP2PConnection(QDBusConnection connection)
{
Q_ASSERT(! m_objects.isEmpty());
m_connections.insert(connection.name(), connection);
CLogMessage(this).debug() << "New Connection from:" << connection.name();
bool success = true;
for (auto i = m_objects.cbegin(); i != m_objects.cend(); ++i)
{
CLogMessage(this).debug() << "Adding" << i.key() << getDBusInterfaceFromClassInfo(i.value()) << "to the new connection.";
bool ok = connection.registerObject(i.key(), i.value(), registerOptions());
Q_ASSERT_X(ok, "CDBusServer::newConnection", "Registration failed");
if (! ok) { success = false; }
}
return success;
}
void CDBusServer::addObject(const QString &path, QObject *object)
{
if (! object) { return; }
m_objects.insert(path, object); // will be registered when P2P connection is established
switch (m_serverMode)
{
case SERVERMODE_SESSIONBUS:
{
QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SessionBus, coreServiceName());
if (connection.registerObject(path, object, registerOptions()))
{
CLogMessage(this).debug() << "Adding" << path << getDBusInterfaceFromClassInfo(object) << "to session bus.";
}
else
{
CLogMessage(this).error("Error, no success with session bus registration");
}
}
break;
case SERVERMODE_SYSTEMBUS:
{
QDBusConnection connection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, coreServiceName());
if (connection.registerObject(path, object, registerOptions()))
{
CLogMessage(this).debug() << "Adding" << path << getDBusInterfaceFromClassInfo(object) << "to system bus.";
}
else
{
CLogMessage(this).error("Error, no success with system bus registration");
}
}
break;
case SERVERMODE_P2P:
{
for (QDBusConnection connection : as_const(m_connections))
{
if (connection.registerObject(path, object, registerOptions()))
{
CLogMessage(this).debug() << "Adding" << path << getDBusInterfaceFromClassInfo(object) << "to" << connection.name();
}
else
{
CLogMessage(this).error("Error, no success with %1 registration") << connection.name();
}
}
}
break;
default:
Q_ASSERT_X(false, "CDBusServer::addObject", "Wrong server mode");
}
}
QDBusError CDBusServer::lastQDBusServerError() const
{
if (! hasQDBusServer()) { return {}; }
return m_busServer->lastError();
}
const QDBusServer *CDBusServer::qDBusServer() const
{
return m_busServer.data();
}
bool CDBusServer::hasQDBusServer() const
{
return ! m_busServer.isNull();
}
void CDBusServer::removeAllObjects()
{
if (m_objects.isEmpty()) { return; }
for (const QString &path : makeKeysRange(as_const(m_objects)))
{
switch (m_serverMode)
{
case SERVERMODE_SESSIONBUS:
QDBusConnection::sessionBus().unregisterObject(path);
break;
case SERVERMODE_SYSTEMBUS:
QDBusConnection::systemBus().unregisterObject(path);
break;
case SERVERMODE_P2P:
{
for (QDBusConnection connection : as_const(m_connections))
{
connection.unregisterObject(path);
}
break;
}
}
}
m_objects.clear();
}
const QDBusConnection &CDBusServer::defaultConnection()
{
static QDBusConnection defaultConnection("default");
return defaultConnection;
}
const QString &CDBusServer::sessionBusAddress()
{
static QString session = "session";
return session;
}
const QString &CDBusServer::systemBusAddress()
{
static QString system = "system";
return system;
}
QDBusConnection CDBusServer::connectToDBus(const QString &dBusAddress, const QString &name)
{
if (dBusAddress == sessionBusAddress())
{
if (name.isEmpty()) return QDBusConnection::sessionBus();
return QDBusConnection::connectToBus(QDBusConnection::SessionBus, name);
}
else if (dBusAddress == systemBusAddress())
{
if (name.isEmpty()) return QDBusConnection::systemBus();
return QDBusConnection::connectToBus(QDBusConnection::SystemBus, name);
}
else if (isP2PAddress(dBusAddress))
{
return QDBusConnection::connectToPeer(dBusAddress,
name.isEmpty() ? CDBusServer::p2pConnectionName() : name);
}
return QDBusConnection("invalid");
}
void CDBusServer::disconnectFromDBus(const QDBusConnection &connection, const QString &dBusAddress)
{
if (CDBusServer::isQtDefaultConnection(connection)) return; // do not touch the default connections
if (CDBusServer::isP2PAddress(dBusAddress))
{
QDBusConnection::disconnectFromPeer(connection.name());
}
else
{
QDBusConnection::disconnectFromBus(connection.name());
}
}
QString CDBusServer::p2pAddress(const QString &host, const QString &port)
{
QString h = host.trimmed().toLower().remove(' ');
if (h.isEmpty()) { h = "127.0.0.1"; }
// check port
bool ok = false;
QString p = port.toLower().trimmed();
if (!p.isEmpty())
{
p.toInt(&ok);
if (!ok)
{
p = ""; // was not a number
}
}
// can handle host and port separately or combined, e.g. "myhost:1234"
if (port.isEmpty())
{
if (h.startsWith("tcp:") && h.contains("host=") && h.contains("port="))
{
// looks we already got a full string
return h;
}
// 192.168.5.3:9300 style
if (h.contains(":"))
{
const QStringList parts = h.split(":");
h = parts.at(0).trimmed();
p = parts.at(1).trimmed();
}
else
{
p = "45000";
}
}
// todo: Replace assert with input validation
Q_ASSERT_X(CNetworkUtils::isValidIPv4Address(p), "CDBusServer::p2pAddress", "Wrong IP in String");
return QString("tcp:host=%1,port=%2").arg(h, p);
}
const QString &CDBusServer::p2pConnectionName()
{
static const QString n("p2pConnection");
return n;
}
QString CDBusServer::normalizeAddress(const QString &address)
{
const QString lc(address.toLower().trimmed());
if (lc.isEmpty()) { return sessionBusAddress(); }
if (lc == sessionBusAddress() || lc == systemBusAddress()) { return lc; }
// some aliases
if (lc.startsWith("sys")) { return systemBusAddress(); }
if (lc.startsWith("ses")) { return sessionBusAddress(); }
// Qt / P2P
if (isQtDBusAddress(address)) { return address; }
return p2pAddress(address);
}
CDBusServer::ServerMode CDBusServer::modeOfAddress(QString address)
{
address = address.toLower();
if (address == systemBusAddress()) { return SERVERMODE_SYSTEMBUS; }
else if (address == sessionBusAddress()) { return SERVERMODE_SESSIONBUS; }
else { return SERVERMODE_P2P; }
}
bool CDBusServer::isDBusAvailable(const QString &address, int port, int timeoutMs)
{
QString unused;
return CNetworkUtils::canConnect(address, port, unused, 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;
return CDBusServer::dBusAddressToHostAndPort(dBusAddress, host, port) ?
CDBusServer::isDBusAvailable(host, port, message, timeoutMs) :
false;
}
else
{
QDBusConnection connection = CDBusServer::connectToDBus(dBusAddress);
const bool isConnected = connection.isConnected();
message = connection.lastError().message();
CDBusServer::disconnectFromDBus(connection, dBusAddress);
return isConnected;
}
}
bool CDBusServer::isDBusAvailable(const QString &dbusAddress, int timeoutMs)
{
QString unused;
return CDBusServer::isDBusAvailable(dbusAddress, unused, timeoutMs);
}
} // namespace