#!/bin/sh

# Meru Networks IPMI query wrapper
# Jason Fieldman

# VERBOSE=1 if the -v option is set; enables debug output
VERBOSE=0

# OVERRIDE=1 if the -o option is set; allows local power change
OVERRIDE=0

# Version info
VERSIONINT="1.0"
VERSIONSTR="ipmiq version ${VERSIONINT} (c) 2007 Meru Networks"

# Address variables to store who we are and who we are talking to
TARGETCMD=
TARGETADDR=
SOURCEADDR=
TARGETADDRARG=
SOURCEADDRARG=

# General IPMI application parameters.  These do not need to change unless
# some other kernel module needs major device 250.
ICMD="ipmitool -I open"
IPMIDEV="/dev/ipmi0"
IPMIMAJOR=250

# Error codes
EUSAGE=200
ENODEVICE=201
ENOMODULE=202
ENOIPMI=203
EIPMIERR=204
ENOOVERRIDE=205
ENOSENSOR=206

# errmsg: print the message in $1 if the -v flag is set
errmsg () {
    if [ $VERBOSE -eq 1 ]; then
        echo $1
    fi
}

# diemsg: print the message in $2 if the -v flag is set, then exit 
# with error code $1
diemsg () {
    if [ $VERBOSE -eq 1 ]; then
        echo $2
    fi
    exit $1
}

# Print the version number and exit
echoversionint () {
    echo $VERSIONINT;
    exit 0;
}

# Print the version string and exit
echoversion () {
    echo $VERSIONSTR;
    exit 0;
}

# Usage output
usage() {
    echo $VERSIONSTR;
    echo "Usage: ipmiq [options] [command [arguments]]"
    echo ""
    echo "Options:"
    echo "  -h, -?, --help                Displays usage output."
    echo ""
    echo "  -t XX                         Specifies the target device."
    echo ""
    echo "                                XX may be the raw IPMB address of the target"
    echo "                                expressed in hexadecimal notation (e.g. 0x84)"
    echo "                                or decimal notation (e.g. 132)."
    echo ""
    echo "                                XX may also be expressed in slot notation" 
    echo "                                (slot number proceeded by the lowercase"
    echo "                                's', e.g. s2)."
    echo ""
    echo "  -o                            Enable safety override.  Required for power"
    echo "                                commands that affect the local device."
    echo ""
    echo "  -v                            Enable verbose output."
    echo ""
    echo "  -V                            Display version information."
    echo "  -VV                           Display version number only."
    echo ""
    echo "Commands:"
    echo "  install [nomod]               Prepares the local environment for IPMI"
    echo "                                interaction.  Pass the nomod argument"
    echo "                                if IPMI support is built into the kernel."
    echo ""
    echo "  remove [nomod]                Removes IPMI device support from the local"
    echo "                                environment.  Pass the nomod argument"
    echo "                                if IPMI support is built into the kernel."
    echo ""
    echo "  getaddr                       Returns the IPMB address of the device."
    echo ""
    echo "  getslot                       Returns the logical slot ID of the device."
    echo ""
    echo "  power on                      Turns on the device."
    echo "  power off                     Turns off the device."
    echo "  power cycle                   Performs a complete power cycle."
    echo "  power reset                   Performs a reset without cycling power."
    echo "  power status                  Returns the power state of the device."
    echo ""
    echo "  sensor [name]                 Returns the current value of the sensor."
	echo "  sensor list                   Lists available sensors."
    echo "" 
    echo "  led on                        Turns on the hotswap LED."
    echo "  led off                       Turns off the hotswap LED."
    echo "  led blink                     Causes the hotswap LED to blink."
    echo "  led restore                   Restores control of the LED to the device."
    echo ""
    exit $EUSAGE;
}

# vrfy_ipmi: checks that the ipmitool application is available
vrfy_ipmi () {
    $ICMD -V &>/dev/null

    if [ $? -ne 0 ]; then
        diemsg ENOIPMI "ipmitool program not found"
    fi
}

# my_addr: retrieves the local IPMB address using the ipmitool tool
my_addr () {    
    if [ "$MYADDR" = "" ]; then
        MYADDR=`$ICMD picmg addrinfo | grep IPMB | awk '{print $4}'`
    fi
}

# fix_addr transforms the -t XX command line option into meaningful
# address arguments for TARGETADDRARG and SOURCEADDRARG
fix_addr () {

    # If no argument is specified, then leave the addresses as default
    if [ "$TARGETCMD" = "" ]; then
        return 0;
    fi

    # If a "sX" argument is specified, translate it into the correct hex
    if [ "${TARGETCMD:0:1}" = "s" -o "${TARGETCMD:0:1}" = "S" ]; then
        TARGETSLOTID=${TARGETCMD:1}
        TARGETSLOTID=`expr $TARGETSLOTID \* 2`
        TARGETCMD=`expr 128 + $TARGETSLOTID`
    fi

    # Since we're addressing someone, we also need to know our own address
    # for return messages
    my_addr;

    # Now fill in the address globals for the ipmitool app
    TARGETADDR="${TARGETCMD}"
    SOURCEADDR="${MYADDR}"
    TARGETADDRARG="-t ${TARGETCMD}"
    SOURCEADDRARG="-m ${MYADDR}"
}

