Мне нужна помощь в запуске служб, которые обмениваются данными через сеансовую (не системную) шину 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?
Чтобы это работало, вам нужно несколько вещей.
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_DIRman systemd.service
для Type = dbus, BusName = и неявной зависимости от dbus.socketman sd_listen_fds
для получения информации о переменной окружения LISTEN_FDS