1#!/bin/sh 2# 3# Turn off/on the VDEV's enclosure fault LEDs when the pool's state changes. 4# 5# Turn the VDEV's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL. 6# Turn the LED off when it's back ONLINE again. 7# 8# This script run in two basic modes: 9# 10# 1. If $ZEVENT_VDEV_ENC_SYSFS_PATH and $ZEVENT_VDEV_STATE_STR are set, then 11# only set the LED for that particular VDEV. This is the case for statechange 12# events and some vdev_* events. 13# 14# 2. If those vars are not set, then check the state of all VDEVs in the pool 15# and set the LEDs accordingly. This is the case for pool_import events. 16# 17# Note that this script requires that your enclosure be supported by the 18# Linux SCSI enclosure services (ses) driver. The script will do nothing 19# if you have no enclosure, or if your enclosure isn't supported. 20# 21# Exit codes: 22# 0: enclosure led successfully set 23# 1: enclosure leds not available 24# 2: enclosure leds administratively disabled 25# 3: The led sysfs path passed from ZFS does not exist 26# 4: $ZPOOL not set 27# 5: awk is not installed 28 29[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc" 30. "${ZED_ZEDLET_DIR}/zed-functions.sh" 31 32if [ ! -d /sys/class/enclosure ] ; then 33 exit 1 34fi 35 36if [ "${ZED_USE_ENCLOSURE_LEDS}" != "1" ] ; then 37 exit 2 38fi 39 40zed_check_cmd "$ZPOOL" || exit 4 41zed_check_cmd awk || exit 5 42 43# Global used in set_led debug print 44vdev="" 45 46# check_and_set_led (file, val) 47# 48# Read an enclosure sysfs file, and write it if it's not already set to 'val' 49# 50# Arguments 51# file: sysfs file to set (like /sys/class/enclosure/0:0:1:0/SLOT 10/fault) 52# val: value to set it to 53# 54# Return 55# 0 on success, 3 on missing sysfs path 56# 57check_and_set_led() 58{ 59 file="$1" 60 val="$2" 61 62 if [ ! -e "$file" ] ; then 63 return 3 64 fi 65 66 # If another process is accessing the LED when we attempt to update it, 67 # the update will be lost so retry until the LED actually changes or we 68 # timeout. 69 for _ in $(seq 1 5); do 70 # We want to check the current state first, since writing to the 71 # 'fault' entry always causes a SES command, even if the 72 # current state is already what you want. 73 current=$(cat "${file}") 74 75 # On some enclosures if you write 1 to fault, and read it back, 76 # it will return 2. Treat all non-zero values as 1 for 77 # simplicity. 78 if [ "$current" != "0" ] ; then 79 current=1 80 fi 81 82 if [ "$current" != "$val" ] ; then 83 echo "$val" > "$file" 84 zed_log_msg "vdev $vdev set '$file' LED to $val" 85 else 86 break 87 fi 88 done 89} 90 91state_to_val() 92{ 93 state="$1" 94 if [ "$state" = "FAULTED" ] || [ "$state" = "DEGRADED" ] || \ 95 [ "$state" = "UNAVAIL" ] ; then 96 echo 1 97 elif [ "$state" = "ONLINE" ] ; then 98 echo 0 99 fi 100} 101 102# process_pool ([pool]) 103# 104# Iterate through a pool (or pools) and set the VDEV's enclosure slot LEDs to 105# the VDEV's state. 106# 107# Arguments 108# pool: Optional pool name. If not specified, iterate though all pools. 109# 110# Return 111# 0 on success, 3 on missing sysfs path 112# 113process_pool() 114{ 115 pool="$1" 116 rc=0 117 118 # Lookup all the current LED values and paths in parallel 119 #shellcheck disable=SC2016 120 cmd='echo led_token=$(cat "$VDEV_ENC_SYSFS_PATH/fault"),"$VDEV_ENC_SYSFS_PATH",' 121 out=$($ZPOOL status -vc "$cmd" "$pool" | grep 'led_token=') 122 123 #shellcheck disable=SC2034 124 echo "$out" | while read -r vdev state read write chksum therest; do 125 # Read out current LED value and path 126 tmp=$(echo "$therest" | sed 's/^.*led_token=//g') 127 vdev_enc_sysfs_path=$(echo "$tmp" | awk -F ',' '{print $2}') 128 current_val=$(echo "$tmp" | awk -F ',' '{print $1}') 129 130 if [ "$current_val" != "0" ] ; then 131 current_val=1 132 fi 133 134 if [ -z "$vdev_enc_sysfs_path" ] ; then 135 # Skip anything with no sysfs LED entries 136 continue 137 fi 138 139 if [ ! -e "$vdev_enc_sysfs_path/fault" ] ; then 140 #shellcheck disable=SC2030 141 rc=1 142 zed_log_msg "vdev $vdev '$file/fault' doesn't exist" 143 continue; 144 fi 145 146 val=$(state_to_val "$state") 147 148 if [ "$current_val" = "$val" ] ; then 149 # LED is already set correctly 150 continue; 151 fi 152 153 if ! check_and_set_led "$vdev_enc_sysfs_path/fault" "$val"; then 154 rc=1 155 fi 156 157 done 158 159 #shellcheck disable=SC2031 160 if [ "$rc" = "0" ] ; then 161 return 0 162 else 163 # We didn't see a sysfs entry that we wanted to set 164 return 3 165 fi 166} 167 168if [ -n "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ -n "$ZEVENT_VDEV_STATE_STR" ] ; then 169 # Got a statechange for an individual VDEV 170 val=$(state_to_val "$ZEVENT_VDEV_STATE_STR") 171 vdev=$(basename "$ZEVENT_VDEV_PATH") 172 check_and_set_led "$ZEVENT_VDEV_ENC_SYSFS_PATH/fault" "$val" 173else 174 # Process the entire pool 175 poolname=$(zed_guid_to_pool "$ZEVENT_POOL_GUID") 176 process_pool "$poolname" 177fi 178