Agente hotplug para automontaje de dispositivos USB.


En un articulo
anterior publicado en Bulma por Josep Sort
, se explicaba cómo
automatizar el montaje de dispositivos usb-storage.

Inspirado en ese articulo y en otros de internet, propongo un script para
hotplug (agent) que realizará el montaje automático de estos
dispositivos permitiendo configurar el funcionamiento para dispositivos
conocidos.
Actualizado: Soporte para varias particiones y unidades sin particionar

Presentación y requisitos

El script nos permitirá:

  • Solucionar el problema de la gestión de los dispositivos de
    almacenamiento usb, pues cada vez que los enchufamos se les
    puede asignar un device distinto
    (/dev/sda, /dev/sdb….).
  • Usar supermount, con las ventajas asociadas. Adaptarlo a otros
    sistemas es sencillo.
  • Montar (o crear un enlace simbólico) de los dispositivos usb
    conectados.
  • Definir un directorio por defecto en el que se montarán los
    dispositivos usb-storage no configurados
    (añadiendo un sufijo numérico
    si está ocupado dicho punto de montaje)
  • Configurar determinados dispositivos, pudiendo definir un
    punto de montaje alternativo, para cada dispositivo, LUN, o
    partición
  • Configurar un enlace simbólico que se creará apuntando al
    dispositivo usb, en vez de montarlo. Esto es muy interesante
    con supermout, pues podemos configurar supermount en
    nuestro
    /etc/fstab
    para que apunte a este enlace en vez del dispositivo.
  • Soporte de dispositivos con varios luns, como los
    lectores de tarjetas n in 1.

Para su funcionamiento correcto necesitaremos lo siguiente:

  • hotplug
  • usb-utils
  • supermount, aunque es sencillo adaptarlo a otros
    sistemas.
  • kernel 2.6.X, con soporte del sistema de ficheros
    /sys.
  • Soporte usb-storage en el kernel
  • Activar la opción
    CONFIG_SCSI_MULTI_LUN
    si queremos usar lectores de tarjetas n in 1

Nota: El script sólo ha sido probado en Debian, y es posible que en otras
distribuciones no existan algunos directorios y ficheros de configuración
aquí utilizados, o que el funcionamiento del software empleado se distinto.
Se agradece cualquier aclaración o comentario al respecto :).

Además daré por supuesto que el sistema está correctamente configurado para
el uso de este tipo de sistemas.

No entraré en detalles de la programación del script, sólo de su configuración.




