Changeset View
Standalone View
usr.sbin/service/service.sh
Show All 40 Lines | usage () { | ||||
echo '-e Show services that are enabled' | echo '-e Show services that are enabled' | ||||
echo "-R Stop and start enabled $local_startup services" | echo "-R Stop and start enabled $local_startup services" | ||||
echo "-l List all scripts in /etc/rc.d and $local_startup" | echo "-l List all scripts in /etc/rc.d and $local_startup" | ||||
echo '-r Show the results of boot time rcorder' | echo '-r Show the results of boot time rcorder' | ||||
echo '-v Verbose' | echo '-v Verbose' | ||||
echo '' | echo '' | ||||
} | } | ||||
load_script_vars() { | |||||
eval "$( SQ="'" sh -c ' | |||||
jilles: Invoking a new shell isolates the called script from `service` a bit more, but is probably… | |||||
Not Done Inline ActionsHas to be done; How else are you going to set $0 for the script? E.g., "pkg install -y openvpn; vi /usr/local/etc/rc.d/openvpn" (notice importance of $0). Speed is sacrificed for accuracy (unless you know of a more performant way to set $0 for the current script without invoking a new shell). dteske: Has to be done; How else are you going to set $0 for the script? E.g., "pkg install -y openvpn… | |||||
Not Done Inline ActionsOh right. The openvpn rc.d script has some hacks to find its name based on $0, $file and $_file. If it detects it is being run from service, it bases name on $file. This is ugly but a change like this will not break it. It is not possible to change $0 in an existing shell and there is also no way to get the pathname of a dot script (other than knowing the variable that contains it). jilles: Oh right. The openvpn rc.d script has some hacks to find its name based on `$0`, `$file` and… | |||||
Not Done Inline ActionsI'm looking at the following file/revision: $FreeBSD: head/security/openvpn/files/openvpn.in 340872 2014-01-24 00:14:07Z mat $This is from installing openvpn-2.3.10 via "pkg install -y openvpn" on FreeBSD 10.1-STABLE amd64. In this version of the file, I can say that it only detects if it is running from /etc/rc and not from service. I don't know if it has since-been changed, but this is the snippet that I am working against: case "$0" in
*) $0 ;; esac The principle task of this review is to address PR ports/208534 which is on 10.3-RELEASE, so maybe later versions of the openvpn rc.d script detect service as well as /etc/rc, but on the release where I was trying to remediate the issue, the case is that the openvpn script only detects being run under /etc/rc as its parent. Given the above, would you agree that it's probably going to produce the most predictable results and least surprise if we go with the $0 based method (spawning a new shell)? It would certainly help for future cases if other scripts are lackadaisical about detecting when running under /etc/rc and/or service. dteske: I'm looking at the following file/revision:
# $FreeBSD: head/security/openvpn/files/openvpn.in… | |||||
Not Done Inline ActionsI looked at the version in ports head/, $FreeBSD: head/security/openvpn/files/openvpn.in 412541 2016-04-05 02:17:40Z mandree $. This version has a special case for $0 matching */service. Perhaps such logic should be added to /etc/rc.subr, since I think rc.d scripts should not even check $0 for /etc/rc*. Apart from that, spawning a new shell will ease things for rc.d scripts a little at the cost of making service uglier and slower. jilles: I looked at the version in ports head/, `$FreeBSD: head/security/openvpn/files/openvpn.in… | |||||
exec 9<&1 > /dev/null 2>&1 | |||||
Done Inline ActionsClosing file descriptors 0, 1 or 2 may cause strange results because an unrelated file ends up on a "standard" fd number. The shell itself is unlikely to be affected because it moves internal file descriptors to number 10 or higher but programs called from the shell may be affected. Please open /dev/null instead to discard output. jilles: Closing file descriptors 0, 1 or 2 may cause strange results because an unrelated file ends up… | |||||
Done Inline ActionsFair enough; will do in next update dteske: Fair enough; will do in next update | |||||
. /etc/rc.subr | |||||
Done Inline ActionsIf word splitting and pathname generation are not needed (which seems to be the case here), for var do would both be shorter and avoid unnecessary operations. jilles: If word splitting and pathname generation are not needed (which seems to be the case here)… | |||||
Done Inline ActionsWow, didn't know about that feature. What's better? (a) for var do (b) for var; do dteske: Wow, didn't know about that feature. What's better? (a) for var do (b) for var; do | |||||
Done Inline ActionsActually, in my testing I've found that some rc.d scripts (e.g., /etc/rc.d/jail) call "shift" which alters the number of arguments before the EXIT trap fires. The next update will take this into consideration (continuing to use $* but doing-so in a fashion that causes the value to be expanded into the trap argument, meaning that the arguments to the function are codified before the rc.d script source has a chance to alter them. dteske: Actually, in my testing I've found that some rc.d scripts (e.g., /etc/rc.d/jail) call "shift"… | |||||
load_rc_config() { name="$1"; } | |||||
Done Inline ActionsThis will work for name and rcvar which contain few special characters but will not work in the general case where variable values may contain characters like ", $ and \. Fixing that leads to uglier code, so it may not be appropriate. jilles: This will work for `name` and `rcvar` which contain few special characters but will not work in… | |||||
Done Inline ActionsIt's trivial to fix and not that ugly in my honest opinion. I will fix it. That way people can copy/paste the function where need-be without running the risk of copying something that has such an edge-case. dteske: It's trivial to fix and not that ugly in my honest opinion. I will fix it. That way people can… | |||||
run_rc_command() { :; } | |||||
dumpvar() { | |||||
eval local left= right=\"\$$var\" | |||||
while case "$right" in *$SQ*) : ;; *) false; esac; do | |||||
left="$left${right%%$SQ*}$SQ\\$SQ$SQ" | |||||
Done Inline ActionsWhen running this in a subshell environment, we could rely on the fact that /etc/rc.subr has already been sourced by redefining the two functions and sourcing the script as is. This may be ugly though. jilles: When running this in a subshell environment, we could rely on the fact that `/etc/rc.subr` has… | |||||
Done Inline ActionsI might do just that so we can eliminate the use of awk. I note however, that the only reason this will even work is because is because /etc/rc.subr contains the following: if [ -z "${_rc_subr_loaded}" ]; then ...fi # [ -z "${_rc_subr_loaded}" ] If /etc/rc.subr didn't include that code, we wouldn't be able to redefine the functions, considering that 98.15% of system startup scripts (in /etc/rc.d) immediately source /etc/rc.subr at start (3 out of 162 do not: msgs, serial, and setup_mach). dteske: I might do just that so we can eliminate the use of awk. I note however, that the only reason… | |||||
right="${right#*$SQ}" | |||||
done | |||||
echo "$var=$SQ$left${right#*$SQ}$SQ" | |||||
} | |||||
# Trap expanded $* since script could alter it before EXIT | |||||
trap "for var in $*; do dumpvar >&9; done" EXIT | |||||
. "$0" | |||||
' -- "$@" )" | |||||
} | |||||
while getopts 'ehlrRv' COMMAND_LINE_ARGUMENT ; do | while getopts 'ehlrRv' COMMAND_LINE_ARGUMENT ; do | ||||
case "${COMMAND_LINE_ARGUMENT}" in | case "${COMMAND_LINE_ARGUMENT}" in | ||||
e) ENABLED=eopt ;; | e) ENABLED=eopt ;; | ||||
h) usage ; exit 0 ;; | h) usage ; exit 0 ;; | ||||
l) LIST=lopt ;; | l) LIST=lopt ;; | ||||
r) RCORDER=ropt ;; | r) RCORDER=ropt ;; | ||||
R) RESTART=Ropt ;; | R) RESTART=Ropt ;; | ||||
v) VERBOSE=vopt ;; | v) VERBOSE=vopt ;; | ||||
*) usage ; exit 1 ;; | *) usage ; exit 1 ;; | ||||
esac | esac | ||||
done | done | ||||
shift $(( $OPTIND - 1 )) | shift $(( $OPTIND - 1 )) | ||||
if [ -n "$RESTART" ]; then | if [ -n "$RESTART" ]; then | ||||
skip="-s nostart" | skip="-s nostart" | ||||
if [ `/sbin/sysctl -n security.jail.jailed` -eq 1 ]; then | if [ `/sbin/sysctl -n security.jail.jailed` -eq 1 ]; then | ||||
skip="$skip -s nojail" | skip="$skip -s nojail" | ||||
fi | fi | ||||
[ -n "$local_startup" ] && find_local_scripts_new | [ -n "$local_startup" ] && find_local_scripts_new | ||||
files=`rcorder ${skip} ${local_rc} 2>/dev/null` | files=`rcorder ${skip} ${local_rc} 2>/dev/null` | ||||
for file in `reverse_list ${files}`; do | for file in `reverse_list ${files}`; do | ||||
if grep -q ^rcvar $file; then | if grep -q ^rcvar $file; then | ||||
eval `grep ^name= $file` | load_script_vars $file name rcvar | ||||
eval `grep ^rcvar $file` | if [ -n "$name" -a -n "$rcvar" ]; then | ||||
if [ -n "$rcvar" ]; then | |||||
load_rc_config_var ${name} ${rcvar} | load_rc_config_var ${name} ${rcvar} | ||||
fi | fi | ||||
checkyesno $rcvar 2>/dev/null && run_rc_script ${file} stop | checkyesno $rcvar 2>/dev/null && run_rc_script ${file} stop | ||||
fi | fi | ||||
done | done | ||||
for file in $files; do | for file in $files; do | ||||
if grep -q ^rcvar $file; then | if grep -q ^rcvar $file; then | ||||
eval `grep ^name= $file` | load_script_vars $file name rcvar | ||||
eval `grep ^rcvar $file` | |||||
checkyesno $rcvar 2>/dev/null && run_rc_script ${file} start | checkyesno $rcvar 2>/dev/null && run_rc_script ${file} start | ||||
fi | fi | ||||
done | done | ||||
exit 0 | exit 0 | ||||
fi | fi | ||||
if [ -n "$ENABLED" -o -n "$RCORDER" ]; then | if [ -n "$ENABLED" -o -n "$RCORDER" ]; then | ||||
# Copied from /etc/rc | # Copied from /etc/rc | ||||
skip="-s nostart" | skip="-s nostart" | ||||
if [ `/sbin/sysctl -n security.jail.jailed` -eq 1 ]; then | if [ `/sbin/sysctl -n security.jail.jailed` -eq 1 ]; then | ||||
skip="$skip -s nojail" | skip="$skip -s nojail" | ||||
fi | fi | ||||
[ -n "$local_startup" ] && find_local_scripts_new | [ -n "$local_startup" ] && find_local_scripts_new | ||||
files=`rcorder ${skip} /etc/rc.d/* ${local_rc} 2>/dev/null` | files=`rcorder ${skip} /etc/rc.d/* ${local_rc} 2>/dev/null` | ||||
fi | fi | ||||
if [ -n "$ENABLED" ]; then | if [ -n "$ENABLED" ]; then | ||||
for file in $files; do | for file in $files; do | ||||
if grep -q ^rcvar $file; then | if grep -q ^rcvar $file; then | ||||
eval `grep ^name= $file` | load_script_vars $file name rcvar | ||||
eval `grep ^rcvar $file` | if [ -n "$name" -a -n "$rcvar" ]; then | ||||
if [ -n "$rcvar" ]; then | |||||
load_rc_config_var ${name} ${rcvar} | load_rc_config_var ${name} ${rcvar} | ||||
fi | fi | ||||
checkyesno $rcvar 2>/dev/null && echo $file | checkyesno $rcvar 2>/dev/null && echo $file | ||||
fi | fi | ||||
done | done | ||||
exit 0 | exit 0 | ||||
fi | fi | ||||
▲ Show 20 Lines • Show All 41 Lines • Show Last 20 Lines |
Invoking a new shell isolates the called script from service a bit more, but is probably slower than sourcing the script in a subshell environment like run_rc_script does. It also adds an additional level of quoting.