Назад | Перейти на главную страницу

Запуск сервисов systemd, разделяющих сеанс D-Bus в автономной системе

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

Пока что мне удалось запустить демон D-Bus и протестировать связь D-Bus от имени пользователя («другой пользователь»), который не вошел в систему, на трех разных терминалах:

В первом терминале я запускаю демон D-Bus для «другого пользователя»:

$ sudo -u otheruser dbus-daemon --session --print-address 1
unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48

Во втором терминале я запускаю серверное приложение D-Bus, используя приведенный выше ответ DBUS_SESSION_BUS_ADDRESS:

$ sudo -u otheruser DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48" /usr/bin/my-dbus-service

Затем в третьем терминале я могу проверить соединение:

$ sudo -u otheruser DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-a5cU7r4IHc,guid=6c0a9bbfd02f5f68da0fe32f5a5e0a48" gdbus introspect --session --dest com.mycompany.myappname --object-path /com/mycompany/interface

Но я хочу запустить серверное приложение D-Bus, а также несколько клиентских сервисов D-Bus через systemd. Как мне запустить сеанс D-Bus через systemd, чтобы его переменная среды DBUS_SESSION_BUS_ADDRESS распространялась на сервер D-Bus и клиентские службы для «другого пользователя»?

Одним из возможных решений может быть перенаправление вывода dbus-daemon в «somefile», а затем установка DBUS_SESSION_BUS_ADDRESS = $ (cat somefile) перед запуском сервера и клиентов D-Bus. Мне это кажется слишком неудобным; особенно потому, что я знаю, что есть некоторая магия с директивой "Busname" в служебном файле systemd для система Соединения D-Bus. Как мне правильно запустить службы systemd для «другого пользователя», чтобы эти службы systemd могли взаимодействовать с сеансовым интерфейсом D-Bus?

Чтобы это работало, вам нужно несколько вещей.

  1. Включение пользовательских служб для запуска во время загрузки без входа пользователя в систему (systemd linger).
  2. Файл сокета systemd, чтобы указать сокет D-Bus, который должен выделить systemd.
  3. Служба systemd для запуска сеансовой шины D-Bus, которая запускается, а затем устанавливает переменную окружения DBUS_SESSION_BUS_ADDRESS для других служб systemd.
  4. Убедитесь, что ваш systemd my-dbus-client.service файлы имеют Type=dbus или зависеть от dbus.socket unit, чтобы убедиться, что они выделяют сокет сеансовой шины dbus и запускают сеансовую службу dbus, если она еще не запущена.

Во-первых, чтобы службы Systemd для данного пользователя запускались во время загрузки без входа в систему, вам необходимо включить задержку пользователя systemd - это нужно сделать только один раз с правами root при настройке, чтобы включить его для пользователя:

# loginctl enable-linger otheruser

Затем, если вы используете систему на основе Debian, для следующих двух шагов вы можете просто установить пакет dbus-user-session package:

# apt-get install dbus-user-session

Если вы используете какой-то другой дистрибутив, хотите сделать это вручную или просто хотите понять, как это работает, продолжайте. В противном случае пропустите создание dbus.service и dbus.socket.

Создайте файл /usr/lib/systemd/user/dbus.socket (обратите внимание, что в некоторых дистрибутивах каталог пользователя может находиться в /lib вместо того /usr/lib) со следующим содержанием:

[Unit]
Description=D-Bus User Message Bus Socket

[Socket]
ListenStream=%t/bus
ExecStartPost=-/bin/systemctl --user set-environment DBUS_SESSION_BUS_ADDRESS=unix:path=%t/bus

[Install]
WantedBy=sockets.target
Also=dbus.service

Распространение DBUS_SESSION_BUS_ADDRESS ко всем услугам, что было вашей главной заботой, адресовано ExecPostStart строка ниже - все следующие службы будут иметь этот набор.

%t заменяется на XDG_RUNTIME_DIR - временный каталог в /run создается systemd для конкретного сеанса пользователя, в который вы можете загружать файлы. Если вы хотите создать этот сокет в другом месте, нет причин, по которым вы не можете этого сделать. Просто убедитесь, что он где-то временный, или он будет очищен при перезагрузке / разрыве сеанса.

У меня были некоторые проблемы с попыткой сделать сокет dbus unix абстрактным - systemd, похоже, не понравилась форма unix:abstract= или @ префикс почему-то.

Теперь создайте файл /usr/lib/systemd/user/dbus.service со следующим содержанием:

[Unit]
Description=D-Bus User Message Bus
Requires=dbus.socket

[Service]
ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation
ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig

[Install]
Also=dbus.socket

Здесь за кулисами systemd творится немного волшебства, чтобы передать уже созданный unix-сокет dbus-daemon. Systemd использует информацию из dbus.socket для создания сокета, и его файловый дескриптор устанавливается в переменной среды LISTEN_FDS, который передается в dbus-daemon. Специальные опции, перечисленные выше, заставляют dbus-daemon использовать переданный файловый дескриптор вместо создания нового. Это позволяет клиентам dbus запускаться параллельно запуску dbus-daemon, не беспокоясь о том, что сокет не существует.

Наконец, создайте свои собственные пользовательские сервисы systemd, убедившись, что вы либо установили тип на Type=dbus, устанавливать BusName= к имени одного из имен службы dbus, которое будет зарегистрировано этой службой, или убедившись, что Requires=dbus.socket указан в разделе Unit. Вот пример:

[Unit]
Description=Config Server Startup

[Service]
Type=dbus
BusName=com.example.app.configuree
ExecStart=/opt/example/app/configuration_server
Restart=on-failure

[Install]
WantedBy=default.target

Вы можете разместить их в одном из нескольких мест: - $HOME/.config/systemd/user - /usr/lib/systemd/user

Включите свои услуги с systemctl --user enable <service name> и перезагрузитесь, и все должно работать.


Ссылки:

  • man loginctl задержаться
  • man pam_systemd для информации XDG_RUNTIME_DIR
  • man systemd.service для Type = dbus, BusName = и неявной зависимости от dbus.socket
  • man sd_listen_fds для получения информации о переменной окружения LISTEN_FDS
  • https://wiki.archlinux.org/index.php/Systemd/User - общая информация о пользовательских сессиях systemd