diff --git a/net/nut/Makefile b/net/nut/Makefile index fa5acf83bf9286..86264d81930ddc 100644 --- a/net/nut/Makefile +++ b/net/nut/Makefile @@ -1,5 +1,5 @@ -# Copyright (C) 2006-2016 OpenWrt.org +# Copyright (C) 2006-2026 OpenWrt.org # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. @@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=nut -PKG_VERSION:=2.8.4 -PKG_RELEASE:=3 +PKG_VERSION:=2.8.5 +PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=https://networkupstools.org/source/2.8/ -PKG_HASH:=a2fe55bc2d90b4a848d6ff8bac361e6d1c97f899a545219cad707d17a27ff127 +PKG_SOURCE_URL:=https://www.networkupstools.org/source/2.8/ +PKG_HASH:=18bf32e59eb764b13da3c4fa70384926d7fa584cb31d2fe7f137a570633eeec1 PKG_LICENSE:=GPL-2.0-or-later GPL-3.0-or-later GPL-1.0-or-later Artistic-1.0-Perl PKG_LICENSE_FILES:=LICENSE-GPL2 LICENSE-GPL3 COPYING PKG_FIXUP:=autoreconf @@ -69,8 +69,12 @@ define Package/nut-server/install $(INSTALL_DIR) $(1)/etc/nut $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_DIR) $(1)/lib/functions/nut $(INSTALL_DIR) $(1)/usr/share/nut $(INSTALL_BIN) ./files/nut-server.init $(1)/etc/init.d/nut-server + $(INSTALL_DATA) ./files/nut-common.sh.functions $(1)/lib/functions/nut/nut-common.sh + $(INSTALL_DATA) ./files/nut-config.sh.functions $(1)/lib/functions/nut/nut-config.sh + $(INSTALL_DATA) ./files/nut-service.sh.functions $(1)/lib/functions/nut/nut-service.sh $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/upsd $(1)/usr/sbin $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/share/nut/cmdvartab $(1)/usr/share/nut/ $(INSTALL_DIR) $(1)/etc/config @@ -383,9 +387,9 @@ define DriverPackage endef define Package/nut-driver-$(2)/install - $(INSTALL_DIR) $$(1)/lib/nut - $(CP) $$(PKG_INSTALL_DIR)/lib/nut/$(2) $$(1)/lib/nut/ - $(if $(filter $(2),clone),$(CP) $$(PKG_INSTALL_DIR)/lib/nut/$(2)-outlet $$(1)/lib/nut/) + $(INSTALL_DIR) $$(1)/usr/libexec/nut + $(CP) $$(PKG_INSTALL_DIR)/usr/libexec/nut/$(2) $$(1)/usr/libexec/nut/ + $(if $(filter $(2),clone),$(CP) $$(PKG_INSTALL_DIR)/usr/libexec/nut/$(2)-outlet $$(1)/usr/libexec/nut/) endef endef define DriverDescription @@ -406,7 +410,7 @@ SERIAL_DRIVERLIST = al175 bcmxcp belkin belkinunv bestfcom \ gamatronic genericups isbmex liebert liebert-esp2 liebert-gxe masterguard metasys \ mge-utalk microdowell microsol-apc mge-shut nutdrv_hashx oneac optiups powercom powervar_cx_ser rhino \ safenet nutdrv_siemens-sitop solis tripplite tripplitesu upscode2 victronups powerpanel \ - blazer_ser ivtscd apcsmart apcsmart-old riello_ser sms_ser bicker_ser ve-direct \ + blazer_ser ivtscd apcsmart apcsmart-old riello_ser sms_ser bicker_ser ve-direct meanwell_ntu \ nutdrv_qx SERIAL_DRIVERLIST += nhs_ser SNMP_DRIVERLIST = snmp-ups @@ -527,8 +531,10 @@ $(eval $(call DriverDescription,serial,bicker_ser,\ Driver for Bicker DC UPS via serial port connections)) $(eval $(call DriverDescription,serial,ve-direct,\ Driver for Victron UPS unit running on VE.Direct serial protocol)) +$(eval $(call DriverDescription,serial,meanwell_ntu,\ + Driver for Mean Well NTU series equipment with serial port)) $(eval $(call DriverDescription,serial,nhs_ser,\ - Driver for NHS Nobreaks, senoidal line, with serial port)) + Driver for NHS Nobreaks - senoidal line - with serial port)) $(eval $(call DriverDescription,snmp,snmp-ups,\ Multi-MIB Driver for SNMP UPS equipment)) $(eval $(call DriverDescription,usb,usbhid-ups,\ @@ -556,7 +562,8 @@ CONFIGURE_VARS += \ ac_cv_path_AR=$(TARGET_AR) CONFIGURE_ARGS += \ - --sysconfdir=/etc/nut \ + --sysconfdir=/etc \ + --with-confdir-suffix=/nut \ --datadir=/usr/share/nut \ --with-dev \ --$(if $(CONFIG_NUT_DRIVER_USB),with,without)-usb \ @@ -579,7 +586,7 @@ CONFIGURE_ARGS += \ --without-nut_monitor \ --with-statepath=/var/run/nut \ --with-pidpath=/var/run \ - --with-drvpath=/lib/nut \ + --with-drvpath=/usr/libexec/nut \ --with-user=nut \ --with-group=nut \ $(if $(CONFIG_PACKAGE_nut-web-cgi),--with-gd-includes="`pkg-config --cflags gdlib`") \ diff --git a/net/nut/files/30-libhid-ups.head b/net/nut/files/30-libhid-ups.head index eebda998ce2fbd..77f075e666f402 100755 --- a/net/nut/files/30-libhid-ups.head +++ b/net/nut/files/30-libhid-ups.head @@ -4,13 +4,15 @@ nut_driver_config() { local cfg="$1" local nomatch="$2" - config_get runas "$cfg" runas "nut" + . /lib/functions/nut/nut-common.sh + srv_runas + config_get vendorid "$cfg" vendorid config_get productid "$cfg" productid [ "$ACTION" = "add" ] && [ -n "$DEVNAME" ] && { chmod 0660 /dev/"$DEVNAME" - chown "${runas:-root}":"$(id -gn "${runas:-root}")" /dev/"$DEVNAME" + chown "${RUNAS:-root}":"$(id -gn "${RUNAS:-root}")" /dev/"$DEVNAME" } if [ "$nomatch" = "1" ]; then diff --git a/net/nut/files/nut-cgi.init b/net/nut/files/nut-cgi.init old mode 100755 new mode 100644 index 68d39f668b85bb..220d81320eff87 --- a/net/nut/files/nut-cgi.init +++ b/net/nut/files/nut-cgi.init @@ -1,9 +1,13 @@ #!/bin/sh /etc/rc.common -# Copyright © 2012 OpenWrt.org +# Copyright © 2012-2026 OpenWrt.org +# +# shellcheck shell=busybox # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. # + +# shellcheck disable=SC2034 START=87 STOP=23 USE_PROCD=1 @@ -38,14 +42,17 @@ nut_upscgi_add() { config_get pass "$cfg" password system="$upsname@$hostname" if [ -n "$port" ]; then - system="$system:$port"; + system="$system:$port" fi config_get displayname "$cfg" displayname - echo "MONITOR $system \"$displayname\"" >> "$UPSCGI_C" + echo "MONITOR $system \"$displayname\"" >>"$UPSCGI_C" } service_reload() { - mkdir -m 0755 -p "$(dirname "$UPSCGI_C")" + if [ ! -d "$(dirname "$UPSCGI_C")" ]; then + mkdir -p "$(dirname "$UPSCGI_C")" + chmod 0755 "$(dirname "$UPSCGI_C")" + fi rm -f "$UPSCGI_C" rm -f "$UPSCGI_S" diff --git a/net/nut/files/nut-common.sh.functions b/net/nut/files/nut-common.sh.functions new file mode 100644 index 00000000000000..7b6018d9dfff05 --- /dev/null +++ b/net/nut/files/nut-common.sh.functions @@ -0,0 +1,38 @@ +#!/bin/sh /etc/rc.common + +# Common helper functions for NUT server initscript + +# shellcheck shell=ash + +# config_load nut_server is already called by sourcing script + +# Store path for NUT working data in the global variable STATEPATH +srv_statepath() { + local statepath + + config_get statepath upsd statepath /var/run/nut + STATEPATH="$statepath" +} + +# Store user under which to run server (upsd) and driver processes in +# global variable 'RUNAS' +srv_runas() { + local runas + + [ -n "$RUNAS" ] && return 0 + + config_get runas upsd runas nut + RUNAS="$runas" +} + +prevent_hotplug_restart() { + # Avoid hotplug inadvertently restarting driver during + # forced shutdown + + srv_statepath + + [ -f /var/run/killpower ] && return 0 + if [ -d "${STATEPATH}" ] && [ -f "${STATEPATH}"/disable-hotplug ]; then + return 0 + fi +} diff --git a/net/nut/files/nut-config.sh.functions b/net/nut/files/nut-config.sh.functions new file mode 100644 index 00000000000000..4e5c0cb2c2bc9a --- /dev/null +++ b/net/nut/files/nut-config.sh.functions @@ -0,0 +1,293 @@ +#!/bin/sh /etc/rc.common + +# Create configuration for upsd and UPS drivers for NUT + +# shellcheck shell=ash + +# config_load nut_server is already called by sourcing script +# srv_statepath and srv_runas which set STATEPATH and RUNAS +# are also already run by sourcing script + +# Ephemeral files, recreated on each OpenWrt boot +USERS_C=/var/etc/nut/upsd.users +UPSD_C=/var/etc/nut/upsd.conf +UPS_C=/var/etc/nut/ups.conf + +# shellcheck source=net/nut/files/nut-common.sh.functions +. /lib/functions/nut/nut-common.sh + +# Writes configuration values to the UPS run-time configuration file +get_write_ups_config() { + local ups="$1" + local var="$2" + local def="$3" + local flag="$4" + local val + + [ -z "$flag" ] && { + config_get val "$ups" "$var" "$def" + [ -n "$val" ] && [ "$val" != "0" ] && echo "$var = $val" >>"$UPS_C" + } + + [ -n "$flag" ] && { + config_get_bool val "$ups" "$var" "$def" + [ "$val" = 1 ] && echo "$var" >>"$UPS_C" + } +} + +# Add upsd listen address and port to the run-time configuration +listen_address() { + local srv="$1" + + config_get address "$srv" address "::1" + config_get port "$srv" port + # shellcheck disable=SC2154 + echo "LISTEN $address $port" >>"$UPSD_C" +} + +# Adds upsd (NUT server) run-time configuration +srv_config() { + local srv="$1" + local maxage maxconn certfile + + config_get maxage "$srv" maxage + [ -n "$maxage" ] && echo "MAXAGE $maxage" >>"$UPSD_C" + + [ -n "$statepath" ] && echo "STATEPATH $statepath" >>"$UPSD_C" + + config_get maxconn "$srv" maxconn + [ -n "$maxconn" ] && echo "MAXCONN $maxconn" >>"$UPSD_C" + + #NOTE: certs only apply to SSL-enabled version + config_get certfile "$srv" certfile + [ -n "$certfile" ] && echo "CERTFILE $certfile" >>"$UPSD_C" +} + +# Adds run-time configuration for NUT users to the upsd.users file +nut_user_add() { + local user="$1" + local a + local val + + config_get val "$user" username "$1" + echo "[$val]" >>"$USERS_C" + + config_get val "$user" password + echo " password = $val" >>"$USERS_C" + + config_get val "$user" actions + for a in $val; do + echo " actions = $a" >>"$USERS_C" + done + + instcmd() { + # shellcheck disable=2317 + local val="$1" + # shellcheck disable=2317 + echo " instcmds = $val" >>"$USERS_C" + } + + config_list_foreach "$user" instcmd instcmd + + config_get val "$user" upsmon + if [ -n "$val" ]; then + echo " upsmon $val" >>"$USERS_C" + fi +} + +# Builds upsd (NUT server) run-time configuration +build_server_config() { + # UPS_C is created in build_config which calls this function + chmod 0640 "$UPS_C" + + mkdir -p "$(dirname "$UPSD_C")" + rm -f "$USERS_C" + rm -f "$UPSD_C" + rm -f /var/etc/nut/nut.conf + + echo "# Config file automatically generated from UCI config" >"$USERS_C" + echo "# Config file automatically generated from UCI config" >"$UPSD_C" + + config_foreach nut_user_add user + config_foreach listen_address listen_address + config_foreach srv_config upsd + echo "MODE=netserver" >>/var/etc/nut/nut.conf + + chmod 0640 "$USERS_C" + chmod 0640 "$UPSD_C" + chmod 0644 /var/etc/nut/nut.conf + + if [ -n "$RUNAS" ]; then + chgrp "$(id -gn "$RUNAS")" "$USERS_C" + chgrp "$(id -gn "$RUNAS")" "$UPSD_C" + fi + # shellcheck disable=SC2034 + havesrvcfg=1 +} + +# Builds UPS-specific run-time configuration +build_ups_config() { + local ups="$1" + + echo "[$ups]" >>"$UPS_C" + + get_write_ups_config "$ups" bus + get_write_ups_config "$ups" cable + get_write_ups_config "$ups" community + get_write_ups_config "$ups" desc + get_write_ups_config "$ups" driver "usbhid-ups" + get_write_ups_config "$ups" ignorelb 0 1 + get_write_ups_config "$ups" interruptonly 0 1 + get_write_ups_config "$ups" interruptsize + get_write_ups_config "$ups" maxreport + get_write_ups_config "$ups" maxstartdelay + get_write_ups_config "$ups" mfr + get_write_ups_config "$ups" model + get_write_ups_config "$ups" nolock 0 1 + get_write_ups_config "$ups" notransferoids 0 1 + get_write_ups_config "$ups" offdelay + get_write_ups_config "$ups" ondelay + get_write_ups_config "$ups" pollfreq + get_write_ups_config "$ups" port "auto" + get_write_ups_config "$ups" product + get_write_ups_config "$ups" productid + get_write_ups_config "$ups" retrydelay + get_write_ups_config "$ups" sdorder + get_write_ups_config "$ups" sdtime + get_write_ups_config "$ups" serial + get_write_ups_config "$ups" shutdown_delay + get_write_ups_config "$ups" snmp_version + get_write_ups_config "$ups" snmp_retries + get_write_ups_config "$ups" snmp_timeout + get_write_ups_config "$ups" synchronous + get_write_ups_config "$ups" usd + get_write_ups_config "$ups" vendor + get_write_ups_config "$ups" vendorid + + # Params specific to NetXML driver + get_write_ups_config "$ups" login + get_write_ups_config "$ups" password + get_write_ups_config "$ups" subscribe 0 1 + + # shellcheck disable=SC2317 + defoverride() { + local overvar="$1" + local defover="$2" + local overtype + local overval + + overtype="$(echo "$overvar" | tr '_' '.')" + + config_get overval "${defover}_${overvar}" value + [ -n "$overval" ] && echo "${defover}.${overtype} = $overval" >>"$UPS_C" + } + + config_list_foreach "$ups" override defoverride override + config_list_foreach "$ups" default defoverride default + + # From documentation and fixing of do_other function by + # in https://github.com/rpavlik in + # https://github.com/openwrt/packages/pull/28308 + # + # For each value "x" in the per-driver list "other", get the + # per-driver config variable "other_x", and if it is not empty, + # write "x = {value of other_x}" to the config file. + # For each value "x" in the per-driver list "otherflag", get the + # per-driver config variable "otherflag_x", and if it is true, write + # "x" to the config file. + + do_other() { + # shellcheck disable=SC2317 + local othervar="$1" + # shellcheck disable=SC2317 + local othervarflag="$2" + # shellcheck disable=SC2317 + local otherval + + # shellcheck disable=SC2317 + if [ "$othervarflag" = "otherflag" ]; then + config_get_bool otherval "${ups}" "${othervarflag}_${othervar}" value + [ "$otherval" = "1" ] && echo "${othervar}" >>"$UPS_C" + else + config_get otherval "${ups}" "${othervarflag}_${othervar}" value + [ -n "$otherval" ] && echo "${othervar} = $otherval" >>"$UPS_C" + fi + } + + config_list_foreach "$ups" other do_other other + config_list_foreach "$ups" otherflag do_other otherflag + + # For each entry in the list "other", get the + # variable "other_${entry}", and if not empty, write + # "${entry} = ${value of other_${entry}}" to the config file. + config_list_foreach "$ups" other do_other other + + # For each entry in the list "otherflag", get the + # variable "otherflag_${entry}", and if true, write "${entry}" to + # the config file. + config_list_foreach "$ups" otherflag do_other otherflag + + echo "" >>$UPS_C + # shellcheck disable=SC2034 + haveupscfg=1 +} + +# Add global drivers settings to run-time config +build_global_driver_config() { + local cfg="$1" + + # Global driver config + get_write_ups_config "$cfg" chroot + get_write_ups_config "$cfg" driverpath + get_write_ups_config "$cfg" maxstartdelay + get_write_ups_config "$cfg" maxretry + get_write_ups_config "$cfg" retrydelay + get_write_ups_config "$cfg" pollinterval + get_write_ups_config "$cfg" synchronous + config_get runas "$cfg" user nut + RUNAS="$runas" + + echo "" >>"$UPS_C" +} + +# Orchestrate the run-time configuration building process +build_config() { + + mkdir -p "$(dirname "$UPS_C")" + rm -f "$UPS_C" + echo "# Config file automatically generated from UCI config" >"$UPS_C" + chmod 0640 "$UPS_C" + + [ -d "${STATEPATH}" ] || { + mkdir -p "${STATEPATH}" || { + # We hard fail here, as we cannot rely on service state if + # STATEPATH does not exit + logger -s -t nut-server "ERROR: failed to create ${STATEPATH}" + exit 1 + } + } + chmod 0770 "${STATEPATH}" || { + # We hard fail here, as we cannot rely on service state if + # STATEPATH does not have the appropriate permissions + logger -s -t nut-server "ERROR: failed to set proper permissions on ${STATEPATH}" + exit 1 + } + + if [ -n "$RUNAS" ]; then + chown root:"$(id -gn "$RUNAS")" "${STATEPATH}" + fi + + SRV_RUNAS="$RUNAS" + config_foreach build_global_driver_config driver_global + if [ "$SRV_RUNAS" != "$RUNAS" ]; then + # We soft-fail here as this should be a recoverable error, once + # RUNAS is adjusted + echo "WARNING: for proper communication drivers and server must 'runas' the same user" | logger -s -t nut-server + fi + RUNAS="$SRV_RUNAS" + + config_foreach build_ups_config driver + + build_server_config + [ -n "$RUNAS" ] && chgrp "$(id -gn "$RUNAS")" "$UPS_C" +} diff --git a/net/nut/files/nut-monitor.init b/net/nut/files/nut-monitor.init index cd6a460141d4f5..28cacef22dd12e 100644 --- a/net/nut/files/nut-monitor.init +++ b/net/nut/files/nut-monitor.init @@ -1,11 +1,12 @@ #!/bin/sh /etc/rc.common -# shellcheck shell=ash +# shellcheck shell=busybox # shellcheck disable=SC2034 START=82 STOP=28 USE_PROCD=1 + UPSMON_C=/var/etc/nut/upsmon.conf PIDFILE=/var/run/upsmon.pid @@ -99,8 +100,8 @@ nut_upsmon_conf() { # debugmin must be a positive integer or zero config_get val "$cfg" debugmin 0 case "$val" in - ''|*[!0-9]*) ;; - *) echo "DEBUG_MIN $val" >>"$UPSMON_C" ;; + '' | *[!0-9]*) ;; + *) echo "DEBUG_MIN $val" >>"$UPSMON_C" ;; esac config_get val "$cfg" defaultnotify "SYSLOG" diff --git a/net/nut/files/nut-sendmail-notify.default b/net/nut/files/nut-sendmail-notify.default index ebffba55322e08..d27753abdb418b 100644 --- a/net/nut/files/nut-sendmail-notify.default +++ b/net/nut/files/nut-sendmail-notify.default @@ -1,5 +1,8 @@ #!/bin/sh +# shellcheck shell=busybox + +# shellcheck disable=SC1091 . "${IPKG_INSTROOT}"/lib/functions.sh REMOVEDEFAULTNOTIFY=0 @@ -12,11 +15,11 @@ upsmon() { config_get val "$cfg" defaultnotify if [ -n "$val" ]; then - if echo "$val" |grep -q "IGNORE"; then + if echo "$val" | grep -q "IGNORE"; then REMOVEDEFAULTNOTIFY=1 else SKIPADDSYSLOG=1 - if echo "$val" |grep -q "EXEC"; then + if echo "$val" | grep -q "EXEC"; then SKIPADDEXEC=1 fi fi diff --git a/net/nut/files/nut-server.init b/net/nut/files/nut-server.init old mode 100755 new mode 100644 index be1cd45e20a5ca..c67e88398f9fa8 --- a/net/nut/files/nut-server.init +++ b/net/nut/files/nut-server.init @@ -1,364 +1,48 @@ #!/bin/sh /etc/rc.common -# Copyright © 2012 OpenWrt.org +# Copyright © 2012-2026 OpenWrt.org +# +# Startup script for NUT (UPS) server # # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. # -# shellcheck shell=ash +# shellcheck shell=busybox # shellcheck disable=SC2034 START=70 STOP=30 -USERS_C=/var/etc/nut/upsd.users -UPSD_C=/var/etc/nut/upsd.conf -UPS_C=/var/etc/nut/ups.conf - USE_PROCD=1 -get_write_ups_config() { - local ups="$1" - local var="$2" - local def="$3" - local flag="$4" - local val - - [ -z "$flag" ] && { - config_get val "$ups" "$var" "$def" - [ -n "$val" ] && [ "$val" != "0" ] && echo "$var = $val" >>"$UPS_C" - } - - [ -n "$flag" ] && { - config_get_bool val "$ups" "$var" "$def" - [ "$val" = 1 ] && echo "$var" >>"$UPS_C" - } -} - -srv_statepath() { - local statepath - - config_get statepath upsd statepath /var/run/nut - STATEPATH="$statepath" -} - -srv_runas() { - local runas - - [ -n "$RUNAS" ] && return 0 - - config_get runas upsd runas nut - RUNAS="$runas" -} - -listen_address() { - local srv="$1" - - config_get address "$srv" address "::1" - config_get port "$srv" port - # shellcheck disable=SC2154 - echo "LISTEN $address $port" >>"$UPSD_C" -} - -srv_config() { - local srv="$1" - local maxage maxconn certfile runas statepath - - # Note runas support requires you make sure USB device file is readable by - # the runas user - config_get runas "$srv" runas nut - RUNAS="$runas" - - config_get statepath "$srv" statepath /var/run/nut - STATEPATH="$statepath" - - config_get maxage "$srv" maxage - [ -n "$maxage" ] && echo "MAXAGE $maxage" >>"$UPSD_C" - - [ -n "$statepath" ] && echo "STATEPATH $statepath" >>"$UPSD_C" - - config_get maxconn "$srv" maxconn - [ -n "$maxconn" ] && echo "MAXCONN $maxconn" >>"$UPSD_C" - - #NOTE: certs only apply to SSL-enabled version - config_get certfile "$srv" certfile - [ -n "$certfile" ] && echo "CERTFILE $certfile" >>"$UPSD_C" -} - -nut_user_add() { - local user="$1" - local a - local val - - config_get val "$user" username "$1" - echo "[$val]" >> "$USERS_C" - - config_get val "$user" password - echo " password = $val" >> "$USERS_C" - - config_get val "$user" actions - for a in $val; do - echo " actions = $a" >> "$USERS_C" - done - - instcmd() { - # shellcheck disable=2317 - local val="$1" - # shellcheck disable=2317 - echo " instcmds = $val" >> "$USERS_C" - } - - config_list_foreach "$user" instcmd instcmd - - config_get val "$user" upsmon - if [ -n "$val" ]; then - echo " upsmon $val" >> "$USERS_C" - fi -} - -build_server_config() { - mkdir -p "$(dirname "$UPSD_C")" - chmod 0640 "$UPS_C" - rm -f "$USERS_C" - rm -f "$UPSD_C" - rm -f /var/etc/nut/nut.conf - - echo "# Config file automatically generated from UCI config" > "$USERS_C" - echo "# Config file automatically generated from UCI config" > "$UPSD_C" - - config_foreach nut_user_add user - config_foreach listen_address listen_address - config_foreach srv_config upsd - echo "MODE=netserver" >>/var/etc/nut/nut.conf - - chmod 0640 "$USERS_C" - chmod 0640 "$UPSD_C" - chmod 0644 /var/etc/nut/nut.conf - - if [ -n "$RUNAS" ]; then - chgrp "$(id -gn "$RUNAS")" "$USERS_C" - chgrp "$(id -gn "$RUNAS")" "$UPSD_C" - fi - havesrvcfg=1 -} - -build_ups_config() { - local ups="$1" - - echo "[$ups]" >>"$UPS_C" - - get_write_ups_config "$ups" bus - get_write_ups_config "$ups" cable - get_write_ups_config "$ups" community - get_write_ups_config "$ups" desc - get_write_ups_config "$ups" driver "usbhid-ups" - get_write_ups_config "$ups" ignorelb 0 1 - get_write_ups_config "$ups" interruptonly 0 1 - get_write_ups_config "$ups" interruptsize - get_write_ups_config "$ups" maxreport - get_write_ups_config "$ups" maxstartdelay - get_write_ups_config "$ups" mfr - get_write_ups_config "$ups" model - get_write_ups_config "$ups" nolock 0 1 - get_write_ups_config "$ups" notransferoids 0 1 - get_write_ups_config "$ups" offdelay - get_write_ups_config "$ups" ondelay - get_write_ups_config "$ups" pollfreq - get_write_ups_config "$ups" port "auto" - get_write_ups_config "$ups" product - get_write_ups_config "$ups" productid - get_write_ups_config "$ups" retrydelay - get_write_ups_config "$ups" sdorder - get_write_ups_config "$ups" sdtime - get_write_ups_config "$ups" serial - get_write_ups_config "$ups" shutdown_delay - get_write_ups_config "$ups" snmp_version - get_write_ups_config "$ups" snmp_retries - get_write_ups_config "$ups" snmp_timeout - get_write_ups_config "$ups" synchronous - get_write_ups_config "$ups" usd - get_write_ups_config "$ups" vendor - get_write_ups_config "$ups" vendorid - - # Params specific to NetXML driver - get_write_ups_config "$ups" login - get_write_ups_config "$ups" password - get_write_ups_config "$ups" subscribe 0 1 - - # shellcheck disable=SC2317 - defoverride() { - local overvar="$1" - local defover="$2" - local overtype - local overval - - overtype="$(echo "$overvar" | tr '_' '.')" - - config_get overval "${defover}_${overvar}" value - [ -n "$overval" ] && echo "${defover}.${overtype} = $overval" >>"$UPS_C" - } - - config_list_foreach "$ups" override defoverride override - config_list_foreach "$ups" default defoverride default - - other() { - # shellcheck disable=SC2317 - local othervar="$1" - # shellcheck disable=SC2317 - local othervarflag="$2" - # shellcheck disable=SC2317 - local otherval - - # shellcheck disable=SC2317 - if [ "$othervarflag" = "otherflag" ]; then - config_get_bool otherval "${othervarflag}_${othervar}" value - [ "$otherval" = "1" ] && echo "${othervar}" >>"$UPS_C" - else - config_get otherval "${othervarflag}_${othervar}" value - [ -n "$otherval" ] && echo "${othervar} = $otherval" >>"$UPS_C" - fi - } - - config_list_foreach "$ups" other other other - config_list_foreach "$ups" otherflag other otherflag - echo "" >>$UPS_C - haveupscfg=1 -} - -build_global_driver_config() { - local cfg="$1" - - # Global driver config - get_write_ups_config "$cfg" chroot - get_write_ups_config "$cfg" driverpath - get_write_ups_config "$cfg" maxstartdelay - get_write_ups_config "$cfg" maxretry - get_write_ups_config "$cfg" retrydelay - get_write_ups_config "$cfg" pollinterval - get_write_ups_config "$cfg" synchronous - config_get runas "$cfg" user nut - RUNAS="$runas" - - echo "" >>"$UPS_C" -} - -build_config() { - local STATEPATH=/var/run/nut - - mkdir -p "$(dirname "$UPS_C")" - rm -f "$UPS_C" - echo "# Config file automatically generated from UCI config" > "$UPS_C" - chmod 0640 "$UPS_C" - - config_load nut_server - - srv_runas - srv_statepath - - [ -d "${STATEPATH}" ] || { - mkdir -p "${STATEPATH}" - } - chmod 0770 "${STATEPATH}" - - if [ -n "$RUNAS" ]; then - chown root:"$(id -gn "$RUNAS")" "${STATEPATH}" - fi - - SRV_RUNAS="$RUNAS" - config_foreach build_global_driver_config driver_global - if [ "$SRV_RUNAS" != "$RUNAS" ]; then - echo "WARNING: for proper communication drivers and server must 'runas' the same user" | logger -t nut-server - fi - - config_foreach build_ups_config driver - - build_server_config - [ -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 +config_load nut_server - # Next filter by serial, if provided - if [ -n "$serial" ] && [ "$serial" != "$(cat "$device_path"/serial)" ]; then - continue - fi +# shellcheck source=net/nut/files/nut-common.sh.functions +. "${IPKG_INSTROOT}"/lib/functions/nut/nut-common.sh - usb_bus="$(printf "%03d" "$(cat "$device_path"/busnum)")" - usb_dev="$(printf "%03d" "$(cat "$device_path"/devnum)")" +srv_statepath # set STATEPATH +srv_runas # set RUNAS - # 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 +# shellcheck source=net/nut/files/nut-config.sh.functions +. "${IPKG_INSTROOT}"/lib/functions/nut/nut-config.sh +# shellcheck source=net/nut/files/nut-service.sh.functions +. "${IPKG_INSTROOT}"/lib/functions/nut/nut-service.sh - # 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 -} +# shellcheck source=/dev/null +. "${IPKG_INSTROOT}"/lib/functions/network.sh -# Must be called from start_service +# Start a ups driver instance start_ups_driver() { local ups="$1" local requested="$2" local driver - local STATEPATH=/var/run/nut - local RUNAS=nut # If wanting a specific instance, only start it if [ "$requested" != "$ups" ] && [ "$requested" != "" ]; then return 0 fi - # Avoid hotplug inadvertently restarting driver during - # forced shutdown - [ -f /var/run/killpower ] && return 0 - if [ -d /var/run/nut ] && [ -f /var/run/nut/disable-hotplug ]; then - return 0 - fi - - # Depends on config_load from start_service - srv_statepath - srv_runas + prevent_hotplug_restart ensure_usb_ups_access "$ups" config_get driver "$ups" driver "usbhid-ups" @@ -368,20 +52,22 @@ start_ups_driver() { procd_set_param stdout 0 # Subset of stderr procd_set_param env NUT_QUIET_INIT_UPSNOTIFY=true procd_set_param env NUT_STATEPATH="${STATEPATH}" - procd_set_param command /lib/nut/"${driver}" -FF -a "$ups" ${RUNAS:+-u "$RUNAS"} + procd_set_param command /usr/libexec/nut/"${driver}" -FF -a "$ups" + if [ -n "$RUNAS" ]; then + procd_append_param command -u "$RUNAS" + fi procd_close_instance haveupscfg=1 } +# Setup triggers in procd for changes to specified network interfaces interface_triggers() { local action="$1" local triggerlist trigger config_get triggerlist upsd triggerlist - # shellcheck disable=SC1091 - . /lib/functions/network.sh - + # If we are handling only specific network interfaces if [ -n "$triggerlist" ]; then for trigger in $triggerlist; do if [ "$action" = "add_trigger" ]; then @@ -391,6 +77,7 @@ interface_triggers() { fi done else + # If we are handling all network interfaces if [ "$action" = "add_trigger" ]; then procd_add_raw_trigger "interface.*.up" 2000 /etc/init.d/nut-server reload else @@ -400,6 +87,7 @@ interface_triggers() { [ "$action" = "add_trigger" ] || return 1 } +# Start upsd instance start_server_instance() { local srv="$1" @@ -413,85 +101,29 @@ start_server_instance() { procd_close_instance } -# shellcheck disable=SC2120 -start_service() { - local STATEPATH=/var/run/nut - local haveupscfg=0 - local havesrvcfg=0 - - # Avoid hotplug inadvertently restarting driver during - # forced shutdown - [ -f /var/run/killpower ] && return 0 - - srv_statepath - config_load nut_server - build_config - - should_start_srv=1 - [ "$havesrvcfg" = "1" ] || should_start_srv=0 - # Avoid crashloop on server (upsd) when no ups is configured; make sure server - # is not running if no ups is found in configuration - [ "$haveupscfg" = "1" ] || should_start_srv=0 - interface_triggers "check_interface_up" || should_start_srv=0 - - [ "$should_start_srv" = "1" ] || return 0 - - # We only start one service (upsd or one driver) from a given invocation - case "$1" in - "") - config_foreach start_ups_driver driver - start_server_instance upsd - ;; - *upsd*) - start_server_instance upsd - ;; - *) - config_foreach start_ups_driver driver "$1" - ;; - esac -} - -server_active() { - local nut_server_active - - nut_server_active=$(_procd_ubus_call list | jsonfilter -l 1 -e "@['nut-server']") - [ "$nut_server_active" = "{ }" ] && return 0 -} +# Reload NUT drivers and upsd (NUT server) +reload_ups_driver() { + local ups="$1" + local requested="$2" + local driver -list_running_instances() { - local service="$1" - local running_instances + # If wanting a specific instance, only reload that instance + if [ "$requested" != "$ups" ] && [ "$requested" != "" ]; then + return 0 + fi - running_instances=$(_procd_ubus_call list | jsonfilter -e "@['$service'][@.*.running=true]") + prevent_hotplug_restart - if [ -n "$running_instances" ]; then - json_init - json_load "$running_instances" - json_get_keys instance_names - # shellcheck disable=SC2154 - echo "$instance_names" - json_cleanup - fi -} + config_get driver "$ups" driver "usbhid-ups" -signal_instance() { - local instance_name="$1" - local process_name="$2" - local signal_command="$3" - local signal="$4" - local pidfile="$5" - local secondary_command="$6" - - if [ -s "$pidfile" ]; then - $signal_command | logger -t nut-server - elif pgrep "$process_name" >/dev/null 2>/dev/null; then - procd_send_signal nut-server "$instance_name" "$signal" 2>&1 | logger -t nut-server - fi - if [ -n "$secondary_command" ] && procd_running nut-server "$instance_name"; then - $secondary_command 2>&1 | logger -t nut-server + # Try to reload, otherwise exit politely, then stop and restart procd instance + if procd_running nut-server "$ups"; then + signal_instance "$ups" "$driver" "/usr/libexec/nut/'${driver}' -c reload-or-exit -a '${ups}'" HUP "${STATEPATH}/${driver}-${ups}.pid" fi + /etc/init.d/nut-server start "$ups" 2>&1 | logger -t nut-server } +# Stop NUT drivers and upsd (NUT server) stop_ups_driver() { local ups="$1" # The ups (driver instance) local requested="$2" @@ -502,7 +134,6 @@ stop_ups_driver() { return 0 fi - srv_statepath build_ups_config "$ups" # If we don't have UPS configuration simply stop all instances @@ -513,162 +144,187 @@ stop_ups_driver() { return 0 fi - config_get driver "$ups" driver "usbhid-ups" + config_get driver "$ups" driver "usbhid-ups" # usbhid-ups is a default + # If there is a running driver instance for this UPS if procd_running nut-server "$ups"; then signal_instance "$ups" "$driver" "/lib/nut/'${driver}' -c exit -a '${ups}'" "TERM" "${STATEPATH}/${driver}-${ups}.pid" + # If there a NUT server instance if procd_running nut-server upsd >/dev/null 2>&1; then signal_instance upsd upsd "upsd -c stop" "TERM" "${STATEPATH}/upsd.pid" "procd_kill nut-server upsd" fi fi } -reload_ups_driver() { - local ups="$1" - local requested="$2" - local driver - - # If wanting a specific instance, only reload that instance - if [ "$requested" != "$ups" ] && [ "$requested" != "" ]; then - return 0 - fi - - # Avoid hotplug inadvertently restarting driver during - # forced shutdown - [ -f /var/run/killpower ] && return 0 - if [ -d /var/run/nut ] && [ -f /var/run/nut/disable-hotplug ]; then - return 0 - fi - - config_get driver "$ups" driver "usbhid-ups" - - srv_statepath - - # Try to reload, otherwise exit politely, then stop and restart procd instance - if procd_running nut-server "$ups"; then - signal_instance "$ups" "$driver" "/lib/nut/'${driver}' -c reload-or-exit -a '${ups}'" HUP "${STATEPATH}/${driver}-${ups}.pid" +stop_all_services() { + if procd_running nut-server upsd >/dev/null 2>&1; then + procd_kill nut-server upsd 2>&1 | logger -t nut-server fi - /etc/init.d/nut-server start "$ups" 2>&1 | logger -t nut-server + config_foreach stop_ups_driver driver } -reload_service() { +service_preconditions() { local should_stop_srv - local STATEPATH=/var/run/nut + local should_start_srv local havesrvcfg=0 local haveupscfg=0 - local running_instances - local driver - # Avoid hotplug inadvertently restarting driver during forced shutdown - [ -f /var/run/killpower ] && return 0 + prevent_hotplug_restart - config_load nut_server build_config - should_stop_srv=0 - [ "$havesrvcfg" = "1" ] || should_stop_srv=1 - # Avoid crashloop on server (upsd) when no ups is configured; make sure server - # is not running if no ups is found in configuration - [ "$haveupscfg" = "1" ] || should_stop_srv=1 - interface_triggers "check_interface_up" || should_stop_srv=1 + if [ "$action" = "start" ]; then + # Make sure we have a server (upsd) and at lease one driver (UPS) + # configuration, and that at least one interface is up, before + # starting nut-server service. + should_start_srv=1 + [ "$havesrvcfg" = "1" ] || should_start_srv=0 + # Avoid crashloop on server (upsd) when no ups is configured; make sure server + # is not running if no ups is found in configuration + [ "$haveupscfg" = "1" ] || should_start_srv=0 + interface_triggers "check_interface_up" || should_start_srv=0 + elif [ "$action" = "reload" ]; then + should_stop_srv=0 + [ "$havesrvcfg" = "1" ] || should_stop_srv=1 + # Avoid crashloop on server (upsd) when no ups is configured; make sure server + # is not running if no ups is found in configuration + [ "$haveupscfg" = "1" ] || should_stop_srv=1 + interface_triggers "check_interface_up" || should_stop_srv=1 + fi - if [ "$should_stop_srv" != "0" ]; then - if procd_running nut-server upsd >/dev/null 2>&1; then - procd_kill nut-server upsd 2>&1 | logger -t nut-server - fi - config_foreach stop_ups_driver driver + [ "$should_start_srv" = "1" ] || { + logger -s -t nut-server "Missing configuration or interface. Not starting." + return 1 + } - # Also stop any driver instances which are no longer configured - for instance in $(list_running_instances "nut-server"); do - if [ "$instance" != "upsd" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then - procd_kill nut-server "$instance" 2>&1 | logger -t nut-server - fi - done + if [ "$should_stop_srv" != "0" ]; then + stop_all_services fi - # If nut-server was started but has no instances (even upsd) - if server_active; then - logger -t nut-server "nut-server active with no instances" - /etc/init.d/nut-server start 2>&1 | logger -t nut-server - # Otherwise, if we have at least one instance running - elif procd_running nut-server; then - # If server (upsd) is running - if procd_running nut-server upsd; then - # Try to signal server (upsd) to reload configuration - signal_instance "upsd" "upsd" "upsd -c reload" HUP "${STATEPATH}/upsd.pid" - # If server (upsd) is not running - else - # Start server (upsd) - /etc/init.d/nut-server start "upsd" 2>&1 | logger -t nut-server + # Stop any driver instances which are no longer configured + # We can only reliably do this for instances managed by procd + for instance in $(list_running_instances "nut-server"); do + if [ "$instance" = "upsd" ]; then + continue + fi + unset driver + config_get driver "$instance" driver + if [ -z "$driver" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then + procd_kill nut-server "$instance" 2>&1 | logger -t nut-server fi - config_foreach reload_ups_driver driver + done - # Stop any driver instances which are no longer configured - # We can only reliably do this for instances managed by procd - for instance in $(list_running_instances "nut-server"); do - if [ "$instance" = "upsd" ]; then - continue - fi - unset driver - config_get driver "$instance" driver - if [ -z "$driver" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then - procd_kill nut-server "$instance" 2>&1 | logger -t nut-server - fi - done - # Nut-server is not started, so start it - else - /etc/init.d/nut-server start 2>&1 | logger -t nut-server - fi + return 0 } -stop_service() { - config_load nut_server - srv_statepath +# Stop or reload active instances +manage_instances() { + local action="$1" + local active_upsd_command + local active_upsd_signal_signal + local active_upsd_secondary_command + shift + + if [ "$action" = "reload" ]; then + active_upsd_command="upsd -c reload" + active_upsd_signal=HUP + elif [ "$action" = "stop" ]; then + active_upsd_command="upsd -c stop" + active_upsd_signal=TERM + active_upsd_secondary_command="procd_kill nut-server upsd" + fi - # We only handle the first parameter passed - case "$1" in - "") + if [ "$action" = "reload" ] || [ "$action" = "stop" ]; then # If nut-server was started but has no instances (even upsd) if server_active; then logger -t nut-server "nut-server active with no instances" procd_kill nut-server 2>&1 | logger -t nut-server elif procd_running nut-server; then # if have at least one instance - signal_instance "upsd" "upsd" "upsd -c stop" TERM "${STATEPATH}/upsd.pid" "procd_kill nut-server upsd" + if procd_running nut-server upsd; then + signal_instance "upsd" "upsd" "$active_upsd_command" "$active_upsd_signal" "${STATEPATH}/upsd.pid" "$active_upsd_secondary_command" + fi + if [ "$action" = "reload" ]; then + config_foreach reload_ups_driver driver + elif [ "$action" = "stop" ]; then + stop_all_services + fi + elif [ "$action" = "reload" ]; then # no instances and reload + /etc/init.d/nut-server start 2>&1 | logger -t nut-server + fi + fi - config_foreach stop_ups_driver driver + if [ "$action" = "stop" ]; then + procd_kill nut-server 2>&1 | logger -t nut-server + fi - # Also stop any driver instances which are no longer configured - for instance in $(list_running_instances "nut-server"); do - if [ "$instance" != "upsd" ] && procd_running nut-server "$instance" >/dev/null 2>&1; then - procd_kill nut-server "$instance" 2>&1 | logger -t nut-server - fi - done +} - # If nut-server active but has no instances (even upsd) - if server_active >/dev/null 2>&1; then - procd_kill nut-server 2>&1 | logger -t nut-server - fi - fi - ;; - *upsd*) - if procd_running nut-server upsd; then - signal_instance "upsd" "upsd" "upsd -c stop" TERM "${STATEPATH}/upsd.pid" "procd_kill nut-server upsd" - # If nut-server is active with no instances - if server_active; then - procd_kill nut-server 2>&1 | logger -t nut-server +# Manage NUT drivers and upsd (NUT server) services + +# shellcheck disable=SC2120 +manage_service() { + local driver + local action="$1" + shift + local ups="$1" + + service_preconditions "$action" || return 0 + manage_instances "$action" + + if [ "$action" = "start" ]; then + # We only start one service (upsd or one driver) from a given invocation + case "$ups" in + "") + config_foreach start_ups_driver driver + start_server_instance upsd + ;; + *upsd*) + start_server_instance upsd + ;; + *) + config_foreach start_ups_driver driver "$ups" + ;; + esac + elif [ "$action" = "reload" ]; then + manage_instances reload "$@" + elif [ "$action" = "stop" ]; then + case "$ups" in + "") + manage_instances "stop" + ;; + *upsd*) + if procd_running nut-server upsd; then + signal_instance "upsd" "upsd" "upsd -c stop" TERM "${STATEPATH}/upsd.pid" "procd_kill nut-server upsd" + # If nut-server is active with no instances + if server_active; then + procd_kill nut-server 2>&1 | logger -t nut-server + fi fi - fi - ;; - *) - # We only handle the first parameter, so do not pass in all parameters - config_foreach stop_ups_driver driver "$1" - ;; - esac + ;; + *) + # We only handle the first parameter, so do not pass in all parameters + config_foreach stop_ups_driver driver "$ups" + ;; + esac + fi } -service_triggers() { - config_load nut_server +# Start NUT drivers and upsd (NUT server) +start_service() { + manage_service start "$@" +} + +# Reload NUT drivers and upsd (NUT server) +reload_service() { + manage_service reload "$@" +} + +# Stop NUT drivers and upsd (NUT server) +stop_service() { + manage_service stop "$@" +} +service_triggers() { interface_triggers "add_trigger" procd_add_reload_trigger "nut_server" } diff --git a/net/nut/files/nut-service.sh.functions b/net/nut/files/nut-service.sh.functions new file mode 100644 index 00000000000000..283e5553824e72 --- /dev/null +++ b/net/nut/files/nut-service.sh.functions @@ -0,0 +1,142 @@ +#!/bin/sh /etc/rc.common + +# Helper functions for NUT server service handling + +# shellcheck shell=ash + +# config_load nut_server is already called by sourcing script +# srv_statepath and srv_runas which set STATEPATH and RUNAS +# are also already run by sourcing script + +# shellcheck source=net/nut/files/nut-common.sh.functions +. /lib/functions/nut/nut-common.sh + +# Ensure the RUNAS user (server under which upsd and drivers run), has +# access to the USB UPS device +ensure_usb_ups_access() { + local ups="$1" + local vendorid + local productid + + config_get vendorid "$ups" vendorid + config_get productid "$ups" productid + config_get serial "$ups" serial + + [ -n "$vendorid" ] || return + [ -n "$productid" ] || return + + local NL=' +' + + # Loop on the idVendor of all USB devices attached to the system + 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 [ -z "$(cat "$vendor_path")" ]; then + logger -s -t nut-server "ERROR: could not read vendor ID from $vendor_path" + continue + fi + + if [ "$(cat "$vendor_path" 2>/dev/null)" != "$vendorid" ]; then + continue + fi + + device_path="$(dirname "$vendor_path")" + + # Then filter by product ID + + if [ -z "$(cat "$device_path/idProduct")" ]; then + logger -s -t nut-server "ERROR: could not read product ID from $device_path/idProduct" + continue + fi + + if [ "$(cat "$device_path/idProduct" 2>/dev/null)" != "$productid" ]; then + continue + fi + + # Next filter by serial (number), if provided + + if [ -z "$(cat "$device_path/serial")" ]; then + logger -s -t nut-server "ERROR: could not read serial (number) from $device_path/serial" + continue + fi + + 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 method used + # to match UPS device to a configuration, in this script + fi + done +} + +# Detect if upsd is running under procd, even if it has no instances +# (that is no UPS drivers running) +server_active() { + local nut_server_active + + nut_server_active=$(_procd_ubus_call list | jsonfilter -l 1 -e "@['nut-server']") + [ "$nut_server_active" = "{ }" ] && return 0 +} + +# Emit list of running UPS driver instances +list_running_instances() { + local service="$1" + local running_instances + + running_instances=$(_procd_ubus_call list | jsonfilter -e "@['$service'][@.*.running=true]") + + if [ -n "$running_instances" ]; then + json_init + json_load "$running_instances" + json_get_keys instance_names + # shellcheck disable=SC2154 + echo "$instance_names" + json_cleanup + fi +} + +# Send a signal to a UPS driver or server instance +signal_instance() { + local instance_name="$1" + local process_name="$2" + local signal_command="$3" + local signal="$4" + local pidfile="$5" + local secondary_command="$6" + + # Prefer sending signal using '$signal_command', which requires a + # PID file exists + # Otherwise, send signal(s) the process(es) with the instance name + if [ -s "$pidfile" ]; then + logger -s -t nut-server "Sending signal_command '$signal_command' to $instance_name" + $signal_command | logger -s -t nut-server + elif pgrep "$process_name" >/dev/null 2>/dev/null; then + logger -s -t nut-server "Sending signal $signal to $instance_name" + procd_send_signal nut-server "$instance_name" "$signal" 2>&1 | logger -s -t nut-server + fi + + if [ -n "$secondary_command" ] && procd_running nut-server "$instance_name"; then + logger -s -t nut-server "Issuing secondary_command '$secondary_command' for $instance_name" + $secondary_command 2>&1 | logger -s -t nut-server + fi +} diff --git a/net/nut/files/nut_serial.hotplug b/net/nut/files/nut_serial.hotplug index 4bab3b6944d1db..60dafe84868f5f 100644 --- a/net/nut/files/nut_serial.hotplug +++ b/net/nut/files/nut_serial.hotplug @@ -5,16 +5,13 @@ nut_serial() { local runas enable_usb_serial port config_get_bool enable_usb_serial "$cfg" enable_usb_serial 0 config_get port "$cfg" port - config_get runas "$cfg" runas "nut" - - [ -z "$runas" ] && config_get runas upsd runas "nut" [ "$enable_usb_serial" -eq 1 ] && { # If port is specified only change tty's matching port if [ -n "$port" ] && [ "$port" != /dev/"$DEVNAME" ]; then return 0 fi - [ -n "$runas" ] && chgrp "$(id -gn "${runas}")" /dev/"$DEVNAME" + [ -n "$RUNAS" ] && chgrp "$(id -gn "${RUNAS}")" /dev/"$DEVNAME" chmod g+rw /dev/"$DEVNAME" } } @@ -23,6 +20,11 @@ nut_on_hotplug_add() { . "${IPKG_INSTROOT}"/lib/functions.sh config load nut_server + + . /lib/functions/nut/nut-common.sh + srv_runas + srv_statepath + config_foreach nut_serial driver } diff --git a/net/nut/files/nutshutdown b/net/nut/files/nutshutdown index 7270f983f1088e..f8115270465f9d 100755 --- a/net/nut/files/nutshutdown +++ b/net/nut/files/nutshutdown @@ -14,7 +14,7 @@ shutdown_instance() { # Only FSD if killpower was indicated if [ -f /var/run/killpower ]; then - /lib/nut/"${driver}" -a "$cfg" -k + /usr/libexec/nut/"${driver}" -a "$cfg" -k fi } diff --git a/net/nut/test-version.sh b/net/nut/test-version.sh new file mode 100644 index 00000000000000..2b07817d0a98d3 --- /dev/null +++ b/net/nut/test-version.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +if [ "$PKG_NAME" = "nut" ]; then + exit 0 +fi + +EXEC="${PKG_NAME#nut-}" + +case "$EXEC" in +common | upsmon-sendmail-notify | avahi-service) + exit 0 + ;; +driver-*) + DRIVER="${EXEC#driver-}" + /usr/libexec/nut/"$DRIVER" -V 2>&1 | grep -qF "${PKG_VERSION}" + ;; +server) + "upsd" -V 2>&1 | grep -qF "${PKG_VERSION}" && "upsdrvctl" -V 2>&1 | grep -qF "${PKG_VERSION}" + ;; +upssched) + # Only intended to be run from upsmon + exit 0 + ;; +web-cgi) + # Only runs as CGI scripts + exit 0 + ;; +*) + "$EXEC" -V 2>&1 | grep -qF "${PKG_VERSION}" + ;; +esac