Jak wykonać skrypt powłoki, kiedy podłączam urządzenie USB

28

Chcę wykonać skrypt po podłączeniu urządzenia do komputera z systemem Linux. Na przykład uruchom xinputna myszy lub w kopii zapasowej na określonym dysku.

Widziałem wiele artykułów na ten temat, ostatnio tu i tutaj . Ale po prostu nie mogę go uruchomić.

Oto kilka prostych przykładów, które próbują uzyskać przynajmniej pewien rodzaj odpowiedzi.

/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

Folder reguł jest obserwowany inotifyi powinien być natychmiast aktywny. Ciągle ponownie instaluję klawiaturę, mysz, tablet, pamięć i dysk USB, ale nic. Nie dotknięto pliku dziennika.

Jaki byłby najprostszy sposób, aby przynajmniej wiedzieć, że coś działa? Łatwiej jest pracować z czegoś, co działa, niż z czegoś, co nie działa.

Redsandro
źródło
1
Czy nie chciałeś pisać na Unixie i Linuksie ? Jaka jest twoja wersja jądra? Czy uruchomiłeś udevadm triggerlub podłączyłeś urządzenie, aby zastosować nową regułę?
Gilles „SO- przestań być zły”
Tak, robię to po każdej edycji reguł, aby je wypróbować. Zredagowałem to pytanie odpowiednio. W ten sposób udev działa przez jakiś czas, ale ja działam 3.5.0-23-generic.
Redsandro

Odpowiedzi:

24

Jeśli chcesz uruchomić skrypt na określonym urządzeniu, możesz użyć identyfikatora dostawcy i produktu

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

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

Za pomocą envmożesz zobaczyć, jakie środowisko jest ustawione w udev, a za pomocą fileodkryjesz typ pliku.

Konkretne atrybuty urządzenia można odkryć za pomocą lsusb

lsusb

daje

...
Magistrala 001 Urządzenie 016: ID 152d: 2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 SATA Bridge
...

Olaf Dietsche
źródło
1
To jest interesujące! Wygląda na to, że nie ma uprawnień do zapisu w / log /. To czyni zapis do / tmp /. Wydaje mi się, że nie miał też pozwolenia na czytanie moich poprzednich skryptów testowych.
Redsandro
@Redsandro Nie było to celowe, tylko do celów testowych. W każdym razie cieszę się, że to pomogło. ;-)
Olaf Dietsche
Chciałbym zachęcić cię również do sprawdzenia tego pytania i sprawdzenia, czy Twoja wiedza może być tam cenna. :)
Redsandro
3
Możesz także dodać ACTION=="add",bezpośrednio do definicji reguły.
Avindra Goolcharan
4

Nie chodzi bezpośrednio o twoje pytanie, ale o to, co robisz. Jeśli uruchomisz skrypt kopii zapasowej z udev, napotkasz dwa główne problemy:

  1. Twoja zdrapka może zostać uruchomiona, zanim urządzenie będzie gotowe i będzie można ją zamontować, musisz zachować warunek KERNEL == "sd *", jeśli chcesz użyć węzła / dev do zamontowania
  2. Co ważniejsze, jeśli wykonanie skryptu zajmuje trochę czasu (co może być łatwe w przypadku skryptu zapasowego), zostanie zabite wkrótce po uruchomieniu (około 5 sekund)
  3. Napotkasz wiele skomplikowanych problemów z uprawnieniami użytkowników

Moja rada to utworzenie skryptu w domu użytkownika, który będzie nasłuchiwał nazwanego potoku i który zostanie uruchomiony asynchronicznie:

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

Uwaga: Używam automatycznego montowania z KDE, więc sprawdzam, czy folder się pojawia. Możesz przekazać parametr / dev / sd * w fifo z reguły udev i zamontować go sam w skrypcie. Aby pisać w fifo, nie zapominaj, że udev nie jest powłoką i że przekierowanie nie działa. Twój RUN powinien wyglądać następująco:

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

Olivier Laporte
źródło
Świetne wykorzystanie nazwanych rur tutaj. Zastanawiałem się, czy możesz po prostu utworzyć dowolny plik w tmp i poszukać go zamiast nazwanego potoku, prawda?
jamescampbell
1

Opublikowałem rozwiązanie na /ubuntu//a/516336, a także tutaj kopiuję i wklejam rozwiązanie.

Napisałem skrypt Pythona używając pyudev, który pozostawiam uruchomiony w tle. Ten skrypt nasłuchuje zdarzeń udev (dlatego jest bardzo wydajny) i uruchamia dowolny kod, jaki chcę. W moim przypadku uruchamia xinputpolecenia, aby skonfigurować moje urządzenia ( link do najnowszej wersji ).

Oto krótka wersja tego samego skryptu:

#!/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
źródło
1
Wygląda na fajny skrypt +1. Jedną rzeczą, którą zasugerowałem, jest użycie listy zamiast tylko jednego ciągu call(). W ten sposób, jeśli konieczne jest dostarczenie argumentów do foobar.shskryptu, możesz to zrobić dynamicznie.
Sergiy Kolodyazhnyy
1
Uczciwy punkt. Mój „prawdziwy” skrypt (link z odpowiedzi) korzysta z listy. W tej minimalistycznej wersji, którą tutaj wkleiłem, popsułem i przypadkowo użyłem sznurka. Dzięki! Zaktualizowałem odpowiedź.
Denilson Sá Maia
-1

Aby uruchomić skrypt podczas rozruchu po włożeniu urządzenia USB, korzystam z rozwiązania poniżej:

Sformatuj pendrive lub inną pamięć USB i nadaj jej nazwę. Następnie /etc/rc.local dodaj linięls -q /dev/disk/by-label > /home/pi/label.txt

utworzy plik txt o nazwie label.txt (może mieć dowolną inną nazwę)

następnie ponownie w /etc/rc.local dodaj kolejne 2 linie:

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

Teraz za każdym razem, gdy wstawiany jest pendrive o nazwie USB_drive_name, uruchamia skrypt.

Z kilkoma małymi modyfikacjami powyższego rozwiązania można użyć, gdy system jest uruchomiony.

Aleksander Celewicz
źródło
Nie odpowiada na pytanie: Dotyczy to tylko czasu rozruchu (a używanie udevna inne czasy nie jest „kilkoma małymi modyfikacjami”) i Raspberry Pi. Istnieje niepotrzebne sudo- rc.localdziała jako root, jest to problem z eskalacją uprawnień - plik, który może edytować zwykły użytkownik, jest uruchamiany jako root.
Gert van den Berg