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