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

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

До сих пор я был в состоянии запустить демон D-Bus и проверить связь по D-Bus от имени пользователя ("otheruser"), который не вошел в систему, в трех разных терминалах:

В первом терминале я запускаю демон 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 и в клиентские службы для "otheruser"?

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

1 ответ

Решение

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

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

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

# loginctl enable-linger otheruser

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

# 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. Systemd использует информацию из dbus.socket создать сокет, и его файловый дескриптор устанавливается в переменной окружения LISTEN_FDS, который передается в dbus-daemon, Специальные опции, перечисленные выше, заставляют dbus-daemon использовать переданный файловый дескриптор вместо создания нового. Это позволяет клиентам dbus запускаться параллельно с запуском dbus-демона, не беспокоясь о том, что сокет не существует.

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

[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
Другие вопросы по тегам