web-dev-qa-db-fra.com

règle udev pour exécuter le script Python

J'essaie d'exécuter automatiquement le script this chaque fois que je me connecte à mon casque Bluetooth.

J'ai créé le fichier /etc/udev/rules.d/80-bt-headset.rules avec la ligne

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"

mais ça ne fait rien. Les conditions sont bonnes, un simple test de commande est déclenché lorsque je l'entre à la place. Le script lui-même fonctionne également bien lorsqu'il est exécuté manuellement.

Qu'est-ce qui ne va pas ici?

Update : Il y a une erreur lors de l'exécution du script avec Sudo -u USER (voir ci-dessous pour plus de détails). Est-ce que ceci pourrait être le problème? Et comment est-ce que Sudo-ing, chez le même utilisateur, casse les choses?

Update 2 : Après avoir remplacé toutes les instances de pacmd par pactl dans a2dp.py (et remplacé par list-sinks avec list sinks pour en faire une commande pactl valide), Sudo -u USER fonctionne, mais la règle udev ne fonctionne toujours pas. Dans /var/log/syslog je viens de voir la ligne

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

Update 3 (Solution) : Le skript modifié (pacmd -> pactl, voir Update 2) avec les variables d'environnement DISPLAY=:0 et XAUTHORITY=/home/USER/.Xauthority a fait le tour. La règle 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"

fonctionne comme prévu.

(Le seul problème qui reste est que le script lui-même déclenchera la règle, car il reconnectera le casque, ce qui produira une boucle infinie. Cependant, cette question est distincte et il ne devrait pas être trop difficile de trouver une solution de contournement. fait je m'attendais à ce comportement quand j'ai commencé ce fil.)

Ce qui fonctionne:

  1. Les conditions sont bonnes: La ligne:

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

    créera un nouveau répertoire lorsque je me connecterai au casque.

  2. Le script a2dp.py lui-même fonctionne bien lorsqu'il est exécuté à partir du terminal via

    /home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50
    
  3. Lancer un script simplement Python via udev:

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

    où atestscript.py contient:

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

    créera à nouveau un dossier lorsque l'appareil est connecté.

Qu'est-ce qui fonctionne après le remplacement de pacmd par pactl:

  1. L'exécution du script à partir du terminal avec Sudo -u USER ou même Sudo -u root fonctionne désormais comme prévu (pour le script d'origine, cela s'est traduit par:

    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
    

Ce qui ne marche pas:

  1. Exécuter le script comme ci-dessus ou avec l'une des lignes suivantes comme partie RUN+=:

    /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"
    

    Même le script modifié ne fonctionnera pas:

    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"
    

Informations complémentaires: Sortie du moniteur udevadm lors de la connexion au casque:

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)
5
hife

Ma solution de travail

  1. Modifiez a2dp.py en remplaçant toutes les instances de pacmd par pactl en ajustant pacmd list-sinks à pactl list sinks (dans mon cas, enregistré sous le nom /usr/local/bin/a2dp_2.sh).

  2. Créer un 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. Ajoutez la ligne suivante à /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}"
    

Ce script de wrapper accomplit les tâches suivantes:

  1. Il découvre le $USER possédant l'instance en cours de pulseaudio, puis définit les variables d'environnement DISPLAY=:0 et XAUTHORITY=/home/$USER/.Xauthority nécessaires au fonctionnement de pactl. Cela devrait le faire fonctionner pour tous les utilisateurs sur une machine. (Je n'ai pas testé les effets de plusieurs utilisateurs connectés en même temps.)

  2. Il vérifie si le récepteur correspondant est suspendu et ne lance alors que a2dp_2.py. Cela est nécessaire pour éviter une boucle infinie causée par a2dp_2.py reconnexion du périphérique et ainsi le déclenchement de la règle.

  3. Il exécute a2dp_2.py comme $ USER. S'il est exécuté en tant que root, a2dp_2.py laissera pulseaudio, et donc tous les paramètres audio, inaccessibles sans privilèges root.

Alternatives: boucle dbus/package fixe

  1. Une solution alternative utilisant une boucle dbus peut être trouvée sur le page du développeur de sript .

  2. Un correctif pour le bogue original est maintenant disponible ici et peut être facilement installé en ajoutant ppa:ubuntu-audio-dev/Pulse-testing et en mettant à jour les paquetages disponibles.

Astuce: trouver l'adresse MAC de votre appareil

Ne fait pas strictement partie du problème initial, mais cela pourrait être utile pour référence future. Il existe de nombreuses façons de trouver l'adresse MAC de votre appareil. Ce qui suit est celui que j’estime le plus utile pour les règles udev:

  1. Recherchez le chemin du périphérique en exécutant udevadm monitor puis en connectant votre périphérique. Votre sortie devrait ressembler à ceci:

    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)
    

    Arrêtez le moniteur avec Ctrl+C. Nous avons trouvé trois chemins de périphérique. Celui qui nous intéresse est /devices/virtual/input/input68.

  2. Branchez le chemin obtenu dans 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}==""
    

    Nous apprenons que l'adresse MAC est 00:22:37:3D:DA:50 et aussi qu'elle est stockée sous le nom ATTR{name}.

Même si le résultat est complètement différent, ces deux commandes seront un bon début pour rechercher les conditions pertinentes pour une règle udev.

Expérimental: Capture de périphériques audio Bluetooth arbitraires

La règle:

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

déclenchera pour tout périphérique d'entrée ayant un attribut de nom qui ressemble à une adresse MAC et la condition dans le script d'encapsidation doit s'assurer qu'aucune action involontaire n'est entreprise.

Je ne dispose d'aucun autre périphérique audio bluetooth pour le tester, mais je vois un certain nombre de problèmes potentiels:

  1. Cela ne fonctionnera que pour un périphérique Bluetooth reconnu comme périphérique d'entrée contenant l'adresse MAC dans l'attribut name. Tous les appareils ne peuvent pas être reconnus comme tels.

  2. Cette solution n’est pas très élégante, car la règle sera déclenchée pour tout périphérique d’entrée. Cependant, je n'ai pas été en mesure de trouver des indicateurs clairs permettant d'identifier un périphérique audio Bluetooth. (Comme indiqué ci-dessus, le périphérique d'entrée n'a pas d'attribut supplémentaire et le périphérique Bluetooth n'indique pas qu'il s'agit d'un périphérique audio et ne contient pas non plus l'adresse MAC. Peut-être qu'ACPI serait préférable pour cela.)

  3. Vous voudrez peut-être ne pas traiter tous les périphériques audio Bluetooth de la même manière: vous voudrez peut-être utiliser le protocole HSP pour votre oreillette ou ne pas vouloir basculer automatiquement sur les haut-parleurs de votre colocataire, avec lesquels vous êtes jumelés à un moment donné, chaque fois qu'ils le sont. disponible. Dans ces cas, il est probablement préférable d’avoir une règle distincte pour chaque périphérique.

Je continuerai à mettre à jour ce post à mesure que j'apprends.

3
hife