From 669ee0a10f5ce66213df89e637e85e0ae721d42b Mon Sep 17 00:00:00 2001 From: Jacob Russell Date: Sat, 19 Sep 2026 13:48:19 -0500 Subject: [PATCH 1/2] Fix PWM10+ discovery and sort order in Common.php Two bugs, both hit by any hwmon device with 10+ PWM channels (e.g. ARCTIC Fan Controller, 10 channels): 1. glob("pwm[0-9]") and find -iname 'pwm[0-9]' match exactly one digit, so pwm10 (and above) are silently dropped from both build_pwm_map() and list_pwm(). Fixed by globbing broadly (pwm*) and filtering with a strict ^pwm\d+$ regex, which also avoids matching auxiliary attributes like pwm1_enable, pwm1_auto_point1_pwm, etc. 2. list_pwm()'s usort() used strcmp(), which sorts alphabetically ("pwm10" < "pwm2" as strings) rather than numerically. Fixed by switching to strnatcmp(). Verified against a live 10-channel ARCTIC Fan Controller on Unraid: before the fix, channel 10 was missing from the plugin UI entirely and pwm10 sorted between pwm1 and pwm2; after, all 10 channels appear in correct numeric order. --- .../emhttp/plugins/fanctrlplus2/include/Common.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php b/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php index 23a7dee..db43dbe 100644 --- a/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php +++ b/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php @@ -79,7 +79,8 @@ function build_pwm_map(): array { $chip = normalize_chip_name(trim(file_get_contents($name_file))); - foreach (glob("$dir/pwm[0-9]") as $pwm_path) { + foreach (glob("$dir/pwm*") as $pwm_path) { + if (!preg_match('/^pwm\d+$/', basename($pwm_path))) continue; $pwmN = basename($pwm_path); $real = realpath($pwm_path) ?: $pwm_path; $map["$chip:$pwmN"] = $real; @@ -234,15 +235,16 @@ function migrate_cfg_and_labels(string $plugin): void { function list_pwm() { $out = []; - exec("find /sys/devices -type f -iname 'pwm[0-9]' -exec dirname \"{}\" + | uniq", $chips); + exec("find /sys/devices -type f -regextype posix-extended -regex '.*/pwm[0-9]+' -exec dirname \"{}\" + | uniq", $chips); foreach ($chips as $chip) { $name = is_file("$chip/name") ? trim(file_get_contents("$chip/name")) : ''; - foreach (glob("$chip/pwm[0-9]") as $pwm) { + foreach (glob("$chip/pwm*") as $pwm) { + if (!preg_match('/^pwm\d+$/', basename($pwm))) continue; $out[] = ['chip' => $name, 'name' => basename($pwm), 'sensor' => $pwm]; } } - usort($out, fn($a, $b) => strcmp($a['name'], $b['name'])); + usort($out, fn($a, $b) => strnatcmp($a['name'], $b['name'])); return $out; } From 75e0dcd9fa2f19d3ebea0e697a1b159f8897be64 Mon Sep 17 00:00:00 2001 From: Jacob Russell Date: Sun, 20 Sep 2026 17:21:18 -0500 Subject: [PATCH 2/2] Address review: drop find/regextype, avoid pwm[0-9]* auxiliary-attribute match - Replaced the find(1) + GNU-only -regextype chip discovery in list_pwm() with the same glob('/sys/class/hwmon/hwmon*') approach build_pwm_map() already uses. No more shell exec() for this, and no portability concern about -regextype being a GNU find extension. - Kept the strict ^pwm\d+$ regex filter rather than switching to a bare "pwm[0-9]*" glob as suggested in review. Verified directly: a glob char class only consumes one character, so "pwm[0-9]*" still matches "pwm1_enable", "pwm10_enable", "pwm1_auto_point1_pwm", etc. -- the same auxiliary-attribute flooding the regex filter was added to prevent. fnmatch() confirms this: pwm1_enable vs pwm[0-9]*: MATCHES pwm10_enable vs pwm[0-9]*: MATCHES pwm1_auto_point1_pwm vs pwm[0-9]*: MATCHES Happy to go with a different structure if preferred, but "pwm[0-9]*" specifically doesn't fix the bug this PR is for. --- .../emhttp/plugins/fanctrlplus2/include/Common.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php b/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php index db43dbe..602c7c2 100644 --- a/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php +++ b/src/usr/local/emhttp/plugins/fanctrlplus2/include/Common.php @@ -235,8 +235,15 @@ function migrate_cfg_and_labels(string $plugin): void { function list_pwm() { $out = []; - exec("find /sys/devices -type f -regextype posix-extended -regex '.*/pwm[0-9]+' -exec dirname \"{}\" + | uniq", $chips); - foreach ($chips as $chip) { + // Enumerate via /sys/class/hwmon, same as build_pwm_map() above -- avoids + // a shell exec() + GNU-only `find -regextype` (unavailable on some find + // implementations), and glob("pwm*") + a strict regex filter correctly + // excludes per-channel attributes (pwm1_enable, pwm1_auto_point1_pwm, + // etc.) that a bare "pwm[0-9]*" pattern would also match, since a glob + // char class only consumes one character before the trailing "*" takes + // over -- "pwm[0-9]*" still matches "pwm1_enable" the same way "pwm*" + // does. + foreach (glob('/sys/class/hwmon/hwmon*') as $chip) { $name = is_file("$chip/name") ? trim(file_get_contents("$chip/name")) : ''; foreach (glob("$chip/pwm*") as $pwm) { if (!preg_match('/^pwm\d+$/', basename($pwm))) continue;