#!/bin/sh SDIR=$( cd ${ dirname $0; }; print -n "$PWD" ) typeset -r FPROG=${.sh.file} typeset -r PROG=${FPROG##*/} LIC='[-?$Id$] [-copyright?Copyright (c) 2013 Jens Elkner. All rights reserved.] [-license?CDDL 1.0]' for H in log.kshlib man.kshlib ; do HELPER=$SDIR/$H [[ -r $HELPER ]] && . $HELPER && continue HELPER=${ whence $H; } if [[ -n $HELPER ]]; then . $HELPER elif [[ -r $SDIR/etc/$H ]]; then . $SDIR/etc/$H else print -u2 "$H not found - exiting." && exit fi done unset HELPER typeset -A INFO=( ) # dueto a ksh bug we can't use -S Lun_t.INFO typeset IDX='' # LUN indices - wanna preserve order typeset -T Lun_t=( typeset -h 'scsi_device address' H_ADDR='' typeset -ih 'Instance' H_INST=-1 typeset -h 'Serial Number' H_SN='' typeset -h 'Vendor ID' H_VID='' typeset -h 'Product ID' H_PID='' # tunables typeset -ih 'throttle-max (max #cmds allowed in transport)' T_TMAX=-1 typeset -h 'controller-type' T_CTYPE='' typeset -ih 'retries-notready (# of tries before error)' T_RNR=-1 typeset -ih 'retries-busy (# of tries before cmd failing)' T_BR=-1 typeset -ih 'retries-reset (# of tries before reset)' T_RR=-1 typeset -ih 'timeout-releasereservation' T_TRR=-1 typeset -ih 'throttle-min (min #cmds allowed in transport)' T_TMIN=-1 typeset -h 'disksort (LBA sorting enabled)' T_DS='' typeset -h 'reset-lun (device supports LUN reset)' T_RLE='' typeset -h 'cache-nonvolatile (suppress cache flush)' T_NVC='' typeset -h 'power-condition (power condition enabled)' T_PCD='' typeset -ih 'delay-busy (µs to wait before retry throttled cmd)' T_BT=-1 typeset -ih 'retries-timeout (# of times to retry a normal op)' T_RT=-1 typeset -h 'rmw-type' T_RMWTYPE='' typeset -h 'emulation-rmw (RMW forced)' T_RMWEMU='' typeset -iuh 'physical-block-size [byte]]' T_PBSZ=-1 typeset -h 'mmc-gesn-polling (GetEventStatusPolling enabled)' T_GESNP='' # misc typeset -iuh 'system blocksize [byte]]' SBSZ=-1 typeset -iuh 'sd logical blocksize [byte]]' LBSZ=-1 typeset -iulh 'sd system blocks' SBC=-1 typeset -ih 'Command timeout (Timeout for completion)' CMDT=-1 typeset -iuh 'max. DMA transfer size' MAXXSZ=-1 typeset -ih 'is SSD' SSD=-1 typeset -ih 'sync cache command supported' SCCMD=-1 typeset -ih 'sync nv cache supported' SNVCMD=-1 typeset -ih 'sync nv cache required' SNVCMDR=-1 typeset -ih 'write cache enabled' RWCE=-1 typeset -ih 'adaptive throttling enabled' AT=-1 typeset -ih 'Power Management enabled' PME=-1 typeset -ih 'Power Management supported' PMS=-1 typeset -ih 'is Hotpluggable' HP=-1 typeset -ih 'Current Power Level' PLVL=-1 typeset -h 'Dummy for ksh bug' H_DUMMY='' # workaround for off by 1 bug typeset -S H_TVARS='' typeset -S H_MVARS='' function generateInfo { integer START=0 BUG=0 T typeset NAME LINE LAST typeset -l TVAR Lun_t --api 2>&1 | while read LINE ; do if (( ${START} == 0 )); then if [[ $LINE == '.H1 H_ADDR' ]]; then START=1 read LINE LINE=${LINE#*.} [[ -z $LINE ]] && BUG=1 fi continue fi if [[ ${LINE:0:4} == '.H1 ' ]]; then NAME=${LINE:4} if [[ ${NAME:0:2} == 'T_' ]]; then H_TVARS+="$NAME " T=1 elif [[ ${NAME:0:2} != 'H_' ]]; then H_MVARS+="$NAME " T=0 fi else LINE=${LINE#*. } if (( $T )); then TVAR=$LINE LINE=$TVAR fi if (( $BUG )); then INFO["${LAST}"]=${LINE%.} LAST=$NAME else INFO["${NAME}"]=${LINE%.} fi [[ $NAME == 'H_DUMMY' ]] && break fi done } function getDetails { [[ -z $H_TVARS ]] && _.generateInfo print "\nsd${_.H_INST}:\t'${_.H_VID}${_.H_PID}'\tS/N: ${_.H_SN}" print '\tTunables:' for NAME in $H_TVARS ; do typeset -n V=_.${NAME} printf '\t\t%-50s:\t%s\n' "${INFO[$NAME]}" "${V}" done print '\tMisc:' for NAME in $H_MVARS ; do typeset -n V=_.${NAME} printf '\t\t%-50s:\t%s\n' "${INFO[$NAME]}" "${V}" done } # typeset -ft generateInfo getDetails ) Lun_t -A LUNS function getCtype { case $1 in 0) print '0 (CDROM)' ;; 1) print '1 (MD21)' ;; 2) print '2 (SCSI ComnCmdSet)' ;; 3) print '3 (RW Optical Disk)' ;; 4) print '4 (PXRE)' ;; *) print "$1" ;; esac } function getBool { if [[ $1 == "0" ]]; then [[ -z $2 ]] && print 'false' || print 'true' return 0 fi [[ -z $2 ]] && print 'true' || print 'false' } function getRMWtype { case $1 in 0) print '0 (RMW with warn)' ;; 1) print '1 (RMW without warn)' ;; 2) print '2 (no RMW -> error)' ;; *) print "$1" ;; esac } # see http://src.iws.cs.ovgu.de/source/xref/illumos-gate/usr/src/uts/common/sys/scsi/targets/sddef.h#217 function getLUNs { typeset ADDR='' LINE V N print '*sd_state::walk softstate |::print -dL "struct sd_lun"' | \ $PFEXEC mdb -k | while read LINE ; do [[ ${LINE: -1:1} == '{' || ${LINE: -1:1} == '}' ]] && continue # extract plain name and value N=${LINE% = *} V=${.sh.match# = } case "$N" in un_sd) IDX+="$V "; ADDR=$V ; LUNS[$ADDR]=( H_ADDR=$V ) ;; un_throttle) LUNS[$ADDR].T_TMAX=${V#0t} ;; un_ctype) LUNS[$ADDR].T_CTYPE=${ getCtype ${V#0t} ; } ;; un_notready_retry_count) LUNS[$ADDR].T_RNR=${V#0t} ;; un_busy_retry_count) LUNS[$ADDR].T_BR=${V#0t} ;; un_reset_retry_count) LUNS[$ADDR].T_RR=${V#0t} ;; un_reserve_release_time) LUNS[$ADDR].T_TRR=${V#0t} ;; un_min_throttle) LUNS[$ADDR].T_TMIN=${V#0t} ;; un_f_disksort_disabled) LUNS[$ADDR].T_DS=${ getBool ${V#0t} not ; } ;; un_f_lun_reset_enabled) LUNS[$ADDR].T_RLE=${ getBool ${V#0t} ; } ;; un_f_suppress_cache_flush) LUNS[$ADDR].T_NVC=${ getBool ${V#0t} ; };; un_f_power_condition_disabled) LUNS[$ADDR].T_PCD=${ getBool ${V#0t} not ; } ;; un_busy_timeout) LUNS[$ADDR].T_BT=${V#0t} ;; un_retry_count) LUNS[$ADDR].T_RT=${V#0t} ;; un_f_rmw_type) LUNS[$ADDR].T_RMWTYPE=${ getRMWtype ${V#0t} ; } ;; un_f_enable_rmw) LUNS[$ADDR].T_RMWEMU=${ getBool ${V#0t} ; } ;; un_phy_blocksize) LUNS[$ADDR].T_PBSZ=${V#0t} ;; un_f_mmc_gesn_polling) LUNS[$ADDR].T_GESNP=${V#0t} ;; un_sys_blocksize) LUNS[$ADDR].SBSZ=${V#0t} ;; un_tgt_blocksize) LUNS[$ADDR].LBSZ=${V#0t} ;; un_blockcount) LUNS[$ADDR].SBC=${V#0t} ;; un_cmd_timeout) LUNS[$ADDR].CMDT=${V#0t} ;; un_max_xfer_size) LUNS[$ADDR].MAXXSZ=${V#0t} ;; un_f_is_solid_state) LUNS[$ADDR].SSD=${V#0t} ;; un_f_sync_cache_supported) LUNS[$ADDR].SCCMD=${V#0t} ;; un_f_sync_nv_supported) LUNS[$ADDR].SNVCMD=${V#0t} ;; un_f_sync_cache_required) LUNS[$ADDR].SNVCMDR=${V#0t} ;; un_f_write_cache_enabled) LUNS[$ADDR].RWCE=${V#0t} ;; un_f_use_adaptive_throttle) LUNS[$ADDR].AT=${V#0t} ;; un_f_pm_is_enabled) LUNS[$ADDR].PME=${V#0t} ;; un_f_is_hotpluggable) LUNS[$ADDR].HP=${V#0t} ;; un_power_level) LUNS[$ADDR].PLVL=${V#0t} ;; un_f_pm_supported) LUNS[$ADDR].PMS=${V#0t} ;; esac done } function getInqData { typeset DEVS="${!LUNS[*]}" N V ADDR typeset -a ADEVS=( $DEVS ) integer I=-1 print -r '::echo "'"${DEVS// /\\n}"'" |' \ '::print "struct scsi_device" sd_inq |' \ '::print "struct scsi_inquiry" inq_vid inq_pid inq_serial ' | \ $PFEXEC mdb -k | while read LINE ; do N=${LINE% = *} V=${.sh.match:6} [[ ${V: -3:3} != '" ]' ]] && V='' case $N in inq_vid) (( I++ )) ; ADDR=${ADEVS[$I]} ; LUNS[$ADDR].H_VID=${V%\"*} ;; inq_pid) LUNS[$ADDR].H_PID=${V%\"*} ;; inq_serial) LUNS[$ADDR].H_SN=${V%\"*} ;; esac done I=0 print -r '::echo "'"${DEVS// /\\n}"'" |' \ '::print "struct scsi_device" sd_dev |' \ '::print -d "struct dev_info" devi_instance' | \ $PFEXEC mdb -k | while read N N V ; do ADDR=${ADEVS[$I]} LUNS[$ADDR].H_INST=${V#0t} (( I++ )) done } function startsWith { typeset -n LIST=$1 typeset ID=$2 X [[ -z $ID ]] && return 1 for X in ${LIST[@]}; do [[ ${ID:0:${#X}} == "$X" ]] && return 0 done return 1 } function printDetails { typeset -a L=( ) SNS=( ) VIDS=( ) PIDS=( ) typeset SDS='' X T integer NEEDMATCH=0 OK # normalize lists if [[ -n $SDLIST ]]; then L=( ${SDLIST//sd} ) (( ${#L[*]} )) && SDS=" ${L[*]} " && (( NEEDMATCH++ )) fi if [[ -n $SNLIST ]]; then SNS=( $SNLIST ) (( ${#SNS[*]} )) && (( NEEDMATCH++ )) fi if [[ -n $VIDLIST ]]; then VIDS=( $VIDLIST ) (( ${#VIDS[*]} )) && (( NEEDMATCH++ )) fi if [[ -n $PIDLIST ]]; then PIDS=( $PIDLIST ) (( ${#PIDS[*]} )) && (( NEEDMATCH++ )) fi for X in $IDX ; do if (( $NEEDMATCH )); then OK=0 if [[ -n $SDS ]]; then T=${LUNS[$X].H_INST} [[ $SDS == ~(F)" $T " ]] && OK=1 fi (( ! $OK )) && startsWith SNS ${LUNS[$X].H_SN} && OK=1 (( ! $OK )) && startsWith VIDS ${LUNS[$X].H_VID} && OK=1 (( ! $OK )) && startsWith PIDS ${LUNS[$X].H_PID} && OK=1 (( ! $OK )) && continue fi T=${LUNS[$X].getDetails} print "$T" done } function showUsage { typeset WHAT="$2" getopts -a "$PROG" "${ print ${Man.FUNC[$WHAT]}; }" OPT --man } Man.addFunc MAIN '' '[+NAME?'"$PROG"' - script to print out sd properties.] [+DESCRIPTION?Get the properties assigned to a disk instance.] [h:help?Print this help and exit.] [F:functions?Print out a list of names of all currently defined script functions and exit (see \btypeset +f\b).] [H:usage]:[function?Show the usage info for the function with the given name if available and exit. (see also option \b-F\b).] [T:trace]:[fname_list?A comma or whitespace separated list of function names, which should be traced when running this script. Because tracing gets activated when this option gets processed, it should be given as the first option to the script. Otherwise you may miss some traces for options processed earlier.] [i:instance]:[sd_list?A comma or whitespace separated list of sd instances to list (e.g. sd1,sd5,sd10).] [s:sn]:[sn_list?A comma or whitespace separated list of serial numbers of the HDDs to list (e.g. 6TB1234,6TB3456). A match occurs, if a HDD serial number starts with a string in the list.] [v:vid]:[vid_list?A comma or whitespace separated list of vendor IDs of the HDDs to list (e.g. STEC,OCZ). A match occurs, if a HDD vendor ID starts with a string in the list.] [p:pid]:[pid_list?A comma or whitespace separated list of product IDs of the HDDs to list (e.g. ST,Talos). A match occurs, if a HDD product ID starts with a string in the list.] ' SDLIST='' SNLIST='' VIDLIST='' PIDLIST='' while getopts "${ print ${Man.FUNC[MAIN]}; }" option ; do case "$option" in h) showUsage $PROG MAIN ; exit 0 ;; F) typeset +f ; exit 0 ;;F) typeset +f ; exit 0 ;; T) typeset -ft ${OPTARG//,/ } ;; H) if [[ ${OPTARG%_t} != $OPTARG ]]; then $OPTARG --man # self-defined types else showUsage "$OPTARG" "$OPTARG" # functions fi exit 0 ;; i) SDLIST+="${OPTARG//,/ } " ;; s) SNLIST+="${OPTARG//,/ } " ;; v) VIDLIST+="${OPTARG//,/ } " ;; p) PIDLIST+="${OPTARG//,/ } " ;; esac done X=$(( OPTIND - 1 )) shift $X (( ${ id -u ; } == 0 )) && PFEXEC='' || PFEXEC='pfexec' getLUNs getInqData printDetails