How to control and setup service?

Devin Teske dteske at FreeBSD.org
Thu Aug 27 21:38:25 UTC 2015


My last e-mail needs some distilling perhaps …


> On Aug 27, 2015, at 2:21 PM, Devin Teske <dteske at FreeBSD.org> wrote:
> 
>> On Aug 27, 2015, at 8:00 AM, Allan Jude <allanjude at freebsd.org <mailto:allanjude at freebsd.org>> wrote:
>> 
>> sysrc automatically detects values from rc.conf.d directories, and can
>> edit them if you ask it to. It just defaults to writing your changes to
>> /etc/rc.conf
>> 
>> My puppet scripts use:
>> sysrc -f /etc/rc.conf.d/varnish varnishd_identity="PDX1-01"
>> 
>> 
> 
[snip]

> You can better simulate this effect without the `-s service_name’ enhancement,
> using the currently available `-f file’ argument (more pedantic than Allan Jude’s
> approach from above):
> 
> sysrc -f "/etc/rc.conf.d/$_name $( sysrc -n local_startup%/rc.d )/rc.conf.d/$_name" vimage_enable
> 
> Where $_name is the service_name.
> A `-s service_name’ argument to sysrc would make this easier.
> 

Don’t do that. That assumes that $local_startup is a single directory
when in reality, it should be treated as a white-space separated list
of one-or-more directories where startup scripts can live.

NB: It also ignores the fact that some rc.d scripts call load_rc_config()
more than once with different arguments. E.g, dhclient


> ASIDE: While I was diving into this, I discovered a code typo in /etc/rc.subr
> NB: The typo has gone unnoticed because it has no effect on outcome
> 
> === BEGIN DIFF ===
> --- rc.subr.orig	2015-08-27 12:56:24.445475772 -0700
> +++ rc.subr	2015-08-27 12:56:33.980474637 -0700
> @@ -1333,7 +1333,7 @@ load_rc_config()
>  		_rc_conf_loaded=true
>  	fi
>  
> -	for _d in /etc ${local_startup%*/rc.d}; do
> +	for _d in /etc ${local_startup%/rc.d}; do
>  		if [ -f ${_d}/rc.conf.d/"$_name" ]; then
>  			debug "Sourcing ${_d}/rc.conf.d/$_name"
>  			. ${_d}/rc.conf.d/"$_name”
> === END DIFF ===
> 

Oh, looks like someone already fixed that… (thx Jilles)
https://svnweb.freebsd.org/base?view=revision&revision=286163


> FURTHER ASIDE: Why is the code in /etc/rc.subr treating
> $local_startup as though it’s a single item? There are many
> locations in code that consider $local_startup to be a white-
> space separated list of directories where rc.d scripts can live?
> I think the above code should perhaps be changed to:
> 
> === BEGIN DIFF ===
> --- rc.subr.orig	2015-08-27 12:56:24.445475772 -0700
> +++ rc.subr	2015-08-27 13:53:30.976240109 -0700
> @@ -1333,7 +1333,8 @@ load_rc_config()
>  		_rc_conf_loaded=true
>  	fi
>  
> -	for _d in /etc ${local_startup%*/rc.d}; do
> +	for _d in /etc/rc.d $local_startup; do
> +		_d="${_d%/rc.d}"
>  		if [ -f ${_d}/rc.conf.d/"$_name" ]; then
>  			debug "Sourcing ${_d}/rc.conf.d/$_name"
>  			. ${_d}/rc.conf.d/"$_name"
> === END DIFF ===


Lol, jilles arrived at the same exact change (albeit superfluous use of curlies).
https://svnweb.freebsd.org/base?view=revision&revision=286163


> 
> 
>>> 
>>> As a user I would love to have a cool tool to control and configure
>>> services in way like OpenBSD's rcctl(8) does
>>> http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/rcctl.8?query=rcctl <http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/rcctl.8?query=rcctl>.
>>> # cooltool set mysql-server status on
>>> That's all. It would take care of those things like getting right
>>> $rcvar name, choose right rc config file to edit (remember, we have a
>>> lot of places to check, see above) and enable service, etc.
>>> This $cooltool can be an extended version of service(8) tool.
>>> More difficult example:
>>> # cooltool set flow_capture status on  flags "-e 2200 -n 23 -N 0 -V 5"
>>> port "8787" datadir "/storage/flows/all_routers"
>>> would enable flow_capture service and set other stuff using right $rcvar.
>>> # cooltool get flow_capture
>>> would print all of the configured stuff for $service
>>> # cooltool get flow_capture status
>>> would print only a status YES/NO (don't forget about exit code ;) )
>>> and etc.
>> 
>> sysrc can do that, it will search all of the directories and find the
>> final answer.
>> 
>> The only thing it doesn't do is the translation between the 'service
>> name' (mysql-server) and the 'rcvar prefix' (mysql_),
> 
> The rcvar “prefix" is not actually what you’re after.
> In load_rc_config() of /etc/rc.subr we can see that it uses $_name
> to append to /etc/rc.conf.d/ and /usr/local/etc/rc.conf.d/
> 
> What exactly is “$_name” you ask? Good question…
> 
> Almost 100% of the time, it’s whatever “name=“ in the rc.d script.
> This is not guaranteed to be ${0##*/} (the rc.d script name).
> However, this just happens to be because almost 100% of the
> time, rc.d scripts do the following:
> 
> 	load_rc_config "$name"
> 
> So for sysrc to know the proper name of the thing that /etc/rc.subr
> will source from rc.conf.d collection of directories, we need to know
> what name= in the rc.d script.
> 
> This seems to be the simplest approach:
> 
> === BEGIN SCRIPT: sconfigs.sh ===
> #!/bin/sh
> sname="$1" sconfigs=
> _name=$( service "$sname" rcvar 2> /dev/null |
> 	awk 'NR==1&&sub(/^# /,""){print;exit}?' 2> /dev/null )
> if [ "$_name" ]; then
> 	sconfigs="/etc/rc.conf.d/$_name"
> 	local_etc=$( sysrc -qn local_startup%/rc.d 2> /dev/null )
> 	[ "$local_etc" ] &&
> 		sconfigs="$sconfigs $local_etc/rc.d/rc.conf.d/$_name"
> 	sconfigs="${sconfigs# }"
> fi
> echo "sconfigs=[$sconfigs]"
> === END SCRIPT: sconfigs.sh ===
> 
> Examples:
> 
> $ ./sconfigs.sh
> sconfigs=[]
> $ ./sconfigs.sh zfs
> sconfigs=[/etc/rc.conf.d/zfs /usr/local/etc/rc.d/rc.conf.d/zfs]
> $ ./sconfigs.sh jail
> sconfigs=[/etc/rc.conf.d/jail /usr/local/etc/rc.d/rc.conf.d/jail]
> $ ./sconfigs.sh nosuchservice
> sconfigs=[]
> 
> So naturally, if the logic in sconfigs.sh yields a non-NULL
> value for $sconfigs, this would become the value of `-f files’
> via service name.
> 

