Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Управление Linux десктопом через D-Bus

Оригинал: Control Your Linux Desktop with D-Bus
Автор: Koen Vervloesem
Дата публикации: 01 Января 2011
Перевод: Максим Хвощ
Дата публикации перевода: 28 января 2013 г.

Используя D-Bus, вы можете персонализировать и автоматизировать ваш десктоп.

Каждая современная Linux среда рабочего стола использует D-Bus, систему, позволяющую программным приложениям общаться друг с другом. Благодаря D-Bus, ваш десктоп может быть настроен точно так, как вы хотите. В этой статье я привожу примеры некоторых возможностей D-Dus. Будьте готовы к модификации вашего десктопа.

D-Bus (Desktop Bus) - система межпроцессного взаимодействия (IPC system), которая позволяет приложениям в операционной системе общаться друг с другом. Создатели D-Bus построили свою систему с нуля, но находились под сильным влиянием системы DCOP (Desktop COmmunication Protocol) из среды KDE. В настоящее время D-Bus используется везде - в KDE 4 DCOP уступила D-Bus, а GNOME переходит на D-Bus вместо своей собственной системы Bonobo. Так что D-Bus стал независимым от десктопной среды механизмом межпроцессорного взаимодействия. ПО, которое использует D-Bus, незаметно интегрируется в ваш десктоп, независимо от того, какую среду вы используете. D-Bus является частью кросс-платформенного проекта freedesktop.org, главным участником которого является Red Hat.

D-Bus регистрирует каждую программу, имеющую сервисы, доступные для других приложений. Таким образом, эти приложения видят, какие сервисы доступны. Также, программы могут становиться доступными для событий системных сервисов (например, определять горячую замену железа).

D-Bus не разрешает прямую коммуникацию между процессами, а работает через шину. Системная шина обеспечивает маршрутизацию сообщений между процессами через себя, таким образом, процессы могут взаимодействовать с несколькими приложениями одновременно. Каждое приложение может посылать сообщения на шину или реагировать на события, получаемые через шину.

Обычно, D-Bus создает две шины: привилегированную системную шину и сессионную шину. Системная шина позволяет осуществлять широковещательное взаимодействие между процессами, имеющими необходимые права доступа. Ее главное назначение - доставка событий от HAL (Hardware Abstraction Layer) к процессам, работающим с аппаратными событиями. Таким аппаратным событием может быть обнаружение нового аппаратного устройства, или изменение в очереди печати. Вторая шина - сессионная, создается во время авторизации, и с ее помощью будут общаться приложения, с которыми работает пользователь.

Работа с D-Bus

Каждое приложение, работающее с D-Bus, располагает какими-то объектами, которые в основном мапируются на внутренние объекты GObject, C++ или Python. Одно приложение может послать сообщение определенному объекту из другого приложения. К каждому D-Bus объекту мы обращаемся через уникальное имя, которое выглядит как путь файловой системы. Для обеспечения уникальности имен, каждое имя D-Bus объекта обычно имеет префикс, специфичный для разработчика, например /org/kde или /com/redhat. В языке программирования Java для этих целей используются имена пакетов (например, org.sun). D-Bus путь состоит из трех частей: имя сервиса, путь к объекту и интерфейс (я приведу примеры чуть позже в этой статье).

Итак, как же можно использовать D-Bus в своем собственном приложении? Основной API написан на С довольно низкого уровня. Он не задумывался для использования программистами приложений. Различные языки программирования и среды разработки, такие как GLib, Qt, Python, Ruby, Perl и Mono имеют надстроенные над этим API связующие слои. Я не буду углубляться в С или GLib (основаная библиотека в GNOME), но я приведу несколько примеров, написанных на скриптовых языках Python и Ruby, а так же примеры скриптов командной оболочки.

Какие приложения используют D-Bus?

