mirror of
https://github.com/openwrt/packages.git
synced 2026-04-15 10:51:55 +00:00
nut: fix no permissions to use USB UPS, and more
When a USB UPS is first configured, the permissions on the device under `/dev/bus/usb` have not yet been set to allow the nut user access. This resulted in errors such as: Fri Feb 13 23:39:01 2026 daemon.debug upsd[3504]: [D1] mainloop: UPS [eco550ups] is not currently connected, trying to reconnect Fri Feb 13 23:39:01 2026 daemon.debug upsd[3504]: [D1] mainloop: UPS [eco550ups] is still not connected (FD -1) Fri Feb 13 23:39:03 2026 daemon.debug upsd[3504]: [D1] mainloop: UPS [eco550ups] is not currently connected, trying to reconnect Fri Feb 13 23:39:03 2026 daemon.debug upsd[3504]: [D1] mainloop: UPS [eco550ups] is still not connected (FD -1) or Fri Feb 13 23:38:44 2026 daemon.err usbhid-ups[3083]: No matching HID UPS found Fri Feb 13 23:38:49 2026 daemon.warn procd: failed adding instance cgroup for nut-server: No error information Fri Feb 13 23:38:49 2026 daemon.err usbhid-ups[3115]: libusb1: Could not open any HID devices: insufficient permissions on everything Fri Feb 13 23:38:49 2026 daemon.err usbhid-ups[3115]: No matching HID UPS found Fri Feb 13 23:38:54 2026 daemon.warn procd: failed adding instance cgroup for nut-server: No error information and upsd would enter a procd crashloop. We fix that by looking in `sysfs` (under `/sys/devices`) to find the correct USB device and set its ownership and permissions to allow acces to the user the driver is running under. Copilot complained about a few things * nut-server.init had potential word-splitting issues in various spots. * it also had some commands missing an argument * improved documentation was required to clarify a dependency * an incorrect sed could mangle names as well as remove the intended name Additionally, while fixing those issues the author noticed that the case of multiple UPS devices with the same vendorid:productid were not correctly handled. A check of the serial number, if provided, was added along with a fallback to allowing NUT communications with all UPS devices with a given vendorid:productid, if no serial number was given. Improve efficiency and decrease McCabe complexity of ensure_usb_ups_access, while also fixing Copilot complaints. $@ in case is a problem, and we only handle the first parameter in any event, so change $@ to "$1" Copilot caught a missing 2>&1 and we silence some shellcheck false positives Signed-off-by: Daniel F. Dickinson <dfdpublic@wildtechgarden.ca>
This commit is contained in:
committed by
Michael Heimpold
parent
9cfa3f657d
commit
072f32fee3
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
# shellcheck shell=ash
|
# shellcheck shell=ash
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
START=82
|
START=82
|
||||||
STOP=28
|
STOP=28
|
||||||
USE_PROCD=1
|
USE_PROCD=1
|
||||||
@@ -151,6 +152,7 @@ nut_upsmon_add() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build_config() {
|
build_config() {
|
||||||
|
# shellcheck disable=SC2174
|
||||||
mkdir -m 0750 -p "$(dirname "$UPSMON_C")"
|
mkdir -m 0750 -p "$(dirname "$UPSMON_C")"
|
||||||
|
|
||||||
config_load nut_monitor
|
config_load nut_monitor
|
||||||
@@ -174,6 +176,7 @@ interface_triggers() {
|
|||||||
|
|
||||||
config_get triggerlist "upsmon" triggerlist
|
config_get triggerlist "upsmon" triggerlist
|
||||||
|
|
||||||
|
# shellcheck disable=SC1091
|
||||||
. "${IPKG_INSTROOT}"/lib/functions/network.sh
|
. "${IPKG_INSTROOT}"/lib/functions/network.sh
|
||||||
|
|
||||||
if [ -n "$triggerlist" ]; then
|
if [ -n "$triggerlist" ]; then
|
||||||
@@ -221,7 +224,7 @@ pgrepkill() {
|
|||||||
|
|
||||||
[ $# -eq 2 ] || return 1
|
[ $# -eq 2 ] || return 1
|
||||||
|
|
||||||
pids="$(pgrep "$1")"
|
pids="$(pgrep "$1" 2>/dev/null)" || return 0
|
||||||
|
|
||||||
for pid in $pids; do
|
for pid in $pids; do
|
||||||
kill -"$2" "$pid"
|
kill -"$2" "$pid"
|
||||||
@@ -259,7 +262,7 @@ reload_service() {
|
|||||||
else
|
else
|
||||||
if procd_running nut-monitor upsmon; then
|
if procd_running nut-monitor upsmon; then
|
||||||
if [ -s "$PIDFILE" ]; then
|
if [ -s "$PIDFILE" ]; then
|
||||||
upsmon -c stop | logger -t nut-monitor
|
upsmon -c stop 2>&1 | logger -t nut-monitor
|
||||||
else
|
else
|
||||||
pgrepkill upsmon TERM >/dev/null 2>/dev/null
|
pgrepkill upsmon TERM >/dev/null 2>/dev/null
|
||||||
fi
|
fi
|
||||||
@@ -270,7 +273,7 @@ reload_service() {
|
|||||||
|
|
||||||
stop_service() {
|
stop_service() {
|
||||||
if [ -s "$PIDFILE" ]; then
|
if [ -s "$PIDFILE" ]; then
|
||||||
upsmon -c stop | logger -t nut-monitor
|
upsmon -c stop 2>&1 | logger -t nut-monitor
|
||||||
procd_kill nut-monitor 2>/dev/null | logger -t nut-monitor
|
procd_kill nut-monitor 2>/dev/null | logger -t nut-monitor
|
||||||
else
|
else
|
||||||
pgrepkill upsmon TERM >/dev/null 2>/dev/null
|
pgrepkill upsmon TERM >/dev/null 2>/dev/null
|
||||||
|
|||||||
@@ -276,6 +276,67 @@ build_config() {
|
|||||||
[ -n "$RUNAS" ] && chgrp "$(id -gn "$RUNAS")" "$UPS_C"
|
[ -n "$RUNAS" ] && chgrp "$(id -gn "$RUNAS")" "$UPS_C"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensure_usb_ups_access() {
|
||||||
|
local ups="$1"
|
||||||
|
local vendorid
|
||||||
|
local productid
|
||||||
|
local runas=nut
|
||||||
|
|
||||||
|
runas="$RUNAS"
|
||||||
|
|
||||||
|
config_load nut_server
|
||||||
|
config_get vendorid "$ups" vendorid
|
||||||
|
config_get productid "$ups" productid
|
||||||
|
config_get serial "$ups" serial
|
||||||
|
|
||||||
|
[ -n "$vendorid" ] || return
|
||||||
|
[ -n "$productid" ] || return
|
||||||
|
|
||||||
|
local NL='
|
||||||
|
'
|
||||||
|
|
||||||
|
find /sys/devices -name idVendor -a -path '*usb*'| while IFS="$NL" read -r vendor_path; do
|
||||||
|
local usb_bus usb_dev device_path
|
||||||
|
|
||||||
|
# Filter by vendor ID first
|
||||||
|
if [ "$(cat "$vendor_path" 2>/dev/null)" != "$vendorid" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
device_path="$(dirname "$vendor_path")"
|
||||||
|
|
||||||
|
# Then filter by product ID
|
||||||
|
if [ "$(cat "$device_path/idProduct" 2>/dev/null)" != "$productid" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Next filter by serial, if provided
|
||||||
|
if [ -n "$serial" ] && [ "$serial" != "$(cat "$device_path"/serial)" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
usb_bus="$(printf "%03d" "$(cat "$device_path"/busnum)")"
|
||||||
|
usb_dev="$(printf "%03d" "$(cat "$device_path"/devnum)")"
|
||||||
|
|
||||||
|
# usb_bus and usb_dev must each be at least 001
|
||||||
|
# a missing value will be present as 000 due to 'printf "%03d"'
|
||||||
|
local MISSING_USB_NUM="000"
|
||||||
|
if [ "$usb_bus" != "$MISSING_USB_NUM" ] && [ "$usb_dev" != "$MISSING_USB_NUM" ]; then
|
||||||
|
chmod 0660 /dev/bus/usb/"$usb_bus"/"$usb_dev"
|
||||||
|
chown "${runas:-root}":"$(id -gn "${runas:-root}")" /dev/bus/usb/"$usb_bus"/"$usb_dev"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Serial numbers are defined as unique, so do not loop further if serial
|
||||||
|
# was present and matched
|
||||||
|
if [ -n "$serial" ]; then
|
||||||
|
break
|
||||||
|
# If a serial number is not provided we need all vendor:product matches
|
||||||
|
# to have permissions for NUT as we do not know the matching method here
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Must be called from start_service
|
||||||
start_ups_driver() {
|
start_ups_driver() {
|
||||||
local ups="$1"
|
local ups="$1"
|
||||||
local requested="$2"
|
local requested="$2"
|
||||||
@@ -295,8 +356,10 @@ start_ups_driver() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Depends on config_load from start_service
|
||||||
srv_statepath
|
srv_statepath
|
||||||
srv_runas
|
srv_runas
|
||||||
|
ensure_usb_ups_access "$ups"
|
||||||
|
|
||||||
config_get driver "$ups" driver "usbhid-ups"
|
config_get driver "$ups" driver "usbhid-ups"
|
||||||
procd_open_instance "$ups"
|
procd_open_instance "$ups"
|
||||||
@@ -373,7 +436,8 @@ start_service() {
|
|||||||
|
|
||||||
[ "$should_start_srv" = "1" ] || return 0
|
[ "$should_start_srv" = "1" ] || return 0
|
||||||
|
|
||||||
case $@ in
|
# We only start one service (upsd or one driver) from a given invocation
|
||||||
|
case "$1" in
|
||||||
"")
|
"")
|
||||||
config_foreach start_ups_driver driver
|
config_foreach start_ups_driver driver
|
||||||
start_server_instance upsd
|
start_server_instance upsd
|
||||||
@@ -382,7 +446,7 @@ start_service() {
|
|||||||
start_server_instance upsd
|
start_server_instance upsd
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
config_foreach start_ups_driver driver "$@"
|
config_foreach start_ups_driver driver "$1"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -423,7 +487,7 @@ signal_instance() {
|
|||||||
elif pgrep "$process_name" >/dev/null 2>/dev/null; then
|
elif pgrep "$process_name" >/dev/null 2>/dev/null; then
|
||||||
procd_send_signal nut-server "$instance_name" "$signal" 2>&1 | logger -t nut-server
|
procd_send_signal nut-server "$instance_name" "$signal" 2>&1 | logger -t nut-server
|
||||||
fi
|
fi
|
||||||
if [ -n "$secondary_command" ] && procd_running "$instance_name"; then
|
if [ -n "$secondary_command" ] && procd_running nut-server "$instance_name"; then
|
||||||
$secondary_command 2>&1 | logger -t nut-server
|
$secondary_command 2>&1 | logger -t nut-server
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -454,7 +518,7 @@ stop_ups_driver() {
|
|||||||
if procd_running nut-server "$ups"; then
|
if procd_running nut-server "$ups"; then
|
||||||
signal_instance "$ups" "$driver" "/lib/nut/'${driver}' -c exit -a '${ups}'" "TERM" "${STATEPATH}/${driver}-${ups}.pid"
|
signal_instance "$ups" "$driver" "/lib/nut/'${driver}' -c exit -a '${ups}'" "TERM" "${STATEPATH}/${driver}-${ups}.pid"
|
||||||
if procd_running nut-server upsd >/dev/null 2>&1; then
|
if procd_running nut-server upsd >/dev/null 2>&1; then
|
||||||
signal_instance upsd upsd "upsd -c stop" "TERM" "${STATEPATH}/upsd.pid" "procd_kill upsd"
|
signal_instance upsd upsd "upsd -c stop" "TERM" "${STATEPATH}/upsd.pid" "procd_kill nut-server upsd"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -515,8 +579,8 @@ reload_service() {
|
|||||||
config_foreach stop_ups_driver driver
|
config_foreach stop_ups_driver driver
|
||||||
|
|
||||||
# Also stop any driver instances which are no longer configured
|
# Also stop any driver instances which are no longer configured
|
||||||
for instance in $(list_running_instances "nut-server"|sed -e 's/upsd//'); do
|
for instance in $(list_running_instances "nut-server"); do
|
||||||
if procd_running nut-server "$instance" >/dev/null 2>&1; then
|
if [ "$instance" != "upsd" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then
|
||||||
procd_kill nut-server "$instance" 2>&1 | logger -t nut-server
|
procd_kill nut-server "$instance" 2>&1 | logger -t nut-server
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -541,7 +605,10 @@ reload_service() {
|
|||||||
|
|
||||||
# Stop any driver instances which are no longer configured
|
# Stop any driver instances which are no longer configured
|
||||||
# We can only reliably do this for instances managed by procd
|
# We can only reliably do this for instances managed by procd
|
||||||
for instance in $(list_running_instances "nut-server"|sed -e 's/upsd//'); do
|
for instance in $(list_running_instances "nut-server"); do
|
||||||
|
if [ "$instance" = "upsd" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
unset driver
|
unset driver
|
||||||
config_get driver "$instance" driver
|
config_get driver "$instance" driver
|
||||||
if [ -z "$driver" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then
|
if [ -z "$driver" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then
|
||||||
@@ -558,7 +625,8 @@ stop_service() {
|
|||||||
config_load nut_server
|
config_load nut_server
|
||||||
srv_statepath
|
srv_statepath
|
||||||
|
|
||||||
case $@ in
|
# We only handle the first parameter passed
|
||||||
|
case "$1" in
|
||||||
"")
|
"")
|
||||||
# If nut-server was started but has no instances (even upsd)
|
# If nut-server was started but has no instances (even upsd)
|
||||||
if server_active; then
|
if server_active; then
|
||||||
@@ -570,8 +638,8 @@ stop_service() {
|
|||||||
config_foreach stop_ups_driver driver
|
config_foreach stop_ups_driver driver
|
||||||
|
|
||||||
# Also stop any driver instances which are no longer configured
|
# Also stop any driver instances which are no longer configured
|
||||||
for instance in $(list_running_instances "nut-server"|sed -e 's/upsd//'); do
|
for instance in $(list_running_instances "nut-server"); do
|
||||||
if procd_running nut-server "$instance" >/dev/null 2>&1; then
|
if [ "$instance" != "upsd" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then
|
||||||
procd_kill nut-server "$instance" 2>&1 | logger -t nut-server
|
procd_kill nut-server "$instance" 2>&1 | logger -t nut-server
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -592,7 +660,8 @@ stop_service() {
|
|||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
config_foreach stop_ups_driver driver "$@"
|
# We only handle the first parameter, so do not pass in all parameters
|
||||||
|
config_foreach stop_ups_driver driver "$1"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user