The “simplest” approach has edge-cases described below and
should be avoided. The more holistic approach is below.


> 
>> which might be a
>> useful addition, but might make more sense in the service command,
>> because when I pass something to sysrc, I expect it to be interpreted
>> literally. I guess it could be a flag for sysrc to specify the service
>> instead of the rcvar.
>> 
>> sysrc -s mysql datadir=/var/db/mysql
> 
> Given above talking-points, that would cause:
> 
> a. sysrc makes sure that “mysql” is an actual service
> b. sysrc uses “service mysql rcvar" (piped into awk) to get `name=‘ value
> NB: This could potentially result in a disconnect IFF the rc.d script passes something other than “$name” to load_rc_config() (if we’re worried about this, I have another recipe — further below).
> c. Sets `-f files’ value to "/etc/rc.conf.d/mysql /usr/local/etc/rc.conf.d/mysql”
> 
> ASIDE: The only edge-case would be, for example...
> 
> === BEGIN FILE: /etc/rc.d/fooserv ===
> #!/bin/sh
> . /etc/rc.subr
> name=foooooooooooooOOOooo
> load_rc_config foooooooooo
> run_rc_command "$@“
> === END FILE: /etc/rc.d/fooserv ===
> 
> Wherein we get the following from “service … rcvar”:
> 
> $ service fooserv rcvar | awk NR==1
> # foooooooooooooOOOooo
> 
> The above command returns the “name=“ value but the script doesn’t
> actually pass $name to load_rc_config() and thus what /etc/rc.subr
> would check for in this case is /etc/rc.conf.d/foooooooooo and
> /usr/local/etc/rc.conf.d/foooooooooo (versus the camel-case $name).
> 
> This edge-case exists for a number of services in the base:
> 
> $ awk '$1 ~ /^name=/ { name = $0; fn = FILENAME } $1 == "load_rc_config" && $2 !~ /\$(name|{name})/{ print "--"; if (fn == FILENAME) print fn ":" name; print FILENAME ":" $0 }' /etc/rc.d/*
> --
> /etc/rc.d/dhclient:name="dhclient"
> /etc/rc.d/dhclient:load_rc_config network
> --
> /etc/rc.d/fooserv:name=foooooooooooooOOOooo
> /etc/rc.d/fooserv:load_rc_config foooooooooo
> --
> /etc/rc.d/initrandom:name="initrandom"
> /etc/rc.d/initrandom:load_rc_config random
> --
> /etc/rc.d/othermta:load_rc_config 'XXX'
> --
> /etc/rc.d/postrandom:name="postrandom"
> /etc/rc.d/postrandom:load_rc_config random
> --
> /etc/rc.d/swaplate:name="swaplate"
> /etc/rc.d/swaplate:load_rc_config swap
> 
> 
> NB: Ignore the /etc/rc.d/fooserv script which I created specifically to illustrate the disconnect.
> 
> For the above edge-case services in base, load_rc_config() of /etc/rc.subr will check the following:
> 
> dhclient:
> 	/etc/rc.conf.d/network
> 	/usr/local/etc/rc.conf.d/network
> initrandom:
> 	/etc/rc.conf.d/random
> 	/usr/local/etc/rc.conf.d/random
> othermta:
> 	/etc/rc.conf.d/XXX
> 	/usr/local/etc/rc.conf.d/XXX
> postrandom:
> 	/etc/rc.conf.d/random
> 	/usr/local/etc/rc.conf.d/random
> swaplate:
> 	/etc/rc.conf.d/swap
> 	/usr/local/etc/rc.conf.d/swap
> 
> Let’s ignore for a second that it seems like an obvious error to be sourcing
> /usr/local/etc/rc.conf.d/ANYTHING for a base service in /etc/rc.d/
> NB: I can see someone utilizing that as a form of value-add anyways
> 
> What stands out at first glance is:
> 
> + If /etc/rc.conf.d/XXX or /usr/local/etc/rc.conf.d/XXX exists,
>    it will be sourced when working on the “othermta” service
> 
> NB: My testing indicates that othermta can obtain the desired results
> (of not sourcing any additional rc.conf.d files) by instead passing a
> single “.” to load_c_config() — which does not allow a NULL arg1.
> 
> + The “service <name> rcvar | awk …” approach to get “name=“
> value fails for dhclient, initrandom, postrandom, and swaplate.
> 
> Oh, but it gets worse when you look deeper at the above-mentioned scripts:
> 
> $ grep -c load_rc_config /etc/rc.d/* | grep -v ':[01]$' | sed -e 's/:[[:digit:]]\{1,\}$//' | xargs grep -n load_rc_config 
> /etc/rc.d/dhclient:54:load_rc_config $name
> /etc/rc.d/dhclient:55:load_rc_config network
> /etc/rc.d/local_unbound:32:load_rc_config $name
> /etc/rc.d/local_unbound:90:load_rc_config $name
> 
> It turns out that the dhclient script will actually source all of (if any exist; in order):
> 1. /etc/rc.conf.d/dhclient
> 2. /usr/local/etc/rc.conf.d/dhclient
> 3. /etc/rc.conf.d/network
> 4. /usr/local/etc/rc.conf.d/network
> 
> I have a recipe that overcomes this edge-case that I’d like to offer
> as a potential solution:
> 
> === BEGIN FILE: sconfigs2.sh ===
> #!/bin/sh
> sname="$1" sconfigs=
> local_startup=$( sysrc -qn local_startup )
> for dir in /etc/rc.d $local_startup; do
> 	spath="$dir/$sname"
> 	[ -f "$spath" -a -x "$spath" ] || spath= continue
> 	break
> done
> if [ ! "$spath" ]; then
> 	echo "$sname does not exist in /etc/rc.d or the local startup"
> 	echo "directories ($local_startup)"
> 	exit 1
> fi
> _names=
> case "$( file -b "$spath" 2> /dev/null )" in *"shell script"*)
> 	_names=$( exec 9<&1 1>&- 2>&-
> 		last_name=
> 		print_name() {
> 			local name="$1"
> 			[ "$name" = "$last_name" ] && return
> 			echo "$name" >&9
> 			last_name="$name"
> 		}
> 		eval "$( awk '{
> 			gsub(/load_rc_config /, "print_name ")
> 			gsub(/run_rc_command /, ": ")
> 			print
> 		}' "$spath" )"
> 	) ;;
> esac
> for _name in $_names; do
>         for dir in /etc/rc.d $local_startup; do
>                 sconfigs="$sconfigs ${dir%/rc.d}/rc.conf.d/$_name"
>         done
> done
> sconfigs="${sconfigs# }"
> echo "sconfigs=[$sconfigs]”
> === END FILE: sconfigs2.sh ===
> 
> Given the above code, we can handle the afore-mentioned edge-casen:
> 
> $ ./sconfigs2.sh dhclient
> sconfigs=[/etc/rc.conf.d/dhclient /usr/local/etc/rc.conf.d/dhclient /etc/rc.conf.d/network /usr/local/etc/rc.conf.d/network]
> $ ./sconfigs2.sh initrandom
> sconfigs=[/etc/rc.conf.d/random /usr/local/etc/rc.conf.d/random]
> $ ./sconfigs2.sh postrandom
> sconfigs=[/etc/rc.conf.d/random /usr/local/etc/rc.conf.d/random]
> $ ./sconfigs2.sh swaplate
> sconfigs=[/etc/rc.conf.d/swap /usr/local/etc/rc.conf.d/swap]
> $ ./sconfigs2.sh local_unbound
> sconfigs=[/etc/rc.conf.d/local_unbound /usr/local/etc/rc.conf.d/local_unbound]
> $ ./sconfigs2.sh 
>  does not exist in /etc/rc.d or the local startup
> directories (/usr/local/etc/rc.d)
> $ ./sconfigs2.sh nosuchservice
> nosuchservice does not exist in /etc/rc.d or the local startup
> directories (/usr/local/etc/rc.d)
> 
> 
> This is looking like a much better approach.
> 
[snip]

— 
Cheers,
Devin


More information about the freebsd-hackers mailing list