#!/bin/bash
#
# Manage USB stick state, events come from hotplug or base software (FO, ISU)...
#
# Usage : usb_state_manager Evt [block_device]
#
# With Evt as integer, and bloc device as /dev/sda1
#
#   This file use /tmp/usb_stick/usb_state_manager.set file for storage his permanent data.
# This file updates /tmp/usb_stick/usb_event according to event it receives.
# When it want to modify /tmp/usb_stick/usb_event it locks /tmp/usb_stick/usb_event.lock lock file. Do not read file when lock is not free!!!
#
# It must have only one running instance of this script, therefore when the script start it lock itself, an other call will wait until the first one finished.
#
# Input EVT :
#   1 : new usb mounted (required another argument device bloc name)
#   2 : usb removed (required another argument device bloc name)
#   3 : Partitionning error on a new plugged key
#   4 : File system error on a new plugged key
#   5 : new plugged usb can't be managed by the system
# 10 : umount usb key ( from application)
#
# Output : script always returns 0. Real effact are on /tmp/usb_stick/usb_event
#   This file have 2 lines : the first one is an event/order, other is a error message.
# Output EVT :
#   1 : Don't display any USB icon
#   2 : Display "USB in use" icon
#   3 : Display "Error USB" icon
#   4 : Display "USB free" icon
#
# Error MESSAGE :
#
# 0 : no error
# 1 : USB stick partitionning error
# 2 : USB stick's file system error
# 3 : USB stick don't managed by system
# 4 : USB stick unplugged without prior unmounting ( no application use it anymore), data loss can occurs.
# 5 : USB stick unplugged without prior unmounting ( application still use it), data loss can occurs and USB mounting point is lock on until every application stop using it (critical).
# 6 : USB stick can't be released because application still using it. Application which block the sitck are listed in /tmp/usb_stick_users .


readonly SCRIPT_RUNNING_LOCK_FILE="/run/usb_state_manager.lock"
readonly SCRIPT_STATE_STORAGE_FILE="/tmp/usb_stick/usb_state_manager.set"
readonly SCRIPT_USB_EVT_FILE="/tmp/usb_stick/usb_event"
readonly STICK_USER_LIST="/tmp/usb_stick/usb_stick_users"
readonly STICK_BLOCK_DEVICE="/dev/Usb_Flash"
readonly STICK_USER_DIRECTORY="user/usbflash"
readonly DEBUG="OFF"

STICK_STATE="NOT USED"
CURRENT_DEVICE=""

function my_logger {
    logger -i -t usb-state-manager "$1"
    return 0
}


# Read the parameters file and restore it if it is possible
function restore_previous_state {
    # state file doesn't exist keep default state
    if [ ! -e $SCRIPT_STATE_STORAGE_FILE ]
    then
        return 0
    fi
    STICK_STATE=`head -n 1 $SCRIPT_STATE_STORAGE_FILE`
    CURRENT_DEVICE=`tail -n 1 $SCRIPT_STATE_STORAGE_FILE`

    # check parameters
    if [ "$STICK_STATE" != "NOT USED" -a "$STICK_STATE" != "USED" ]
    then
        STICK_STATE="NOT USED"
    fi
    return 0
}

# store script parameters for futur use
function store_state {
    echo $STICK_STATE > $SCRIPT_STATE_STORAGE_FILE
    echo $CURRENT_DEVICE >> $SCRIPT_STATE_STORAGE_FILE
}

# Write in /tmp/usb_stick/usb_event, need 2 args
function write_in_event_file {
    flock -x $SCRIPT_USB_EVT_FILE -c "echo $1 > $SCRIPT_USB_EVT_FILE;echo $2 >> $SCRIPT_USB_EVT_FILE"

    # Send signal here
    # get ISU Pid
    if [ -e "/var/run/isu.pid" ]
    then
        ISU_PID=`cat "/var/run/isu.pid"`
        kill -s SIGUSR1 $ISU_PID
    fi
}

function usage {
    echo "Usage : usb_state_manager evt [device]"
    exit 0
}

###############################################################

