D-Bus — это система межпроцессного взаимодействия, которая предоставляет приложениям несколько шин для передачи сообщений. Она обеспечивает беспроблемную связь десктопных приложений между собой и связь между десктопными приложениями и системными сервисами. Поддерживается не только широковещательная рассылка сообщений (сигналов), но и удалённый вызов методов.
Некоторые системные сервисы, например как HAL или WPA Supplicant, предоставляют соответствующие D-Bus сервисы на системной шине сообщений, таким образом мы можем с ними общаться с помощью любого D-Bus клиента.
Отметим также, что архитектура сервис-объект-интерфейс свойственна многим компонентым системам типа COM, XPCOM, CORBA и т.д.
После соединения с шиной приложение, которое создаёт на шине новый сервис, получает уникальный идентификатор для соединения в рамках шины. Также любой D-Bus сервис может присвоить себе дополнительное, более понятное имя (например, ru.org.linux.trollhouse). Этот механизм схож с созданием символьных ссылок на файловой системе - у вас может быть один реальный файл и несколько ссылок на него с разными именами. Имя сервиса, автоматически создаваемое для него системой D-Bus, имеет вид ":X.Y", где X и Y - компоненты, определяющие уникальность данного сервиса, обычно числа, например ":1.5".
Следующим шагом при инициализации приложения является установка фильтров, указывающих, какие сообщения приложением принимаются. Фильтровать можно по типу сообщения (signal, method_call), отправителю (sender), интерфейсу, пути к объекту и имени метода.
Затем задаются обработчики сигналов и методов (будет рассмотрено ниже).
Пример на Tcl:
:
package require dbus 0.7
proc processMethodCall {info thing} {
puts "Method call: $info"
puts "Argument: $thing"
return "$thing ne nuzhno!"
}
set conn [ dbus::connect session ]
puts "Connection ID: $conn"
dbus name session "ru.org.linux.trollhouse"
dbus filter session add -interface ru.org.linux.Troll -member SaySomething -path /troll
dbus register session /troll processMethodCall
vwait forever
Тестирование:
dbus-send --session --dest=ru.org.linux.trollhouse --type=method_call --print-reply /troll ru.org.linux.Troll.SaySomething string:KDE
Сигнал с точки зрения шины представляет собой сообщение с destination=NULL, т.е. которое надо доставить всем (с учётом фильтров, разумеется).
Пример:
package require dbus 0.7
dbus connect session
dbus name session ru.org.linux.trollhouse
dbus signal session \
-signature si \
/troll ru.org.linux.troll.Eat NeedSugar \
"I want more sugar!" 2
Получение сигналов от HAL при появлении/извлечении устройства:
package require dbus 0.7
proc processDeviceAddRemove {info udi} {
if {[ dict get $info member ] == "DeviceAdded"} {
puts "Device added: $udi"
} else {
puts "Device removed: $udi"
}
}
dbus connect system
foreach member {DeviceAdded DeviceRemoved} {
dbus filter system add \
-sender org.freedesktop.Hal \
-interface org.freedesktop.Hal.Manager \
-member $member \
-type signal \
-path /org/freedesktop/Hal/Manager
}
dbus register system \
/org/freedesktop/Hal/Manager processDeviceAddRemove
vwait forever
Вызов метода состоит из отправки двух сообщений:
Исходя из вышеописанного, существует два способа удалённого вызова методов через D-Bus: синхроннный (вызывающий процесс блокируется до прихода ответа) и асинхронный (ответ обрабатывается по мере получения).
Пример синхронного вызова:
package require dbus 0.7
dbus connect session
dbus call session \
-dest ru.org.linux.trollhouse \
-signature s \
/troll ru.org.linux.Troll SaySomething \
"Gnome"
Пример асинхронного вызова:
package require dbus 0.7
proc resultHandler {info result} {
if {[ dict get $info messagetype ] == "error"} {
puts "Unable to speak to troll: $result"
} else {
puts "Troll says: $result"
}
}
dbus connect session
dbus call session \
-dest ru.org.linux.trollhouse \
-handler resultHandler \
-signature s \
/troll ru.org.linux.Troll SaySomething \
"Gnome"
vwait forever
Пример асинхронной обработки вызова метода:
package require dbus 0.7
proc processMethodCall {info thing} {
puts "Method call: $info"
puts "Argument: $thing"
set bus session
set dest [ dict get $info sender ]
set serial [ dict get $info serial ]
if [ string match -nocase $thing unix ] {
# возврат ошибки
set action [ list dbus error $bus $dest $serial "UNIX rulez!" ]
} else {
# возврат результата
set action [ list dbus return $bus $dest $serial "$thing is not needed!" ]
}
# специально вставим задержку, чтобы показать неблокирующий режим работы
after 10000 $action
}
set conn [ dbus::connect session ]
puts "Connection ID: $conn"
dbus name session "ru.org.linux.trollhouse"
dbus filter session add -interface ru.org.linux.Troll -member SaySomething -path /troll
dbus register session -async \
/troll processMethodCall
vwait forever
Существует несколько предопределенных интерфейсов, использование которых может быть полезно многим программам[3][4].
Почти все библиотеки для работы с dbus позволяют автоматически генерировать методы для интроспекции (кроме низкоуровневых вроде libdbus или dbus-tcl).
Для того, чтобы получить информацию о методах, сигналах и свойствах, надо вызвать метод Introspect из интерфейса org.freedesktop.DBus.Introspectable. Результат отдаётся в виде строки, которая представляет собой XML документ. Этот документ содержит корневой элемент типа node. Под ним располагаются элементы типов interface (содержащие описание реализуемых интерфейсов; имя интерфейса хранится в атрибуте name) и элементы типа node, указывающие, что в дереве объектов есть объекты ниже данного (атрибут name показывает, что надо дописать к пути, чтобы туда достучаться).
Описание интерфейса содержит элементы типа method (имя задаётся в атрибуте name), signal и property. Внутри каждого из этих элементов находится произвольное количество элементов типа arg, обозначающих аргументы функции или сигнала.
Атрибуты: name --- имя параметра (может отсутствовать), direction --- направление (in --- аргументы метода, out --- возвращаемое значение; только для методов), type --- тип элемента (например: i, s, as...).
Пример:
$ qdbus --system org.freedesktop.Hal / org.freedesktop.DBus.Introspectable.Introspect
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<node name="org"/>
</node>
| TODO | пример с интроспекцией |
Для работы со свойствами существует интерфейс org.freedesktop.DBus.Properties, у которого определены 3 метода: Get(in STRING interface_name, in STRING property_name, out VARIANT value), Set(in STRING interface_name, in STRING property_name, in VARIANT value) и GetAll(in STRING interface_name, out DICT<STRING,VARIANT> props);
| TODO | пример |
| TODO | описать |
По мере исправления сноски необходимо удалять.