LINUX.ORG.RU

Как стартовать pulseaudio из udev?

 , , , ,


0

1

Хочу чтобы при подключении к ноуту телевизора через hdmi, вывод звука(и изображения, но с этим всё нормально) переключался на телик.

Что я сделал:
1) cat /etc/udev/rules.d/hdmi.rules:

SUBSYSTEM=="drm", RUN+="/usr/local/bin/hdmi_toggle.sh"

2) cat /usr/local/bin/hdmi_toggle.sh:
#!/bin/sh

sleep 4
#USER_NAME=`who | grep "(:0)" | cut -f 1zx -d ' '| sort -u`
#USER_NAME=`who | cut -f 1 -d ' '`
USER_NAME='username'
USER_ID=$(id -u "$USER_NAME")
HDMI_STATUS=$(cat /sys/class/drm/card0/*HDMI*/status)
SIGNAL_DIR='/tmp/_out_to_hdmi'
export PULSE_RUNTIME_PATH='/run/user/'"$USER_ID"'/pulse'
PULSE_SERVER="unix:${PULSE_RUNTIME_PATH}/native"
export DISPLAY=':0'

if [ xconnected = x"$HDMI_STATUS" ]; then
    echo "hdmi_connected" >> /tmp/_out_to_hdmi.log
    if [ ! -d "$SIGNAL_DIR" ]; then
        echo "signal_dir does not exists" >> /tmp/_out_to_hdmi.log
        mkdir "$SIGNAL_DIR"

        sudo -u "$USER_NAME" xrandr --output HDMI1 --mode 1920x1080 --output LVDS1 --off
        sudo -u "$USER_NAME" xset s off -dpms
        sudo -u "$USER_NAME" xrdb -merge /home/"$USER_NAME"/.Xresources_tv

        # if [ ! -e "${PULSE_SERVER#*:}" ]; then
        #     # sudo -u "$USER_NAME" pulseaudio -k
        #     sudo -u "$USER_NAME" sh -c 'pulseaudio --start --log-target=syslog'
        # fi
        if [ -e "${PULSE_SERVER#*:}" ]; then
            sudo -u "$USER_NAME" pactl --server "$PULSE_SERVER" set-card-profile 0 output:hdmi-stereo+input:analog-stereo
        else
            sudo -u "$USER_NAME" pactl set-card-profile 0 output:hdmi-stereo+input:analog-stereo
        fi
    fi
else
    echo "hdmi_disconnected" >> /tmp/_out_to_hdmi.log
    if [ -d "$SIGNAL_DIR" ]; then
        echo "signal_dir does exists" >> /tmp/_out_to_hdmi.log
        rm -rf "$SIGNAL_DIR"

        sudo -u "$USER_NAME" xrandr --output LVDS1 --mode 1366x768 --output HDMI1 --off
        sudo -u "$USER_NAME" xset s on +dpms
        sudo -u "$USER_NAME" xrdb -merge /home/"$USER_NAME"/.Xresources

        if [ -e "${PULSE_SERVER#*:}" ]; then
            sudo -u "$USER_NAME" pactl --server "$PULSE_SERVER" set-card-profile 0 output:analog-stereo+input:analog-stereo
        else
            sudo -u "$USER_NAME" pactl set-card-profile 0 output:analog-stereo+input:analog-stereo
        fi
    fi
fi

Это работает, если пульсеаудио уже запущен, а вот если он не запущен, то скрипт зависает.
Сначало было это:
SELinux is preventing /usr/libexec/rtkit-daemon from using the setsched access on a process. For complete SE
мар 10 20:19:50 localhost.localdomain python[1586]: SELinux is preventing /usr/libexec/rtkit-daemon from using the setsched access on a process.

*****  Plugin catchall (100. confidence) suggests   **************************

If you believe that rtkit-daemon should be allowed setsched access on processes labeled udev_t by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# grep rtkit-daemon /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp

Эта строчка помогла:
grep rtkit-daemon /var/log/audit/audit.log | audit2allow -M mypol
semodule -i mypol.pp

Теперь же в момент вызова pactl set-card-profile появляются 3 процесса pulseaudio и скрипт зависает пока их не грохнешь, звука нет при этом. Тоже самое если запустить из скрипта пульсу так: sudo -u "$USER_NAME" pulseaudio --start #или -D

★★★★

sleep 4

запуск long-lived процессов из контекста udev

Дальше не читал. Не хочешь сделать по-нормальному? Версия федоры какая?

intelfx ★★★★★ ()
Ответ на: комментарий от intelfx

sleep 4
запуск long-lived процессов из контекста udev

4 секунды, это не лонг-ливед, вроде. Да и этот слип можно убрать — результат одинаковый(только иногда экран почему-то мигает(т.е. как-будто переключается туда-обратно, событий удева там куча генерится и их нельзя различить толком), по-идее так быть не должно благодаря тому что создание директории — атомарная операция(лог подтверждает)).
Это всё делается без DE, gdm не запускаю, иксы запускаю с помощью startx).

Не хочешь сделать по-нормальному?

по-нормальному — это написать юнит системд? Думал об этом, но этот юнит будет всё равно запускать этот же скрипт. Что-то у меня большие сомнения, что что-то изменится.

Версия федоры какая?

