]> git.proxmox.com Git - mirror_zfs.git/blob - cmd/zed/zed.d/statechange-led.sh
Enable shellcheck to run for select scripts
[mirror_zfs.git] / cmd / zed / zed.d / statechange-led.sh
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 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
32 if [ ! -d /sys/class/enclosure ] ; then
33 exit 1
34 fi
35
36 if [ "${ZED_USE_ENCLOSURE_LEDS}" != "1" ] ; then
37 exit 2
38 fi
39
40 zed_check_cmd "$ZPOOL" || exit 4
41 zed_check_cmd awk || exit 5
42
43 # Global used in set_led debug print
44 vdev=""
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 #
57 check_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 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
91 state_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 #
113 process_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
168 if [ ! -z "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ ! -z "$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"
173 else
174 # Process the entire pool
175 poolname=$(zed_guid_to_pool "$ZEVENT_POOL_GUID")
176 process_pool "$poolname"
177 fi