Comment exécuter un shellscript lorsque je branche un périphérique USB

28

Je veux exécuter un script lorsque je branche un périphérique sur ma machine Linux. Par exemple, exécutez xinputsur la souris ou un script de sauvegarde sur un certain lecteur.

J'ai vu beaucoup d'articles à ce sujet, plus récemment ici et ici . Mais je n'arrive pas à le faire fonctionner.

Voici quelques exemples simples essayant d'obtenir au moins une sorte de réponse.

/etc/udev/rules.d/test.rules

#KERNEL=="sd*", ATTRS{vendor}=="*", ATTRS{model}=="*", ATTRS{serial}=="*", RUN+="/usr/local/bin/test.sh"
#KERNEL=="sd*", ACTION=="add", "SUBSYSTEM=="usb", ATTRS{model}=="My Book 1140    ", ATTRS{serial}=="0841752394756103457194857249", RUN+="/usr/local/bin/test.sh"
#ACTION=="add", "SUBSYSTEM=="usb", RUN+="/usr/local/bin/test.sh"
#KERNEL=="sd*", ACTION=={add}, RUN+="/usr/local/bin/test.sh"
KERNEL=="sd*", RUN+="/usr/local/bin/test.sh"
KERNEL=="*", RUN+="/usr/local/bin/test.sh"

/usr/local/bin/test.sh

#!/usr/bin/env bash
echo touched >> /var/log/test.log

if [ "${ACTION}" = "add" ] && [ -f "${DEVICE}" ]
then
    echo ${DEVICE} >> /var/log/test.log
fi

Le dossier des règles est surveillé par inotifyet doit être actif immédiatement. Je continue de rebrancher mon clavier, ma souris, ma tablette, mon memorystick et ma clé USB, mais rien. Aucun fichier journal touché.

Maintenant, quelle serait la façon la plus simple de savoir au moins que quelque chose fonctionne? Il est plus facile de travailler à partir de quelque chose qui fonctionne que de quelque chose qui ne fonctionne pas.

Redsandro
la source
1
Vous ne vouliez pas publier sur Unix et Linux ? Quelle est votre version du noyau? Avez-vous exécuté udevadm triggerou branché un appareil pour appliquer la nouvelle règle?
Gilles 'SO- arrête d'être méchant'
Oui, je le fais après chaque modification des règles pour les essayer. J'ai modifié la question en conséquence. C'est ainsi que udev fonctionne depuis un moment maintenant, mais je cours 3.5.0-23-generic.
Redsandro

Réponses:

24

Si vous souhaitez exécuter le script sur un appareil spécifique, vous pouvez utiliser les ID de fournisseur et de produit

  • Dans /etc/udev/rules.d/test.rules:

    ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
  • dans test.sh:

    #! /bin/sh
    
    env >>/tmp/test.log
    file "/sys${DEVPATH}" >>/tmp/test.log
    
    if [ "${ACTION}" = add -a -d "/sys${DEVPATH}" ]; then
    echo "add ${DEVPATH}" >>/tmp/test.log
    fi
    

Avec env, vous pouvez voir quel environnement est défini depuis udev et avec file, vous découvrirez le type de fichier.

Les attributs concrets de votre appareil peuvent être découverts avec lsusb

lsusb

donne

...
Bus 001 Périphérique 016: ID 152d: 2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA Bridge
...

Olaf Dietsche
la source
1
C'est intéressant! Il semble qu'il n'ait pas la permission d'écrire dans / log /. Il fait écrire à / tmp /. Je suppose qu'il n'avait pas non plus la permission de lire mes scripts de test précédents.
Redsandro
@Redsandro Ce n'était pas intentionnel, juste à des fins de test. Quoi qu'il en soit, je suis content que cela ait aidé. ;-)
Olaf Dietsche
Je vous encourage à consulter également cette question et à voir si vos connaissances peuvent y être utiles. :)
Redsandro
3
Vous pouvez également ajouter ACTION=="add",directement à la définition de règle.
Avindra Goolcharan
4

Il ne s'agit pas directement de votre question mais de ce que vous faites. Si vous démarrez un script de sauvegarde depuis udev, vous serez confronté à deux problèmes principaux:

  1. Votre scrpit peut être démarré avant que le périphérique ne soit prêt et puisse être monté, vous devez conserver la condition KERNEL == "sd *" si vous souhaitez utiliser le nœud / dev pour le monter
  2. Plus important encore, si votre scirpt prend un certain temps à s'exécuter (ce qui peut facilement être le cas avec un script de sauvegarde), il sera tué peu de temps après son démarrage (environ 5 s)
  3. Vous rencontrerez de nombreux problèmes de permission utilisateur compliqués

