First Commit
[librecmc/package-feed.git] / net / mini_snmpd / files / mini_snmpd.init
1 #!/bin/sh /etc/rc.common
2 # Copyright (C) 2009-2016 OpenWrt.org
3 # Copyright (C) 2016 Luke McKee <hojuruku@gmail.com>
4 # Procd init script reference: http://wiki.prplfoundation.org/wiki/Procd_reference
5
6 START=98
7 USE_PROCD=1
8 PROG=/usr/bin/mini_snmpd
9 NAME=mini_snmpd
10
11 _log() {
12         logger -p daemon.info -t mini_snmpd "$@"
13 }
14
15 _err() {
16         logger -p daemon.err -t mini_snmpd "$@"
17 }
18
19
20 # mini_snmpd 1.3+ now starts later in the game. Expects filesystems monitored to be already mounted, or wont pass args to mini_snmpd 
21 # and at least configuration entry for network physical interface defined in /etc/config/network
22 # It handles network interfaces not yet present (e.g. ppp) but will statfs() the root/wrong filesystem if device not mounted
23 # Tip: complex scripts run faster without in openwrt if you stop busybox forking and searching for applets. Faster bootups
24 #       CONFIG_BUSYBOX_CONFIG_FEATURE_SH_NOFORK
25 #       CONFIG_BUSYBOX_CONFIG_FEATURE_PREFER_APPLETS
26 #       BUSYBOX_CONFIG_ASH_OPTIMIZE_FOR_SIZE [=n]
27 #       CONFIG_BUSYBOX_CONFIG_ASH_CMDCMD
28
29 mini_snmpd_validation="enabled:bool:0 \
30                 ipv6:bool:0 \
31                 debug:bool:0 \
32                 auth:bool:1 \
33                 community:rangelength(1,32):public \
34                 contact:maxlength(255) \
35                 location:maxlength(255) \
36                 listen_interface:uciname \
37                 udp_port:port \
38                 tcp_port:port \
39                 vendor_oid:string \
40                 mib_timeout:and(min(1),uinteger) \
41                 disks:list(directory) \
42                 interfaces:list(uciname) \
43                 respawn_threshold:uinteger respawn_timeout:uinteger respawn_retry:uinteger"
44 # busybox ash has no array variable support, when put validations in a string be careful to have no spaces in each validate constraint
45 # this makes it very difficult to use the 'or(uciname, "all")' test, so listen_interface '' or undefined now meands bind to "all".
46 # 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
47 # in syslog
48
49 append_disk() {
50         local disk="$1" disk_count
51         [ -z $disk_count ] && disk_count=0
52         if grep -qF "$disk" /proc/mounts ; then
53                 # check the fileystem is mountpoint, and directory search permissions available for statfs()
54                 # presence as a directory -d test done is already done by uci_validate_section()
55                 [ -x "$disk" ] || {
56                         _err "$cfg: mountpoint $disk for snmp monitoring EACCES error. Check permissions, ignoring"
57                         return 1
58                 }
59                 if [ $disk_count -lt 4 ] ;  then  
60                         append disks_arg "$disk" ','
61                         disk_count=$((disk_count++))
62                 else
63                         _err "$cfg: more than 4 mountpoints defined in uci. Disc $disk ignored."
64                 fi
65         else
66                 _err "$cfg: mountpoint $disk for snmp monitoring not mounted, ignoring."
67         fi
68 }
69
70 append_interface() {
71         local name="$1" netdev netdev_count
72         [ -z $netdev_count ] && netdev_count=0
73         # for the purposes of snmp monitoring it doesn't need to be up, it just needs to exist in /proc/net/dev
74         netdev=$(ubus -S call network.interface dump|jsonfilter -e "@.interface[@.interface=\"$name\"].l3_device")  
75         if [ -n "$netdev" ] && grep -qF "$netdev" /proc/net/dev ]; then 
76                 [ $netdev_count -ge 4 ] && {
77                         _err "$cfg: too many network interfaces configured, ignoring $name"
78                         return
79                 }
80                 netdev_count=$((netdev_count++))        
81                 if [ -n "$interfaces_arg" ]; then       
82                         append interfaces_arg "$netdev" ','
83                 else    
84                         append interfaces_arg "$netdev"
85                 fi
86         else
87                  _err "$cfg: physical interface for network $name not found in uci or kernel so not monitoring"
88         fi
89 }
90
91 append_arg() {
92         local var="$2"
93         local opt="$1"
94         [ -n "$var" ] && procd_append_param command $opt "$var"
95 }
96
97 watch_interfaces() {
98         local cfg="$1"
99         local enabled listen_interface # listen_interface_up
100         config_get_bool enabled "$cfg" "enabled" '1'
101         [ "$enabled" -gt 0 ] || return 0
102         config_get listen_interface "$cfg" listen_interface
103         # listen_interface_up=$(ubus -S call network.interface dump | jsonfilter -e "@.interface[@.interface=\"$listen_interface\"].up")
104         # 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
105         # Regardless of ubus knowing about an interface (in the case it's not yet configured)
106         [ -n "$listen_interface" ] && trigger_interfaces="${listen_interface} ${trigger_interfaces} "
107 }
108
109 validate_mini_snmpd_section() {
110         # validate a mini_snmpd instance in uci config file mini_snmpd
111         # http://luci.subsignal.org/trac/wiki/Documentation/Datatypes ubox/validate/validate.c
112         uci_validate_section mini_snmpd mini_snmpd "${1}" $mini_snmpd_validation
113 }
114
115
116 service_triggers() {
117         config_load 'mini_snmpd'
118         procd_open_trigger
119         procd_add_config_trigger "config.change" "mini_snmpd" /etc/init.d/mini_snmpd reload
120         config_foreach watch_interfaces 'mini_snmpd' 
121         # this only watches interfaces for which there is no running instance due to interface down / not in ubus
122         # hence start not reload, this trigger will not affect running instances as another start will not change their procd command arguments
123         # or stop the already running process
124         [ -n "$trigger_interfaces" ] & {
125                 for n in $trigger_interfaces ; do
126                         procd_add_interface_trigger "interface.*" $n /etc/init.d/mini_snmpd start
127                 done
128         }
129         procd_close_trigger
130         procd_add_validation validate_mini_snmpd_section
131 }
132
133
134 start_instance() {
135         local cfg validation_failed validation_err disks_arg interfaces_arg
136         cfg="$1"        
137         #uci_validate_section should unset undefined variables from other instances
138         #however defining uci variables as local will scope them to this instance
139         #"local variables are also visible to functions called by the parent function" so it's good practice
140         local enabled ipv6 debug auth community contact location listen_interface \
141                 udp_port tcp_port vendor_oid mib_timeout
142         local disks="" interfaces=""
143         validate_mini_snmpd_section "$cfg" 2>/dev/null || validation_failed=1
144         [ "$enabled" == 1 ] || { 
145                 _log "instance:$cfg disabled not starting" 
146                  return 1
147         }
148         
149         local listen_interface_json listen_interface_ip listen_interface_device listen_interface_up ubus_exit ubus_err
150         [ -n "$listen_interface" ] && {
151                 listen_interface_json=$(ubus -S call network.interface.$listen_interface status)
152                 ubus_exit=$?
153                 [ $ubus_exit = 4 ] && {
154                         _err "$cfg: listen_interface $listen_interface not properly configured in ubus network.interface.* not starting this instance "
155                         return 1
156                 }
157                 [ $ubus_exit = 255 -a -z "$listen_interface_json" ] && { 
158                         _log "$cfg: ubusd not yet up, will try to start mini_snmpd shorlty when procd detects $listen_interface comes up"
159                         return 1
160                 }
161                 [ -z "$listen_interface_json" ] && { 
162                         ubus_err=`ubus call network.interface.$listen_interface status 2>&1 >/dev/null`
163                         _err "$cfg: unknown ubus error. exit: $ubus_exit errormsg: $ubus_err "
164                         return 1
165                 }
166                 listen_interface_up=$(jsonfilter -s "$listen_interface_json" -e '@.up')
167                 if [ "$ipv6" = 1 ]; then
168                         listen_interface_ip=$(jsonfilter -s "$listen_interface_json" -e "@['ipv6-address'][0].address")
169                 else
170                         listen_interface_ip=$(jsonfilter -s "$listen_interface_json" -e "@['ipv4-address'][0].address")
171                 fi
172                 [ -n "$listen_interface_ip" -a "$listen_interface_up" = 'true' ] || {
173                         _log "$cfg:listen interface $listen_interface not up yet / not configured properly"
174                         _log "$cfg:procd will try again when interface state changes"
175                         return 1
176                 }
177                 listen_interface_device=$(jsonfilter -s "$listen_interface_json" -e '@.l3_device')
178         }
179
180         [ $validation_failed ] && {  
181                 _err "validation of $NAME configuration for $cfg instance failed, all tests should be within constraints"
182                 _err "please edit the configuration values below using [l]uci "
183                 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//' `
184                 _err "${validation_err}"
185                 return 1
186         }
187         config_list_foreach "$cfg" 'disks' append_disk
188         config_list_foreach "$cfg" 'interfaces' append_interface
189         # test if variables are unset or zero length
190         [ -z "${disks_arg:+1}" -a -z "${interfaces_arg:+1}" ] && {
191                 _err "$cfg: you haven't sucessfully configured any mountpoints or disks for this instance, not starting"
192                 return 1
193         }
194         
195         procd_open_instance
196
197         procd_set_param command "$PROG" -n
198         procd_set_param stdout "1"
199         procd_set_param stderr "1"
200         # don't the like default respawn values? you can override through uci.
201         # vars left as global so you only need to do it in the first mini_snmpd instance
202         procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-10} ${respawn_retry:-1}
203         # this monitors ubus changes
204         [ -n "$listen_interface" ] && {
205                  #procd_open_trigger
206                  #procd_add_interface_trigger "interface.*" $listen_interface /etc/init.d/mini_snmpd reload 
207                  #procd_close_trigger
208                  procd_add_reload_interface_trigger $listen_interface #or use shorthand of above
209         }
210         # this re-starts the daemon if a properly configured network interface is changed whilst it is already running
211         # igmpproxy has this as well as "procd_set_param netdev" 
212
213         append_arg "-c" "$community" 
214         append_arg "-L" "${location}"
215         append_arg "-C" "${contact}" 
216         append_arg "-p" $udp_port  
217         append_arg "-P" $tcp_port  
218         append_arg "-V" "${vendor_oid}"  
219         append_arg "-t" $mib_timeout 
220         
221         [ "$ipv6" = 1  ] && procd_append_param command "-6"
222         [ "$debug" = 1 ] && procd_append_param command "-v"
223         # uci_validate_section() aka /sbin/validate_data can only cast default values not defined in /etc/config/* to string 
224         # e.g. ="1" however it sets bools defined in /etc/config/* to =1 / =0
225         [ "$auth" = 1 -o "$auth" = "1" ] && procd_append_param command "-a"
226         [ -n "$disks_arg" ] && procd_append_param command "-d $disks_arg" 
227         [ -n "$interfaces_arg" ] && procd_append_param command "-i $interfaces_arg"
228         [ -n "$listen_interface_device" ] && {
229                  procd_append_param command "-I" "$listen_interface_device"
230                  # and this monitors the hardware device for changes outside of ubus - just a guess
231                  procd_set_param netdev $listen_interface_device
232         }
233         procd_close_instance
234 }
235
236 start_service() {
237         config_load 'mini_snmpd'
238         config_foreach start_instance 'mini_snmpd'
239 }
240