1#!/bin/sh 2# 3# Turn off/on vdevs' enclosure fault LEDs when their pool's state changes. 4# 5# Turn a vdev's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL. 6# Turn its 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 [ -z "$val" ]; then 63 return 0 64 fi 65 66 if [ ! -e "$file" ] ; then 67 return 3 68 fi 69 70 # If another process is accessing the LED when we attempt to update it, 71 # the update will be lost so retry until the LED actually changes or we 72 # timeout. 73 for _ in 1 2 3 4 5; do 74 # We want to check the current state first, since writing to the 75 # 'fault' entry always causes a SES command, even if the 76 # current state is already what you want. 77 read -r current < "${file}" 78 79 # On some enclosures if you write 1 to fault, and read it back, 80 # it will return 2. Treat all non-zero values as 1 for 81 # simplicity. 82 if [ "$current" != "0" ] ; then 83 current=1 84 fi 85 86 if [ "$current" != "$val" ] ; then 87 echo "$val" > "$file" 88 zed_log_msg "vdev $vdev set '$file' LED to $val" 89 else 90 break 91 fi 92 done 93} 94 95state_to_val() 96{ 97 state="$1" 98 case "$state" in 99 FAULTED|DEGRADED|UNAVAIL) 100 echo 1 101 ;; 102 ONLINE) 103 echo 0 104 ;; 105 esac 106} 107 108# process_pool (pool) 109# 110# Iterate through a pool and set the vdevs' enclosure slot LEDs to 111# those vdevs' state. 112# 113# Arguments 114# pool: Pool name. 115# 116# Return 117# 0 on success, 3 on missing sysfs path 118# 119process_pool() 120{ 121 pool="$1" 122 123 # The output will be the vdevs only (from "grep '/dev/'"): 124 # 125 # U45 ONLINE 0 0 0 /dev/sdk 0 126 # U46 ONLINE 0 0 0 /dev/sdm 0 127 # U47 ONLINE 0 0 0 /dev/sdn 0 128 # U50 ONLINE 0 0 0 /dev/sdbn 0 129 # 130 ZPOOL_SCRIPTS_AS_ROOT=1 $ZPOOL status -c upath,fault_led "$pool" | grep '/dev/' | ( 131 rc=0 132 while read -r vdev state _ _ _ therest; do 133 # Read out current LED value and path 134 # Get dev name (like 'sda') 135 dev=$(basename "$(echo "$therest" | awk '{print $(NF-1)}')") 136 vdev_enc_sysfs_path=$(realpath "/sys/class/block/$dev/device/enclosure_device"*) 137 current_val=$(echo "$therest" | awk '{print $NF}') 138 139 if [ "$current_val" != "0" ] ; then 140 current_val=1 141 fi 142 143 if [ -z "$vdev_enc_sysfs_path" ] ; then 144 # Skip anything with no sysfs LED entries 145 continue 146 fi 147 148 if [ ! -e "$vdev_enc_sysfs_path/fault" ] ; then 149 rc=3 150 zed_log_msg "vdev $vdev '$file/fault' doesn't exist" 151 continue 152 fi 153 154 val=$(state_to_val "$state") 155 156 if [ "$current_val" = "$val" ] ; then 157 # LED is already set correctly 158 continue 159 fi 160 161 if ! check_and_set_led "$vdev_enc_sysfs_path/fault" "$val"; then 162 rc=3 163 fi 164 done 165 exit "$rc"; ) 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