22, вроде. Вобщем та, которая уже не самая новая, но для которой обновления ещё клепают.

Ладно) на выходных попробую юнит написать и напишу чего там как.

Bad_ptr ★★★★ ()
Ответ на: комментарий от Bad_ptr

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

Bad_ptr ★★★★ ()
Последнее исправление: Bad_ptr (всего исправлений: 1)
Ответ на: комментарий от Bad_ptr

4 секунды, это не лонг-ливед, вроде

Не. Long-lived — это автоспавнящаяся пульса. А слип просто выглядит как костыль.

Во-первых, сейчас в апстриме пульсы её рекомендуют запускать через systemd --user, т. е. именно что автоматически при первом логине пользователя, а автоспавн не использовать. Хотя как с этим дело в 22-й федоре, не знаю. В 23-й всё обстоит именно так.

Во-вторых, вместо возни с $PULSE_SERVER, $PULSE_RUNTIME_PATH и так далее должно быть достаточно просто установить XDG_RUNTIME_DIR=/run/user/<UID>.

В-третьих, мне кажется, что в самой пульсе должны быть какие-то средства автоматического переключения профилей ( AEP?) вместо ручного вызова pactl set-card-profile.

Ну и да, если запихнуть какие-то вещи в юнит, а потом делать ему start/stop, то можно получить идемпотентность без хаков с созданием директорий-«флагов».

intelfx ★★★★★ ()
Последнее исправление: intelfx (всего исправлений: 5)
Ответ на: комментарий от intelfx

Cделал пока так:

cat /etc/udev/hdmi.rules:

SUBSYSTEM=="drm", RUN+="/usr/bin/systemctl start --no-block myhdmi@username.service"

cat /etc/systemd/system/myhdmi@.service:
[Unit]
Description=Toggle HDMI output

[Service]
Type=simple
User=%i
Group=users
ExecStart=/usr/local/bin/hdmi_toggle.sh

cat /usr/local/bin/hdmi_toggle.sh:
#!/bin/sh

sleep 10
USER_NAME=`who | cut -f 1 -d ' '`
USER_HOME=/home/"$USER_NAME"
USER_ID=$(id -u "$USER_NAME")
HDMI_STATUS=$(cat /sys/class/drm/card0/*HDMI*/status)
SIGNAL_DIR="$USER_HOME"'/_out_to_hdmi'
XDG_RUNTIME_DIR=/run/user/"$USER_ID"
export XDG_RUNTIME_DIR
DISPLAY=':0'
export DISPLAY

echo "$(date)" >> "$USER_HOME"/_out_to_hdmi.log
if [ xconnected = x"$HDMI_STATUS" ]; then
    echo hdmi_connected >> "$USER_HOME"/_out_to_hdmi.log
    if [ ! -d "$SIGNAL_DIR" ]; then
        echo signal_dir does not exists >> "$USER_HOME"/_out_to_hdmi.log
        mkdir "$SIGNAL_DIR"

        xrandr --output HDMI1 --mode 1920x1080 --output LVDS1 --off
        xset s off -dpms
        xrdb -merge "$USER_HOME"/.Xresources_hdmi

        pactl set-card-profile 0 output:hdmi-stereo+input:analog-stereo
    fi
else
    echo hdmi_disconnected >> "$USER_HOME"/_out_to_hdmi.log
    if [ -d "$SIGNAL_DIR" ]; then
        echo signal_dir does exists >> "$USER_HOME"/_out_to_hdmi.log
        rm -rf "$SIGNAL_DIR"

        xrandr --output LVDS1 --mode 1366x768 --output HDMI1 --off
        xset s on +dpms
        xrdb -merge "$USER_HOME"/.Xresources

        pactl set-card-profile 0 output:analog-stereo+input:analog-stereo
    fi
fi



Работает.(можно и сто start/stop заморочится, но это уже не принципиально)

Посмотрел я как делать юзерский системд — ужаснулся. Слишком много придётся юнитов/таргетов писать чтобы всё было «правильно», да и то не факт что заработает(это по впечатлениям от читки Archwiki/systemd/User). К тому же ещё непонятно как со взаимодействием узерских и системных юнитов быть. Вот в этой задаче, например, подключение кабеля hdmi — это общесистемное явление и по-идее, насколько я понимаю, тут надо писать что-то типа такого:
cat /etc/systemd/system/hdmi_connected.target:
[Unit]
Description=Active if hdmi connected

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/mkdir /tmp/_hdmi_connected
ExecStop=/bin/rm -rf /tmp/_hdmi_connected

cat /etc/udev/hdmi.rules:
SUBSYSTEM=="drm", RUN+="/usr/local/bin/hdmi_toggle.sh"

cat /usr/local/bin/hdmi_toggle.sh:
HDMI_STATUS=$(cat /sys/class/drm/card0/*HDMI*/status)

if [ xconnected = x"$HDMI_STATUS" ]; then
    /usr/bin/systemctl start --no-block hdmi_connected.target
else
    /usr/bin/systemctl stop --no-block hdmi_connected.target
fi


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

А ещё вопрос как ты стартуешь иксы? Хотя это, конечно, уже отдельная история.
В общем спасибо за помощь, работает и ладно. Дальше буду копать уже когда обновлюсь на 23 федору.

Bad_ptr ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.