Mon conseil est de créer un script dans votre home utilisateur qui écoutera un canal nommé et qui sera démarré de manière asynchrone comme:

#!/bin/bash

PIPE="/tmp/IomegaUsbPipe"
REMOTE_PATH="/path/to/mount/point"
LOCAL_PATH="/local/path/"


doSynchronization()
{
  #your backup here
}

trap "rm -f $PIPE" EXIT

#If the pipe doesn't exists, create it
if [[ ! -p $PIPE ]]; then
    mkfifo $PIPE
fi

#If the disk is already plugged on startup, do a syn
if [[ -e "$REMOTE_PATH" ]]
then
    doSynchronization
fi

#Make the permanent loop to watch the usb connection
while true
do
    if read line <$PIPE; then
        #Test the message red from the fifo
        if [[ "$line" == "connected" ]]
        then
            #The usb has been plugged, wait for disk to be mounted by KDE
            while [[ ! -e "$REMOTE_PATH" ]]
            do
                sleep 1
            done
            doSynchronization
        else
            echo "Unhandled message frome fifo : [$line]"
        fi
    fi
done
echo "Reader exiting"

Remarque: J'utilise le montage automatique avec kde donc je vérifie que le dossier apparaît. Vous pouvez passer le paramètre / dev / sd * dans le fifo à partir de la règle udev et le monter vous-même dans le script. Pour écrire dans le fifo, n'oubliez pas que udev n'est pas un shell et que la redirection ne fonctionne pas. Votre RUN doit ressembler à:

RUN + = "/ bin / sh -c '/ bin / echo connecté >> / tmp / IomegaUsbPipe'"

Olivier Laporte
la source
Grande utilisation des tuyaux nommés ici. Je me demandais si vous pouviez également créer un fichier arbitraire dans tmp et le rechercher également au lieu d'un canal nommé, n'est-ce pas?
jamescampbell
1

J'ai publié une solution sur /ubuntu//a/516336 et je copie également la solution ici.

J'ai écrit un script Python en utilisant pyudev que je laisse tourner en arrière-plan. Ce script écoute les événements udev (donc il est très efficace) et exécute le code que je veux. Dans mon cas, il exécute des xinputcommandes pour configurer mes appareils ( lien vers la version la plus récente ).

Voici une version courte du même script:

#!/usr/bin/env python3

import pyudev
import subprocess

def main():
    context = pyudev.Context()
    monitor = pyudev.Monitor.from_netlink(context)
    monitor.filter_by(subsystem='usb')
    monitor.start()

    for device in iter(monitor.poll, None):
        # I can add more logic here, to run different scripts for different devices.
        subprocess.call(['/home/foo/foobar.sh', '--foo', '--bar'])

if __name__ == '__main__':
    main()
Denilson Sá Maia
la source
1
On dirait un joli script, +1. Une chose que je suggère d'utiliser la liste au lieu d'une seule chaîne call(). De cette façon, s'il est nécessaire de fournir des arguments au foobar.shscript, vous pouvez le faire dynamiquement.
Sergiy Kolodyazhnyy
1
Bon point. Mon "vrai" script (lié à la réponse) utilise une liste. Dans cette version minimaliste que j'ai collée ici, j'ai foiré et utilisé accidentellement une chaîne. Merci! J'ai mis à jour la réponse.
Denilson Sá Maia,
-1

Pour exécuter le script au démarrage lorsque le périphérique USB est inséré, j'utilise la solution ci-dessous:

Formatez la clé USB ou tout autre stockage USB et donnez-lui un nom lors de cette opération. Puis dans la /etc/rc.local ligne d'ajoutls -q /dev/disk/by-label > /home/pi/label.txt

il créera un fichier txt appelé label.txt (peut être tout autre nom)

puis à nouveau dans /etc/rc.local, ajoutez 2 autres lignes:

if  grep -q USB_drive_name /home/pi/label.txt; then
sudo /home/pi/script.sh

Maintenant, chaque fois qu'une clé USB portant le nom USB_drive_name est insérée, le script s'exécute.

Avec quelques petites modifications ci-dessus, la solution peut être utilisée lorsque le système est opérationnel.

Aleksander Celewicz
la source
Ne répond pas à la question: cela ne couvre que le temps de démarrage (et l'utilisation udevpour d'autres moments ne sont pas «quelques petites modifications») et le Raspberry Pi. Il existe un problème inutile sudo- rc.locals'exécute en tant que root, c'est un problème d'élévation de privilèges - un fichier modifiable par un utilisateur normal est exécuté en tant que root.
Gert van den Berg