Python / C++ application D-Bus usage issues
Kip Warner
kip at thevertigo.com
Tue Dec 11 20:47:37 PST 2012
Hey list,
I have two applications, a Python and a C++ application. The former uses
Python3, GTK+3, and GObject. It invokes the latter, a C++ executable in
its own shell. The C++ application begins by registering itself on the
session bus as such:
// Default constructor...
DBusInterface::DBusInterface()
: m_BusName("com.myproject.MyService"),
m_ObjectPath("/com/myproject/SomeObject"),
m_Interface("com.myproject.SomeInterface"),
m_ReadySignal("Ready"),
m_Connection(NULL)
{
// Clear the bus error variable...
dbus_error_init(&m_Error);
// Register on the session bus...
RegisterOnSessionBus();
}
Here is the actual registration method...
// Register on the session bus...
void DBusInterface::RegisterOnSessionBus()
{
// Alert user...
Message(Console::Info) << "d-bus: registering on the session
bus..." << endl;
// Clear the bus error variable...
dbus_error_init(&m_Error);
// Open a connection the session bus...
m_Connection = dbus_bus_get(DBUS_BUS_SESSION, &m_Error);
// Failed...
if(!m_Connection)
{
// Alert user and terminate...
Message(Console::Error)
<< "d-bus: could not connect with the session bus ("
<< m_Error.message << ")" << endl;
exit(EXIT_FAILURE);
}
// Register ourselves on the session bus...
const int Result = dbus_bus_request_name(
m_Connection, m_BusName.c_str(),
DBUS_NAME_FLAG_ALLOW_REPLACEMENT |
DBUS_NAME_FLAG_REPLACE_EXISTING |
DBUS_NAME_FLAG_DO_NOT_QUEUE,
&m_Error);
// Failed...
if(dbus_error_is_set(&m_Error))
{
// Alert user and terminate...
Message(Console::Error)
<< "d-bus: could not register service with session
bus... ("
<< m_Error.message << ")" << endl;
// Cleanup and terminate...
dbus_error_free(&m_Error);
exit(EXIT_FAILURE);
}
// There's probably some other instance already running and it's
not
// ourselves...
if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != Result &&
DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != Result)
{
// Alert user and terminate...
Message(Console::Error)
<< "d-bus: already running instance detected that could
not be replaced..."
<< endl;
// Cleanup and terminate...
exit(EXIT_FAILURE);
}
// Add a rule so we can receive signals of interest...
// Format rule...
stringstream MatchRule;
MatchRule << "type='signal'," << "interface='" <<
m_Interface << "'";
// Register...
dbus_bus_add_match(m_Connection, MatchRule.str().c_str(),
&m_Error);
dbus_connection_flush(m_Connection);
// Failed...
if(dbus_error_is_set(&m_Error))
{
// Alert user and terminate...
Message(Console::Error)
<< "d-bus: match error... ("
<< m_Error.message << ")" << endl;
// Cleanup and terminate...
dbus_error_free(&m_Error);
exit(EXIT_FAILURE);
}
}
After it has registered, the C++ application then waits for a D-Bus
signal the Python application is suppose to broadcast on the session bus
before doing some work. This is the "Ready" signal. Once it receives it,
it does not need to reply. The C++ application carries on with some more
work. This is the method in the C++ application that blocks until the
signal is received:
// Wait for a D-Bus signal before unblocking or throw an error...
void DBusInterface::WaitRemoteStart()
{
// Flag toggled when we received successfully the Ready
signal...
bool Ready = false;
// Keep checking the message queue, without hammering the CPU,
until the
// Ready signal arrives...
while(!Ready)
{
// Alert user...
Message(Console::Info)
<< "d-bus: awaiting start signal, please wait..." <<
endl;
// Block until next message becomes available and check for
error...
if(!dbus_connection_read_write(m_Connection, -1))
throw string("d-bus: connection closed prematurely...");
// Retrieve the message...
DBusMessage *IncomingMessage =
dbus_connection_pop_message(m_Connection);
// Failed...
if(!IncomingMessage)
{
// Alert user, then try again...
Message(Console::Error)
<< "d-bus: empty message queue, trying again..."
<< endl;
continue;
}
// This is not any kind of signal, ignore it...
if(DBUS_MESSAGE_TYPE_SIGNAL !=
dbus_message_get_type(IncomingMessage))
{
// Free it and check the next message...
dbus_message_unref(IncomingMessage);
continue;
}
// Verify this was the Ready signal...
Ready = dbus_message_is_signal(
IncomingMessage, m_Interface.c_str(),
m_ReadySignal.c_str());
// Not the Ready signal...
if(!Ready)
{
// Which signal was it?
const char *SignalNameTemp =
dbus_message_get_member(IncomingMessage);
string SignalName(SignalNameTemp ? SignalNameTemp :
"");
// Something else. Alert user...
Message(Console::Error)
<< "Unrecognized signal... ("
<< dbus_message_get_member(IncomingMessage) <<
"), skipping..."
<< endl;
Message(Console::Error)
<< "d-bus: expected interface \""
<< m_Interface
<< "\", but got \""
<< dbus_message_get_interface(IncomingMessage)
<< "\""
<< endl;
DBusMessageIter Arguments;
// read the parameters
if(!dbus_message_iter_init(IncomingMessage,
&Arguments))
Message(Console::Error) << "message has no
arguments!" << endl;
else if (DBUS_TYPE_STRING !=
dbus_message_iter_get_arg_type(&Arguments))
Message(Console::Error) << "argument is not
string!" << endl;
else
{
char *sigvalue = NULL;
dbus_message_iter_get_basic(&Arguments,
&sigvalue);
Message(Console::Error) << "got signal with
value " << sigvalue << endl;
}
}
// Cleanup and wait for next message...
dbus_message_unref(IncomingMessage);
}
}
Meanwhile, the Python / Gtk+3 / GObject application, having launched the
former C++ application, registers itself on the session bus as such:
# Connect to the session bus...
dbus_loop = DBusGMainLoop()
self._sessionBus = dbus.SessionBus(mainloop=dbus_loop)
...
# Some constants to help find the C++ application...
DBUS_SERVICE_NAME = "com.myproject.MyService"
DBUS_OBJECT_PATH = "/com/myproject/MyObject"
DBUS_INTERFACE = "com.myproject.MyInterface"
# Register our D-Bus signal handler callbacks...
sys.stdout.write("Waiting for C++ application's D-Bus service to
register")
# Keep trying to connect until successful...
while True:
# Alert user with each dot being a new attempt...
sys.stdout.write(".")
sys.stdout.flush()
# Try to connect to the C++ application...
try:
# Initialize the remote proxy...
objectProxy = self._sessionBus.get_object(
DBUS_SERVICE_NAME, DBUS_OBJECT_PATH)
...
# We're connected. End alert with new line...
print("ok")
break
# C++ application probably isn't running...
except DBusException as error:
# Pump the Gtk+ event handler before we try again...
while Gtk.events_pending():
Gtk.main_iteration()
# Pause a moment, then try again...
sleep(0.1)
continue
# Fire off the ready signal now that we have connected...
message = SignalMessage(DBUS_OBJECT_PATH, DBUS_INTERFACE,
"Ready")
self._sessionBus.send_message(message)
self._sessionBus.flush()
The Python application prints the following on the console:
ERROR:dbus.proxies:Introspect error
on :1.568:/com/myproject/MyObject: dbus.exceptions.DBusException:
org.freedesktop.DBus.Error.NoReply: Message did not receive a reply
(timeout by message bus)
But the problem is the C++ application doesn't receive the signal
properly. It prints the following out on the console:
info: d-bus: registering on the session bus...
info: d-bus: awaiting start signal, please wait...
error: Unrecognized signal... (NameAcquired), skipping...
error: d-bus: expected interface "com.myproject.MyInterface",
but got "org.freedesktop.DBus"
error: got signal with value :1.568
info: d-bus: awaiting start signal, please wait...
error: Unrecognized signal... (NameAcquired), skipping...
error: d-bus: expected interface "com.myproject.MyInterface",
but got "org.freedesktop.DBus"
error: got signal with value com.myproject.MyService
Any help appreciated =)
--
Kip Warner -- Software Engineer
OpenPGP encrypted/signed mail preferred
http://www.thevertigo.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part
URL: <http://lists.freedesktop.org/archives/dbus/attachments/20121211/e11fed0e/attachment.pgp>
More information about the dbus
mailing list