syncevolution/src/gdbus/gdbus-cxx-bridge.cpp
Patrick Ohly d89ca72d01 D-Bus: use streams for direct IPC with GIO
When using GIO, it is possible to avoid the DBusServer listening on a
publicly accessible address. Connection setup becomes more reliable,
too, because the D-Bus server side can detect that a child died
because the connection will be closed.

When using libdbus, the traditional server/listen and client/connect
model is still used. GDBus GIO mimicks the same API to minimize
changes in ForkExec. The API gets simplified to support both
approaches:
- Choosing an address is no longer possible (was only used in the
  test example anyway)
- The new connection callback must be specified already when starting
  the server.

The GDBus GIO implementation uses SyncEvolution utility code. Strictly
speaking, this creates a circular dependency
libsyncevolution<->libgdbus. This is solved by not linking libgdbus
against libsyncevolution and expecting executables to do that instead.

The GDBus GIO example no longer linked because of that; it gets removed
because it was of little value.

Now that we started using CLOEXEC, we might as well use it for the
permanent file descriptors created for each ForkExec instance.
2014-04-01 16:45:09 +02:00

157 lines
5.2 KiB
C++

/*
* Copyright (C) 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "gdbus-cxx-bridge.h"
#include <boost/algorithm/string/predicate.hpp>
#include <stdio.h>
void intrusive_ptr_add_ref(DBusConnection *con) { dbus_connection_ref(con); }
void intrusive_ptr_release(DBusConnection *con) { dbus_connection_unref(con); }
void intrusive_ptr_add_ref(DBusMessage *msg) { dbus_message_ref(msg); }
void intrusive_ptr_release(DBusMessage *msg) { dbus_message_unref(msg); }
void intrusive_ptr_add_ref(DBusPendingCall *call) { dbus_pending_call_ref (call); }
void intrusive_ptr_release(DBusPendingCall *call) { dbus_pending_call_unref (call); }
namespace GDBusCXX {
DBusConnectionPtr dbus_get_bus_connection(const char *busType,
const char *name,
bool unshared,
DBusErrorCXX *err)
{
return DBusConnectionPtr(b_dbus_setup_bus(boost::iequals(busType, "SYSTEM") ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION,
name, unshared, err),
false);
}
static void ConnectionLost(DBusConnection *connection,
void *user_data)
{
DBusConnectionPtr::Disconnect_t *cb = static_cast<DBusConnectionPtr::Disconnect_t *>(user_data);
(*cb)();
}
static void DestroyDisconnect(void *user_data)
{
DBusConnectionPtr::Disconnect_t *cb = static_cast<DBusConnectionPtr::Disconnect_t *>(user_data);
delete cb;
}
void DBusConnectionPtr::setDisconnect(const Disconnect_t &func)
{
b_dbus_set_disconnect_function(get(),
ConnectionLost,
new Disconnect_t(func),
DestroyDisconnect);
}
DBusConnectionPtr dbus_get_bus_connection(const std::string &address,
DBusErrorCXX *err)
{
DBusConnectionPtr conn(dbus_connection_open_private(address.c_str(), err), false);
if (conn) {
b_dbus_setup_connection(conn.get(), TRUE, NULL);
dbus_connection_set_exit_on_disconnect(conn.get(), FALSE);
}
return conn;
}
void dbus_bus_connection_undelay(const DBusConnectionPtr &/*ptr*/)
{
// no op
}
boost::shared_ptr<DBusServerCXX> DBusServerCXX::listen(const NewConnection_t &newConnection, DBusErrorCXX *err)
{
DBusServer *server = NULL;
char address[80];
address[0] = 0;
for (int counter = 1; counter < 100 && !server; counter++) {
if (*err) {
g_debug("dbus_server_listen(%s) failed, trying next candidate: %s",
address, err->message);
dbus_error_init(err);
}
sprintf(address, "unix:abstract=gdbuscxx-%d", counter);
server = dbus_server_listen(address, err);
}
if (!server) {
return boost::shared_ptr<DBusServerCXX>();
}
b_dbus_setup_server(server);
boost::shared_ptr<DBusServerCXX> res(new DBusServerCXX(server, address));
res->m_newConnection = newConnection;
dbus_server_set_new_connection_function(server, DBusServerCXX::newConnection, res.get(), NULL);
return res;
}
void DBusServerCXX::newConnection(DBusServer *server, DBusConnection *newConn, void *data) throw()
{
DBusServerCXX *me = static_cast<DBusServerCXX *>(data);
if (me->m_newConnection) {
try {
b_dbus_setup_connection(newConn, FALSE, NULL);
dbus_connection_set_exit_on_disconnect(newConn, FALSE);
DBusConnectionPtr conn(newConn);
me->m_newConnection(*me, conn);
} catch (...) {
g_error("handling new D-Bus connection failed with C++ exception");
}
}
}
DBusServerCXX::DBusServerCXX(DBusServer *server, const std::string &address) :
m_server(server),
m_address(address)
{
}
DBusServerCXX::~DBusServerCXX()
{
if (m_server) {
dbus_server_disconnect(m_server.get());
}
}
bool CheckError(const DBusMessagePtr &reply,
std::string &buffer)
{
const char* errname = dbus_message_get_error_name (reply.get());
if (errname) {
buffer = errname;
DBusMessageIter iter;
if (dbus_message_iter_init(reply.get(), &iter)) {
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
buffer += ": ";
const char *str;
dbus_message_iter_get_basic(&iter, &str);
buffer += str;
}
}
return true;
} else {
return false;
}
}
}