diff --git a/README.md b/README.md index 72448cb..3162bfd 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,15 @@ Please see `scripts/ugreen-leds.conf` for an example. cp ugreen-blink-disk /usr/bin ``` +- (_Optional_) Similarly, to reduce the latency of the standby check, you can enter the `scripts` directory and do the following things: + ```bash + # compile the disk standby checker + g++ -std=c++17 -O2 check-standby.cpp -o ugreen-check-standby + + # copy the binary file (the path can be changed, see STANDBY_MON_PATH in ugreen-leds.conf) + cp ugreen-check-standby /usr/bin + ``` + ## Disk Mapping To make the disk LEDs useful, we should map the disk LEDs to correct disk slots. First of all, we should highlight that using `/dev/sdX` is never a smart idea, as it may change at every boot. diff --git a/SPECS/kmod-led-ugreen.spec b/SPECS/kmod-led-ugreen.spec index c580f3e..f31b4d9 100644 --- a/SPECS/kmod-led-ugreen.spec +++ b/SPECS/kmod-led-ugreen.spec @@ -8,7 +8,7 @@ Name: kmod-%{kmod_name} Version: 0.1 -Release: 16%{?dist} +Release: 17%{?dist} Summary: %{kmod_name} kernel module(s) Group: System Environment/Kernel License: GPLv2 @@ -83,6 +83,7 @@ popd pushd scripts %{__cxx} -std=c++17 -O2 blink-disk.cpp -o ugreen-blink-disk +%{__cxx} -std=c++17 -O2 check-standby.cpp -o ugreen-check-standby popd whitelist="/lib/modules/kabi-current/kabi_stablelist_%{_target_cpu}" @@ -112,6 +113,7 @@ mkdir -p %{buildroot}%{_bindir}/ %{__install} -m 0755 scripts/ugreen-netdevmon %{buildroot}%{_bindir}/ %{__install} -m 0755 scripts/ugreen-probe-leds %{buildroot}%{_bindir}/ %{__install} -m 0755 scripts/ugreen-blink-disk %{buildroot}%{_bindir}/ +%{__install} -m 0755 scripts/ugreen-check-standby %{buildroot}%{_bindir}/ mkdir -p %{buildroot}%{_unitdir}/ %{__install} -m 0644 scripts/ugreen-netdevmon@.service %{buildroot}%{_unitdir}/ @@ -221,6 +223,7 @@ exit 0 %attr(0755, root, root) %{_bindir}/ugreen-netdevmon %attr(0755, root, root) %{_bindir}/ugreen-probe-leds %attr(0755, root, root) %{_bindir}/ugreen-blink-disk +%attr(0755, root, root) %{_bindir}/ugreen-check-standby %{_unitdir}/ugreen-netdevmon@.service %{_unitdir}/ugreen-diskiomon.service diff --git a/build-scripts/debian/Dockerfile b/build-scripts/debian/Dockerfile new file mode 100644 index 0000000..acbf6b4 --- /dev/null +++ b/build-scripts/debian/Dockerfile @@ -0,0 +1,17 @@ +FROM debian:bookworm + +RUN set -ex \ + && sed -i -- 's/Types: deb/Types: deb deb-src/g' /etc/apt/sources.list.d/debian.sources \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + cdbs \ + devscripts \ + equivs \ + fakeroot \ + wget \ + git \ + && apt-get clean \ + && rm -rf /tmp/* /var/tmp/* + + diff --git a/build-scripts/debian/README.md b/build-scripts/debian/README.md index d12116d..e5de6e2 100644 --- a/build-scripts/debian/README.md +++ b/build-scripts/debian/README.md @@ -1,2 +1,8 @@ -To build a deb, run `sudo bash build.sh`. +Build it in docker: + +``` +bash docker-run.sh +``` + +The outputs can be found in `./build` directory. diff --git a/build-scripts/debian/build-dkms-deb.sh b/build-scripts/debian/build-dkms-deb.sh index 8a759fe..a0f4aeb 100644 --- a/build-scripts/debian/build-dkms-deb.sh +++ b/build-scripts/debian/build-dkms-deb.sh @@ -15,7 +15,7 @@ Version: $pkgver Architecture: amd64 Maintainer: Yuhao Zhou Depends: dkms -Homepage: https://github.com/miskcoo/ugreen_dx4600_leds_controller +Homepage: https://github.com/miskcoo/ugreen_leds_controller Description: UGREEN NAS LED driver A reverse-engineered LED driver of UGREEN NAS. EOF diff --git a/build-scripts/debian/build-utils-deb.sh b/build-scripts/debian/build-utils-deb.sh index d5ee51a..bbe0434 100644 --- a/build-scripts/debian/build-utils-deb.sh +++ b/build-scripts/debian/build-utils-deb.sh @@ -15,7 +15,7 @@ Version: $pkgver Architecture: amd64 Maintainer: Yuhao Zhou Depends: dmidecode, smartmontools -Homepage: https://github.com/miskcoo/ugreen_dx4600_leds_controller +Homepage: https://github.com/miskcoo/ugreen_leds_controller Description: UGREEN NAS LED tools A reverse-engineered LED tools of UGREEN NAS. EOF @@ -35,11 +35,18 @@ done mkdir -p $pkgname/etc/systemd/system cp scripts/*.service $pkgname/etc/systemd/system/ # cp scripts/ugreen-ledmon@.service $pkgname/etc/systemd/system/ -# # example config file cp scripts/ugreen-leds.conf $pkgname/etc/ugreen-leds.example.conf +# compile the disk activities monitor +g++ -std=c++17 -O2 scripts/blink-disk.cpp -o ugreen-blink-disk +cp ugreen-blink-disk $pkgname/usr/bin + +# compile the disk standby monitor +g++ -std=c++17 -O2 scripts/check-standby.cpp -o ugreen-check-standby +cp ugreen-check-standby $pkgname/usr/bin + # change to root chown -R root:root $pkgname/ diff --git a/build-scripts/debian/build.sh b/build-scripts/debian/build.sh index 163988e..afb30d0 100644 --- a/build-scripts/debian/build.sh +++ b/build-scripts/debian/build.sh @@ -1,14 +1,16 @@ #!/usr/bin/bash -git clone https://github.com/miskcoo/ugreen_dx4600_leds_controller.git +set -x -cp build-dkms-deb.sh ugreen_dx4600_leds_controller -cp build-utils-deb.sh ugreen_dx4600_leds_controller +git clone https://github.com/miskcoo/ugreen_leds_controller.git +cd ugreen_leds_controller -cd ugreen_dx4600_leds_controller -bash build-dkms-deb.sh -bash build-utils-deb.sh +if [ ! -z $1 ]; then + git checkout $1 +fi +bash build-scripts/debian/build-dkms-deb.sh +bash build-scripts/debian/build-utils-deb.sh dpkg-name led-ugreen-dkms.deb dpkg-name led-ugreen-utils.deb -mv *.deb ../ +mv *.deb .. diff --git a/build-scripts/debian/docker-run.sh b/build-scripts/debian/docker-run.sh new file mode 100644 index 0000000..a9ad322 --- /dev/null +++ b/build-scripts/debian/docker-run.sh @@ -0,0 +1,15 @@ + + +set -x + +if [[ ! -d ./build ]]; then + mkdir build +fi + +docker build --tag bookworm-build . +docker run \ + --rm \ + --mount type=bind,source=$(pwd)/build,target=/build \ + --mount type=bind,source=$(pwd)/build.sh,target=/build.sh \ + bookworm-build \ + bash -c "cd /build && bash /build.sh $1" diff --git a/scripts/check-standby.cpp b/scripts/check-standby.cpp new file mode 100644 index 0000000..b6e12a7 --- /dev/null +++ b/scripts/check-standby.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +std::optional is_standby_mode(const std::string &device) { + + int fd = open(device.c_str(), O_RDONLY | O_NONBLOCK); + if (fd == -1) { + std::cerr << "Failed to open device: " << device << std::endl; + return std::nullopt; + } + + unsigned char args[4] = { WIN_CHECKPOWERMODE1, 0, 0, 0 }; + + if (ioctl(fd, HDIO_DRIVE_CMD, args) == -1) { + std::cerr << "ioctl failed in checking power mode of " << device << std::endl; + close(fd); + return std::nullopt; + } + + close(fd); + + return args[2] == 0x00; +} + +int main(int argc, char *argv[]) { + + constexpr int preleading_args = 4; + + if (argc < preleading_args + 2 || (argc - preleading_args) % 2 != 0) { + std::cerr << "Usage: " << argv[0] + << " " + << " " + << " " + << " ...\n"; + return 1; + } + + int num_devices = (argc - preleading_args) / 2; + int checker_sleep_ms = int(std::stod(argv[1]) * 1000); + + std::string standby_color = argv[2]; + std::string normal_color = argv[3]; + std::vector block_device_paths; + std::vector led_device_color_paths; + + std::cout << normal_color; + + for (int i = 0; i < num_devices; i++) { + std::string block_device = argv[preleading_args + 2 * i]; + std::string led_device = argv[preleading_args + 2 * i + 1]; + + + block_device_paths.push_back( + "/dev/" + block_device); + led_device_color_paths.push_back( + "/sys/class/leds/" + led_device + "/color"); + } + + std::vector device_valid(num_devices, true); + std::vector device_standby(num_devices, false); + + while (true) { + for (int i = 0; i < num_devices; i++) { + + if (!device_valid[i]) { + continue; + } + + auto is_standby = is_standby_mode(block_device_paths[i]); + + if (!is_standby.has_value()) { + device_valid[i] = false; + continue; + } + + if (is_standby.value() != device_standby[i]) { + std::fstream led_color(led_device_color_paths[i], std::ios::in | std::ios::out); + if (!led_color) { + std::cerr << "Failed to open led color file: " + << led_device_color_paths[i] << std::endl; + device_valid[i] = false; + continue; + } + + std::string current_color; + std::getline(led_color, current_color); + + if (device_standby[i] && current_color == standby_color) { + led_color << normal_color << std::endl; + } else if (!device_standby[i] && current_color == normal_color) { + led_color << standby_color << std::endl; + } + + device_standby[i] = is_standby.value(); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(checker_sleep_ms)); + } + + return 0; +} diff --git a/scripts/ugreen-diskiomon b/scripts/ugreen-diskiomon index 2e437eb..6cfc72f 100755 --- a/scripts/ugreen-diskiomon +++ b/scripts/ugreen-diskiomon @@ -8,6 +8,7 @@ exit-ugreen-diskiomon() { kill $smart_check_pid 2>/dev/null kill $zpool_check_pid 2>/dev/null kill $disk_online_check_pid 2>/dev/null + kill $standby_checker_pid 2>/dev/null } # trap exit and remove lockfile @@ -111,6 +112,14 @@ BRIGHTNESS_DISK_LEDS=${BRIGHTNESS_DISK_LEDS:="255"} { lsmod | grep ledtrig_oneshot > /dev/null; } || { modprobe -v ledtrig_oneshot && sleep 2; } +function is_disk_healthy_or_standby() { + if [[ "$1" == "$COLOR_DISK_HEALTH" || "$1" == "$COLOR_DISK_STANDBY" ]]; then + return 0 # 0 means successful + else + return 1 + fi +} + function disk_enumerating_string() { if [[ $MAPPING_METHOD == ata ]]; then ls -ahl /sys/block | sed 's/\/$//' | awk '{ @@ -221,8 +230,9 @@ if [ "$CHECK_ZPOOL" = true ]; then if [[ -v "zpool_ledmap[${zpool_dev_name}]" ]]; then led=${zpool_ledmap[$zpool_dev_name]} + led_color=$(cat /sys/class/leds/$led/color) - if [[ "$(cat /sys/class/leds/$led/color)" != "$COLOR_DISK_HEALTH" ]]; then + if ! is_disk_healthy_or_standby "$led_color"; then continue; fi @@ -256,7 +266,9 @@ if [ "$CHECK_SMART" = true ]; then ( while true; do for led in "${!devices[@]}"; do - if [[ "$(cat /sys/class/leds/$led/color)" != "$COLOR_DISK_HEALTH" ]]; then + + led_color=$(cat /sys/class/leds/$led/color) + if ! is_disk_healthy_or_standby "$led_color"; then continue; fi @@ -265,9 +277,7 @@ if [ "$CHECK_SMART" = true ]; then /usr/sbin/smartctl -H /dev/${dev} -n standby,1 &> /dev/null RET=$? - if [[ $RET -eq 1 ]]; then - echo "$COLOR_DISK_STANDBY" > /sys/class/leds/$led/color - elif [[ $RET -gt 1 ]]; then + if [[ $RET -gt 1 ]]; 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 @@ -286,21 +296,7 @@ fi dev=${devices[$led]} led_color=$(cat /sys/class/leds/$led/color) - - if [[ "$led_color" == "$COLOR_DISK_STANDBY" ]]; then - # Check if disk is still in standby - /usr/sbin/smartctl -H /dev/${dev} -n standby,1 &> /dev/null - RET=$? - - if [[ $RET -eq 0 ]]; then - led_color="$COLOR_DISK_HEALTH" - echo $led_color > /sys/class/leds/$led/color - elif [[ $RET -gt 1 ]]; 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 - elif [[ "$led_color" != "$COLOR_DISK_HEALTH" ]]; then + if ! is_disk_healthy_or_standby "$led_color"; then continue; fi @@ -315,18 +311,26 @@ fi ) & disk_online_check_pid=$! + +diskiomon_parameters() { + for led in "${!devices[@]}"; do + echo ${devices[$led]} $led + done +} + +# monitor disk standby modes +STANDBY_MON_PATH=${STANDBY_MON_PATH:=/usr/bin/ugreen-check-standby} +STANDBY_CHECK_INTERVAL=${STANDBY_CHECK_INTERVAL:=1} +if [ -f "${STANDBY_MON_PATH}" ]; then + ${STANDBY_MON_PATH} ${STANDBY_CHECK_INTERVAL} "${COLOR_DISK_STANDBY}" "${COLOR_DISK_HEALTH}" $(diskiomon_parameters) & + standby_checker_pid=$1 +fi + # monitor disk activities BLINK_MON_PATH=${BLINK_MON_PATH:=/usr/bin/ugreen-blink-disk} if [ -f "${BLINK_MON_PATH}" ]; then - diskiomon_parameters() { - echo ${LED_REFRESH_INTERVAL} - for led in "${!devices[@]}"; do - echo ${devices[$led]} $led - done - } - - ${BLINK_MON_PATH} $(diskiomon_parameters) + ${BLINK_MON_PATH} ${LED_REFRESH_INTERVAL} $(diskiomon_parameters) else declare -A diskio_data_rw diff --git a/scripts/ugreen-leds.conf b/scripts/ugreen-leds.conf index 140c1fa..0a59491 100644 --- a/scripts/ugreen-leds.conf +++ b/scripts/ugreen-leds.conf @@ -23,6 +23,12 @@ MAPPING_METHOD=ata # The path of the compiled diskio monitor (OPTIONAL) BLINK_MON_PATH=/usr/bin/ugreen-blink-disk +# The path of the compiled standby monitor (OPTIONAL) +STANDBY_MON_PATH=/usr/bin/ugreen-check-standby + +# The sleep time between disk standby checks (default: 1 seconds) +STANDBY_CHECK_INTERVAL=1 + # The serial numbers of disks (used only when MAPPING_METHOD=serial) # You need to record them before inserting to your NAS, and the corresponding disk slots. # If you have 4 disks, with serial numbers: SN1 SN2 SN3 SN4,