Вопрос или проблема
Я пытаюсь настроить fluidsynth
, чтобы он работал как сервис пользователя на системе Debian. Проблема в том, что он запускается при входе пользователя и нуждается в доступе к /dev/snd/seq
, иначе возникает ошибка, и он превращается в “зомби” (т.е. активен, но фактически не работает):
fluidsynth[2404]: ALSA lib seq_hw.c:466:(snd_seq_hw_open) открыть /dev/snd/seq не удалось: доступ запрещен
Этот доступ (в конце концов) предоставляется правилом udev 70-uaccess.rules
(выполняемым 73-seat-late.rules
) — т.е. после успешного входа я могу перезапустить сервис в терминале, и всё работает.
Существует ли способ указать systemd запускать мой сервис только после завершения выполнения этого правила?
На заданный вопрос (“подождать, пока правила udev закончат выполнение”) вы добавите зависимость от соответствующего .device
юнита:
Requires=dev-snd-seq.device
After=dev-snd-seq.device
Если бы юнита .device не было, вам нужно было бы добавить другое правило udev, которое применяет TAG+="systemd"
, чтобы заставить systemd сгенерировать виртуальный .device юнит.
Однако это не сработает, потому что ваш доступ не “(в конечном счете) предоставляется правилом udev”. Правила udev обрабатываются только в ответ на события на самом устройстве, поэтому, хотя 73-seat-late.rules вызывает встроенное `uaccess` udev для применения начальных ACL к помеченным устройствам, это происходит только один раз, когда для этого устройства генерируется событие add
(намного раньше вашего входа).
Правила для всех устройств не повторно выполняются, когда пользователь входит в систему, так как udev полностью не осведомлен о событиях “входа пользователя” (или любом другом событии, не связанном с устройством, в общем). Вместо этого, после первоначальной обработки udev, доступ обрабатывается совершенно другим сервисом – конкретно, systemd-logind – который управляет взаимосвязями между пользователями и местами (сессиями) и использует libudev для перебора всех "uaccess"
– помеченных устройств и обновления их ACL для узлов устройств. Он не повторно выполняет никаких правил udev на этом этапе.
Проблема здесь в том, что systemd-logind делает это только после того, как он запустил менеджер сервисов пользователя (поскольку применение ACL является функцией ‘места’, то поток выполнения кода должен создать ‘пользователя’, затем сессию (пользователь, место), и только затем уведомить место о новой активной сессии), поэтому ваши сервисы на уровне пользователя, находящиеся в default.target
, скорее всего, никогда не увидят обновление ACL вовремя. (Я вижу, что Fluidsynth 2.x уже поставляется с собственным юнитом user/fluidsynth.service
; я не уверен, как это должно работать, но у меня есть подозрение, что это зависит от того, чтобы пользователь был в группе audio
и всегда имел доступ.)
Вам, возможно, придется запускать сервис другим целевым объектом (например, графической сессией), или добавить ExecStartPre=
, который просто будет зацикливаться до тех пор, пока у устройства не обновятся ACL.
ExecStartPre=/bin/sh -c "until [ -r /dev/snd/seq ]; do sleep 1; done"
Ответ или решение
Решение проблемы с запуском fluidsynth как пользовательского сервиса в Debian
Запуск сервиса fluidsynth в качестве пользовательского сервиса на системе Debian может вызывать проблемы с доступом к /dev/snd/seq
, что приводит к ошибкам и переходу сервиса в состояние "зомби". Это связано с тем, что доступ к данному устройству предоставляется только после выполнения правил udev, которые обрабатываются в процессе инициализации устройства и привязки прав доступа для текущей сессии пользователя.
Причины проблемы
Когда пользователь входит в систему, правила udev, такие как 70-uaccess.rules
, не перезапускаются, и доступ к устройствам не обновляется автоматически. Вместо этого доступ к устройствам после логина управляется сервисом systemd-logind, который обновляет права доступа к устройствам только после завершения создания пользовательской сессии. Это приводит к тому, что сервис может запускаться до того, как права доступа к устройству будут доступны, что вызывает ошибку "Permission denied".
Решение
1. Настройка зависимостей в сервисе systemd
Вы можете попробовать использовать зависимости от соответствующего .device
юнита, но, как было отмечено, это может не всегда помочь в данном контексте. Однако, если такая возможность представляется, добавьте в ваш файл юнита (например, fluidsynth.service
) следующие строки:
[Unit]
Requires=dev-snd-seq.device
After=dev-snd-seq.device
2. Обработка прав доступа к устройству
Если .device
юнит не создаётся автоматически, вы можете добавить правило udev, которое применяет тег TAG+="systemd"
к вашему устройству, чтобы система создала виртуальный юнит:
SUBSYSTEM=snd, KERNEL=seq, TAG+="systemd"
Однако, главная проблема с данной подходом заключается в том, что это могут быть только временные решения, так как systemd-logind обновляет права доступа, только если сессия пользователя уже была инициализирована.
3. Запуск услуги после обновления прав доступа
Ещё одним решением может быть добавление предварительной команды запуска, которая будет проверять наличие доступности устройства в системе:
[Service]
ExecStartPre=/bin/sh -c "until [ -r /dev/snd/seq ]; do sleep 1; done"
Этот подход гарантирует, что ваш сервис fluidsynth
не будет запущен, пока не будет подтвержден доступ к устройству, что поможет избежать ошибок и состояния "зомби".
4. Использование другой цели для запуска
Вы также можете рассмотреть возможность запуска вашего сервиса через другую цель, такую как graphical-session
. Это позволит гарантировать, что вы получите необходимые права доступа до запуска вашего сервиса.
Заключение
Проблема доступа вашего сервиса fluidsynth
к /dev/snd/seq
на системе Debian связана с процессами инициализации и управления правами доступа к устройствам в системе. Применяя приведённые выше рекомендации, вы сможете успешно настроить сервис, обеспечивая его нормальную работу. Если проблема продолжает возникать, рассмотрите возможность изучения альтернативных решений для управления звуковыми устройствами или использования других подходов к настройке сервиса.