Instalación / configuración

  • Copiaremos el script de abajo en
    /etc/hotplug/usb/usb-storage.

    No os olvidéis de los permisos de ejecución!!!.

    Opcionalmente, podemos editarlo para cambiar las opciones por defecto, como
    DEFAULT_MOUNT_POINT
    DEFAULT_MOUNT_OPTIONS.
    También podemos modificar las funciones
    mountUsbDevice y
    umountRemoveScript, para
    que use otro sistema en vez de supermount, o añadir funcionalidades
    extra, como crear enlaces en escritorios.



  • Si deseamos configurar determinados dispositivos, debemos crear el
    fichero
    /etc/hotplug/usb-storage.map
    con la siguiente sintaxis:

    #
    # Configuration file for usb-storage agent
    #
    # mount
    # or
    # symbolic
    #
    # if partition number = 0, it will use whole unit (/dev/sd?)
    #
    05dc:0080 0 0 mount /mnt/disco_usb defaults,user,umask=0002,rw
    0424:223a 0 1 symbolic /dev/usbcard0
    0424:223a 1 1 symbolic /dev/usbcard1
    0424:223a 2 1 symbolic /dev/usbcard2
    0424:223a 2 2 mount /mnt/usbcard_particion_2 defaults,user,umask=0002,rw

    Donde:

    • idVendor:idProduct: son los identificadores de vendedor
      y producto que se pueden obtener con el comando
      lsusb -v. No
      deben incluir el “0x”
    • lun: número de LUN (Logical Unit Number) para este
      dispositivo. Para usar cuando tenemos un dispositivo con
      múltiples unidades, como los lectores de tarjetas.
      Normalmente pondremos 0.

    • partition: Partición dentro de esta unidad. Actualmente
      el script no soporta esto, y siempre emplea la partición 1.
    • mount/symbolic: Si deseamos montar esta partición
      o crear un enlace simbólico del device.
    • mount point: Directorio en el que se montará
    • mount options: Opciones que se pasarán a mount
    • symbolic device: Nombre del enlace simbólico que se
      creará apuntando al dispositivo.

    En ese ejemplo podemos ver que el dispositivo 05dc:0080
    (un disco usb Lexar) es montado en el directorio
    /mnt/disco_usb. Este
    disco usb no tiene particiones, se monta directamente toda la
    unidad, por lo que tiene el número de particion 0

    En cambio, el dispositivo 0424:223a (un lector de tarjetas)
    tiene varios LUNs, y se creará un enlace para cada uno en
    /mnt/usbcard?.

    Para el LUN 2, tenemos configuradas dos particiones, la 1 y la 2,
    y la segunda partición, en vez crear un enlace, se montará.

    Para estos dispositivos tengo lo siguiente en
    /etc/fstab.

    none /mnt/tarxetas/CompactFlash supermount dev=/dev/usbcard0,–,defaults,user,umask=0002,rw 0 0
    none /mnt/tarxetas/MemoryStick supermount dev=/dev/usbcard1,–,defaults,user,umask=0002,rw 0 0
    none /mnt/tarxetas/SmartMedia supermount dev=/dev/usbcard2,–,defaults,user,umask=0002,rw 0 0
    none /mnt/tarxetas/SecureDigital supermount dev=/dev/usbcard3,–,defaults,user,umask=0002,rw 0 0

  • Ahora sólo tenemos que enchufar y desenchufar nuestros dispositivos

    Notas

    • No hagais cosas raras con las particiones y el fichero de
      configuración. Respetad la sintaxis, y no pongais cosas como
      otras particiones cuando poneis la partición especial “0”, etc…
    • Cuando el dispositivo no está configurado tarda en montar, porque
      tiene que comprobar si la partición
      (/dev/sd?1 o /dev/sd?)
      se puede montar.

    Trabajos futuros

    Se pueden mejorar varias cosas, pero destacan:

    • Pasarlo a perl, con lo que sería muuucho más sencillo %-?
    • Traducir este articulo 🙂

    Script usb-storage

    Copia y pega el siguiente script en
    /etc/hotplug/usb/usb-storage:

    #!/bin/bash
    #
    # This code is free software covered by GNU GPL license version 2 or above.
    # Please refer to http://www.gnu.org/ for the full license text.
    #
    # Some code lifted from Konstantin Riabitsev’s diskonkey (GPL)
    #
    # INSTALL
    # ——-
    # Put this in /etc/hotplug/usb/usb-storage
    # Be sure that in /etc/hotplug/usb.distmap are the usb-storage entries
    # Create file etc/hotplug/usb-storage.map with this sintax:
    # mount
    # or
    # symbolic
    #
    # For a device with no partitions (the filesystem is in /dev/sd?),
    # use partition number 0
    #
    # For Example:
    # 05dc:0080 0 0 mount /mnt/disco_usb defaults,user,umask=0002,rw
    # 05dc:0081 0 0 mount /mnt/otro_disco_usb defaults,user,umask=0002,rw
    # 0424:223a 0 1 symbolic /dev/usbcard0
    # 0424:223a 1 1 symbolic /dev/usbcard1
    # 0424:223a 2 1 symbolic /dev/usbcard2_1
    # 0424:223a 2 2 symbolic /dev/usbcard2_2
    #
    # TODO
    # —-
    # Refactorize this script in perl 🙂
    #
    # AUTHOR and SUPPORT
    # ——————
    # Hector Rivas Gandara (keymon), keymon(aT)wanadoo.es .
    #
    DEFAULT_MOUNT_POINT=/mnt/chave
    DEFAULT_MOUNT_OPTIONS=defaults,user,umask=0002,rw
    CONFIG_FILE=/etc/hotplug/usb-storage.map

    # Get’s the usb device serial number for a given usb device.
    # Return the 20 first digits
    function getUsbDeviceSerialNumber {
    lsusb -v -D $1 | grep -m 1 iSerial | sed ‘s/ */ /g’ | cut -f 4 -d ” ” | sed ‘s/\(.\{20\}\).*/\1/g’
    }

    # Get’s the usb device vendor:product for a given usb device.
    function getUsbDeviceVendorProduct {
    local vendor=`lsusb -v -D $1 | grep -m 1 idVendor | sed ‘s/ */ /g;s/^ //’ | cut -f 2 -d ” ” | sed ‘s/0x\(.*\)/\1/’ `
    local product=`lsusb -v -D $1 | grep -m 1 idProduct | sed ‘s/ */ /g;s/^ //’ | cut -f 2 -d ” ” | sed ‘s/0x\(.*\)/\1/’`
    echo $vendor:$product
    }

    # Get’s the scsi host number associated to a usb-storage device
    # with the given serial number
    function getScsiDeviceFromSerialNumber {
    for i in /proc/scsi/usb-storage/*; do
    if grep -q $1 $i ; then
    local scsiDev=`echo $i | cut -f 5 -d /`;
    fi
    done
    echo $scsiDev
    }

    # Gets device name for the given scsi address
    function getScsiDeviceNameFromAddress {
    local scsiAddress=$1
    # get the device minor number and calculate the leter
    local minor=`cat “/sys/class/scsi_device/${scsiAddress}/device/block/”dev | cut -f 2 -d :`
    local letter=`expr substr abcdefghijkmnopqrs \( $minor / 16 + 1 \) 1`
    echo /dev/sd$letter
    }

    # Gets partition devices in the given scsi address
    function getScsiDevicePartitions {
    local scsiAddress=$1
    local devs=””
    for i in “/sys/class/scsi_device/${scsiAddress}/device/block/”sd*; do
    dev=/dev/`echo $tmp | cut -f 8 -d /`
    devs=”$devs $dev”
    done
    echo devs
    }

    # checks for a valid scsi address
    function isValidScsiAddress {
    local scsiAddress=$1
    test -e /sys/class/scsi_device/${scsiAddress}
    return $?
    }

    # check if a device is mountable
    # FIXME: I don’t like too much this way. Have anybody a better idea???
    function isMountable {
    local device=$1;
    local tmpMountPoint=/tmp/.usb-storage.$$
    mkdir -p $tmpMountPoint
    if mount -t auto $device $tmpMountPoint &> /dev/null; then
    umount $tmpMountPoint
    rmdir $tmpMountPoint
    return 0
    else
    rmdir $tmpMountPoint
    return 1
    fi
    }

    # Adds a umount line in the remove script. creates the remove script
    # if necesary
    # Param: mount point of mounted device
    # Param: removeMountPoint = 1 if have to remove or 0 if not
    function addMountPointForRemover {
    local mountPoint=$1
    local removeMountPoint=$2

    # create file if necesary
    if [ ! -e $REMOVER ]; then
    echo “#!/bin/sh”> $REMOVER
    chmod +x $REMOVER
    fi

    # lazy umount
    echo “umount -l \”$mountPoint\”” >> $REMOVER

    if [ “$removeMountPoint” = “1” ]; then
    echo “rmdir \”$mountPoint\”” >> $REMOVER
    fi

    }

    # Mounts a device in the given point, with the given options
    # If mount point is already mounted, create other with a suffix
    # Returns the effective mount point
    function mountDevice {
    local device=$1;
    local mountPoint=$2;
    local mountOptions=$3;
    local removeMountPoint=0;

    # check that mount point is not busy
    local -i counter=2;
    local realMountPoint=$mountPoint;
    while mount | grep -q $realMountPoint; do
    # check that the device is not mounted already in the same mountpoint
    if mount | grep $realMountPoint | grep -q $device; then
    return
    fi
    realMountPoint=$mountPoint$counter
    counter=counter+1
    done

    # Supermount mount
    # Create dir if necesary
    if [ ! -e $realMountPoint ]; then
    mkdir -p $realMountPoint
    removeMountPoint=1
    fi

    mount -t supermount none $realMountPoint -o dev=$device,fs=auto,–,$mountOptions

    # Add it to remove script
    addMountPointForRemover $realMountPoint $removeMountPoint

    }

    # Reads the configuration file, striping comments and removing blanks
    function getConfiguration {
    cat $CONFIG_FILE | sed ‘s/\(.*\)#.*/\1/;s/ */ /g;s/^ //’
    }

    # Process the action for a concrete device and partition
    # params:
    # <“mount”>
    # or
    # <“symbolic”>
    function processPartition {
    local device=$1;
    local action=$2;

    if [ “$action” = “mount” ]; then
    local mountPoint=$3;
    local mountOptions=$4;
    else
    local symbolicDevice=$3;
    fi

    if [ “$action” = “mount” ]; then
    # Mount it to the mount point
    mountDevice $device $mountPoint $mountOptions
    else
    # Set symbolic device
    ln -sf $device $symbolicDevice
    fi

    }

    # Process a device. This function gets the configuration for the defined
    # device and calls processScsiLunPartition for each partition
    # param: usbVendorProduct String with the key “idProduct:idVendor”
    # param: usbHostNumber asigned host number
    # param: lun Scsi lun number
    function processScsiLun {

    local usbVendorProduct=$1
    local usbHostNumber=$2
    local lun=$3

    local confType;
    local mountPoint;
    local mountOptions;
    local symbolicDevice;
    local partition;
    local configurationLine;
    local partitionDevice;

    # Get the base device name (/dev/sd?)
    local deviceBase=$(getScsiDeviceNameFromAddress $scsiHostNumber:0:0:$scsiLun)

    # get all partitions defined in configuration file for this product and lun
    local configuredPartitions=`getConfiguration | grep “$usbVendorProduct $lun” | cut -f 3 -d ” “`

    # if there is configuration, process each partition
    if [ ! “$configuredPartitions” == “” ]; then
    for partition in $configuredPartitions; do
    configurationLine=`getConfiguration | grep -m 1 “$usbVendorProduct $lun $partition”`

    # If there is a configuration line, use it. It should be there 🙂
    if [ ! “$configurationLine” == “” ]; then
    # check for special partition number 0
    if [ “$partition” = “0” ]; then
    partitionDevice=${deviceBase}
    else
    partitionDevice=${deviceBase}${partition}
    fi
    confType=`echo $configurationLine | cut -d ” ” -f 4`
    if [ “$confType” = “mount” ]; then
    mountPoint=`echo $configurationLine | cut -d ” ” -f 5`
    mountOptions=`echo $configurationLine | cut -d ” ” -f 6`
    processPartition $partitionDevice $confType $mountPoint $mountOptions
    else
    symbolicDevice=`echo $configurationLine | cut -d ” ” -f 5`
    processPartition $partitionDevice $confType $symbolicDevice
    fi
    fi
    done
    # if there is no configuration, search for filesystem in sd? or sd?1
    else
    # check if there is a filesystem in sd?1, using fdisk (is faster)
    if fdisk -l ${deviceBase} | grep -q ${deviceBase}1; then
    processPartition ${deviceBase}1 mount $DEFAULT_MOUNT_POINT $DEFAULT_MOUNT_OPTIONS
    else
    # check if there is a filesystem in sd?1
    if isMountable ${deviceBase}; then
    processPartition ${deviceBase} mount $DEFAULT_MOUNT_POINT $DEFAULT_MOUNT_OPTIONS
    fi
    fi
    fi

    }

    if [ “${ACTION}” = “add” ]; then

    #####
    # Set the remover to this script
    # ln -s $0 $REMOVER

    #####
    # Wait a little, for the device to be created
    sleep 1

    #####
    # Get device info
    usbSerial=$(getUsbDeviceSerialNumber $DEVICE);
    usbVendorProduct=$(getUsbDeviceVendorProduct $DEVICE)

    # Get the scsi host
    scsiHostNumber=$(getScsiDeviceFromSerialNumber $usbSerial);

    ####
    # Search for all luns
    declare -i scsiLun=0
    while isValidScsiAddress $scsiHostNumber:0:0:$scsiLun; do
    processScsiLun $usbVendorProduct $scsiHostNumber $scsiLun
    scsiLun=$scsiLun+1
    done

    fi

    if [ “${ACTION}” = “remove” ]; then
    true
    fi

    exit;

    Este post ha sido traido de forma automatica desde https://web.archive.org/web/20140625063149/http:/bulma.net/body.phtml?nIdNoticia=2034 por un robot nigromante, si crees que puede mejorarse, por favor, contactanos.


    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

    Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.