На сайте проекта reedesktop.org имеется неполный список приложений, использующих D-Bus, и имена на шине для каждого приложения. Кстати, вы сами можете найти имена на шине D-Bus, используя некоторые интересные инструменты. Например, Qt имеет графический D-Bus браузер - qdbusviewer (Рисунок 1). В Ubuntu вы можете найти это приложение в пакете qt4-dev-tools. Несмотря на то, что оно является частью KDE, это приложение прекрасно работает и в других средах, включая GNOME.

QDBusViewer, работающий в GNOME

Рисунок 1. QDBusViewer работающий в GNOME

Работая в qdbusviewer, вы видите две вкладки: Сессионная Шина и Системная Шина. В каждой вкладке левая панель показывает список имен сервисов. Если нажать на имя сервиса, то в правой панели появится информация о сервисе, список доступных методов и сигналов. Например, если вы нажмете на сервисе org.freedesktop.PowerManagement, и далее перейдете в правой панели вниз по иерархии - org/freedesktop/PowerManagement/, то вы пройдете две части пути D-Bus: org.freedesktop.PowerManagement в левой панели является именем сервиса, а org/freedesktop/PowerManagement в правой панели является путем до объекта.

Путь до объекта в правой панели имеет еще одну последнюю часть - три так называемых интерфейса: org.freedesktop.DBus.Introspectable, org.freedesktop.DBus.Properties и org.freedesktop.PowerManagement. Каждый интерфейс реализует какие-то методы и сигналы. Это то, с чем вы можете взаимодействовать. Здесь мы заинтересованы в интерфейсе org.freedesktop.PowerManagement, он реализует действия связанные с управлением питанием. Нажав на него, мы видим список из всех доступных методов и сигналов. Если мы нажмем на метод Suspend, то компьютер заснет, и проснется только после нажатия кнопки питания.

Некоторые методы, такие как Shutdown, Reboot, Hibernate и Suspend, осуществляют действия, а другие методы выдают вам информацию о системе. Например, объект org.freedesktop.PowerManagement реализует такие методы как GetLowBattery, GetOnBattery, CanShutdown, и т.д. Если ваша система (лэптоп) работает от батареи, имеющей достаточный заряд, то нажимая на GetOnBattery, мы получаем "Arguments: true", а при нажатии на GetLowBattery, ответ - "Arguments: false".

Стоит упомянуть, что qdbusviewer показывает только имена сервисов, зарегистрированных на текущий момент. Например, если Pidgin не запущен, то программа просмотра не будет его отображать. Имейте это ввиду, исследуя D-Bus сервисы, доступные в вашей системе.

Если вы являетесь поклонником командной строки, то вам не нужно запускать qdbusviewer. Приложение qdbus, работающее из командной строки, предоставляет точно такую же информацию. Если вы запустите qdbus в терминале, то увидите список сервисов, доступных на сессионной шине. Если вы запустите его с флагом --system, то увидите сервисы, доступные системной шине. Если вы хотите узнать, какие объекты содержит сервис, запустите, например, следующую команду:

$ qdbus org.freedesktop.PowerManagement 
/ 
/org 
/org/freedesktop 
/org/freedesktop/PowerManagement 
/org/freedesktop/PowerManagement/Backlight 
/org/freedesktop/PowerManagement/Inhibit 
/org/freedesktop/PowerManagement/Statistics 

Для того что бы узнать, какие интерфейсы реализованы в объекте /org/freedesktop/PowerManagement , используйте:

$ qdbus org.freedesktop.PowerManagement \ 
/org/freedesktop/PowerManagement 

Мы получим тот же самый список из методов и интерфейсов, что мы видели в qdbusviewer. Например, строку:

method bool org.freedesktop.PowerManagement.GetOnBattery()

bool означает, что этот метод возвращает булеву величину, со значением true или false. Если метод не возвращает величину, например org.freedesktop.PowerManagement.Suspend(), то вместо bool используется void.

Также qdbus позволяет нам вызывать эти методы напрямую. Например, если мы хотим вызвать метод Suspend, выполняем:

$ qdbus org.freedesktop.PowerManagement \ 
/org/freedesktop/PowerManagement \ 
org.freedesktop.PowerManagement.Suspend 