# Get arguments and check them.
if [ $# -gt 2 ]
then
    usage
fi

if [ $# -lt 1 ]
then
    usage
fi


if [ "$1" == "1" -o "$1" == "2" ]
then
    if [ "$#" != "2" ]
    then
            my_logger "Mount/Umount without device : exit"
            exit 0
    fi
fi

EVT=$1
DEVICE=$2

if [ $EVT == 1 ]
then
    TEST_FS=$(disktype $DEVICE | grep "file system" | grep "FAT")
    if [ "$TEST_FS" == "" ]     # not a FAT file system
    then
    # Filter : remove event during formating
        TEST_FORMAT=$(ps axc | grep usb_format*)
        if [ "$TEST_FORMAT" != "" ]
        then
            rm -f $SCRIPT_RUNNING_LOCK_FILE
            exit 0
        fi
        EVT=4       # translate event into a not formated device
    fi
fi

#my_logger "Evt = $EVT, Device = $DEVICE"

# Insure there is only one script in exec
(
    mkdir -p $(dirname $SCRIPT_STATE_STORAGE_FILE)
    # Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
    flock -w 10 -x 200  || exit 1

    # Restore previous state if exists ( get back stick state, current_device ...)
    restore_previous_state

    # check if event file exists, if not create it.
    if [ ! -e $SCRIPT_USB_EVT_FILE ]
    then
        echo "0" > "$SCRIPT_USB_EVT_FILE"
        echo "0" >> "$SCRIPT_USB_EVT_FILE"
    fi

    # Filter for usb lock if a previous stick has not been removed correctly.
    # Now check if a block device is not already mount on our mounting point.
    if [ "$STICK_STATE" == "NOT USED" ]
    then
        case "$EVT" in
            "1" | "3" | "4")
                IS_ALREADY_MOUNTED_POINT=$(grep /user/usbflash /etc/mtab)
                if [ "x$IS_ALREADY_MOUNTED_POINT" != "x" ]
                then
                    my_logger "WARNING : $DEVICE can't be used, user/usbflash is still used by an other stick."
                    write_in_event_file "2" "0"
                    store_state
                    exit 0
                fi
            ;;
        esac
    fi

    # graph state
    case "$STICK_STATE" in
        "NOT USED" )
            # managed all events
            case "$EVT" in

            "1" )
                # A usb stick is now mounted, base can use it
                test -x /usr/bin/beep && beep -l 70 -r 2 -D 15 || echo "1500:10" > /dev/buzzer
                if [ ! -e /$STICK_USER_DIRECTORY ]
                then
                    mkdir -p /$STICK_USER_DIRECTORY
                fi
                mount -t auto -o nosuid,umask=000 $DEVICE /$STICK_USER_DIRECTORY
                STICK_STATE="USED"
                CURRENT_DEVICE="$DEVICE"
                my_logger "USB stick is now avaiable"
                write_in_event_file "1" "0"
                # This is for New Gen module : it uses usb stick over nfs
                # Refresh exports dir : allow usbflah to be mounted
                /usr/sbin/exportfs -r
            ;;

            "2" )
                # An USB stick has been removed
                test -x /usr/bin/beep && beep -r 1 -l 70 || echo "500:10" > /dev/buzzer
                if [ "$DEVICE" == "$CURRENT_DEVICE" ]
                then
                    my_logger "Umounted stick has been unplugged."
                    write_in_event_file "0" "0"
                else
                    my_logger "Unplugged stick was not used."
                fi
            ;;

            "3" )
                # stick not partitionned
                my_logger "USB stick not partitionned"
                CURRENT_DEVICE="$DEVICE"
                write_in_event_file "2" "1"
            ;;
            "4" )
                # stick FS error
                my_logger "USB stick have FS ERROR"
                CURRENT_DEVICE="$DEVICE"
                write_in_event_file "2" "2"
            ;;
            "5" )
                # stick not managed by system
                my_logger "USB stick Not managed"
                CURRENT_DEVICE="$DEVICE"
                write_in_event_file "2" "3"
            ;;

            "10" )
                # umount request (should not happens)
                LAST_EVENT=`head -n 1 $SCRIPT_USB_EVT_FILE`
                if [ $LAST_EVENT = "2" ]
                then
                    if [ $CURRENT_DEVICE != "" ]
                    then
                        `/usr/bin/lsof_simple_version.sh $CURRENT_DEVICE`
                        USERS_LIST=`cat "$STICK_USER_LIST"`
                        if [ "$USERS_LIST" = "" ]
                        then
                            write_in_event_file "0" "0"
                            my_logger "Stick unmounted, no icon to display for USB"
                        else
                            my_logger "USB can't be umount, users still use it."
                            write_in_event_file "2" "6"
                        fi
                    else
                        my_logger "Umount asked for device not used."
                        write_in_event_file "0" "0"
                    fi
                fi
            ;;

            esac
        ;;

    "USED" )
        # managed all events
        case "$EVT" in

        "1" | "3" | "4" | "5")
            # nothing to do here : a new key is plugged but one is already in used
            my_logger "Last plugged USB stick is not used, another one is already in used."
        ;;

        "2" )
            # An USB stick has been removed
            if [ "$DEVICE" == "$CURRENT_DEVICE" ]
            then
                # Send SIGUSR1 to automounter to force it to refresh is mountage ( do it now because we will break the
                #     fuser info now with -l option for umount
                umount -lf "$CURRENT_DEVICE"
                rmdir /$STICK_USER_DIRECTORY

                my_logger "Sticked unplugged without umount, no user still used it."
                write_in_event_file "0" "4"
                STICK_STATE="NOT USED"
            else
                my_logger "Unused key has been unplugged."
            fi
        ;;

        "10" )
            # umount request
            # This is for new gen module: remove usbflash from export list and
            # do not block new gen for next access : will be allow when a new stick will be plugged
            /usr/sbin/exportfs -u *:/user/usbflash

            STICK_STATE="NOT USED"
            umount -f "$CURRENT_DEVICE"
            umount -l "$CURRENT_DEVICE"
            rmdir /$STICK_USER_DIRECTORY
            rm "$STICK_BLOCK_DEVICE"
            write_in_event_file "3" "0"
            my_logger "USB stick umounted, waiting for unplugging."
        ;;
        esac
    ;;

    esac

    store_state
) 200>$SCRIPT_RUNNING_LOCK_FILE


exit 0

# END OF FILE
