Come eseguire uno shellscript quando collego un dispositivo USB


28

Voglio eseguire uno script quando collego un dispositivo nella mia macchina Linux. Ad esempio, esegui con xinputil mouse o uno script di backup su una determinata unità.

Ho visto molti articoli su questo, più recentemente qui e qui . Ma non riesco proprio a farlo funzionare.

Ecco alcuni semplici esempi che cercano di ottenere almeno un tipo di risposta.

/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

La cartella delle regole è controllata da inotifye dovrebbe essere immediatamente attiva. Continuo a sostituire tastiera, mouse, tablet, memory stick e unità USB, ma niente. Nessun file di registro toccato.

Ora, quale sarebbe il modo più semplice per sapere almeno qualcosa che funziona? È più facile lavorare da qualcosa che funziona piuttosto che da qualcosa che non lo è.


1
Non intendevi pubblicare su Unix e Linux ? Qual è la tua versione del kernel? Hai eseguito udevadm triggero collegato un dispositivo per applicare la nuova regola?
Gilles 'SO- smetti di essere malvagio' il

Sì, lo faccio dopo ogni modifica delle regole per provarle. Ho modificato la domanda di conseguenza. Questo è il modo in cui udev funziona da un po 'di tempo, ma sto correndo 3.5.0-23-generic.
Redsandro,

Risposte:


24

Se si desidera eseguire lo script su un dispositivo specifico, è possibile utilizzare gli ID fornitore e prodotto

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

    ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
  • in 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

Con env, puoi vedere quale ambiente è impostato da udev e con file, scoprirai il tipo di file.

Gli attributi concreti per il tuo dispositivo possono essere scoperti con lsusb

lsusb

...
Bus 001 Dispositivo 016: ID 152d: 2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 Bridge SATA
...


1
Questo è interessante! Sembra che non abbia il permesso di scrivere su / log /. Si fa in scrittura a / tmp /. Suppongo che non avesse nemmeno il permesso di leggere i miei precedenti script di test.
Redsandro,

@Redsandro Questo non era intenzionale, solo per scopi di test. Ad ogni modo, sono contento che abbia aiutato. ;-)
Olaf Dietsche,

Vorrei incoraggiarti a dare un'occhiata anche a questa domanda e vedere se le tue conoscenze possono essere preziose lì. :)
Redsandro,

3
Puoi anche aggiungere ACTION=="add",direttamente alla definizione della regola.
Avindra Goolcharan,

4

Non si tratta direttamente della tua domanda, ma di quello che stai facendo. Se avvii uno script di backup da udev dovrai affrontare due problemi principali:

  1. Lo scrpit potrebbe essere avviato prima che il dispositivo sia pronto e possa essere montato, è necessario mantenere la condizione KERNEL == "sd *" se si desidera utilizzare il nodo / dev per montarlo
  2. Ancora più importante, se l'esecuzione del tuo script richiede del tempo (che può essere facilmente il caso di uno script di backup) verrà ucciso poco dopo l'avvio (circa 5 secondi)
  3. Dovrai affrontare molti complicati problemi con le autorizzazioni degli utenti

Il mio consiglio è di creare uno script nella home dell'utente che ascolti una pipe denominata e che verrà avviato in modo asincrono come:

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

Nota: utilizzo auto-mount con kde, quindi controllo la visualizzazione della cartella. Puoi passare il parametro / dev / sd * nel file fifo dalla regola udev e montarlo tu stesso nello script. Per scrivere nel Fifo non dimenticare che udev non è una shell e che il reindirizzamento non funziona. Il tuo RUN dovrebbe essere come:

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


Grande uso delle pipe nominate qui. Mi chiedevo che potresti anche creare un file arbitrario in tmp e cercarlo anche al posto di una pipe denominata, giusto?
Jamescampbell,

1

Ho pubblicato una soluzione su /ubuntu//a/516336 e sto anche copiando e incollando la soluzione qui.

Ho scritto uno script Python usando pyudev che lascio correre in background. Quello script ascolta gli eventi udev (quindi, è molto efficiente) ed esegue tutto il codice che voglio. Nel mio caso, esegue i xinputcomandi per configurare i miei dispositivi ( collegamento alla versione più recente ).

Ecco una versione breve dello stesso 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()

1
Sembra una bella sceneggiatura, +1. Una cosa che suggerirei è usare list invece di una sola stringa call(). In questo modo, se è necessario fornire argomenti allo foobar.shscript, è possibile farlo in modo dinamico.
Sergiy Kolodyazhnyy il

1
Punto valido. Il mio script "reale" (collegato dalla risposta) utilizza un elenco. In questa versione minimalista ho incollato qui, ho incasinato e usato accidentalmente una stringa. Grazie! Ho aggiornato la risposta.
Denilson Sá Maia,

-1

Per eseguire lo script all'avvio quando viene inserito il dispositivo USB, utilizzo la soluzione di seguito:

Formatta il pendrive o qualsiasi altro dispositivo di archiviazione USB e dagli un nome quando lo fai. Quindi in /etc/rc.local Aggiungi rigals -q /dev/disk/by-label > /home/pi/label.txt

creerà un file txt chiamato label.txt (può essere qualsiasi altro nome)

poi di nuovo in /etc/rc.local aggiungi altre 2 righe:

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

Ora ogni volta che pendrive con il nome USB_drive_name viene inserito, verrà eseguito lo script.

Con alcune piccole modifiche sopra la soluzione può essere utilizzata quando il sistema è attivo e funzionante.


Non risponde alla domanda: questo copre solo il tempo di avvio (e l'utilizzo udevper altre volte non sono "poche piccole modifiche") e il Raspberry Pi. Non è necessario sudo: rc.localviene eseguito come root, si tratta di un problema di escalation dei privilegi: un file modificabile da un utente normale viene eseguito come root.
Gert van den Berg,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.