#!/bin/sh # Copyright (c) 2003-2004 Gideon Romm (Symbio Technologies, LLC) # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # SYMBIO TECHNOLOGIES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF # OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # # This hotplug program expands on ideas of my previous work and work # done by Scott Balneaves. # # Version: 0.5 # # Changelog: # # 0.5: # # - Added support for USB flash sticks that report bad partition tables # yet *should* be mounted as ../disc # # 0.4: # - Fixed bug in symbolic link generation # # 0.3: # - Reverted to scotty's mount/unmount scripts # # 0.2: # - Modified parser # 0.1: # - Initial release # Hotplug will run this script and set the following environment variables: # # ACTION - What action to take: eg. add/remove # DEVPATH - On 2.6 kernels, the sysfs directory for the device # PRODUCT - idVendor/idProduct/bcdDevice, from device descriptor. Numbers are # hexadecimal, without leading '0x' or zeros: eg. 46d/c281/108 # TYPE - bDeviceClass/bDeviceSubClass/bDeviceProtocol, from device descriptor # when 0/*/* is seen, a variable of type INTERFACE is also provided. # Numbers are decimal: eg. 9/0/0 # INTERFACE - bInterfaceClass/bInterfaceSubClass/bInterfaceProtocol # only for device class zero. # Linux 2.6 gives each interface its own hotplug event, and # /sys/$DEVPATH/bInterfaceNumber tells them apart. Earlier # kernels only reported the first interface. Numbers are decimal. # eg. 3/1/1 # DEVFS - Where USB drivers list lives: eg. /proc/bus/usb # DEVICE - The path to the usbdevfs node for this device: # eg. /proc/bus/usb/002/008 ####################### # The Plan # ####################### # # When an "add" ACTION is triggered, we will search a lookup table for # an appropriate module to load. # Call the function add_ # If the module is not found, let's assume usb-storage... # # When a "remove" ACTION is triggered, then we will run remove_ # and attempt to remove the module ####################### # Set up environment # ####################### # For unknown devices, this will allow us to check syslog for IDs logger "PRODUCT: $PRODUCT (idVendor/idProduct/bcdDevice)" logger "TYPE: $TYPE (bDeviceClass/bDeviceSubClass/bDeviceProtocol)" logger "INTERFACE: $INTERFACE (bInterfaceClass/bInterfaceSubClass/bInterfaceProtocol)" # # Directories # DRIVESDIR=/tmp/drives HOTPLUGDIR=/tmp/drives/.hotplug PROCSCSI=/proc/scsi/scsi SUPERMOUNTS=/proc/fs/supermount/subfs MOUNTS=/proc/mounts TESTMOUNT=/tmp/testmount # # Module location # KERNEL=`uname -r` MODULE_DIR=/lib/modules/$KERNEL USB_MODMAP=$MODULE_DIR/modules.usbmap LTSP_USB_MODMAP=/etc/usblist # # Set environment and exit upon failure # if [ ! -d $HOTPLUGDIR ] ; then exit 0 fi if [ ! -d $TESTMOUNT ]; then mkdir $TESTMOUNT || exit 1 fi grep usbdevfs $MOUNTS >/dev/null 2>&1 if [ "$?" != 0 ]; then mount -t usbdevfs none /proc/bus/usb if [ "$?" != 0 ]; then exit 1 fi fi ####################### # Functions # ####################### deviceInfo() { THISFILE=$1 # Gather info about devices hostArray=`grep Host $THISFILE|cut -d" " -f2` busArray=`grep Host $THISFILE|cut -d" " -f4` targetArray=`grep Host $THISFILE|cut -d" " -f6` lunArray=`grep Host $THISFILE|cut -d" " -f8` j=0 devices= for i in $hostArray; do thisbus=`echo $busArray|cut -d" " -f$(( ${j} + 1 ))` thistarget=`echo $targetArray|cut -d" " -f$(( ${j} + 1 ))` thislun=`echo $lunArray|cut -d" " -f$(( ${j} + 1 ))` devices="$devices /dev/scsi/host${i/scsi/}/bus$(($thisbus%100))/target$(($thistarget))/lun$(($thislun))" j=$(( ${j} + 1 )) done echo $devices } mount_device_num() { DEVNUM=$1 SCSI_DIR=$2 mkdir ${HOTPLUGDIR}/host${DEVNUM} #SCSI_DIR=/dev/scsi/host${DEVNUM}/bus0/target0/lun0 PRODUCT_NAME=`grep Product /proc/scsi/usb-storage-${DEVNUM}/${DEVNUM} | \ sed -e 's/.*: //' | tr ' ' '_'` MODEL=`grep -h -s "^${PRODUCT_NAME}" /etc/devname_map.local /etc/devname_map|\ head -1 | sed 's/ / /g' | tr -s ' ' | cut -d' ' -f 2` [ -n "${MODEL}" ] && PRODUCT_NAME="${MODEL}" COUNT=1 while [ -L ${DRIVESDIR}/${PRODUCT_NAME}_${COUNT} ]; do COUNT=`expr $COUNT + 1` done LINK_NAME=${PRODUCT_NAME}_${COUNT} if [ -b $SCSI_DIR/part1 ] ; then mount $SCSI_DIR/part1 $TESTMOUNT if [ "$?" != 0 ]; then umount $TESTMOUNT else umount $TESTMOUNT echo "${DRIVESDIR}/${LINK_NAME}" >${HOTPLUGDIR}/host${DEVNUM}/LINK_NAME mount -t supermount \ -o fs=vfat:msdos:iso9660:ext2,dev=$SCSI_DIR/part1,--,rw,uid=99,gid=99 \ none $HOTPLUGDIR/host${DEVNUM} cd $HOTPLUGDIR/host${DEVNUM} ln -s `basename $HOTPLUGDIR`/host${DEVNUM} "${DRIVESDIR}/${LINK_NAME}" return fi fi if [ -b $SCSI_DIR/cd ] ; then echo "${DRIVESDIR}/${LINK_NAME}" >${HOTPLUGDIR}/host${DEVNUM}/LINK_NAME mount -t supermount \ -o fs=vfat:msdos:iso9660:ext2,dev=$SCSI_DIR/cd,--,ro,uid=99,gid=99 \ none $HOTPLUGDIR/host${DEVNUM} cd $HOTPLUGDIR/host${DEVNUM} ln -s `basename $HOTPLUGDIR`/host${DEVNUM} "${DRIVESDIR}/${LINK_NAME}" return fi if [ -b $SCSI_DIR/disc ] ; then echo "${DRIVESDIR}/${LINK_NAME}" >${HOTPLUGDIR}/host${DEVNUM}/LINK_NAME mount -t supermount \ -o fs=vfat:msdos:iso9660:ext2,dev=$SCSI_DIR/disc,--,rw,uid=99,gid=99 \ none $HOTPLUGDIR/host${DEVNUM} cd $HOTPLUGDIR/host${DEVNUM} ln -s `basename $HOTPLUGDIR`/host${DEVNUM} "${DRIVESDIR}/${LINK_NAME}" return fi } umount_device_num() { DEVNUM=$1 if [ -d ${HOTPLUGDIR}/host${DEVNUM} ] ; then umount -l ${HOTPLUGDIR}/host${DEVNUM} if [ -f ${HOTPLUGDIR}/host${DEVNUM}/LINK_NAME ]; then rm -f `cat ${HOTPLUGDIR}/host${DEVNUM}/LINK_NAME` rm ${HOTPLUGDIR}/host${DEVNUM}/LINK_NAME fi rmdir ${HOTPLUGDIR}/host${DEVNUM} fi } device_disconnected() { k=$1 USBDEV="/proc/scsi/usb-storage-${k}/${k}" if [ -f ${USBDEV} ] ; then grep "Attached: Yes" $USBDEV > /dev/null echo $? fi } ####################### # Module Functions # ####################### add_usbstorage() { proc_devices=`deviceInfo $PROCSCSI` # Supermount this device # If the device is attached but is not supermounted: count=0 for k in $proc_devices; do DEVICE_DISCONNECTED=`device_disconnected $count` grep $k $SUPERMOUNTS >/dev/null 2>&1 if [ "$?" != 0 -a "$DEVICE_DISCONNECTED" != 1 ]; then # I am connected and not supermounted echo "$k not found in $SUPERMOUNTS" mount_device_num $count $k fi count=$(($count + 1)) done } remove_usbstorage() { proc_devices=`deviceInfo $PROCSCSI` # If the device is in supermount and unmounted, # remove the supermount for this device count=0 for k in $proc_devices; do DEVICE_DISCONNECTED=`device_disconnected $count` grep $k $SUPERMOUNTS >/dev/null 2>&1 if [ "$?" == 0 -a "$DEVICE_DISCONNECTED" == 1 ]; then # This will trigger a removal umount_device_num $count fi count=$(($count + 1)) done } ####################### # Main # ####################### # # ADD event # if [ "$ACTION" == "add" ] ; then # # USB add event # if [ "$DEVFS" == "/proc/bus/usb" ] ; then # # Get vendor and product ID, and find them in the module map # ID_VENDOR=`echo $PRODUCT | cut -d'/' -f 1` ID_PRODUCT=`echo $PRODUCT | cut -d'/' -f 2` #MODULE=`grep -h $ID_VENDOR $LTSP_USB_MODMAP $USB_MODMAP | grep $ID_PRODUCT | cut -d' ' -f 1 | head -1` # Below is an updated usblist parser, which should be more accurate MODULE=`cat $LTSP_USB_MODMAP $USB_MODMAP|sed -e 's/x\(0\)\+/x/g' -e 's/x /x0/g' -e 's/x$/x0/' -e 's/\([[:blank:]]\)\+/ /g'|grep "0x$ID_VENDOR 0x$ID_PRODUCT"| cut -d' ' -f 1 | head -1` MODULE=${MODULE:-"usb-storage"} lsmod | grep $MODULE > /dev/null if [ $? -eq 1 ] ; then modprobe $MODULE fi # This next part *should* be in modules.dep, but... if [ "$MODULE" == "usb-storage" ] ; then lsmod | grep "s[dr]_mod" > /dev/null if [ $? -eq 1 ] ; then modprobe sd_mod && modprobe sr_mod fi fi add_${MODULE/-/} 2>/dev/null fi #end is USB device # # REMOVE action # elif [ "$ACTION" == "remove" ] ; then if [ "$DEVFS" == "/proc/bus/usb" ] ; then # # Get vendor and product ID, and find them in the module map # ID_VENDOR=`echo $PRODUCT | cut -d'/' -f 1` ID_PRODUCT=`echo $PRODUCT | cut -d'/' -f 2` #MODULE=`grep -h $ID_VENDOR $LTSP_USB_MODMAP $USB_MODMAP | grep $ID_PRODUCT | cut -d' ' -f 1 | head -1` # Below is an updated usblist parser, which should be more accurate MODULE=`cat $LTSP_USB_MODMAP $USB_MODMAP|sed -e 's/x\(0\)\+/x/g' -e 's/x /x0/g' -e 's/x$/x0/' -e 's/\([[:blank:]]\)\+/ /g'|grep "0x$ID_VENDOR 0x$ID_PRODUCT"| cut -d' ' -f 1 | head -1` MODULE=${MODULE:-"usb-storage"} remove_${MODULE/-/} 2>/dev/null rmmod $MODULE 2>/dev/null fi # end is USB device fi