This commit fixes bugs, cleans and enhances init.d script: 1. The first disk and interface was passed to mini_snmpd with extra leading space. This bug caused the first monitored disk and interface to be inaccessible by SNMP daemon. 2. Automatically reload deamon if one of monitored interfaces goes up/down. Since mini_snmpd reads interface list only at startup, it won't detect any interface which appeared after the daemon was started. Fortunately we can use procd interface triggers to automatically restart the daemon. 3. Replace hand-written direct ubus calls and json data filtering with standard network functions. Signed-off-by: Marcin Jurkowski <marcin1j@gmail.com>
237 lines
9.1 KiB
Bash
237 lines
9.1 KiB
Bash
#!/bin/sh /etc/rc.common
|
|
# Copyright (C) 2009-2016 OpenWrt.org
|
|
# Copyright (C) 2016 Luke McKee <hojuruku@gmail.com>
|
|
# Procd init script reference: http://wiki.prplfoundation.org/wiki/Procd_reference
|
|
|
|
START=98
|
|
USE_PROCD=1
|
|
PROG=/usr/bin/mini_snmpd
|
|
NAME=mini_snmpd
|
|
|
|
_log() {
|
|
logger -p daemon.info -t mini_snmpd "$@"
|
|
}
|
|
|
|
_err() {
|
|
logger -p daemon.err -t mini_snmpd "$@"
|
|
}
|
|
|
|
|
|
# mini_snmpd 1.3+ now starts later in the game. Expects filesystems monitored to be already mounted, or wont pass args to mini_snmpd
|
|
# and at least configuration entry for network physical interface defined in /etc/config/network
|
|
# It handles network interfaces not yet present (e.g. ppp) but will statfs() the root/wrong filesystem if device not mounted
|
|
# Tip: complex scripts run faster without in openwrt if you stop busybox forking and searching for applets. Faster bootups
|
|
# CONFIG_BUSYBOX_CONFIG_FEATURE_SH_NOFORK
|
|
# CONFIG_BUSYBOX_CONFIG_FEATURE_PREFER_APPLETS
|
|
# BUSYBOX_CONFIG_ASH_OPTIMIZE_FOR_SIZE [=n]
|
|
# CONFIG_BUSYBOX_CONFIG_ASH_CMDCMD
|
|
|
|
mini_snmpd_validation="enabled:bool:0 \
|
|
ipv6:bool:0 \
|
|
debug:bool:0 \
|
|
auth:bool:1 \
|
|
community:rangelength(1,32):public \
|
|
contact:maxlength(255) \
|
|
location:maxlength(255) \
|
|
listen_interface:uciname \
|
|
udp_port:port \
|
|
tcp_port:port \
|
|
vendor_oid:string \
|
|
mib_timeout:and(min(1),uinteger) \
|
|
disks:list(directory) \
|
|
interfaces:list(uciname) \
|
|
respawn_threshold:uinteger respawn_timeout:uinteger respawn_retry:uinteger"
|
|
# busybox ash has no array variable support, when put validations in a string be careful to have no spaces in each validate constraint
|
|
# this makes it very difficult to use the 'or(uciname, "all")' test, so listen_interface '' or undefined now meands bind to "all".
|
|
# this is the sarafice you have to make to avoid typing it all in twice in this script so we can give feedback to user on what's misconfigered
|
|
# in syslog
|
|
|
|
append_disk() {
|
|
local disk="$1" disk_count
|
|
[ -z $disk_count ] && disk_count=0
|
|
if grep -qF "$disk" /proc/mounts ; then
|
|
# check the fileystem is mountpoint, and directory search permissions available for statfs()
|
|
# presence as a directory -d test done is already done by uci_validate_section()
|
|
[ -x "$disk" ] || {
|
|
_err "$cfg: mountpoint $disk for snmp monitoring EACCES error. Check permissions, ignoring"
|
|
return 1
|
|
}
|
|
if [ $disk_count -lt 4 ] ; then
|
|
append disks_arg "$disk" ','
|
|
disk_count=$((disk_count++))
|
|
else
|
|
_err "$cfg: more than 4 mountpoints defined in uci. Disc $disk ignored."
|
|
fi
|
|
else
|
|
_err "$cfg: mountpoint $disk for snmp monitoring not mounted, ignoring."
|
|
fi
|
|
}
|
|
|
|
append_interface() {
|
|
local name="$1" netdev netdev_count
|
|
[ -z $netdev_count ] && netdev_count=0
|
|
# for the purposes of snmp monitoring it doesn't need to be up, it just needs to exist in /proc/net/dev
|
|
network_get_device netdev "$name"
|
|
if [ -n "$netdev" ] && grep -qF "$netdev" /proc/net/dev ]; then
|
|
[ $netdev_count -ge 4 ] && {
|
|
_err "$cfg: too many network interfaces configured, ignoring $name"
|
|
return
|
|
}
|
|
netdev_count=$((netdev_count++))
|
|
if [ -n "$interfaces_arg" ]; then
|
|
append interfaces_arg "$netdev" ','
|
|
else
|
|
append interfaces_arg "$netdev"
|
|
fi
|
|
else
|
|
_log "$cfg: physical interface for network $name not found in uci or kernel so not monitoring"
|
|
fi
|
|
}
|
|
|
|
append_arg() {
|
|
local var="$2"
|
|
local opt="$1"
|
|
[ -n "$var" ] && procd_append_param command $opt "$var"
|
|
}
|
|
|
|
watch_interfaces() {
|
|
local cfg="$1"
|
|
local enabled listen_interface # listen_interface_up
|
|
config_get_bool enabled "$cfg" "enabled" '1'
|
|
[ "$enabled" -gt 0 ] || return 0
|
|
config_get listen_interface "$cfg" listen_interface
|
|
# If the interface is up & instance is running we'll watch at the instance level and only restart that instance if it's bound interface changes
|
|
# Regardless of ubus knowing about an interface (in the case it's not yet configured)
|
|
[ -n "$listen_interface" ] && trigger_interfaces="${listen_interface} ${trigger_interfaces} "
|
|
# Restart daemon if one of monitored interfaces changes
|
|
config_get reload_interfaces "$cfg" interfaces
|
|
|
|
}
|
|
|
|
validate_mini_snmpd_section() {
|
|
# validate a mini_snmpd instance in uci config file mini_snmpd
|
|
# http://luci.subsignal.org/trac/wiki/Documentation/Datatypes ubox/validate/validate.c
|
|
uci_validate_section mini_snmpd mini_snmpd "${1}" $mini_snmpd_validation
|
|
}
|
|
|
|
|
|
service_triggers() {
|
|
config_load 'mini_snmpd'
|
|
procd_open_trigger
|
|
procd_add_config_trigger "config.change" "mini_snmpd" /etc/init.d/mini_snmpd reload
|
|
config_foreach watch_interfaces 'mini_snmpd'
|
|
# this only watches interfaces for which there is no running instance due to interface down / not in ubus
|
|
# hence start not reload, this trigger will not affect running instances as another start will not change their procd command arguments
|
|
# or stop the already running process
|
|
[ -n "$trigger_interfaces" ] & {
|
|
for n in $trigger_interfaces ; do
|
|
procd_add_interface_trigger "interface.*" $n /etc/init.d/mini_snmpd start
|
|
done
|
|
}
|
|
[ -n "$reload_interfaces" ] && {
|
|
for n in $reload_interfaces; do
|
|
procd_add_reload_interface_trigger $n
|
|
done
|
|
}
|
|
procd_close_trigger
|
|
procd_add_validation validate_mini_snmpd_section
|
|
}
|
|
|
|
|
|
start_instance() {
|
|
local cfg validation_failed validation_err disks_arg interfaces_arg
|
|
cfg="$1"
|
|
#uci_validate_section should unset undefined variables from other instances
|
|
#however defining uci variables as local will scope them to this instance
|
|
#"local variables are also visible to functions called by the parent function" so it's good practice
|
|
local enabled ipv6 debug auth community contact location listen_interface \
|
|
udp_port tcp_port vendor_oid mib_timeout
|
|
local disks="" interfaces=""
|
|
validate_mini_snmpd_section "$cfg" 2>/dev/null || validation_failed=1
|
|
[ "$enabled" == 1 ] || {
|
|
_log "instance:$cfg disabled not starting"
|
|
return 1
|
|
}
|
|
|
|
local listen_interface_json listen_interface_ip listen_interface_device listen_interface_up
|
|
[ -n "$listen_interface" ] && {
|
|
if [ "$ipv6" = 1 ]; then
|
|
network_get_ipaddrs6 listen_interface_ip "$listen_interface"
|
|
else
|
|
network_get_ipaddrs listen_interface_ip "$listen_interface"
|
|
fi
|
|
network_is_up "$listen_interface" && [ -n "$listen_interface_ip" ] || {
|
|
_log "$cfg:listen interface $listen_interface not up yet / not configured properly"
|
|
_log "$cfg:procd will try again when interface state changes"
|
|
return 1
|
|
}
|
|
network_get_physdev listen_interface_device "$listen_interface"
|
|
}
|
|
|
|
[ $validation_failed ] && {
|
|
_err "validation of $NAME configuration for $cfg instance failed, all tests should be within constraints"
|
|
_err "please edit the configuration values below using [l]uci "
|
|
validation_err=`/sbin/validate_data mini_snmpd mini_snmpd "$cfg" $mini_snmpd_validation 2>&1 | sed '/with\ false$/!d;s/validates\ as\ /needs\ to\ be\ /;s/with\ false//' `
|
|
_err "${validation_err}"
|
|
return 1
|
|
}
|
|
config_list_foreach "$cfg" 'disks' append_disk
|
|
config_list_foreach "$cfg" 'interfaces' append_interface
|
|
# test if variables are unset or zero length
|
|
[ -z "${disks_arg:+1}" -a -z "${interfaces_arg:+1}" ] && {
|
|
_err "$cfg: you haven't sucessfully configured any mountpoints or disks for this instance, not starting"
|
|
return 1
|
|
}
|
|
|
|
procd_open_instance
|
|
|
|
procd_set_param command "$PROG" -n
|
|
procd_set_param stdout "1"
|
|
procd_set_param stderr "1"
|
|
# don't the like default respawn values? you can override through uci.
|
|
# vars left as global so you only need to do it in the first mini_snmpd instance
|
|
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-10} ${respawn_retry:-1}
|
|
# this monitors ubus changes
|
|
[ -n "$listen_interface" ] && {
|
|
#procd_open_trigger
|
|
#procd_add_interface_trigger "interface.*" $listen_interface /etc/init.d/mini_snmpd reload
|
|
#procd_close_trigger
|
|
procd_add_reload_interface_trigger $listen_interface #or use shorthand of above
|
|
}
|
|
# this re-starts the daemon if a properly configured network interface is changed whilst it is already running
|
|
# igmpproxy has this as well as "procd_set_param netdev"
|
|
|
|
append_arg "-c" "$community"
|
|
append_arg "-L" "${location}"
|
|
append_arg "-C" "${contact}"
|
|
append_arg "-p" $udp_port
|
|
append_arg "-P" $tcp_port
|
|
append_arg "-V" "${vendor_oid}"
|
|
append_arg "-t" $mib_timeout
|
|
|
|
[ "$ipv6" = 1 ] && procd_append_param command "-6"
|
|
[ "$debug" = 1 ] && procd_append_param command "-v"
|
|
# uci_validate_section() aka /sbin/validate_data can only cast default values not defined in /etc/config/* to string
|
|
# e.g. ="1" however it sets bools defined in /etc/config/* to =1 / =0
|
|
[ "$auth" = 1 -o "$auth" = "1" ] && procd_append_param command "-a"
|
|
[ -n "$disks_arg" ] && procd_append_param command "-d" "$disks_arg"
|
|
[ -n "$interfaces_arg" ] && {
|
|
procd_append_param netdev ${interfaces_arg//,/ }
|
|
procd_append_param command "-i" "$interfaces_arg"
|
|
}
|
|
[ -n "$listen_interface_device" ] && {
|
|
procd_append_param command "-I" "$listen_interface_device"
|
|
# and this monitors the hardware device for changes outside of ubus - just a guess
|
|
procd_append_param netdev $listen_interface_device
|
|
}
|
|
procd_close_instance
|
|
}
|
|
|
|
start_service() {
|
|
. /lib/functions.sh
|
|
. /lib/functions/network.sh
|
|
|
|
config_load 'mini_snmpd'
|
|
config_foreach start_instance 'mini_snmpd'
|
|
}
|
|
|