Работаем с D-Bus из командной строки

В оставшейся части статьи мы рассмотрим D-Bus функциональность некоторых популярных приложений, а также напишем скрипты для взаимодействия с этими приложениями и автоматизации ряда задач. Надеюсь, это вдохновит вас на взаимодействие с вашими собственными любимыми приложениями. Я буду использовать разные D-Bus инструменты и скриптовые языки для демонстрации различных способов работы с D-Bus.

Я уже упомянул первый способ работы с D-Bus: использование KDE программ qdbusviewer и qdbus. Если вы не работаете в KDE, то можете использовать программы командной строки dbus-send и dbus-monitor, для отсылки и мониторинга D-Bus сообщений, соответственно. Например, вы можете отправить систему в спящий режим, следующей командой:

$ dbus-send --dest=org.freedesktop.PowerManagement \ 
/org/freedesktop/PowerManagement \ 
org.freedesktop.PowerManagement.Suspend   

Как мы видим, dbus-send вызовы практически идентичны вызовам qdbus. Единственное различие - это необходимость использовать --dest параметр для имени сервиса. Теперь давайте взглянем на что-нибудь новенькое. Если вы смотрите в своем браузере длинное видео на YouTube, то может активизироваться скринсейвер, так как Flash плагин не взаимодействует с остальной системой. С помощью D-Bus мы можем остановить это раздражающее поведение. Вот и волшебная команда:

$ dbus-send --print-reply \ 
--dest=org.gnome.ScreenSaver / \ 
org.gnome.ScreenSaver.Inhibit \ 
string:"YouTube" \ 
string:"Inhibit Screensaver" 

С помощью этой команды мы вызываем Inhibit метод интерфейса org.gnome.ScreenSaver, с двумя аргументами. Первый аргумент - это имя приложения. Здесь я использую “YouTube”, но это может быть любое имя. Второй аргумент - это причина - остановка скринсейвера. dbus-send ожидает, что каждому аргументу будет предшествовать его тип (string, boolean, int16 и т.д.). В этом примере оба аргумента имеют строковой тип. Также я использую аргумент --print-reply потому, что мне нужно ответить на команду: Inhibit метод возвращает uint32 число, которое работает как куки, идентифицируюя запрос на остановку скринсейвера. Если мы хотим вновь запустить скринсейвер, то мы должны переслать этот куки в качестве аргумента:

$ dbus-send --dest=org.gnome.ScreenSaver / \ 
org.gnome.ScreenSaver.UnInhibit \ 
uint32:1234567890

С помощью этих двух команд, мы можем создать свой собственный скрипт командной оболочки для скринсейвера. Примечание: во время запуска первой команды вы должны сохранить куки в переменную или в файл, а затем заменить значение куки в команде, что выше.

Если вы отлаживаете D-Bus скрипт или наблюдаете за методами и сигналами  других D-Bus приложений, то программа командной строки dbus-monitor очень вам в этом поможет. Просто запустите ее через терминал, и вы увидите всю D-Bus активность. dbus-monitor полезен для просмотра D-Bus активности в реальном времени. Если с вашей системой что-то происходит, например пропадает сеть, то на выходе dbus-monitor вы можете видеть как это сообщение поступает на шину D-Bus.  Таким образом вы можете узнать какие сигналы или методы должны использоваться для работы с тем или иным событием.

dbus-monitor так же позволяет вам указать ряд выражений, определяющих за чем вы хотите наблюдать, например:

$ dbus-monitor --system "interface='org.freedesktop.NetworkManager'"  

С помощью этого вы сможете наблюдать за всеми NetworkManager событиями . Я использовал аргумент --system потому, что NetworkManager использует системную шину.

Пишем скрипт для чтения Liferea ленты

