How to Integrate Shell Scripts into the RC Framework
For an introduction a must read is https://docs.freebsd.org/en/articles/rc-scripting/.Motivation
The WLAN interface of my laptop can hang under some circumstances. One can easily launch a few commands to let it recover, but putting the commands in a script is by far more convenient.In order to run the WLAN supervisor script permanently the idea came up to integrate it into the rc framework.First Implementation
The very first implementation of /usr/local/etc/rc.d/wlan has been as below. . /etc/rc.subr
is sourced first. Then the script can make use of its functionality or overwrite functions.
Code:
#!/bin/sh
# PROVIDE: wlan
# REQUIRE: netif
. /etc/rc.subr
name=wlan
rcvar=wlan_enable
# Make sure the pidfile matches what's in the config file.
: ${wlan_enable="NO"}
: ${wlan_pidfile="/var/run/wlan.pid"}
pidfile=${wlan_pidfile}
command=/usr/sbin/daemon
procname=/usr/home/chris/scripts/${name}
command_args="-p ${pidfile} ${procname}"
load_rc_config $name
run_rc_command "$1"
. /etc/rc.subr
in the first line of the script. The code below has been integrated in the script above. logger(1) writes informative lines into /var/log/messages.
Code:
start_cmd="${name}_start"
stop_cmd="${name}_stop"
wlan_start()
{
/usr/bin/logger "WLAN wlan start with $1 PID ${pidfile}"
/usr/sbin/daemon -p ${pidfile} ${command}
}
wlan_stop()
{
/usr/bin/logger "WLAN wlan stop"test -r $pidfile && pid=$(cat ${pidfile}); kill $pid
}
wlan_enable
in /etc/rc.conf or by service wlan start
and service wlan stop
worked. But other commands as service wlan status
failed.There are many files in /usr/local/etc/rc.d/ which overwrite functions of /etc/rc.subr, may be to countermeasure issues in ancient versions of /etc/rc.subr. In contrast /usr/local/etc/rc.d//tinyproxy is almost as the first approach and works without overwriting functions of /etc/rc.subr. Therefore this should be possible somehow with the WLAN supervisor script.Digging into rc.subr
/etc/rc.subr is a huge script. In order to output debug information logger(1) is very useful. An example of its output to /var/log/messages is taken from the function check_pidfile
.
Code:
...
logger "CBR check_pidfile _pid $_pid"
logger "CBR check_pidfile end"
...
_find_processes()
.The pid is already read from /var/run/tinyproxy.pid and /var/run/wlan.pid.
The difference of tinyproxy occurs in the last section of
_find_processes
as below.
Code:
logger "PS $PS _fp_args $_fp_args _psargs $_psargs npid $_npid_jid JID $JID _jid $_jid jid $jid"
logger "$PS 2>/dev/null -o pid= -o jid= -o command= $_psargs"
_proccheck="\
$PS 2>/dev/null -o pid= -o jid= -o command= $_psargs"' |
while read _npid _jid '"$_fp_args"'; do
'"$_fp_match"'
logger "JID: $JID _jid: $_jid";
if [ "$JID" -eq "$_jid" ];
then echo -n "$_pref$_npid";
_pref=" ";
logger "IF Matches";
fi
;;
esac
done'
# debug "in _find_processes: proccheck is ($_proccheck)."
logger "CBR _find_process _procheck $_procheck"
eval $_proccheck
_pref=" "
_jid=0
jid=0
logger "CBR _find_process _procheck $_procheck"
logger "CBR _find_process rc_pid $rc_pid"
logger "CBR _find_process end"
}
_procheck
assembles a command which feeds ps(1) and run by eval $_procheck
. For success $procname must match with the name outputted by ps.The function _find_processes()
takes care if the pid matches. But additionally itchecks if the service is running. Otherwise a stale /var/run/wlan.pid could be there but the service might have crashed without notice. The example of the current situation as on my system running the ps(1) command in a shell is as below.
Code:
/usr/home/chris/> /bin/ps -ww 2>/dev/null -o pid= -o jid= -o command= -p 705
705 0 /bin/sh /usr/home/chris/scripts/wlan
/bin/sh
.Now /usr/local/etc/rc.d/wlan has been rewritten.
Code:
#!/bin/sh
# PROVIDE: wlan
# REQUIRE: netif
. /etc/rc.subr
name=wlan
rcvar=wlan_enable
load_rc_config $name
# Make sure the pidfile matches what's in the config file.
: ${wlan_enable="NO"}
: ${wlan_pidfile="/var/run/wlan.pid"}
pidfile=${wlan_pidfile}
command=/usr/sbin/daemon
procname=/bin/sh
procname_script=/usr/home/chris/scripts/${name}
command_args="-p ${pidfile} ${procname_script}"
run_rc_command "$1"
service wlan status
work as well. Others are not tested up to now. The similar approach should be possible for other interpreters. When everything work as expected the logger(1) commands can be removed.Note: rc.subr(8) mentions interpreter to add information about any involved interpreter. But I have not found any file in /etc/rc.d/ and /usr/local/rc.d/ which makes use of that parameter. This can be a topic for further investigations.
I hope that this how to do is useful.
Kind regards,
Christoph