diff --git a/ledcontrol/all/usr/bin/ugreen-diskiomon b/ledcontrol/all/usr/bin/ugreen-diskiomon new file mode 100755 index 0000000..fbca1b6 --- /dev/null +++ b/ledcontrol/all/usr/bin/ugreen-diskiomon @@ -0,0 +1,314 @@ +#!/usr/bin/bash + +# function for removing lockfile +exit-ugreen-diskiomon() { + if [[ -f "/var/run/ugreen-diskiomon.lock" ]]; then + rm "/var/run/ugreen-diskiomon.lock" + fi + kill $smart_check_pid 2>/dev/null + kill $zpool_check_pid 2>/dev/null + kill $disk_online_check_pid 2>/dev/null +} + +# trap exit and remove lockfile +trap 'exit-ugreen-diskiomon' EXIT + +# check if script is already running +if [[ -f "/var/run/ugreen-diskiomon.lock" ]]; then + echo "ugreen-diskiomon already running!" + exit 1 +fi +touch /var/run/ugreen-diskiomon.lock + +# use variables from config file (unRAID specific) +if [[ -f /boot/config/plugins/ugreenleds-driver/settings.cfg ]]; then + source /boot/config/plugins/ugreenleds-driver/settings.cfg +fi + +# load environment variables +if [[ -f /etc/ugreen-leds.conf ]]; then + source /etc/ugreen-leds.conf +fi + +# led-disk mapping (see https://github.com/miskcoo/ugreen_dx4600_leds_controller/pull/4) +MAPPING_METHOD=${MAPPING_METHOD:=ata} # ata, hctl, serial +led_map=(disk1 disk2 disk3 disk4 disk5 disk6 disk7 disk8) + +# hctl, $> lsblk -S -x hctl -o hctl,serial,name +# NOTE: It is reported that the order below should be adjusted for each model. +# Please check the disk mapping section in https://github.com/miskcoo/ugreen_dx4600_leds_controller/blob/master/README.md. +hctl_map=("0:0:0:0" "1:0:0:0" "2:0:0:0" "3:0:0:0" "4:0:0:0" "5:0:0:0" "6:0:0:0" "7:0:0:0") +# serial number, $> lsblk -S -x hctl -o hctl,serial,name +serial_map=(${DISK_SERIAL}) +# ata number, $> ls /sys/block | egrep ata\d +ata_map=("ata1" "ata2" "ata3" "ata4" "ata5" "ata6" "ata7" "ata8") + +if which dmidecode > /dev/null; then + product_name=$(dmidecode --string system-product-name) + case "${product_name}" in + DXP6800*) # tested on DXP6800 Pro + echo "Found UGREEN DXP6800 series" + hctl_map=("2:0:0:0" "3:0:0:0" "4:0:0:0" "5:0:0:0" "0:0:0:0" "1:0:0:0") + ata_map=("ata3" "ata4" "ata5" "ata6" "ata1" "ata2") + ;; + DX4600*) # tested on DX4600 Pro + echo "Found UGREEN DX4600 series" + ;; + DX4700*) + echo "Found UGREEN DX4700 series" + ;; + DXP2800*) # see issue #19 + echo "Found UGREEN DXP2800 series" + ;; + DXP4800*) + echo "Found UGREEN DXP4800 series" + ;; + DXP8800*) # tested on DXP8800 Plus + echo "Found UGREEN DXP8800 series" + # using the default mapping + ;; + *) + if [[ "${MAPPING_METHOD}" == "hctl" || "${MAPPING_METHOD}" == "ata" ]]; then + echo -e "\033[0;31mUsing the default HCTL order. Please check it maps to your disk slots correctly." + echo -e "If you confirm that the HCTL order is correct, or find it is different, you can " + echo -e "submit an issue to let us know, so we can update the script." + echo -e "Please read the disk mapping section in the link below for more details. " + echo -e " https://github.com/miskcoo/ugreen_dx4600_leds_controller/blob/master/README.md\033[0m" + fi + ;; + esac +elif [[ "${MAPPING_METHOD}" == "hctl" || "${MAPPING_METHOD}" == "ata" ]]; then + echo -e "\033[0;31minstalling the tool `dmidecode` is suggested; otherwise the script cannot detect your device and adjust the hctl/ata_map\033[0m" +fi +declare -A devices + +# set monitor SMART information to true by default if not running unRAID +if [[ -f /etc/unraid-version ]]; then + CHECK_SMART=false +else + CHECK_SMART=${CHECK_SMART:=true} +fi +# polling rate for smartctl. 360 seconds by default +CHECK_SMART_INTERVAL=${CHECK_SMART_INTERVAL:=360} +# refresh interval from disk leds +LED_REFRESH_INTERVAL=${LED_REFRESH_INTERVAL:=0.1} + +# whether to check zpool health +CHECK_ZPOOL=${CHECK_ZPOOL:=false} +# polling rate for checking zpool health. 5 seconds by default +CHECK_ZPOOL_INTERVAL=${CHECK_ZPOOL_INTERVAL:=5} + +# polling rate for checking disk online. 5 seconds by default +CHECK_DISK_ONLINE_INTERVAL=${CHECK_DISK_ONLINE_INTERVAL:=5} + +COLOR_DISK_HEALTH=${COLOR_DISK_HEALTH:="255 255 255"} +COLOR_DISK_UNAVAIL=${COLOR_DISK_UNAVAIL:="255 0 0"} +COLOR_ZPOOL_FAIL=${COLOR_ZPOOL_FAIL:="255 0 0"} +COLOR_SMART_FAIL=${COLOR_SMART_FAIL:="255 0 0"} +BRIGHTNESS_DISK_LEDS=${BRIGHTNESS_DISK_LEDS:="255"} + + +{ lsmod | grep ledtrig_oneshot > /dev/null; } || { modprobe -v ledtrig_oneshot && sleep 2; } + +function disk_enumerating_string() { + if [[ $MAPPING_METHOD == ata ]]; then + ls -ahl /sys/block | sed 's/\/$//' | awk '{ + if (match($0, /ata[0-9]+/)) { + ata = substr($0, RSTART, RLENGTH); + if (match($0, /[^\/]+$/)) { + basename = substr($0, RSTART, RLENGTH); + print basename, ata; + } + } + }' + elif [[ $MAPPING_METHOD == hctl || $MAPPING_METHOD == serial ]]; then + lsblk -S -o name,${MAPPING_METHOD},tran | grep sata + else + echo Unsupported mapping method: ${MAPPING_METHOD} + exit 1 + fi +} + +echo Enumerating disks based on $MAPPING_METHOD... +declare -A dev_map +while read line +do + blk_line=($line) + key=${blk_line[1]} + val=${blk_line[0]} + dev_map[${key}]=${val} + echo $MAPPING_METHOD ${key} ">>" ${dev_map[${key}]} +done <<< "$(disk_enumerating_string)" + +# initialize LEDs +declare -A dev_to_led_map +for i in "${!led_map[@]}"; do + led=${led_map[i]} + if [[ -d /sys/class/leds/$led ]]; then + echo oneshot > /sys/class/leds/$led/trigger + echo 1 > /sys/class/leds/$led/invert + echo 100 > /sys/class/leds/$led/delay_on + echo 100 > /sys/class/leds/$led/delay_off + echo "$COLOR_DISK_HEALTH" > /sys/class/leds/$led/color + echo "$BRIGHTNESS_DISK_LEDS" > /sys/class/leds/$led/brightness + + # find corresponding device + _tmp_str=${MAPPING_METHOD}_map[@] + _tmp_arr=(${!_tmp_str}) + + if [[ -v "dev_map[${_tmp_arr[i]}]" ]]; then + dev=${dev_map[${_tmp_arr[i]}]} + + if [[ -f /sys/class/block/${dev}/stat ]]; then + devices[$led]=${dev} + dev_to_led_map[$dev]=$led + else + # turn off the led if no disk installed on this slot + echo 0 > /sys/class/leds/$led/brightness + echo none > /sys/class/leds/$led/trigger + fi + else + # turn off the led if no disk installed on this slot + echo 0 > /sys/class/leds/$led/brightness + echo none > /sys/class/leds/$led/trigger + fi + fi +done + +# construct zpool device mapping +declare -A zpool_ledmap +if [ "$CHECK_ZPOOL" = true ]; then + echo Enumerating zpool devices... + while read line + do + zpool_dev_line=($line) + zpool_dev_name=${zpool_dev_line[0]} + zpool_scsi_dev_name="unknown" + # zpool_dev_state=${zpool_dev_line[1]} + case "$zpool_dev_name" in + sd*) + # remove the trailing partition number + zpool_scsi_dev_name=$(echo $zpool_dev_name | sed 's/[0-9]*$//') + ;; + dm*) + # find the underlying block device of the encrypted device + dm_slaves=($(ls /sys/block/${zpool_dev_name}/slaves)) + zpool_scsi_dev_name=${dm_slaves[0]} + ;; + *) + echo Unsupported zpool device type ${zpool_dev_name}. + ;; + esac + + # if the detected scsi device can be found in the mapping array + #echo zpool $zpool_dev_name ">>" $zpool_scsi_dev_name ">>" ${dev_to_led_map[${zpool_scsi_dev_name}]} + if [[ -v "dev_to_led_map[${zpool_scsi_dev_name}]" ]]; then + zpool_ledmap[$zpool_dev_name]=${dev_to_led_map[${zpool_scsi_dev_name}]} + echo "zpool device" $zpool_dev_name ">>" $zpool_scsi_dev_name ">> LED:"${zpool_ledmap[$zpool_dev_name]} + fi + done <<< "$(zpool status -L | egrep ^\\s*\(sd\|dm\))" + + function zpool_check_loop() { + while true; do + while read line + do + zpool_dev_line=($line) + zpool_dev_name=${zpool_dev_line[0]} + zpool_dev_state=${zpool_dev_line[1]} + + # TODO: do something if the pool is unhealthy? + + if [[ -v "zpool_ledmap[${zpool_dev_name}]" ]]; then + led=${zpool_ledmap[$zpool_dev_name]} + + if [[ "$(cat /sys/class/leds/$led/color)" != "$COLOR_DISK_HEALTH" ]]; then + continue; + fi + + if [[ "${zpool_dev_state}" != "ONLINE" ]]; then + echo "$COLOR_ZPOOL_FAIL" > /sys/class/leds/$led/color + echo Disk failure detected on /dev/$dev at $(date +%Y-%m-%d' '%H:%M:%S) + fi + + # ==== To recover from an error, you should restart the script ==== + ## case "${zpool_dev_state}" in + ## ONLINE) + ## # echo "$COLOR_DISK_HEALTH" > /sys/class/leds/$led/color + ## ;; + ## *) + ## echo "255 0 0" > /sys/class/leds/$led/color + ## ;; + ## esac + fi + done <<< "$(zpool status -L | egrep ^\\s*\(sd\|dm\))" + + sleep ${CHECK_ZPOOL_INTERVAL}s + done + } + + zpool_check_loop & + zpool_check_pid=$! +fi + +# check disk health if enabled +if [ "$CHECK_SMART" = true ]; then +( + while true; do + for led in "${!devices[@]}"; do + if [[ "$(cat /sys/class/leds/$led/color)" != "$COLOR_DISK_HEALTH" ]]; then + continue; + fi + + dev=${devices[$led]} + + if [[ -z $(smartctl -H /dev/${dev} | grep PASSED) ]]; then + echo "$COLOR_SMART_FAIL" > /sys/class/leds/$led/color + echo Disk failure detected on /dev/$dev at $(date +%Y-%m-%d' '%H:%M:%S) + continue + fi + done + sleep ${CHECK_SMART_INTERVAL}s + done +) & +smart_check_pid=$! +fi + +# check disk online status +( + while true; do + for led in "${!devices[@]}"; do + dev=${devices[$led]} + + if [[ "$(cat /sys/class/leds/$led/color)" != "$COLOR_DISK_HEALTH" ]]; then + continue; + fi + + if [[ ! -f /sys/class/block/${dev}/stat ]]; then + echo "$COLOR_DISK_UNAVAIL" > /sys/class/leds/$led/color 2>/dev/null + echo Disk /dev/$dev went offline at $(date +%Y-%m-%d' '%H:%M:%S) + continue + fi + done + sleep ${CHECK_DISK_ONLINE_INTERVAL}s + done +) & +disk_online_check_pid=$! + +# monitor disk activities +declare -A diskio_data_rw +while true; do + for led in "${!devices[@]}"; do + + # if $dev does not exist, diskio_new_rw="", which will be safe + diskio_new_rw="$(cat /sys/block/${devices[$led]}/stat 2>/dev/null)" + + if [ "${diskio_data_rw[$led]}" != "${diskio_new_rw}" ]; then + echo 1 > /sys/class/leds/$led/shot + fi + + diskio_data_rw[$led]=$diskio_new_rw + done + + sleep ${LED_REFRESH_INTERVAL}s + +done diff --git a/ledcontrol/all/usr/bin/ugreen-netdevmon b/ledcontrol/all/usr/bin/ugreen-netdevmon new file mode 100755 index 0000000..67299c1 --- /dev/null +++ b/ledcontrol/all/usr/bin/ugreen-netdevmon @@ -0,0 +1,88 @@ +#!/usr/bin/bash + +# function for removing lockfile +exit-ugreen-netdevmon() { + if [[ -f "/var/run/ugreen-netdevmon.lock" ]]; then + rm "/var/run/ugreen-netdevmon.lock" + fi +} + +# trap exit and remove lockfile +trap 'exit-ugreen-netdevmon' EXIT + +# check if script is already running +if [[ -f "/var/run/ugreen-netdevmon.lock" ]]; then + echo "ugreen-netdevmon already running!" + exit 1 +fi +touch /var/run/ugreen-netdevmon.lock + +{ lsmod | grep ledtrig_netdev > /dev/null; } || { modprobe -v ledtrig_netdev && sleep 2; } + +# load environment variables +if [[ -f /etc/ugreen-leds.conf ]]; then + source /etc/ugreen-leds.conf +fi + +COLOR_NETDEV_NORMAL=${COLOR_NETDEV_NORMAL:="255 255 255"} +COLOR_NETDEV_GATEWAY_UNREACHABLE=${COLOR_NETDEV_GATEWAY_UNREACHABLE:="255 0 0"} + +BRIGHTNESS_NETDEV_LED=${BRIGHTNESS_NETDEV_LED:="255"} + +CHECK_NETDEV_INTERVAL=${CHECK_NETDEV_INTERVAL:=60} +CHECK_GATEWAY_CONNECTIVITY=${CHECK_GATEWAY_CONNECTIVITY:=false} +CHECK_LINK_SPEED=${CHECK_LINK_SPEED:=false} + +led="netdev" +netdev_name=$1 +echo netdev > /sys/class/leds/$led/trigger +echo $netdev_name > /sys/class/leds/$led/device_name +echo 1 > /sys/class/leds/$led/link +echo ${NETDEV_BLINK_TX:=1} > /sys/class/leds/$led/tx +echo ${NETDEV_BLINK_RX:=1} > /sys/class/leds/$led/rx +echo ${NETDEV_BLINK_INTERVAL:=200} > /sys/class/leds/$led/interval +echo $COLOR_NETDEV_NORMAL > /sys/class/leds/$led/color +echo $BRIGHTNESS_NETDEV_LED > /sys/class/leds/$led/brightness + +function set_netdev_normal_color() { + color=$COLOR_NETDEV_NORMAL + + if [[ $CHECK_LINK_SPEED == true ]]; then + case $(cat /sys/class/net/$netdev_name/speed) in + 100) color=${COLOR_NETDEV_LINK_100:=$COLOR_NETDEV_NORMAL};; + 1000) color=${COLOR_NETDEV_LINK_1000:=$COLOR_NETDEV_NORMAL};; + 2500) color=${COLOR_NETDEV_LINK_2500:=$COLOR_NETDEV_NORMAL};; + 10000) color=${COLOR_NETDEV_LINK_10000:=$COLOR_NETDEV_NORMAL};; + esac + fi + + echo $color > /sys/class/leds/$led/color +} + +if [[ $CHECK_GATEWAY_CONNECTIVITY == false && $CHECK_LINK_SPEED == false ]]; then + exit 0 +fi + +gw_conn=1 + +while true; do + + if [[ $CHECK_GATEWAY_CONNECTIVITY == true ]]; then + gw=$(ip route | awk '/default/ { print $3 }') + if ping -q -c 1 -W 1 $gw >/dev/null; then + gw_conn=1 + else + gw_conn=0 + fi + fi + + if [[ $gw_conn == 1 ]]; then + set_netdev_normal_color + else + echo $COLOR_NETDEV_GATEWAY_UNREACHABLE > /sys/class/leds/$led/color + fi + + + sleep ${CHECK_NETDEV_INTERVAL}s + +done diff --git a/ledcontrol/all/usr/bin/ugreen-probe-leds b/ledcontrol/all/usr/bin/ugreen-probe-leds new file mode 100755 index 0000000..5dd854b --- /dev/null +++ b/ledcontrol/all/usr/bin/ugreen-probe-leds @@ -0,0 +1,21 @@ +#!/usr/bin/bash + +set -e + +{ lsmod | grep i2c-dev ; } || modprobe -v i2c-dev +{ lsmod | grep led-ugreen ; } || modprobe -v led-ugreen + +i2c_dev=$(i2cdetect -l | grep "SMBus I801 adapter" | grep -Po "i2c-\d+") + +if [ $? = 0 ]; then + echo "Found I2C device /dev/${i2c_dev}" + dev_path=/sys/bus/i2c/devices/$i2c_dev/${i2c_dev/i2c-/}-003a + if [ ! -d $dev_path ]; then + echo "led-ugreen 0x3a" > /sys/bus/i2c/devices/${i2c_dev}/new_device + elif [ "$(cat $dev_path/name)" != "led-ugreen" ]; then + echo "ERROR: the device ${i2c_dev/i2c-/}-003a has been registered as $(cat $dev_path/name)" + exit 1 + fi +else + echo "I2C device not found!" +fi diff --git a/ledcontrol/all/usr/bin/ugreen_leds_cli b/ledcontrol/all/usr/bin/ugreen_leds_cli index f00eaae..da39ff6 100755 Binary files a/ledcontrol/all/usr/bin/ugreen_leds_cli and b/ledcontrol/all/usr/bin/ugreen_leds_cli differ diff --git a/ledcontrol/install.sh b/ledcontrol/install.sh index bd072bd..b078cd6 100755 --- a/ledcontrol/install.sh +++ b/ledcontrol/install.sh @@ -13,13 +13,17 @@ if [ "${1}" = "late" ]; then cp -vf /usr/bin/ledcontrol.sh /tmpRoot/usr/bin/ledcontrol.sh cp -vf /usr/bin/ugreen_leds_cli /tmpRoot/usr/bin/ugreen_leds_cli + cp -vf /usr/bin/ugreen-diskiomon /tmpRoot/usr/bin/ugreen-diskiomon + cp -vf /usr/bin/ugreen-netdevmon /tmpRoot/usr/bin/ugreen-netdevmon + cp -vf /usr/bin/ugreen-probe-leds /tmpRoot/usr/bin/ugreen-probe-leds cp -vf /usr/bin/led.conf /tmpRoot/usr/bin/led.conf cp -vf /usr/bin/modules/i2c-algo-bit.ko /tmpRoot/usr/bin/modules/i2c-algo-bit.ko cp -vf /usr/lib/modules/i2c-i801.ko /tmpRoot/usr/lib/modules/i2c-i801.ko cp -vf /usr/lib/modules/i2c-smbus.ko /tmpRoot/usr/lib/modules/i2c-smbus.ko - modprobe i2c-algo-bit - modprobe i2c-i801 - modprobe i2c-smbus + + insmod i2c-algo-bit + insmod i2c-i801 + insmod i2c-smbus rm -f "/tmpRoot/usr/lib/systemd/system/multi-user.target.wants/ledcontrol.service" rm -f "/tmpRoot/usr/lib/systemd/system/ledcontrol.service"