Liferea лента имеет маленький, но интересный набор методов для работы с D-Bus. Наиболее интересный метод - это Subscribe, который позволяет вам добавить к Liferea поток из другого приложения. Одно из приложений, пользующиеся этим - FeedBag, расширение для Firefox, которое изменяет кнопку потока в адресной строке браузера: если вы нажимаете на кнопку, то подписка добавляется к Liferea, а не к Live Bookmarks. Работает FeedBag через вызов метода FeedBag org.gnome.feed.Reader.Subscribe. Тоже самое вы можете сделать и из терминала:

$ feed="http://feeds2.feedburner.com/linuxjournalcom?format=xml" 
$ dbus-send --dest=org.gnome.feed.Reader \ 
/org/gnome/feed/Reader \ 
org.gnome.feed.Reader.Subscribe \ 
string:"$feed" 

Liferea предоставляет скрипт, который делает точно тоже самое, но еще умеет обрабатывать ошибки.

Также, через D-Bus Liferea предоставляет некоторую информацию, которая может оказаться полезной если вы используете альтернативный оконный менеджер, не имеющий области уведомления для Liferea. Вы можете сами сварганить вашу собственную систему оповещения - просто запросите число новых и непрочитанных записей в Liferea и покажите выходные данные:

$ dbus-send --print-reply \ 
--dest=org.gnome.feed.Reader \ 
/org/gnome/feed/Reader \ 
org.gnome.feed.Reader.GetNewItems 

$ dbus-send --print-reply \ 
--dest=org.gnome.feed.Reader \ 
/org/gnome/feed/Reader \ 
org.gnome.feed.Reader.GetUnreadItems 

Без клавиатуры

Если вы хотите выполнить более сложные задания, чем вызов отдельных методов, то вы можете написать скрипт командной оболочки, содержащий dbus-send команды, или используйте язык более высокого уровня, для упрощения задачи. Существуют D-Bus привязки для Python, Ruby и Java языков.

В следующем примере, я реализую скрипт на Python, который меняет ваш статус в Pidgin на “Away from keyboard”, при активизации скринсейвера. Здесь имеются два аспекта D-Bus: скрипт ждет сигнала от скринсейвера, и затем он вызывает метод в Pidgin.  Скрипт показан в листинге 1.

Листинг 1. pidgin_screensaver.py

#!/usr/bin/env python def pidgin_status_func(state): 
obj = bus.get_object("im.pidgin.purple.PurpleService", 
"/im/pidgin/purple/PurpleObject") 
pidgin = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface") 
status = pidgin.PurpleSavedstatusFind("afk") 
if status == 0: 
status = pidgin.PurpleSavedstatusNew("afk", 5) 
if state: 
pidgin.PurpleSavedstatusSetMessage(status, 
"Away from keyboard") 
pidgin.PurpleSavedstatusActivate(status) 

import dbus, gobject 
from dbus.mainloop.glib import DBusGMainLoop 

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 
bus = dbus.SessionBus() 

bus.add_signal_receiver(pidgin_status_func, 
dbus_interface="org.gnome.ScreenSaver", 
signal_name="ActiveChanged") 

loop = gobject.MainLoop() 
loop.run() 

Давайте разберем этот скрипт. Функция pidgin_status_func устанавливает ваш статус в Pidgin. Она получает объект im/pidgin/purple/PurpleObject и интерфейс im.pidgin.purple.PurpleInterface из сессионной шины. Далее, вызывается метод интерфейса. Он создает новый “saved status” тип, после проверки существования типа статус с именем “afk” (“afk” означает “Away From Keyboard”, и 5 - это вид “away” статуса).

Далее функция проверяет переменную state, которая является аргументом функции pidgin_status_func (я объясню, что означает этот аргумент далее). Если аргумент правдив, то сообщению нового статуса “afk” присваивается значение “Away from keyboard”, и статус активируется. В результате Pidgin показывает ваш статус как “afk", с сообщением “Away from keyboard”.