# cmd_install: load the kernel module and character device
cmd_install () {

    # If we're using a kernel module, install the kernel modules if they aren't
    # already installed.. notice that devintf passes the ipmi_major parameter.
    if [ "$1" != "nomod" ]; then
        lsmod | grep ipmi_msghandler &>/dev/null || modprobe ipmi_msghandler &>/dev/null || diemsg ENOMODULE "modprobe failed: ipmi_msghandler";
        lsmod | grep ipmi_si_drv &>/dev/null || modprobe ipmi_si_drv &>/dev/null || diemsg ENOMODULE "modprobe failed: ipmi_si_drv";
        lsmod | grep ipmi_devintf &>/dev/null || modprobe ipmi_devintf ipmi_major=$IPMIMAJOR &>/dev/null || diemsg ENOMODULE "modprobe failed: ipmi_devintf";
    fi

    # If the character device doesn't exist, create it.
    # Notice that this does not check the major number of the device file.
    # This shouldn't be a problem unless someone changes the major number
    # mid-session.  Then you can just run remove/install to reinit.
    if [ ! -c "$IPMIDEV" ]; then
        mknod $IPMIDEV c $IPMIMAJOR 0 &>/dev/null || diemsg ENODEVICE "mknod failed";
    fi

    exit 0;
}

# cmd_remove: simple remove commands to kill the kernel module and char device.
# Note that only the ipmi_devintf module is removed.  The others are left in so
# the IPMI watchdog can remain functional.
cmd_remove () {
    
    if [ -c "$IPMIDEV" ]; then
        rm -f $IPMIDEV &>/dev/null;
    fi
    
    if [ "$1" != "nomod" ]; then
        rmmod ipmi_devintf &>/dev/null;
    fi

    exit 0;
}

# cmd_power: the main power function.  $1 decides which action to take
cmd_power () {

    # Housekeeping before we begin
    vrfy_ipmi;
    fix_addr;

    # Initialize some variables
    POWERCMD=0
    POWERSTATUS=0
    
    # lowercase
    LWRARG=`echo "$1" | tr A-Z a-z`

    # Check the argument and assign variables
    case $LWRARG in
        "off" | 0) POWERCMD="off";;
        "on" | 1)  POWERCMD="on";;
        "cycle" | 2) POWERCMD="cycle";;
        "reset" | 3) POWERCMD="reset";;
        "status" | 4) POWERCMD="status"; POWERSTATUS=1;;
        *) usage;;
    esac

    # If POWERSTATUS=0 then we are performing an action rather than a query.
    if [ $POWERSTATUS -eq 0 ]; then

        # If we are targetting ourselves, the safety override switch -o must be 
        # passed
        if [ "$TARGETADDR" = "$SOURCEADDR" ]; then
            if [ $OVERRIDE -eq 0 ]; then
                diemsg $ENOOVERRIDE "Error: Must provide -o override option to affect yourself"
            fi  
        fi
        
        # If everything is good, run the power command and return
        $ICMD $TARGETADDRARG $SOURCEADDRARG power $POWERCMD &>/dev/null
        RETVAL=$?
        
        if [ $RETVAL -eq 0 ]; then
            echo "1"
        else
            echo "0"
        fi
        
        exit $RETVAL

    # Otherwise we are performing the status query
    else
        # So run the query to get the ipmitool output
        POWERSTR=`$ICMD $TARGETADDRARG $SOURCEADDRARG power $POWERCMD 2>&1`
        if [ $? -ne 0 ]; then
            exit $?
        fi

        # And process that output to check on vs. off
        POWERSTR=`echo "${POWERSTR}" | grep "Chassis Power is" | awk '{print $4}'`
        if [ "$POWERSTR" = "on" ]; then
            echo "1"; exit 0;
        else
            echo "0"; exit 0;
        fi
        
    fi
}


