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 "$( sh -c ' | |||||
jilles: Invoking a new shell isolates the called script from `service` a bit more, but is probably… | |||||
dteskeAuthorUnsubmitted 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… | |||||
jillesUnsubmitted 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… | |||||
dteskeAuthorUnsubmitted 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… | |||||
jillesUnsubmitted 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 1>&- 2>&- | |||||
jillesUnsubmitted 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… | |||||
dteskeAuthorUnsubmitted Done Inline ActionsFair enough; will do in next update dteske: Fair enough; will do in next update | |||||
trap '\''for var in $*; do | |||||
jillesUnsubmitted 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)… | |||||
dteskeAuthorUnsubmitted 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 | |||||
dteskeAuthorUnsubmitted 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"… | |||||
eval echo \"\$var=\\\"\$$var\\\"\" >&9 | |||||
jillesUnsubmitted 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… | |||||
dteskeAuthorUnsubmitted 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… | |||||
done'\'' EXIT | |||||
set_name() { name="$1"; } | |||||
eval "$( awk '\''{ | |||||
gsub(/load_rc_config /, "set_name ") | |||||
gsub(/run_rc_command /, ": ") | |||||
jillesUnsubmitted 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… | |||||
dteskeAuthorUnsubmitted 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… | |||||
}'\'' "$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.