udev para executar o script Python

6

Estou tentando executar automaticamente o este script, sempre que me conecto ao meu bluetooth fone de ouvido.

Eu criei o arquivo /etc/udev/rules.d/80-bt-headset.rules com a linha

ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50"

mas não faz nada. As condições são boas, um comando de teste simples é acionado quando eu insiro isso. O script em si também funciona bem quando executado manualmente.

O que está errado aqui?

Atualizar : há um erro ao executar o script com sudo -u USER (veja abaixo os detalhes). Este poderia ser o problema? E como o sudo-ing para o mesmo usuário quebra as coisas?

Atualização 2 : Após substituir todas as instâncias de pacmd por pactl em a2dp.py (e substituir list-sinks por list sinks para torná-lo um comando pactl válido), sudo -u USER funciona, no entanto, a regra do udev ainda não funciona. Em /var/log/syslog , acabei de ver a linha

systemd-udevd[32629]: Process '/home/USER/.local/bin/a2dp_2.py 00:22:37:3D:DA:50' failed with exit code 1.

Atualização 3 (Solução) : O script modificado (pacmd - > pactl, ver Atualização 2) com as variáveis de ambiente DISPLAY=:0 e XAUTHORITY=/home/USER/.Xauthority fez o truque. A regra do udev:

ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" ENV{DISPLAY}=":0" ENV{XAUTHORITY}="/home/USER/.Xauthority" RUN+="/home/USER/.local/bin/a2dp_2.py 00:22:37:3D:DA:50"

está funcionando conforme o esperado.

(Agora, o único problema remanescente é que o próprio script acionará a regra, pois reconecta o fone de ouvido, resultando em um loop infinito. No entanto, essa é uma pergunta separada, e não deve ser muito difícil encontrar um Na verdade, eu estava esperando esse comportamento quando iniciei este segmento.)

O que funciona:

  1. As condições são boas: a linha:

    ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/bin/mkdir /tmp/testme"
    

    criará um novo diretório quando eu conectar ao fone de ouvido.

  2. O script a2dp.py funciona bem quando executado a partir do terminal via

    /home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50
    
  3. Executando simplesmente um script Python via udev:

    ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/home/USER/.local/bin/atestscript.py"
    

    em que atestscript.py contém:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    import subprocess
    
    def main():
        subprocess.Popen(['mkdir', '/tmp/atestdir'])
    
    if __name__ == '__main__':
        main()
    

    criará novamente uma pasta quando o dispositivo estiver conectado.

