Cómo ejecutar un shellscript cuando conecto un dispositivo USB


28

Quiero ejecutar un script cuando conecto un dispositivo en mi máquina Linux. Por ejemplo, ejecute con xinputel mouse o un script de respaldo en una determinada unidad.

He visto muchos artículos sobre esto, más recientemente aquí y aquí . Pero simplemente no puedo hacer que funcione.

Aquí hay algunos ejemplos simples que intentan obtener al menos algún tipo de respuesta.

/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 carpeta de reglas es vigilada por inotifyy debe estar activa de inmediato. Sigo desconectando mi teclado, mouse, tableta, tarjeta de memoria y unidad USB, pero nada. Ningún archivo de registro tocado.

Ahora, ¿cuál sería la forma más simple de al menos saber que algo está funcionando? Es más fácil trabajar desde algo que funciona que desde algo que no funciona.


1
¿No querías publicar en Unix y Linux ? ¿Cuál es tu versión de kernel? ¿Ejecutó udevadm triggero conectó un dispositivo para aplicar la nueva regla?
Gilles 'SO- deja de ser malvado'

Sí, lo hago después de cada edición de las reglas para probarlas. Edité la pregunta en consecuencia. Esta es la forma en que udev funciona desde hace un tiempo, pero estoy corriendo 3.5.0-23-generic.
Redsandro

Respuestas:


24

Si desea ejecutar el script en un dispositivo específico, puede usar los identificadores del proveedor y del producto

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

    ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
  • en 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, puede ver qué entorno se configura desde udev y con file, descubrirá el tipo de archivo.

Los atributos concretos para su dispositivo se pueden descubrir con lsusb

lsusb

da

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


1
¡Esto es interesante! Parece que no tiene permiso para escribir en / log /. Se hace de escritura a / tmp /. Supongo que tampoco tenía permiso para leer mis guiones de prueba anteriores.
Redsandro

@Redsandro Esto no fue intencional, solo para, bueno, para fines de prueba. De todos modos, me alegro de que haya ayudado. ;-)
Olaf Dietsche

Me gustaría animarlo a que también revise esta pregunta y vea si su conocimiento puede ser valioso allí. :)
Redsandro

3
También puede agregar ACTION=="add",directamente a la definición de la regla.
Avindra Goolcharan

4

No se trata directamente de su pregunta, sino de lo que está haciendo. Si inicia un script de copia de seguridad desde udev, se enfrentará a dos problemas principales:

  1. Es posible que su scrpit se inicie antes de que el dispositivo esté listo y se pueda montar, debe mantener la condición KERNEL == "sd *" si desea usar el nodo / dev para montarlo
  2. Más importante aún, si su scirpt tarda un tiempo en ejecutarse (que puede ser fácilmente el caso con un script de respaldo), se eliminará poco después de que se inicie (aproximadamente 5 segundos)
  3. Te enfrentarás a muchos problemas complicados de permisos de usuario

Mi consejo es crear un script en la página de inicio del usuario que escuche una canalización con nombre y que se iniciará de forma asincrónica como:

#!/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: uso el montaje automático con kde, así que verifico si aparece la carpeta. Puede pasar el parámetro / dev / sd * en el fifo desde la regla udev y montarlo usted mismo en el script. Para escribir en el fifo no olvides que udev no es un shell y que la redirección no funciona. Tu RUN debería ser como:

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


Gran uso de tuberías con nombre aquí. Me preguntaba si también podría crear un archivo arbitrario en tmp y buscarlo también en lugar de una tubería con nombre, ¿correcto?
jamescampbell

1

He publicado una solución en /ubuntu//a/516336 y también estoy copiando la solución aquí.

Escribí un script de Python usando pyudev que dejo ejecutándose en segundo plano. Ese script escucha los eventos de udev (por lo tanto, es muy eficiente) y ejecuta el código que quiera. En mi caso, ejecuta xinputcomandos para configurar mis dispositivos ( enlace a la versión más reciente ).

Aquí hay una versión corta del mismo 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
Parece un buen guión, +1. Una cosa que sugeriría es usar la lista en lugar de solo una cadena call(). De esa manera, si es necesario proporcionar argumentos al foobar.shscript, puede hacerlo dinámicamente.
Sergiy Kolodyazhnyy

1
Punto justo. Mi script "real" (vinculado desde la respuesta) usa una lista. En esta versión minimalista que pegué aquí, me equivoqué y accidentalmente usé una cadena. ¡Gracias! He actualizado la respuesta.
Denilson Sá Maia

-1

Para ejecutar el script en el arranque cuando se inserta el dispositivo usb, uso la solución a continuación:

Formatee el pendrive o cualquier otro almacenamiento usb y asígnele un nombre al hacerlo. Luego en /etc/rc.local agregar líneals -q /dev/disk/by-label > /home/pi/label.txt

creará un archivo txt llamado label.txt (puede ser cualquier otro nombre)

luego nuevamente en /etc/rc.local agregue otras 2 líneas:

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

Ahora, cada vez que se inserta un pendrive con el nombre USB_drive_name, se ejecutará el script.

Con algunas pequeñas modificaciones, la solución anterior se puede utilizar cuando el sistema está en funcionamiento.


No responde la pregunta: esto solo cubre el tiempo de arranque (y el uso udevpara otros momentos no son "algunas pequeñas modificaciones") y la Raspberry Pi. Hay un problema innecesario sudo: se rc.localejecuta como root, es un problema de escalada de privilegios: un archivo que un usuario normal puede editar se ejecuta como root.
Gert van den Berg
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.