Теперь мы должны вызвать эту функцию вместе с активизацией скринсейвера. Поэтому, запускаем dbus.mainloop и соединяемся к сессионной шине. Далее добавляем приемник сигнала, который слушает сигнал ActiveChanged от интерфейса org.gnome.ScreenSaver. Если/когда сигнал срабатывает, он вызывает функцию pidgin_status_func. Так как сигнал ActiveChanged имеет булев аргумент, обозначающий текущее состояние заставки (1 - активная, 0 - не активная), то мы используем только один аргумент (state) в функции pidgin_status_func. Для постоянного прослушивания запускаем бесконечный цикл, работающий пока работает скрипт.

Pidgin имеет очень обширный D-Bus интерфейс; вы можете делать с помощью него практически все. Пусть этот пример послужит вам вдохновением для создания каких-нибудь креативных задач в Pidgin!

Проигрывание с помощью D-Bus

Давайте посмотрим на другой пример, на этот раз на Ruby. Мы создадим скрипт, который будет показывать текущую песню, играющую в Rhythmbox, вместо вашего статуса в Pidgin (Листинг 2).

Листинг 2. pidgin_rhythmbox.rb
#!/usr/bin/env ruby require 'dbus' 

bus = DBus::SessionBus.instance 
rhythmbox = bus.service("org.gnome.Rhythmbox") 
player = rhythmbox.object("/org/gnome/Rhythmbox/Player") 
player.introspect 
player.default_iface = "org.gnome.Rhythmbox.Player" 

pidgin = bus.service("im.pidgin.purple.PurpleService") 
purple = pidgin.object("/im/pidgin/purple/PurpleObject") 
purple.introspect 
purple.default_iface = "im.pidgin.purple.PurpleInterface" 

player.on_signal("playingUriChanged") do |uri| 
status = purple.PurpleSavedstatusFind("rhythmbox").first 
if status == 0 
status = purple.PurpleSavedstatusNew("rhythmbox", 2).first 
end 
purple.PurpleSavedstatusSetMessage(status, uri.to_s) 
purple.PurpleSavedstatusActivate(status) 
end 

Здесь мы видим команды такого же типа, как и в скрипте на Python: начинаем D-Bus сессию, определяем D-Bus сервис, объекты и интерфейс, и определяем приемник сигнала. Цикл постоянно работает для прослушивания D-Bus сигнала.
Конечно, все это можно немного привести в порядок. Например, на данный момент, в качестве статусного сообщения мы показываем только путь к файлу песни. Я предоставляю вам возможность самим заменить путь к файлу на соответствующий ID3 тег.

Заключение

Теперь, когда вы знаете как выполнять D-Bus запросы и как обрабатывать D-Bus сигналы, вы можете заняться автоматизированием заданий на вашем десктопе. Если вы опытный Linux пользователь, то D-Bus точно должен быть в вашем арсенале.
D-Bus содержит намного больше, чем я могу вам показать в этой статье, но с помощью qdbusviewer, qdbus, dbus-send и dbus-monitor, вы сможете освоить остальные возможности сами. Если вы хотите реализовать более сложные задачи по автоматизации, используя D-Bus, то хорошими помощниками вам могут стать языки Python и Ruby. Рассмотрите учебные пособия, упомянутые в приложении, и далее просто дайте волю своей фантазии.
Если вы разработчик программного обеспечения, то раздел, который мы не рассмотрели здесь, - это регистрация сервиса. Эта другая сторона рассказа о D-Bus. Если вы регистрируете сервис, то с помощью D-Bus вы можете предоставлять объекты для других приложений.

Приложение

D-Bus: www.freedesktop.org/wiki/Software/dbus

D-Bus привязки: www.freedesktop.org/wiki/Software/DBusBindings

Учебное пособие по D-Bus на Python: dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html

Учебное пособие по D-Bus на Ruby: trac.luon.net/data/ruby-dbus/tutorial/index.html

Koen Vervloesem -  с 2000-х - журналист-фрилансер, пишущий на тему бесплатного программного обеспечения и программного обеспечения с открытым кодом. Он является магистром компьютерных наук и философии, и не может решить, какая из сфер для него более интересна. Пока что он любит думать - "Я программирую, следовательно я существую".