xref: /dflybsd-src/etc/rc.subr (revision e9bf6173f4cb5878cd3eb5663f2d823de2b07799)
1# $NetBSD: rc.subr,v 1.49 2002/05/21 12:31:01 lukem Exp $
2# $FreeBSD: src/etc/rc.subr,v 1.13 2003/06/09 17:31:06 mtm Exp $
3# $DragonFly: src/etc/rc.subr,v 1.9 2003/12/13 02:49:07 dillon Exp $
4#
5# Copyright (c) 1997-2002 The NetBSD Foundation, Inc.
6# All rights reserved.
7#
8# This code is derived from software contributed to The NetBSD Foundation
9# by Luke Mewburn.
10#
11# Redistribution and use in source and binary forms, with or without
12# modification, are permitted provided that the following conditions
13# are met:
14# 1. Redistributions of source code must retain the above copyright
15#    notice, this list of conditions and the following disclaimer.
16# 2. Redistributions in binary form must reproduce the above copyright
17#    notice, this list of conditions and the following disclaimer in the
18#    documentation and/or other materials provided with the distribution.
19# 3. All advertising materials mentioning features or use of this software
20#    must display the following acknowledgement:
21#        This product includes software developed by the NetBSD
22#        Foundation, Inc. and its contributors.
23# 4. Neither the name of The NetBSD Foundation nor the names of its
24#    contributors may be used to endorse or promote products derived
25#    from this software without specific prior written permission.
26#
27# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37# POSSIBILITY OF SUCH DAMAGE.
38#
39# rc.subr
40#	functions used by various rc scripts
41#
42
43#
44#	Operating System dependent/independent variables
45#
46
47if [ "X$_file" = "X" ]; then
48    _file=$0
49fi
50
51provide_list=`rcorder -p $_file`
52
53SYSCTL="/sbin/sysctl"
54SYSCTL_N="${SYSCTL} -n"
55CMD_OSTYPE="${SYSCTL_N} kern.ostype"
56OSTYPE=`${CMD_OSTYPE}`
57
58RC_RUNNING=0
59RC_FAILED=1
60RC_DISABLED=2
61RC_IRRELEVANT=3
62RC_CONFIGURED=4
63RC_STOPPED=5
64
65case ${OSTYPE} in
66DragonFly)
67	SYSCTL_W="${SYSCTL}"
68	;;
69FreeBSD)
70	SYSCTL_W="${SYSCTL}"
71	;;
72NetBSD)
73	SYSCTL_W="${SYSCTL} -w"
74	;;
75esac
76
77#
78#	functions
79#	---------
80
81#
82# set_rcvar base_var
83#	Set the variable name enabling a specific service.
84#	FreeBSD uses ${service}_enable, while NetBSD uses
85#	just the name of the service. For example:
86#	FreeBSD: sendmail_enable="YES"
87#	NetBSD : sendmail="YES"
88#	$1 - if $name is not the base to work of off, specify
89#	     a different one
90#
91set_rcvar()
92{
93	if [ -z "$1" ]; then
94		base_var=${name}
95	else
96		base_var="$1"
97	fi
98
99	case ${OSTYPE} in
100	DragonFly)
101		echo ${base_var}_enable
102		;;
103	FreeBSD)
104		echo ${base_var}_enable
105		;;
106	NetBSD)
107		echo ${base_var}
108		;;
109	*)
110		echo 'XXX'
111		;;
112	esac
113}
114
115# set_provide_list
116#
117#	$1	should be $rc_arg (start, stop, restart, reload, etc)
118#	$2	return value $RC_*
119#
120#	Set the rcng_* variables associated with elements in provide_list
121#	based on $1 and $2.
122#
123#	Returns non-zero when early termination should occur, in which
124#	case the caller should return with a value of $? - 1
125#
126set_provide_list()
127{
128    # Remember, plret is set to the early termination return code + 1,
129    # or 0 if we want to continue the operation.
130    #
131    for i in $provide_list; do
132	case $1$2 in
133	start$RC_RUNNING|restart$RC_RUNNING)
134	    varsym -s rcng_$i=running
135	    ;;
136	start$RC_FAILED|restart$RC_FAILED)
137	    varsym -s rcng_$i=failed
138	    ;;
139	start$RC_DISABLED|restart$RC_DISABLED|reload$RC_DISABLED)
140	    varsym -s rcng_$i=disabled
141	    ;;
142	start$RC_IRRELEVANT|restart$RC_IRRELEVANT|reload$RC_IRRELEVANT)
143	    varsym -s rcng_$i=irrelevant
144	    ;;
145	start$RC_CONFIGURED|restart$RC_CONFIGURED)
146	    varsym -s rcng_$i=configured
147	    ;;
148	stop$RC_DISABLED)
149	    varsym -s rcng_$i=disabled
150	    ;;
151	stop$RC_IRRELEVANT)
152	    varsym -s rcng_$i=irrelevant
153	    ;;
154	stop*)
155	    varsym -s rcng_$i=stopped
156	    ;;
157	*)
158	    ;;
159	esac
160    done
161}
162
163# check_early_term
164#	$1	should be $rc_arg (start, stop, restart, reload, etc)
165#	$2	return value $RC_*
166#	$3	$rc_force	"" not to force, "anything" to force.
167#
168# The return code is 0 if early termination is not to occur, non-zero if
169# it is to occur.  When early termination is to occur the caller should
170# return check_early_term()'s return code - 1.    That is, early termination
171# can occur with or without an error.
172#
173# The provide list will be adjusted when early termination occurs.
174#
175check_early_term()
176{
177    case $2 in
178    $RC_RUNNING)
179	return 0
180	;;
181    $RC_FAILED)
182	set_provide_list $1 $2
183	[ -z "$3" ] || return 0
184	return 2
185	;;
186    $RC_DISABLED)
187	set_provide_list $1 $2
188	[ -z "$3" ] || return 0
189	return 1
190	;;
191    $RC_IRRELEVANT)
192	set_provide_list $1 $2
193	[ -z "$3" ] || return 0
194	return 1
195	;;
196    $RC_CONFIGURED)
197	return 0
198	;;
199    $RC_STOPPED)
200	return 0
201	;;
202    esac
203    set_provide_list $1 $2
204    [ -z "$3" ] || return 0
205    return 2
206}
207
208# adjust_return_code $1
209#
210#	Convert the return code to an exit code of 0 (success) or 1 (failure)
211#
212adjust_return_code()
213{
214    if [ $1 = $RC_FAILED ]; then
215	return 1
216    fi
217    return 0
218}
219
220#
221# force_depend script
222#	Force a service to start. Intended for use by services
223#	to resolve dependency issues. It is assumed the caller
224#	has check to make sure this call is necessary
225#	$1 - filename of script, in /etc/rc.d, to run
226#
227force_depend()
228{
229	_depend="$1"
230
231	info "${name} depends on ${_depend}, which will be forced to start."
232	if ! /etc/rc.d/${_depend} forcestart ; then
233		warn "Unable to force ${_depend}. It may already be running."
234		return 1
235	fi
236	return 0
237}
238
239#
240# checkyesno var
241#	Test $1 variable, and warn if not set to YES or NO.
242#	Return 0 if it's "yes" (et al), nonzero otherwise.
243#
244checkyesno()
245{
246	eval _value=\$${1}
247	debug "checkyesno: $1 is set to $_value."
248	case $_value in
249
250		#	"yes", "true", "on", or "1"
251	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
252		return 0
253		;;
254
255		#	"no", "false", "off", or "0"
256	[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
257		return 1
258		;;
259	*)
260		warn "\$${1} is not set properly - see rc.conf(5)."
261		return 1
262		;;
263	esac
264}
265
266# reverse_list list
267#	print the list in reverse order
268#
269reverse_list()
270{
271	_revlist=
272	for _revfile in $*; do
273		_revlist="$_revfile $_revlist"
274	done
275	echo $_revlist
276}
277
278#
279# mount_critical_filesystems type
280#	Go through the list of critical filesystems as provided in
281#	the rc.conf(5) variable $critical_filesystems_${type}, checking
282#	each one to see if it is mounted, and if it is not, mounting it.
283#
284mount_critical_filesystems()
285{
286	eval _fslist=\$critical_filesystems_${1}
287	for _fs in $_fslist; do
288		mount | (
289			_ismounted=no
290			while read what _on on _type type; do
291				if [ $on = $_fs ]; then
292					_ismounted=yes
293				fi
294			done
295			if [ $_ismounted = no ]; then
296				mount $_fs >/dev/null 2>&1
297			fi
298		)
299	done
300}
301
302#
303# check_pidfile pidfile procname [interpreter]
304#	Parses the first line of pidfile for a PID, and ensures
305#	that the process is running and matches procname.
306#	Prints the matching PID upon success, nothing otherwise.
307#	interpreter is optional; see _find_processes() for details.
308#
309check_pidfile()
310{
311	_pidfile=$1
312	_procname=$2
313	_interpreter=$3
314	if [ -z "$_pidfile" -o -z "$_procname" ]; then
315		err 3 'USAGE: check_pidfile pidfile procname [interpreter]'
316	fi
317	if [ ! -f $_pidfile ]; then
318		debug "pid file {$_pidfile): not readable."
319		return
320	fi
321	read _pid _junk < $_pidfile
322	if [ -z "$_pid" ]; then
323		debug "pid file {$_pidfile): no pid in file."
324		return
325	fi
326	_find_processes $_procname ${_interpreter:-.} '-p '"$_pid"
327}
328
329#
330# check_process procname [interpreter]
331#	Ensures that a process (or processes) named procname is running.
332#	Prints a list of matching PIDs.
333#	interpreter is optional; see _find_processes() for details.
334#
335check_process()
336{
337	_procname=$1
338	_interpreter=$2
339	if [ -z "$_procname" ]; then
340		err 3 'USAGE: check_process procname [interpreter]'
341	fi
342	_find_processes $_procname ${_interpreter:-.} '-ax'
343}
344
345#
346# _find_processes procname interpreter psargs
347#	Search for procname in the output of ps generated by psargs.
348#	Prints the PIDs of any matching processes, space separated.
349#
350#	If interpreter == ".", check the following variations of procname
351#	against the first word of each command:
352#		procname
353#		`basename procname`
354#		`basename procname` + ":"
355#		"(" + `basename procname` + ")"
356#
357#	If interpreter != ".", read the first line of procname, remove the
358#	leading #!, normalise whitespace, append procname, and attempt to
359#	match that against each command, either as is, or with extra words
360#	at the end.
361#
362_find_processes()
363{
364	if [ $# -ne 3 ]; then
365		err 3 'USAGE: _find_processes procname interpreter psargs'
366	fi
367	_procname=$1
368	_interpreter=$2
369	_psargs=$3
370
371	_pref=
372	if [ $_interpreter != "." ]; then	# an interpreted script
373		read _interp < $_procname	# read interpreter name
374		_interp=${_interp#\#!}		# strip #!
375		set -- $_interp
376		if [ $_interpreter != $1 ]; then
377			warn "\$command_interpreter $_interpreter != $1"
378		fi
379		_interp="$* $_procname"		# cleanup spaces, add _procname
380		_fp_args='_argv'
381		_fp_match='case "$_argv" in
382		    ${_interp}|"${_interp} "*)'
383	else					# a normal daemon
384		_procnamebn=${_procname##*/}
385		_fp_args='_arg0 _argv'
386		_fp_match='case "$_arg0" in
387		    $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})")'
388	fi
389
390	_proccheck='
391		ps -o "pid,command" '"$_psargs"' |
392		while read _npid '"$_fp_args"'; do
393			case "$_npid" in
394			    PID)
395				continue ;;
396			esac ; '"$_fp_match"'
397				echo -n "$_pref$_npid" ;
398				_pref=" "
399				;;
400			esac
401		done'
402
403#	debug "in _find_processes: proccheck is ($_proccheck)."
404	eval $_proccheck
405}
406
407#
408# wait_for_pids pid [pid ...]
409#	spins until none of the pids exist
410#
411wait_for_pids()
412{
413	_list=$*
414	if [ -z "$_list" ]; then
415		return
416	fi
417	_prefix=
418	while true; do
419		_nlist="";
420		for _j in $_list; do
421			if kill -0 $_j 2>/dev/null; then
422				_nlist="${_nlist}${_nlist:+ }$_j"
423			fi
424		done
425		if [ -z "$_nlist" ]; then
426			break
427		fi
428		_list=$_nlist
429		echo -n ${_prefix:-"Waiting for PIDS: "}$_list
430		_prefix=", "
431		sleep 2
432	done
433	if [ -n "$_prefix" ]; then
434		echo "."
435	fi
436}
437
438#
439# run_rc_command argument
440#	Search for argument in the list of supported commands, which is:
441#		"start stop restart rcvar status poll ${extra_commands}"
442#	If there's a match, run ${argument}_cmd or the default method
443#	(see below).
444#
445#	If argument has a given prefix, then change the operation as follows:
446#		Prefix	Operation
447#		------	---------
448#		fast	Skip the pid check, and set rc_fast=yes
449#		force	Set ${rcvar} to YES, and set rc_force=yes
450#
451#	The following globals are used:
452#
453#	Name		Needed	Purpose
454#	----		------	-------
455#	provide_list	(gen)	list of keywords provided by current rcng file
456#
457#	name		y	Name of script.
458#
459#	command		n	Full path to command.
460#				Not needed if ${rc_arg}_cmd is set for
461#				each keyword.
462#
463#	command_args	n	Optional args/shell directives for command.
464#
465#	command_interpreter n	If not empty, command is interpreted, so
466#				call check_{pidfile,process}() appropriately.
467#
468#	extra_commands	n	List of extra commands supported.
469#
470#	pidfile		n	If set, use check_pidfile $pidfile $command,
471#				otherwise use check_process $command.
472#				In either case, only check if $command is set.
473#
474#	procname	n	Process name to check for instead of $command.
475#
476#	rcvar		n	This is checked with checkyesno to determine
477#				if the action should be run.
478#
479#	${name}_chroot	n	Directory to chroot to before running ${command}
480#				Requires /usr to be mounted.
481#
482#	${name}_chdir	n	Directory to cd to before running ${command}
483#				(if not using ${name}_chroot).
484#
485#	${name}_flags	n	Arguments to call ${command} with.
486#				NOTE:	$flags from the parent environment
487#					can be used to override this.
488#
489#	${name}_nice	n	Nice level to run ${command} at.
490#
491#	${name}_user	n	User to run ${command} as, using su(1) if not
492#				using ${name}_chroot.
493#				Requires /usr to be mounted.
494#
495#	${name}_group	n	Group to run chrooted ${command} as.
496#				Requires /usr to be mounted.
497#
498#	${name}_groups	n	Comma separated list of supplementary groups
499#				to run the chrooted ${command} with.
500#				Requires /usr to be mounted.
501#
502#	${rc_arg}_cmd	n	If set, use this as the method when invoked;
503#				Otherwise, use default command (see below)
504#
505#	${rc_arg}_precmd n	If set, run just before performing the
506#				${rc_arg}_cmd method in the default
507#				operation (i.e, after checking for required
508#				bits and process (non)existence).
509#				If this completes with a non-zero exit code,
510#				don't run ${rc_arg}_cmd.
511#
512#	${rc_arg}_postcmd n	If set, run just after performing the
513#				${rc_arg}_cmd method, if that method
514#				returned a zero exit code.
515#
516#	required_dirs	n	If set, check for the existence of the given
517#				directories before running the default
518#				(re)start command.
519#
520#	required_files	n	If set, check for the readability of the given
521#				files before running the default (re)start
522#				command.
523#
524#	required_vars	n	If set, perform checkyesno on each of the
525#				listed variables before running the default
526#				(re)start command.
527#
528#	Default behaviour for a given argument, if no override method is
529#	provided:
530#
531#	Argument	Default behaviour
532#	--------	-----------------
533#	start		if !running && checkyesno ${rcvar}
534#				${command}
535#
536#	stop		if ${pidfile}
537#				rc_pid=$(check_pidfile $pidfile $command)
538#			else
539#				rc_pid=$(check_process $command)
540#			kill $sig_stop $rc_pid
541#			wait_for_pids $rc_pid
542#			($sig_stop defaults to TERM.)
543#
544#	reload		Similar to stop, except use $sig_reload instead,
545#			and doesn't wait_for_pids.
546#			$sig_reload defaults to HUP.
547#
548#	restart		Run `stop' then `start'.
549#
550#	status		Show if ${command} is running, etc.
551#
552#	poll		Wait for ${command} to exit.
553#
554#	rcvar		Display what rc.conf variable is used (if any).
555#
556#	Variables available to methods, and after run_rc_command() has
557#	completed:
558#
559#	Variable	Purpose
560#	--------	-------
561#	rc_arg		Argument to command, after fast/force processing
562#			performed
563#
564#	rc_flags	Flags to start the default command with.
565#			Defaults to ${name}_flags, unless overridden
566#			by $flags from the environment.
567#			This variable may be changed by the precmd method.
568#
569#	rc_pid		PID of command (if appropriate)
570#
571#	rc_fast		Not empty if "fast" was provided (q.v.)
572#
573#	rc_force	Not empty if "force" was provided (q.v.)
574#
575#
576dummy_rc_command()
577{
578	rc_arg=$1
579
580	case "$rc_arg" in
581	fast*)				# "fast" prefix; don't check pid
582		rc_arg=${rc_arg#fast}
583		;;
584	force*)				# "force prefix; always start
585		rc_arg=${rc_arg#force}
586		;;
587	esac
588	set_provide_list $rc_arg $RC_CONFIGURED
589	return 0
590}
591
592run_rc_command()
593{
594	_return=0
595	rc_arg=$1
596	if [ -z "$name" ]; then
597		err 3 'run_rc_command: $name is not set.'
598	fi
599
600	case "$rc_arg" in
601	fast*)				# "fast" prefix; don't check pid
602		rc_arg=${rc_arg#fast}
603		rc_fast=yes
604		;;
605	force*)				# "force prefix; always start
606		rc_arg=${rc_arg#force}
607		rc_force=yes
608		if [ -n "${rcvar}" ]; then
609			eval ${rcvar}=YES
610		fi
611		;;
612	esac
613
614	eval _overide_command=\$${name}_program
615	if [ -n "$_overide_command" ]; then
616		command=$_overide_command
617	fi
618
619	_keywords="start stop restart rcvar $extra_commands"
620	rc_pid=
621	_pidcmd=
622	_procname=${procname:-${command}}
623
624					# setup pid check command if not fast
625	if [ -z "$rc_fast" -a -n "$_procname" ]; then
626		if [ -n "$pidfile" ]; then
627			_pidcmd='rc_pid=$(check_pidfile '"$pidfile $_procname $command_interpreter"')'
628		else
629			_pidcmd='rc_pid=$(check_process '"$_procname $command_interpreter"')'
630		fi
631		if [ -n "$_pidcmd" ]; then
632			_keywords="${_keywords} status poll"
633		fi
634	fi
635
636	if [ -z "$rc_arg" ]; then
637		rc_usage "$_keywords"
638	fi
639
640	if [ -n "$flags" ]; then	# allow override from environment
641		rc_flags=$flags
642	else
643		eval rc_flags=\$${name}_flags
644	fi
645	eval _chdir=\$${name}_chdir	_chroot=\$${name}_chroot \
646	    _nice=\$${name}_nice	_user=\$${name}_user \
647	    _group=\$${name}_group	_groups=\$${name}_groups
648
649	if [ -n "$_user" ]; then	# unset $_user if running as that user
650		if [ "$_user" = "$(id -un)" ]; then
651			unset _user
652		fi
653	fi
654
655					# if ${rcvar} is set, and $1 is not
656					# "rcvar", then run
657					#	checkyesno ${rcvar}
658					# and return if that failed
659					#
660	if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" ]; then
661		if ! checkyesno ${rcvar}; then
662			set_provide_list $rc_arg $RC_DISABLED
663			return 0
664		fi
665	fi
666
667	eval $_pidcmd			# determine the pid if necessary
668
669	for _elem in $_keywords; do
670		if [ "$_elem" != "$rc_arg" ]; then
671			continue
672		fi
673
674					# if there's a custom ${XXX_cmd},
675					# run that instead of the default
676					#
677		eval _cmd=\$${rc_arg}_cmd _precmd=\$${rc_arg}_precmd \
678		    _postcmd=\$${rc_arg}_postcmd
679		if [ -n "$_cmd" ]; then
680					# if the precmd failed and force
681					# isn't set, exit
682					#
683			if [ -n "$_precmd" ]; then
684				debug "run_rc_command: evaluating ${_precmd}()."
685				eval $_precmd
686
687				_return=$?
688				check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
689			fi
690
691			if [ -n "$_cmd" ]; then
692				debug "run_rc_command: evaluating ${_cmd}()."
693				eval $_cmd
694				_return=$?
695				check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
696			fi
697
698			if [ -n "$_postcmd" ]; then
699				debug "run_rc_command: evaluating ${_postcmd}()."
700				eval $_postcmd
701				_return=$?
702				check_early_term $rc_arg $_return "" || return $(($?-1))
703			fi
704			set_provide_list $rc_arg $_return
705			adjust_return_code $_return
706			return $?
707		fi
708
709		case "$rc_arg" in	# default operations...
710
711		status)
712			if [ -n "$rc_pid" ]; then
713				echo "${name} is running as pid $rc_pid."
714			else
715				echo "${name} is not running."
716				return 1
717			fi
718			;;
719
720		start)
721			if [ -n "$rc_pid" ]; then
722				echo "${name} already running? (pid=$rc_pid)."
723				exit 1
724			fi
725
726			if [ ! -x $command ]; then
727				info "run_rc_command: cannot run ($command)."
728				set_provide_list $rc_arg $RC_FAILED
729				adjust_return_code $RC_FAILED
730				return $?
731			fi
732
733					# check for required variables,
734					# directories, and files
735					#
736			for _f in $required_vars; do
737				if ! checkyesno $_f; then
738					warn "\$${_f} is not set."
739					if [ -z "$rc_force" ]; then
740						set_provide_list $rc_arg $RC_FAILED
741						adjust_return_code $RC_FAILED
742						return $?
743					fi
744				fi
745			done
746			for _f in $required_dirs; do
747				if [ ! -d "${_f}/." ]; then
748					warn "${_f} is not a directory."
749					if [ -z "$rc_force" ]; then
750						set_provide_list $rc_arg $RC_FAILED
751						adjust_return_code $RC_FAILED
752						return $?
753					fi
754				fi
755			done
756			for _f in $required_files; do
757				if [ ! -r "${_f}" ]; then
758					warn "${_f} is not readable."
759					if [ -z "$rc_force" ]; then
760						set_provide_list $rc_arg $RC_FAILED
761						adjust_return_code $RC_FAILED
762						return $?
763					fi
764				fi
765			done
766
767					# if the precmd failed and force
768					# isn't set, exit
769					#
770			if [ -n "${_precmd}" ]; then
771				debug "run_rc_command: evaluating ${_precmd}()."
772				eval $_precmd
773				_return=$?
774				check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
775			fi
776
777					# setup the command to run, and run it
778					#
779			echo "Starting ${name}."
780			if [ -n "$_chroot" ]; then
781				_doit="\
782${_nice:+nice -n $_nice }\
783chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\
784$_chroot $command $rc_flags $command_args"
785			else
786				_doit="\
787${_chdir:+cd $_chdir; }\
788${_nice:+nice -n $_nice }\
789$command $rc_flags $command_args"
790				if [ -n "$_user" ]; then
791				    _doit="su -m $_user -c 'sh -c \"$_doit\"'"
792				fi
793			fi
794
795					# if the cmd failed and force
796					# isn't set, exit
797					#
798			debug "run_rc_command: _doit: $_doit"
799			eval $_doit
800			_return=$?
801			check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
802					# finally, run postcmd
803					#
804			if [ -n "${_postcmd}" ]; then
805				debug "run_rc_command: evaluating ${_postcmd}()."
806				eval $_postcmd
807			fi
808			;;
809
810		stop)
811			if [ -z "$rc_pid" ]; then
812				if [ -n "$pidfile" ]; then
813					echo \
814				    "${name} not running? (check $pidfile)."
815				else
816					echo "${name} not running?"
817				fi
818				set_provide_list $rc_arg $RC_STOPPED
819				exit 1
820			fi
821
822					# if the precmd failed and force
823					# isn't set, exit
824					#
825			if [ -n $_precmd ]; then
826				eval $_precmd
827				_return=$?
828				check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
829			fi
830
831					# send the signal to stop
832					#
833			echo "Stopping ${name}."
834			_doit="kill -${sig_stop:-TERM} $rc_pid"
835			if [ -n "$_user" ]; then
836				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
837			fi
838
839					# if the stop cmd failed and force
840					# isn't set, exit
841					#
842			eval $_doit
843			_return=$?
844			check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
845					# wait for the command to exit,
846					# and run postcmd.
847			wait_for_pids $rc_pid
848			if [ -n "$_postcmd" ]; then
849				eval $_postcmd
850				_return=$?
851			fi
852			;;
853
854		reload)
855			if [ -z "$rc_pid" ]; then
856				if [ -n "$pidfile" ]; then
857					echo \
858				    "${name} not running? (check $pidfile)."
859				else
860					echo "${name} not running?"
861				fi
862				set_provide_list $rc_arg $RC_FAILED
863				exit 1
864			fi
865			echo "Reloading ${name} config files."
866			if [ -n "$_precmd" ]; then
867				eval $_precmd
868				_return=$?
869				check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
870			fi
871			_doit="kill -${sig_reload:-HUP} $rc_pid"
872			if [ -n "$_user" ]; then
873				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
874			fi
875			eval $_doit
876			_return=$?
877			check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
878			if [ -n "$_postcmd" ]; then
879				eval $_postcmd
880				_return=$?
881			fi
882			;;
883
884		restart)
885			if [ -n "$_precmd" ]; then
886				eval $_precmd
887				_return=$?
888				check_early_term $rc_arg $_return "$rc_force" || return $(($?-1))
889			fi
890					# prevent restart being called more
891					# than once by any given script
892					#
893			if [ -n "$_rc_restart_done" ]; then
894				return 0
895			fi
896			_rc_restart_done=YES
897
898			( $0 ${rc_force:+force}stop )
899			$0 ${rc_force:+force}start
900			_return=$?
901
902			if [ -n "$_postcmd" ]; then
903				eval $_postcmd
904				adjust_return_code $?
905				_return=$?
906			fi
907			# Do not set_provide_list(), the start command above
908			# will have done it for us and we do not know the
909			# actual RC code to base a setting on here.
910			#
911			return $_return
912			;;
913
914		poll)
915			if [ -n "$rc_pid" ]; then
916				wait_for_pids $rc_pid
917			fi
918			;;
919
920		rcvar)
921			echo "# $name"
922			if [ -n "$rcvar" ]; then
923				if checkyesno ${rcvar}; then
924					echo "\$${rcvar}=YES"
925				else
926					echo "\$${rcvar}=NO"
927				fi
928			fi
929			;;
930
931		*)
932			rc_usage "$_keywords"
933			;;
934
935		esac
936		set_provide_list $rc_arg $_return
937		adjust_return_code $_return
938		return $?
939	done
940
941	echo 1>&2 "$0: unknown directive '$rc_arg'."
942	rc_usage "$_keywords"
943	exit 1
944}
945
946#
947# run_rc_script file arg
948#	Start the script `file' with `arg', and correctly handle the
949#	return value from the script.  If `file' ends with `.sh', it's
950#	sourced into the current environment.  If `file' appears to be
951#	a backup or scratch file, ignore it.  Otherwise if it's
952#	executable run as a child process.
953#
954run_rc_script()
955{
956	_file=$1
957	_arg=$2
958	if [ -z "$_file" -o -z "$_arg" ]; then
959		err 3 'USAGE: run_rc_script file arg'
960	fi
961
962	trap "echo 'Reboot interrupted'; exit 1" 3
963
964	unset	name command command_args command_interpreter \
965		extra_commands pidfile procname \
966		rcvar required_dirs required_files required_vars
967	eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd
968
969	case "$_file" in
970	*.sh)				# run in current shell
971		set $_arg ; . $_file
972		;;
973	*[~#]|*.OLD|*.orig)		# scratch file; skip
974		warn "Ignoring scratch file $_file"
975		;;
976	*)				# run in subshell
977		if [ -x $_file ]; then
978			if [ -n "$rc_fast_and_loose" ]; then
979				set $_arg ; . $_file
980			else
981				( trap "echo 'Reboot interrupted'; exit 1" 3
982				  set $_arg ; . $_file )
983			fi
984		fi
985		;;
986	esac
987}
988
989#
990# load_rc_config
991#	Source in the configuration file for a given command.
992#
993load_rc_config()
994{
995	_command=$1
996	if [ -z "$_command" ]; then
997		err 3 'USAGE: load_rc_config command'
998	fi
999
1000	if [ -z "$_rc_conf_loaded" ]; then
1001		if [ -r /etc/defaults/rc.conf ]; then
1002			debug "Sourcing /etc/defaults/rc.conf"
1003			. /etc/defaults/rc.conf
1004			source_rc_confs
1005		elif [ -r /etc/rc.conf ]; then
1006			debug "Sourcing /etc/rc.conf (/etc/defaults/rc.conf doesn't exist)."
1007			. /etc/rc.conf
1008		fi
1009		_rc_conf_loaded=YES
1010	fi
1011	if [ -f /etc/rc.conf.d/"$_command" ]; then
1012		debug "Sourcing /etc/rc.conf.d/${_command}"
1013		. /etc/rc.conf.d/"$_command"
1014	fi
1015
1016	# XXX - Deprecated variable name support
1017	#
1018	case ${OSTYPE} in
1019	FreeBSD)
1020        	[ -n "$portmap_enable" ] && rpcbind_enable="$portmap_enable"
1021        	[ -n "$portmap_program" ] && rpcbind_program="$portmap_program"
1022        	[ -n "$portmap_flags" ] && rpcbind_flags="$portmap_flags"
1023        	[ -n "$single_mountd_enable" ] && mountd_enable="$single_mountd_enable"
1024        	[ -n "$xntpd_enable" ] && ntpd_enable="$xntpd_enable"
1025        	[ -n "$xntpd_program" ] && ntpd_program="$xntpd_program"
1026        	[ -n "$xntpd_flags" ] && ntpd_flags="$xntpd_flags"
1027		[ -n "$dhcp_program" ] && dhclient_program="$dhcp_program"
1028		[ -n "$dhcp_flags" ] && dhclient_flags="$dhcp_flags"
1029        	;;
1030	DragonFly)
1031        	[ -n "$portmap_enable" ] && rpcbind_enable="$portmap_enable"
1032        	[ -n "$portmap_program" ] && rpcbind_program="$portmap_program"
1033        	[ -n "$portmap_flags" ] && rpcbind_flags="$portmap_flags"
1034        	[ -n "$single_mountd_enable" ] && mountd_enable="$single_mountd_enable"
1035        	[ -n "$xntpd_enable" ] && ntpd_enable="$xntpd_enable"
1036        	[ -n "$xntpd_program" ] && ntpd_program="$xntpd_program"
1037        	[ -n "$xntpd_flags" ] && ntpd_flags="$xntpd_flags"
1038		[ -n "$dhcp_program" ] && dhclient_program="$dhcp_program"
1039		[ -n "$dhcp_flags" ] && dhclient_flags="$dhcp_flags"
1040        	;;
1041	esac
1042
1043}
1044
1045#
1046# rc_usage commands
1047#	Print a usage string for $0, with `commands' being a list of
1048#	valid commands.
1049#
1050rc_usage()
1051{
1052	echo -n 1>&2 "Usage: $0 [fast|force]("
1053
1054	_sep=
1055	for _elem in $*; do
1056		echo -n 1>&2 "$_sep$_elem"
1057		_sep="|"
1058	done
1059	echo 1>&2 ")"
1060	exit 1
1061}
1062
1063#
1064# err exitval message
1065#	Display message to stderr and log to the syslog, and exit with exitval.
1066#
1067err()
1068{
1069	exitval=$1
1070	shift
1071
1072	if [ -x /usr/bin/logger ]; then
1073		logger "$0: ERROR: $*"
1074	fi
1075	echo 1>&2 "$0: ERROR: $*"
1076	exit $exitval
1077}
1078
1079#
1080# warn message
1081#	Display message to stderr and log to the syslog.
1082#
1083warn()
1084{
1085	if [ -x /usr/bin/logger ]; then
1086		logger "$0: WARNING: $*"
1087	fi
1088	echo 1>&2 "$0: WARNING: $*"
1089}
1090
1091#
1092# info message
1093#	Display informational message to stdout and log to syslog.
1094#
1095info()
1096{
1097	if [ -x /usr/bin/logger ]; then
1098		logger "$0: INFO: $*"
1099	fi
1100	echo "$0: INFO: $*"
1101}
1102
1103#
1104# debug message
1105#	If debugging is enabled in rc.conf output message to stderr.
1106#	BEWARE that you don't call any subroutine that itself calls this
1107#	function.
1108#
1109debug()
1110{
1111	case ${rc_debug} in
1112	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
1113		if [ -x /usr/bin/logger ]; then
1114			logger "$0: INFO: $*"
1115		fi
1116        	echo 1>&2 "$0: DEBUG: $*"
1117		;;
1118	esac
1119}
1120
1121#
1122# backup_file action file cur backup
1123#	Make a backup copy of `file' into `cur', and save the previous
1124#	version of `cur' as `backup' or use rcs for archiving.
1125#
1126#	This routine checks the value of the backup_uses_rcs variable,
1127#	which can be either YES or NO.
1128#
1129#	The `action' keyword can be one of the following:
1130#
1131#	add		`file' is now being backed up (and is possibly
1132#			being reentered into the backups system).  `cur'
1133#			is created and RCS files, if necessary, are
1134#			created as well.
1135#
1136#	update		`file' has changed and needs to be backed up.
1137#			If `cur' exists, it is copied to to `back' or
1138#			checked into RCS (if the repository file is old),
1139#			and then `file' is copied to `cur'.  Another RCS
1140#			check in done here if RCS is being used.
1141#
1142#	remove		`file' is no longer being tracked by the backups
1143#			system.  If RCS is not being used, `cur' is moved
1144#			to `back', otherwise an empty file is checked in,
1145#			and then `cur' is removed.
1146#
1147#
1148backup_file()
1149{
1150	_action=$1
1151	_cpfile=$2
1152	_cur=$3
1153	_back=$4
1154
1155	if checkyesno backup_uses_rcs; then
1156		_msg0="backup archive"
1157		_msg1="update"
1158
1159		# ensure that history file is not locked
1160		if [ -f $_cur,v ]; then
1161			rcs -q -u -U -M $_cur
1162		fi
1163
1164		# ensure after switching to rcs that the
1165		# current backup is not lost
1166		if [ -f $_cur ]; then
1167			# no archive, or current newer than archive
1168			if [ ! -f $_cur,v -o $_cur -nt $_cur,v ]; then
1169				ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1170				rcs -q -kb -U $_cur
1171				co -q -f -u $_cur
1172			fi
1173		fi
1174
1175		case $_action in
1176		add|update)
1177			cp -p $_cpfile $_cur
1178			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1179			rcs -q -kb -U $_cur
1180			co -q -f -u $_cur
1181			chown root:wheel $_cur $_cur,v
1182			;;
1183		remove)
1184			cp /dev/null $_cur
1185			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1186			rcs -q -kb -U $_cur
1187			chown root:wheel $_cur $_cur,v
1188			rm $_cur
1189			;;
1190		esac
1191	else
1192		case $_action in
1193		add|update)
1194			if [ -f $_cur ]; then
1195				cp -p $_cur $_back
1196			fi
1197			cp -p $_cpfile $_cur
1198			chown root:wheel $_cur
1199			;;
1200		remove)
1201			mv -f $_cur $_back
1202			;;
1203		esac
1204	fi
1205}
1206