# cmd_sensor: the main sensor function.  $1 decides which sensor to read
cmd_sensor () {

    # Housekeeping before we begin
    vrfy_ipmi;
    fix_addr;

    # Check the argument and assign variables
    case `echo "$1" | tr A-Z a-z` in
        "v48" ) SENSORNAME="Vcc -48V";;
        "v12" ) SENSORNAME="Vcc +12V";;
        "v50" ) SENSORNAME="Vcc +5V";;
        "v33" ) SENSORNAME="Vcc +3.3V";;
        "v25" ) SENSORNAME="Vcc +2.5V";;
        "v18" ) SENSORNAME="Vcc +1.8V";;
        "v15" ) SENSORNAME="Vcc +1.5V";;
        "v1.2" ) SENSORNAME="Vcc +1.2V SUS";;
        "v11" ) SENSORNAME="Vcc +1.1V SUS";;
        "f48a" ) SENSORNAME="-48V A Pres-Fuse";;
        "f48b" ) SENSORNAME="-48V B Pres-Fuse";;
        "troom" ) SENSORNAME="Temp Air Inlet";;
        "tcore0" ) SENSORNAME="Temp CPU 0";;
        "tcore1" ) SENSORNAME="Temp CPU 1";;
        "tmez" ) SENSORNAME="Temp Mez Area";;   
        "list" ) SENSORNAME="list";;
        *) SENSORNAME=$1;;
    esac

    if [ "$SENSORNAME" = "list" ]; then
        $ICMD $TARGETADDRARG $SOURCEADDRARG sdr list all 2>&1 | grep "ok"
        exit 0
    fi

    # Run the sensor command
    SENSORDATA=`$ICMD $TARGETADDRARG $SOURCEADDRARG sdr get "${SENSORNAME}" 2>&1 | grep "Sensor Reading"`
    RETVAL=$?
        
    if [ $RETVAL -ne 0 ]; then
        diemsg $EIPMIERR "IPMI Error attempting to read sensor ${SENSORNAME}"
    fi

    READING=`echo "${SENSORDATA}" | awk '{print $4}'`

    if [ "$READING" = "No" ]; then
        diemsg $ENOSENSOR "Invalid sensor"
    fi

    echo $READING
    exit 0
}   


# cmd_led: the main power function.  $1 decides which action to take
cmd_led () {

    # Housekeeping before we begin
    vrfy_ipmi;
    fix_addr;

    # lowercase
    LWRARG=`echo "$1" | tr A-Z a-z`
    LEDCMD=0

    # Check the argument and assign variables
    case $LWRARG in
        "off" | 0) LEDCMD=0;;
        "on" | 1)  LEDCMD=255;;
        "blink" | 2) LEDCMD=15;;
        "restore" | 3) LEDCMD=252;;
        *) usage;;
    esac

    # Rrun the power command and return
    $ICMD $TARGETADDRARG $SOURCEADDRARG picmg led set 0 0 $LEDCMD 15 1 &>/dev/null
    RETVAL=$?
        
    if [ $RETVAL -eq 0 ]; then
        echo "1"
    else
        echo "0"
    fi
        
    exit $RETVAL

}


# cmd_getslot: returns the physical slot address of the device
cmd_getslot () {
    vrfy_ipmi;
    fix_addr;

    ADDRSTR=`$ICMD $TARGETADDRARG $SOURCEADDRARG picmg addrinfo 2>&1 | grep "Site ID"`
    if [ $? -ne 0 ]; then
        exit $?
    fi
    ADDRSTR=`echo "${ADDRSTR}" | awk '{print $4}'`
    if [ "$ADDRSTR" = "" ]; then
        exit $EIPMIERR;
    fi
    echo $ADDRSTR
    exit 0;
}

# cmd_getaddr: returns the IPMB address of the device
cmd_getaddr () {
    vrfy_ipmi;
    fix_addr;


    ADDRSTR=`$ICMD $TARGETADDRARG $SOURCEADDRARG picmg addrinfo 2>&1 | grep IPMB`
    if [ $? -ne 0 ]; then
        exit $?
    fi
    ADDRSTR=`echo "${ADDRSTR}" | awk '{print $4}'`
    if [ "$ADDRSTR" = "" ]; then
        exit $EIPMIERR;
    fi
    echo $ADDRSTR
    exit 0;
}

if [ $# -eq 0 ]; then
    usage;
fi

while [ $# -ge 1 ]; do
    case $1 in
        "-h" | "-?" | "--help" ) 
            usage;
            ;;
        "-t" ) 
            if [ $# -eq 1 ]; 
            then
                usage;  
            fi

            TARGETCMD="$2"
            shift
            ;;
        "-o" ) 
            OVERRIDE=1
            ;;
        "-v" ) 
            VERBOSE=1
            ;;
        "-V" )
            echoversion;
            ;;
        "-VV" )
            echoversionint;
            ;;
        "install" )
            if [ "$2" = "nomod" ]; then
                cmd_install 1
            else
                cmd_install 0
            fi
            ;;
       "remove" )
            if [ "$2" = "nomod" ]; then
                cmd_remove 1
            else
                cmd_remove 0
            fi
            ;;
        "power" )
            if [ $# -eq 1 ]; 
            then
                usage;  
            fi
            cmd_power $2
            ;;
        "sensor" )
            if [ $# -eq 1 ]; 
            then
                usage;  
            fi
            cmd_sensor $2 $3
            ;;
        "led" )
            if [ $# -eq 1 ];
            then
                usage;
            fi
            cmd_led $2
            ;;
        "getslot" )
            cmd_getslot;
            ;;
        "getaddr" )
            cmd_getaddr;
            ;;
        * )
            usage;
            ;;
    esac
    shift
done