O que funciona depois de substituir o pacmd pelo pactl:

  1. A execução do script a partir do terminal com sudo -u USER ou mesmo sudo -u root funciona agora como pretendido (Para o script original, isso resultou em:

    USER@MACHINE:~$ sudo -u USER /usr/local/bin/a2dp.py 00:22:37:3D:DA:50
    Connection MADE
    Device MAC: 00:22:37:3D:DA:50
    Command: pacmd list-sinks failed with status: 1
    stderr: No PulseAudio daemon running, or not running as session daemon.
    
    Exiting bluetoothctl
    

O que não funciona:

  1. Executando o script como acima ou com qualquer uma das seguintes linhas como RUN+= part:

    /usr/bin/sudo -u USER /usr/bin/python3 /home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50
    /usr/bin/sudo -u USER /home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50
    /usr/bin/python3.5 /usr/local/bin/a2dp.py 00:22:37:3D:DA:50
    ENV{DISPLAY}=":0" RUN+="/usr/local/bin/a2dp.py 00:22:37:3D:DA:50"
    

    Até mesmo o script modificado não funcionará:

    ENV{DISPLAY}=":0" ENV{PULSE_RUNTIME_PATH}="/run/user/1000/pulse/" RUN+="sudo -u USER /home/USER/.local/bin/a2dp_2.py 00:22:37:3D:DA:50"
    

Mais informações: udevadm monitora a saída na conexão ao fone de ouvido:

KERNEL[104388.664737] add      /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
UDEV  [104388.667185] add      /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
KERNEL[104390.848157] add      /devices/virtual/input/input46 (input)
UDEV  [104390.849150] add      /devices/virtual/input/input46 (input)
KERNEL[104390.849471] add      /devices/virtual/input/input46/event17 (input)
UDEV  [104390.864692] add      /devices/virtual/input/input46/event17 (input)
    
por hife 30.04.2017 / 17:13

1 resposta

4

Minha solução de trabalho

  1. Modifique a2dp.py substituindo todas as instâncias de pacmd por pactl adjusting pacmd list-sinks a pactl list sinks (no meu caso, salvo como /usr/local/bin/a2dp_2.sh ).

  2. Crie um script de wrapper /usr/local/bin/a2dp-wrapper.sh

    #!/bin/bash
    
    MAC=$1
    MACMOD=$(echo $MAC | sed 's/:/_/g')
    
    PID=$(pgrep pulseaudio)
    USER=$(grep -z USER= /proc/$PID/environ | sed 's/.*=//')
    
    export DISPLAY=:0
    export XAUTHORITY=/home/$USER/.Xauthority
    
    if pactl list sinks short | grep "bluez_sink\.$MACMOD.*SUSPENDED" 
        then
        sudo -u $USER /usr/local/bin/a2dp_2.py $MAC
    fi
    
  3. Adicione a seguinte linha a /etc/udev/rules.d/80-bt-headset.rules :

    ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/usr/local/bin/a2dp-wrapper.sh $attr{name}"
    

Este script de wrapper realiza o seguinte:

  1. Ele descobre que $USER possui a instância em execução de pulseaudio, depois define as variáveis de ambiente DISPLAY=:0 e XAUTHORITY=/home/$USER/.Xauthority necessárias para que pactl funcione. Isso deve fazer com que funcione para todos os usuários em uma máquina. (Eu não testei os efeitos de vários usuários conectados ao mesmo tempo.)

  2. Verifica se o coletor correspondente está suspenso e só então executa a2dp_2.py . Isso é necessário para evitar um loop infinito causado por a2dp_2.py reconectando o dispositivo e, assim, acionando a regra.

  3. Ele executa a2dp_2.py como $ USER. Se for executado como root, a2dp_2.py deixará o pulseaudio e, portanto, qualquer configuração de áudio, inacessível sem privilégios de root.

Alternativas: loop dbus / pacote fixo

  1. Uma solução alternativa usando um loop dbus pode ser encontrada na página do desenvolvedor do sript .

  2. Uma correção para o bug original já está disponível aqui e pode ser facilmente instalado adicionando ppa:ubuntu-audio-dev/pulse-testing e atualizando os pacotes disponíveis.

Dica: encontrar o endereço MAC do seu dispositivo

Não é estritamente parte do problema original, mas isso pode ser útil para referência futura. Existem várias maneiras de encontrar o endereço MAC do seu dispositivo. O que segue é o que considero mais útil para as regras do udev:

  1. Encontre o caminho do dispositivo executando udevadm monitor e conectando seu dispositivo. Sua saída deve ser algo como isto:

    USER@MACHINE:~$ udevadm monitor
    monitor will print the received events for:
    UDEV - the event which udev sends out after rule processing
    KERNEL - the kernel uevent
    
    KERNEL[123043.617276] add      /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
    UDEV  [123043.647291] add      /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
    KERNEL[123044.153776] add      /devices/virtual/input/input68 (input)
    KERNEL[123044.153911] add      /devices/virtual/input/input68/event17 (input)
    UDEV  [123044.193415] add      /devices/virtual/input/input68 (input)
    UDEV  [123044.213213] add      /devices/virtual/input/input68/event17 (input)
    

    Pare o monitor com Ctrl+C . Encontramos três caminhos de dispositivos. O que é relevante para nós é /devices/virtual/input/input68 .

  2. Conecte o caminho obtido em udevadm info :

    USER@MACHINE:~$ udevadm info -a -p /devices/virtual/input/input68
    
    Udevadm info starts with the device specified by the devpath and then
    walks up the chain of parent devices. It prints for every device
    found, all possible attributes in the udev rules key format.
    A rule to match, can be composed by the attributes of the device
    and the attributes from one single parent device.
    
      looking at device '/devices/virtual/input/input68':
        KERNEL=="input68"
        SUBSYSTEM=="input"
        DRIVER==""
        ATTR{name}=="00:22:37:3D:DA:50"
        ATTR{phys}==""
        ATTR{properties}=="0"
        ATTR{uniq}==""
    

    Aprendemos que o endereço MAC é 00:22:37:3D:DA:50 e também que é armazenado como ATTR{name} .

Mesmo que a saída pareça completamente diferente, esses dois comandos serão um bom começo para procurar as condições relevantes para uma regra do udev.

Experimental: captura de dispositivos de áudio Bluetooth arbitrários

A regra:

ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="??:??:??:??:??:??" RUN+="/usr/local/bin/a2dp-wrapper.sh $attr{name}"

será acionado para qualquer dispositivo de entrada que tenha um atributo de nome que se pareça com um endereço MAC, e o condicional no script de wrapper deve garantir que nenhuma ação indesejada seja tomada.

Eu não tenho nenhum outro dispositivo de áudio Bluetooth disponível para testar isso, mas vejo diversos problemas em potencial:

  1. Isso funcionará apenas para um dispositivo Bluetooth reconhecido como um dispositivo de entrada contendo o endereço MAC no atributo name. Nem todo dispositivo pode ser reconhecido como tal.

  2. Esta solução não é muito elegante, pois a regra será acionada para qualquer dispositivo de entrada. No entanto, não consegui encontrar indicadores claros para identificar um dispositivo de áudio bluetooth. (Como visto acima, o dispositivo de entrada não tem atributos adicionais, e o dispositivo bluetooth não mostra nenhuma indicação de ser um dispositivo de áudio, nem contém o endereço MAC. Talvez o ACPI seja melhor para isso.)

  3. Você pode não querer tratar todos os dispositivos de áudio bluetooth da mesma forma: você pode usar o protocolo HSP para o seu fone de ouvido ou pode não querer alternar automaticamente para os alto-falantes do seu housemate, emparelhados algum ponto, sempre que estiverem disponíveis. Nesses casos, é preferível ter uma regra separada para cada dispositivo.

Vou continuar atualizando esta postagem enquanto aprendo mais.

    
por hife 07.05.2017 / 20:33