rc.subr revision 157657
1# $NetBSD: rc.subr,v 1.65 2004/10/12 14:45:29 lukem Exp $
2# $FreeBSD: head/etc/rc.subr 157657 2006-04-11 09:20:47Z flz $
3#
4# Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
5# All rights reserved.
6#
7# This code is derived from software contributed to The NetBSD Foundation
8# by Luke Mewburn.
9#
10# Redistribution and use in source and binary forms, with or without
11# modification, are permitted provided that the following conditions
12# are met:
13# 1. Redistributions of source code must retain the above copyright
14#    notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16#    notice, this list of conditions and the following disclaimer in the
17#    documentation and/or other materials provided with the distribution.
18# 3. All advertising materials mentioning features or use of this software
19#    must display the following acknowledgement:
20#        This product includes software developed by the NetBSD
21#        Foundation, Inc. and its contributors.
22# 4. Neither the name of The NetBSD Foundation nor the names of its
23#    contributors may be used to endorse or promote products derived
24#    from this software without specific prior written permission.
25#
26# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36# POSSIBILITY OF SUCH DAMAGE.
37#
38# rc.subr
39#	functions used by various rc scripts
40#
41
42: ${rcvar_manpage:='rc.conf(5)'}
43
44#
45#	Operating System dependent/independent variables
46#
47
48if [ -z "${_rc_subr_loaded}" ]; then
49
50_rc_subr_loaded="YES"
51
52SYSCTL="/sbin/sysctl"
53SYSCTL_N="${SYSCTL} -n"
54CMD_OSTYPE="${SYSCTL_N} kern.ostype"
55OSTYPE=`${CMD_OSTYPE}`
56ID="/usr/bin/id"
57JID=`ps -p $$ -o jid | tail -1 | tr -d ' '`
58IDCMD="if [ -x $ID ]; then $ID -un; fi"
59
60case ${OSTYPE} in
61FreeBSD)
62	SYSCTL_W="${SYSCTL}"
63	;;
64NetBSD)
65	SYSCTL_W="${SYSCTL} -w"
66	;;
67esac
68
69#
70#	functions
71#	---------
72
73#
74# set_rcvar base_var
75#	Set the variable name enabling a specific service.
76#	FreeBSD uses ${service}_enable, while NetBSD uses
77#	just the name of the service. For example:
78#	FreeBSD: sendmail_enable="YES"
79#	NetBSD : sendmail="YES"
80#	$1 - if $name is not the base to work of off, specify
81#	     a different one
82#
83set_rcvar()
84{
85	if [ -z "$1" ]; then
86		base_var=${name}
87	else
88		base_var="$1"
89	fi
90
91	case ${OSTYPE} in
92	FreeBSD)
93		echo ${base_var}_enable
94		;;
95	NetBSD)
96		echo ${base_var}
97		;;
98	*)
99		echo 'XXX'
100		;;
101	esac
102}
103
104#
105# force_depend script
106#	Force a service to start. Intended for use by services
107#	to resolve dependency issues. It is assumed the caller
108#	has check to make sure this call is necessary
109#	$1 - filename of script, in /etc/rc.d, to run
110#
111force_depend()
112{
113	_depend="$1"
114
115	info "${name} depends on ${_depend}, which will be forced to start."
116	if ! /etc/rc.d/${_depend} forcestart; then
117		warn "Unable to force ${_depend}. It may already be running."
118		return 1
119	fi
120	return 0
121}
122
123#
124# checkyesno var
125#	Test $1 variable, and warn if not set to YES or NO.
126#	Return 0 if it's "yes" (et al), nonzero otherwise.
127#
128checkyesno()
129{
130	eval _value=\$${1}
131	debug "checkyesno: $1 is set to $_value."
132	case $_value in
133
134		#	"yes", "true", "on", or "1"
135	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
136		return 0
137		;;
138
139		#	"no", "false", "off", or "0"
140	[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
141		return 1
142		;;
143	*)
144		warn "\$${1} is not set properly - see ${rcvar_manpage}."
145		return 1
146		;;
147	esac
148}
149
150#
151# reverse_list list
152#	print the list in reverse order
153#
154reverse_list()
155{
156	_revlist=
157	for _revfile; do
158		_revlist="$_revfile $_revlist"
159	done
160	echo $_revlist
161}
162
163#
164# mount_critical_filesystems type
165#	Go through the list of critical filesystems as provided in
166#	the rc.conf(5) variable $critical_filesystems_${type}, checking
167#	each one to see if it is mounted, and if it is not, mounting it.
168#
169mount_critical_filesystems()
170{
171	eval _fslist=\$critical_filesystems_${1}
172	for _fs in $_fslist; do
173		mount | (
174			_ismounted=false
175			while read what _on on _type type; do
176				if [ $on = $_fs ]; then
177					_ismounted=true
178				fi
179			done
180			if $_ismounted; then
181				:
182			else
183				mount $_fs >/dev/null 2>&1
184			fi
185		)
186	done
187}
188
189#
190# check_pidfile pidfile procname [interpreter]
191#	Parses the first line of pidfile for a PID, and ensures
192#	that the process is running and matches procname.
193#	Prints the matching PID upon success, nothing otherwise.
194#	interpreter is optional; see _find_processes() for details.
195#
196check_pidfile()
197{
198	_pidfile=$1
199	_procname=$2
200	_interpreter=$3
201	if [ -z "$_pidfile" -o -z "$_procname" ]; then
202		err 3 'USAGE: check_pidfile pidfile procname [interpreter]'
203	fi
204	if [ ! -f $_pidfile ]; then
205		debug "pid file ($_pidfile): not readable."
206		return
207	fi
208	read _pid _junk < $_pidfile
209	if [ -z "$_pid" ]; then
210		debug "pid file ($_pidfile): no pid in file."
211		return
212	fi
213	_find_processes $_procname ${_interpreter:-.} '-p '"$_pid"
214}
215
216#
217# check_process procname [interpreter]
218#	Ensures that a process (or processes) named procname is running.
219#	Prints a list of matching PIDs.
220#	interpreter is optional; see _find_processes() for details.
221#
222check_process()
223{
224	_procname=$1
225	_interpreter=$2
226	if [ -z "$_procname" ]; then
227		err 3 'USAGE: check_process procname [interpreter]'
228	fi
229	_find_processes $_procname ${_interpreter:-.} '-ax'
230}
231
232#
233# _find_processes procname interpreter psargs
234#	Search for procname in the output of ps generated by psargs.
235#	Prints the PIDs of any matching processes, space separated.
236#
237#	If interpreter == ".", check the following variations of procname
238#	against the first word of each command:
239#		procname
240#		`basename procname`
241#		`basename procname` + ":"
242#		"(" + `basename procname` + ")"
243#		"[" + `basename procname` + "]"
244#
245#	If interpreter != ".", read the first line of procname, remove the
246#	leading #!, normalise whitespace, append procname, and attempt to
247#	match that against each command, either as is, or with extra words
248#	at the end.
249#
250_find_processes()
251{
252	if [ $# -ne 3 ]; then
253		err 3 'USAGE: _find_processes procname interpreter psargs'
254	fi
255	_procname=$1
256	_interpreter=$2
257	_psargs=$3
258
259	_pref=
260	if [ $_interpreter != "." ]; then	# an interpreted script
261		read _interp < $_procname	# read interpreter name
262		_interp=${_interp#\#!}		# strip #!
263		set -- $_interp
264		if [ $_interpreter != $1 ]; then
265			warn "\$command_interpreter $_interpreter != $1"
266		fi
267		_interp="$* $_procname"		# cleanup spaces, add _procname
268		_fp_args='_argv'
269		_fp_match='case "$_argv" in
270		    ${_interp}|"${_interp} "*)'
271	else					# a normal daemon
272		_procnamebn=${_procname##*/}
273		_fp_args='_arg0 _argv'
274		_fp_match='case "$_arg0" in
275		    $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})"|"[${_procnamebn}]")'
276	fi
277
278	_proccheck='
279		ps 2>/dev/null -o "pid,jid,command" '"$_psargs"' |
280		while read _npid _jid '"$_fp_args"'; do
281			case "$_npid" in
282			PID)
283				continue;;
284			esac; '"$_fp_match"'
285				if [ "$JID" -eq "$_jid" ];
286				then echo -n "$_pref$_npid";
287				_pref=" ";
288				fi
289				;;
290			esac
291		done'
292
293#	debug "in _find_processes: proccheck is ($_proccheck)."
294	eval $_proccheck
295}
296
297#
298# wait_for_pids pid [pid ...]
299#	spins until none of the pids exist
300#
301wait_for_pids()
302{
303	_list="$@"
304	if [ -z "$_list" ]; then
305		return
306	fi
307	_prefix=
308	while true; do
309		_nlist="";
310		for _j in $_list; do
311			if kill -0 $_j 2>/dev/null; then
312				_nlist="${_nlist}${_nlist:+ }$_j"
313			fi
314		done
315		if [ -z "$_nlist" ]; then
316			break
317		fi
318		_list=$_nlist
319		echo -n ${_prefix:-"Waiting for PIDS: "}$_list
320		_prefix=", "
321		sleep 2
322	done
323	if [ -n "$_prefix" ]; then
324		echo "."
325	fi
326}
327
328#
329# run_rc_command argument
330#	Search for argument in the list of supported commands, which is:
331#		"start stop restart rcvar status poll ${extra_commands}"
332#	If there's a match, run ${argument}_cmd or the default method
333#	(see below).
334#
335#	If argument has a given prefix, then change the operation as follows:
336#		Prefix	Operation
337#		------	---------
338#		fast	Skip the pid check, and set rc_fast=yes
339#		force	Set ${rcvar} to YES, and set rc_force=yes
340#		one	Set ${rcvar} to YES
341#
342#	The following globals are used:
343#
344#	Name		Needed	Purpose
345#	----		------	-------
346#	name		y	Name of script.
347#
348#	command		n	Full path to command.
349#				Not needed if ${rc_arg}_cmd is set for
350#				each keyword.
351#
352#	command_args	n	Optional args/shell directives for command.
353#
354#	command_interpreter n	If not empty, command is interpreted, so
355#				call check_{pidfile,process}() appropriately.
356#
357#	extra_commands	n	List of extra commands supported.
358#
359#	pidfile		n	If set, use check_pidfile $pidfile $command,
360#				otherwise use check_process $command.
361#				In either case, only check if $command is set.
362#
363#	procname	n	Process name to check for instead of $command.
364#
365#	rcvar		n	This is checked with checkyesno to determine
366#				if the action should be run.
367#
368#	${name}_program	n	Full path to command.
369#				Meant to be used in /etc/rc.conf to override
370#				${command}.
371#
372#	${name}_chroot	n	Directory to chroot to before running ${command}
373#				Requires /usr to be mounted.
374#
375#	${name}_chdir	n	Directory to cd to before running ${command}
376#				(if not using ${name}_chroot).
377#
378#	${name}_flags	n	Arguments to call ${command} with.
379#				NOTE:	$flags from the parent environment
380#					can be used to override this.
381#
382#	${name}_nice	n	Nice level to run ${command} at.
383#
384#	${name}_user	n	User to run ${command} as, using su(1) if not
385#				using ${name}_chroot.
386#				Requires /usr to be mounted.
387#
388#	${name}_group	n	Group to run chrooted ${command} as.
389#				Requires /usr to be mounted.
390#
391#	${name}_groups	n	Comma separated list of supplementary groups
392#				to run the chrooted ${command} with.
393#				Requires /usr to be mounted.
394#
395#	${rc_arg}_cmd	n	If set, use this as the method when invoked;
396#				Otherwise, use default command (see below)
397#
398#	${rc_arg}_precmd n	If set, run just before performing the
399#				${rc_arg}_cmd method in the default
400#				operation (i.e, after checking for required
401#				bits and process (non)existence).
402#				If this completes with a non-zero exit code,
403#				don't run ${rc_arg}_cmd.
404#
405#	${rc_arg}_postcmd n	If set, run just after performing the
406#				${rc_arg}_cmd method, if that method
407#				returned a zero exit code.
408#
409#	required_dirs	n	If set, check for the existence of the given
410#				directories before running the default
411#				(re)start command.
412#
413#	required_files	n	If set, check for the readability of the given
414#				files before running the default (re)start
415#				command.
416#
417#	required_vars	n	If set, perform checkyesno on each of the
418#				listed variables before running the default
419#				(re)start command.
420#
421#	Default behaviour for a given argument, if no override method is
422#	provided:
423#
424#	Argument	Default behaviour
425#	--------	-----------------
426#	start		if !running && checkyesno ${rcvar}
427#				${command}
428#
429#	stop		if ${pidfile}
430#				rc_pid=$(check_pidfile $pidfile $command)
431#			else
432#				rc_pid=$(check_process $command)
433#			kill $sig_stop $rc_pid
434#			wait_for_pids $rc_pid
435#			($sig_stop defaults to TERM.)
436#
437#	reload		Similar to stop, except use $sig_reload instead,
438#			and doesn't wait_for_pids.
439#			$sig_reload defaults to HUP.
440#			Note that `reload' isn't provided by default,
441#			it should be enabled via $extra_commands.
442#
443#	restart		Run `stop' then `start'.
444#
445#	status		Show if ${command} is running, etc.
446#
447#	poll		Wait for ${command} to exit.
448#
449#	rcvar		Display what rc.conf variable is used (if any).
450#
451#	Variables available to methods, and after run_rc_command() has
452#	completed:
453#
454#	Variable	Purpose
455#	--------	-------
456#	rc_arg		Argument to command, after fast/force/one processing
457#			performed
458#
459#	rc_flags	Flags to start the default command with.
460#			Defaults to ${name}_flags, unless overridden
461#			by $flags from the environment.
462#			This variable may be changed by the precmd method.
463#
464#	rc_pid		PID of command (if appropriate)
465#
466#	rc_fast		Not empty if "fast" was provided (q.v.)
467#
468#	rc_force	Not empty if "force" was provided (q.v.)
469#
470#
471run_rc_command()
472{
473	_return=0
474	rc_arg=$1
475	if [ -z "$name" ]; then
476		err 3 'run_rc_command: $name is not set.'
477	fi
478
479	# Don't repeat the first argument when passing additional command-
480	# line arguments to the command subroutines.
481	#
482	shift 1
483	rc_extra_args="$*"
484
485	_rc_prefix=
486	case "$rc_arg" in
487	fast*)				# "fast" prefix; don't check pid
488		rc_arg=${rc_arg#fast}
489		rc_fast=yes
490		;;
491	force*)				# "force prefix; always run
492		rc_force=yes
493		_rc_prefix=force
494		rc_arg=${rc_arg#${_rc_prefix}}
495		if [ -n "${rcvar}" ]; then
496			eval ${rcvar}=YES
497		fi
498		;;
499	one*)				# "one" prefix; set ${rcvar}=yes
500		_rc_prefix=one
501		rc_arg=${rc_arg#${_rc_prefix}}
502		if [ -n "${rcvar}" ]; then
503			eval ${rcvar}=YES
504		fi
505		;;
506	esac
507
508	_keywords="start stop restart rcvar $extra_commands"
509	rc_pid=
510	_pidcmd=
511	_procname=${procname:-${command}}
512
513					# setup pid check command
514	if [ -n "$_procname" ]; then
515		if [ -n "$pidfile" ]; then
516			_pidcmd='rc_pid=$(check_pidfile '"$pidfile $_procname $command_interpreter"')'
517		else
518			_pidcmd='rc_pid=$(check_process '"$_procname $command_interpreter"')'
519		fi
520		if [ -n "$_pidcmd" ]; then
521			_keywords="${_keywords} status poll"
522		fi
523	fi
524
525	if [ -z "$rc_arg" ]; then
526		rc_usage $_keywords
527	fi
528
529	if [ -n "$flags" ]; then	# allow override from environment
530		rc_flags=$flags
531	else
532		eval rc_flags=\$${name}_flags
533	fi
534	eval _chdir=\$${name}_chdir	_chroot=\$${name}_chroot \
535	    _nice=\$${name}_nice	_user=\$${name}_user \
536	    _group=\$${name}_group	_groups=\$${name}_groups
537
538	if [ -n "$_user" ]; then	# unset $_user if running as that user
539		if [ "$_user" = "$(eval $IDCMD)" ]; then
540			unset _user
541		fi
542	fi
543
544					# if ${rcvar} is set, and $1 is not
545					# "rcvar", then run
546					#	checkyesno ${rcvar}
547					# and return if that failed
548					#
549	if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" ]; then
550		if ! checkyesno ${rcvar}; then
551			return 0
552		fi
553	fi
554
555	eval $_pidcmd			# determine the pid if necessary
556
557	for _elem in $_keywords; do
558		if [ "$_elem" != "$rc_arg" ]; then
559			continue
560		fi
561
562					# if there's a custom ${XXX_cmd},
563					# run that instead of the default
564					#
565		eval _cmd=\$${rc_arg}_cmd _precmd=\$${rc_arg}_precmd \
566		    _postcmd=\$${rc_arg}_postcmd
567		if [ -n "$_cmd" ]; then
568					# if the precmd failed and force
569					# isn't set, exit
570					#
571			if [ -n "$_precmd" ]; then
572				debug "run_rc_command: evaluating ${_precmd}()."
573				eval $_precmd $rc_extra_args
574				_return=$?
575				[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
576				    return 1
577			fi
578
579			if [ -n "$_cmd" ]; then
580				debug "run_rc_command: evaluating ${_cmd}()."
581				eval $_cmd $rc_extra_args
582				_return=$?
583				[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
584				    return 1
585			fi
586
587			if [ -n "$_postcmd" ]; then
588				debug "run_rc_command: evaluating ${_postcmd}()."
589				 eval $_postcmd $rc_extra_args
590				_return=$?
591			fi
592			return $_return
593		fi
594
595		case "$rc_arg" in	# default operations...
596
597		status)
598			if [ -n "$rc_pid" ]; then
599				echo "${name} is running as pid $rc_pid."
600			else
601				echo "${name} is not running."
602				return 1
603			fi
604			;;
605
606		start)
607			if [ -z "$rc_fast" -a -n "$rc_pid" ]; then
608				echo 1>&2 "${name} already running? (pid=$rc_pid)."
609				return 1
610			fi
611
612			if [ ! -x ${_chroot}${command} ]; then
613				info "run_rc_command: cannot run ($command)."
614				return 1
615			fi
616
617					# check for required variables,
618					# directories, and files
619					#
620			for _f in $required_vars; do
621				if ! checkyesno $_f; then
622					warn "\$${_f} is not enabled."
623					if [ -z "$rc_force" ]; then
624						return 1
625					fi
626				fi
627			done
628			for _f in $required_dirs; do
629				if [ ! -d "${_f}/." ]; then
630					warn "${_f} is not a directory."
631					if [ -z "$rc_force" ]; then
632						return 1
633					fi
634				fi
635			done
636			for _f in $required_files; do
637				if [ ! -r "${_f}" ]; then
638					warn "${_f} is not readable."
639					if [ -z "$rc_force" ]; then
640						return 1
641					fi
642				fi
643			done
644
645					# if the precmd failed and force
646					# isn't set, exit
647					#
648			if [ -n "${_precmd}" ]; then
649				debug "run_rc_command: evaluating ${_precmd}()."
650				eval $_precmd
651				_return=$?
652				[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
653				    return 1
654			fi
655
656					# setup the command to run, and run it
657					#
658			echo "Starting ${name}."
659			if [ -n "$_chroot" ]; then
660				_doit="\
661${_nice:+nice -n $_nice }\
662chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\
663$_chroot $command $rc_flags $command_args"
664			else
665				_doit="\
666${_chdir:+cd $_chdir; }\
667${_nice:+nice -n $_nice }\
668$command $rc_flags $command_args"
669				if [ -n "$_user" ]; then
670				    _doit="su -m $_user -c 'sh -c \"$_doit\"'"
671				fi
672			fi
673
674					# if the cmd failed and force
675					# isn't set, exit
676					#
677			debug "run_rc_command: _doit: $_doit"
678			eval $_doit
679			_return=$?
680			[ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
681
682					# finally, run postcmd
683					#
684			if [ -n "${_postcmd}" ]; then
685				debug "run_rc_command: evaluating ${_postcmd}()."
686				eval $_postcmd
687			fi
688			;;
689
690		stop)
691			if [ -z "$rc_pid" ]; then
692				[ -n "$rc_fast" ] && return 0
693				if [ -n "$pidfile" ]; then
694					echo 1>&2 \
695				    "${name} not running? (check $pidfile)."
696				else
697					echo 1>&2 "${name} not running?"
698				fi
699				return 1
700			fi
701
702					# if the precmd failed and force
703					# isn't set, exit
704					#
705			if [ -n "$_precmd" ]; then
706				eval $_precmd
707				_return=$?
708				[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
709				    return 1
710			fi
711
712					# send the signal to stop
713					#
714			echo "Stopping ${name}."
715			_doit="kill -${sig_stop:-TERM} $rc_pid"
716			if [ -n "$_user" ]; then
717				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
718			fi
719
720					# if the stop cmd failed and force
721					# isn't set, exit
722					#
723			eval $_doit
724			_return=$?
725			[ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
726
727					# wait for the command to exit,
728					# and run postcmd.
729			wait_for_pids $rc_pid
730			if [ -n "$_postcmd" ]; then
731				eval $_postcmd
732				_return=$?
733			fi
734			;;
735
736		reload)
737			if [ -z "$rc_pid" ]; then
738				if [ -n "$pidfile" ]; then
739					echo 1>&2 \
740				    "${name} not running? (check $pidfile)."
741				else
742					echo 1>&2 "${name} not running?"
743				fi
744				return 1
745			fi
746			echo "Reloading ${name} config files."
747			if [ -n "$_precmd" ]; then
748				eval $_precmd
749				_return=$?
750				[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
751				    return 1
752			fi
753			_doit="kill -${sig_reload:-HUP} $rc_pid"
754			if [ -n "$_user" ]; then
755				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
756			fi
757			eval $_doit
758			_return=$?
759			[ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
760			if [ -n "$_postcmd" ]; then
761				eval $_postcmd
762				_return=$?
763			fi
764			;;
765
766		restart)
767			if [ -n "$_precmd" ]; then
768				eval $_precmd $rc_extra_args
769				_return=$?
770				[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
771				    return 1
772			fi
773					# prevent restart being called more
774					# than once by any given script
775					#
776			if ${_rc_restart_done:-false}; then
777				return 0
778			fi
779			_rc_restart_done=true
780
781			# run stop in a subshell to keep variables for start
782			( run_rc_command ${_rc_prefix}stop $rc_extra_args )
783			run_rc_command ${_rc_prefix}start $rc_extra_args
784
785			if [ -n "$_postcmd" ]; then
786				eval $_postcmd $rc_extra_args
787				_return=$?
788			fi
789			;;
790
791		poll)
792			if [ -n "$rc_pid" ]; then
793				wait_for_pids $rc_pid
794			fi
795			;;
796
797		rcvar)
798			echo "# $name"
799			if [ -n "$rcvar" ]; then
800				if checkyesno ${rcvar}; then
801					echo "\$${rcvar}=YES"
802				else
803					echo "\$${rcvar}=NO"
804				fi
805			fi
806			;;
807
808		*)
809			rc_usage $_keywords
810			;;
811
812		esac
813		return $_return
814	done
815
816	echo 1>&2 "$0: unknown directive '$rc_arg'."
817	rc_usage $_keywords
818	# not reached
819}
820
821#
822# run_rc_script file arg
823#	Start the script `file' with `arg', and correctly handle the
824#	return value from the script.  If `file' ends with `.sh', it's
825#	sourced into the current environment.  If `file' appears to be
826#	a backup or scratch file, ignore it.  Otherwise if it's
827#	executable run as a child process.
828#
829run_rc_script()
830{
831	_file=$1
832	_arg=$2
833	if [ -z "$_file" -o -z "$_arg" ]; then
834		err 3 'USAGE: run_rc_script file arg'
835	fi
836
837	unset	name command command_args command_interpreter \
838		extra_commands pidfile procname \
839		rcvar required_dirs required_files required_vars
840	eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd
841
842	case "$_file" in
843	/etc/rc.d/*.sh)			# run in current shell
844		set $_arg; . $_file
845		;;
846	*[~#]|*.OLD|*.bak|*.orig|*,v)	# scratch file; skip
847		warn "Ignoring scratch file $_file"
848		;;
849	*)				# run in subshell
850		if [ -x $_file ]; then
851			if [ -n "$rc_fast_and_loose" ]; then
852				set $_arg; . $_file
853			else
854				( trap "echo Script $_file interrupted; kill -QUIT $$" 3
855				  trap "echo Script $_file interrupted; exit 1" 2
856				  set $_arg; . $_file )
857			fi
858		fi
859		;;
860	esac
861}
862
863#
864# load_rc_config name
865#	Source in the configuration file for a given name.
866#
867load_rc_config()
868{
869	local _tmp
870
871	_name=$1
872	if [ -z "$_name" ]; then
873		err 3 'USAGE: load_rc_config name'
874	fi
875
876	if ${_rc_conf_loaded:-false}; then
877		:
878	else
879		if [ -r /etc/defaults/rc.conf ]; then
880			debug "Sourcing /etc/defaults/rc.conf"
881			. /etc/defaults/rc.conf
882			source_rc_confs
883		elif [ -r /etc/rc.conf ]; then
884			debug "Sourcing /etc/rc.conf (/etc/defaults/rc.conf doesn't exist)."
885			. /etc/rc.conf
886		fi
887		_rc_conf_loaded=true
888	fi
889
890	eval _override_command=\$${name}_program
891	command=${command:+${_override_command:-$command}}
892	
893	if [ -z "${command}" ]; then
894		_tmp=`/bin/realpath $0`
895		prefix=${_tmp%/etc/rc.d/*}/
896	else
897		prefix=${command%/*bin/*}/
898	fi
899	if [ "${prefix}" = "/" -o "${prefix}" = "/usr/" ] ; then
900		etcdir="/etc"
901	else
902		etcdir="${prefix}etc"
903	fi
904
905	# XXX - Deprecated
906	if [ -f /etc/rc.conf.d/${_name} -a ${etcdir} != "/etc" ]; then
907		debug "Sourcing /etc/rc.conf.d/${_name}"
908		warn "Warning: /etc/rc.conf.d/${_name} is deprecated, please use ${etcdir}/rc.conf.d/${_name} instead."
909		if [ -f ${etcdir}/rc.conf.d/${_name} ]; then
910			warn "Warning: Both /etc/rc.conf.d/${_name} and ${etcdir}/rc.conf.d/${_name} exist."
911		fi
912		. /etc/rc.conf.d/${_name}
913	fi
914
915	if [ -f ${etcdir}/rc.conf.d/${_name} ]; then
916		debug "Sourcing ${etcdir}/rc.conf.d/${_name}"
917		. ${etcdir}/rc.conf.d/${_name}
918	fi
919
920	# XXX - Deprecated variable name support
921	#
922	case ${OSTYPE} in
923	FreeBSD)
924		[ -n "$portmap_enable" ] && rpcbind_enable="$portmap_enable"
925		[ -n "$portmap_program" ] && rpcbind_program="$portmap_program"
926		[ -n "$portmap_flags" ] && rpcbind_flags="$portmap_flags"
927		[ -n "$single_mountd_enable" ] && mountd_enable="$single_mountd_enable"
928		[ -n "$xntpd_enable" ] && ntpd_enable="$xntpd_enable"
929		[ -n "$xntpd_program" ] && ntpd_program="$xntpd_program"
930		[ -n "$xntpd_flags" ] && ntpd_flags="$xntpd_flags"
931		[ -n "$dhcp_program" ] && dhclient_program="$dhcp_program"
932		[ -n "$dhcp_flags" ] && dhclient_flags="$dhcp_flags"
933		;;
934	esac
935}
936  
937#
938# load_rc_config_var name var
939#	Read the rc.conf(5) var for name and set in the
940#	current shell, using load_rc_config in a subshell to prevent
941#	unwanted side effects from other variable assignments.
942#
943load_rc_config_var()
944{
945	if [ $# -ne 2 ]; then
946		err 3 'USAGE: load_rc_config_var name var'
947	fi
948	eval $(eval '(
949		load_rc_config '$1' >/dev/null;
950                if [ -n "${'$2'}" -o "${'$2'-UNSET}" != "UNSET" ]; then
951			echo '$2'=\'\''${'$2'}\'\'';
952		fi
953	)' )
954}
955
956#
957# rc_usage commands
958#	Print a usage string for $0, with `commands' being a list of
959#	valid commands.
960#
961rc_usage()
962{
963	echo -n 1>&2 "Usage: $0 [fast|force|one]("
964
965	_sep=
966	for _elem; do
967		echo -n 1>&2 "$_sep$_elem"
968		_sep="|"
969	done
970	echo 1>&2 ")"
971	exit 1
972}
973
974#
975# err exitval message
976#	Display message to stderr and log to the syslog, and exit with exitval.
977#
978err()
979{
980	exitval=$1
981	shift
982
983	if [ -x /usr/bin/logger ]; then
984		logger "$0: ERROR: $*"
985	fi
986	echo 1>&2 "$0: ERROR: $*"
987	exit $exitval
988}
989
990#
991# warn message
992#	Display message to stderr and log to the syslog.
993#
994warn()
995{
996	if [ -x /usr/bin/logger ]; then
997		logger "$0: WARNING: $*"
998	fi
999	echo 1>&2 "$0: WARNING: $*"
1000}
1001
1002#
1003# info message
1004#	Display informational message to stdout and log to syslog.
1005#
1006info()
1007{
1008	case ${rc_info} in
1009	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
1010		if [ -x /usr/bin/logger ]; then
1011			logger "$0: INFO: $*"
1012		fi
1013		echo "$0: INFO: $*"
1014		;;
1015	esac
1016}
1017
1018#
1019# debug message
1020#	If debugging is enabled in rc.conf output message to stderr.
1021#	BEWARE that you don't call any subroutine that itself calls this
1022#	function.
1023#
1024debug()
1025{
1026	case ${rc_debug} in
1027	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
1028		if [ -x /usr/bin/logger ]; then
1029			logger "$0: INFO: $*"
1030		fi
1031		echo 1>&2 "$0: DEBUG: $*"
1032		;;
1033	esac
1034}
1035
1036#
1037# backup_file action file cur backup
1038#	Make a backup copy of `file' into `cur', and save the previous
1039#	version of `cur' as `backup' or use rcs for archiving.
1040#
1041#	This routine checks the value of the backup_uses_rcs variable,
1042#	which can be either YES or NO.
1043#
1044#	The `action' keyword can be one of the following:
1045#
1046#	add		`file' is now being backed up (and is possibly
1047#			being reentered into the backups system).  `cur'
1048#			is created and RCS files, if necessary, are
1049#			created as well.
1050#
1051#	update		`file' has changed and needs to be backed up.
1052#			If `cur' exists, it is copied to to `back' or
1053#			checked into RCS (if the repository file is old),
1054#			and then `file' is copied to `cur'.  Another RCS
1055#			check in done here if RCS is being used.
1056#
1057#	remove		`file' is no longer being tracked by the backups
1058#			system.  If RCS is not being used, `cur' is moved
1059#			to `back', otherwise an empty file is checked in,
1060#			and then `cur' is removed.
1061#
1062#
1063backup_file()
1064{
1065	_action=$1
1066	_file=$2
1067	_cur=$3
1068	_back=$4
1069
1070	if checkyesno backup_uses_rcs; then
1071		_msg0="backup archive"
1072		_msg1="update"
1073
1074		# ensure that history file is not locked
1075		if [ -f $_cur,v ]; then
1076			rcs -q -u -U -M $_cur
1077		fi
1078
1079		# ensure after switching to rcs that the
1080		# current backup is not lost
1081		if [ -f $_cur ]; then
1082			# no archive, or current newer than archive
1083			if [ ! -f $_cur,v -o $_cur -nt $_cur,v ]; then
1084				ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1085				rcs -q -kb -U $_cur
1086				co -q -f -u $_cur
1087			fi
1088		fi
1089
1090		case $_action in
1091		add|update)
1092			cp -p $_file $_cur
1093			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1094			rcs -q -kb -U $_cur
1095			co -q -f -u $_cur
1096			chown root:wheel $_cur $_cur,v
1097			;;
1098		remove)
1099			cp /dev/null $_cur
1100			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1101			rcs -q -kb -U $_cur
1102			chown root:wheel $_cur $_cur,v
1103			rm $_cur
1104			;;
1105		esac
1106	else
1107		case $_action in
1108		add|update)
1109			if [ -f $_cur ]; then
1110				cp -p $_cur $_back
1111			fi
1112			cp -p $_file $_cur
1113			chown root:wheel $_cur
1114			;;
1115		remove)
1116			mv -f $_cur $_back
1117			;;
1118		esac
1119	fi
1120}
1121
1122_rc_subr_loaded=:
1123
1124# make_symlink src link
1125#	Make a symbolic link 'link' to src from basedir. If the
1126#	directory in which link is to be created does not exist
1127#	a warning will be displayed and an error will be returned.
1128#	Returns 0 on sucess, 1 otherwise.
1129#
1130make_symlink()
1131{
1132	local src link linkdir _me
1133	src="$1"
1134	link="$2"
1135	linkdir="`dirname $link`"
1136	_me="make_symlink()"
1137
1138	if [ -z "$src" -o -z "$link" ]; then
1139		warn "$_me: requires two arguments."
1140		return 1
1141	fi
1142	if [ ! -d "$linkdir" ]; then
1143		warn "$_me: the directory $linkdir does not exist"
1144		return 1
1145	fi
1146	if ! ln -sf $src $link; then
1147		warn "$_me: unable to make a symbolic link from $link to $src"
1148		return 1
1149	fi
1150	return 0
1151}
1152
1153# devfs_rulesets_from_file file
1154#	Reads a set of devfs commands from file, and creates
1155#	the specified rulesets with their rules. Returns non-zero
1156#	if there was an error.
1157#
1158devfs_rulesets_from_file()
1159{
1160	local file _err _me
1161	file="$1"
1162	_me="devfs_rulesets_from_file"
1163	_err=0
1164
1165	if [ -z "$file" ]; then
1166		warn "$_me: you must specify a file"
1167		return 1
1168	fi
1169	if [ ! -e "$file" ]; then
1170		debug "$_me: no such file ($file)"
1171		return 0
1172	fi
1173	debug "reading rulesets from file ($file)"
1174	{ while read line
1175	do
1176		case $line in
1177		\#*)
1178			continue
1179			;;
1180		\[*\]*)
1181			rulenum=`expr "$line" : "\[.*=\([0-9]*\)\]"`
1182			if [ -z "$rulenum" ]; then
1183				warn "$_me: cannot extract rule number ($line)"
1184				_err=1
1185				break
1186			fi
1187			rulename=`expr "$line" : "\[\(.*\)=[0-9]*\]"`
1188			if [ -z "$rulename" ]; then
1189				warn "$_me: cannot extract rule name ($line)"
1190				_err=1
1191				break;
1192			fi
1193			eval $rulename=\$rulenum
1194			debug "found ruleset: $rulename=$rulenum"
1195			if ! /sbin/devfs rule -s $rulenum delset; then
1196				_err=1
1197				break
1198			fi
1199			;;
1200		*)
1201			rulecmd="${line%%"\#*"}"
1202			# evaluate the command incase it includes
1203			# other rules
1204			if [ -n "$rulecmd" ]; then
1205				debug "adding rule ($rulecmd)"
1206				if ! eval /sbin/devfs rule -s $rulenum $rulecmd
1207				then
1208					_err=1
1209					break
1210				fi
1211			fi
1212			;;
1213		esac
1214		if [ $_err -ne 0 ]; then
1215			debug "error in $_me"
1216			break
1217		fi
1218	done } < $file
1219	return $_err
1220}
1221
1222# devfs_init_rulesets
1223#	Initializes rulesets from configuration files. Returns
1224#	non-zero if there was an error.
1225#
1226devfs_init_rulesets()
1227{
1228	local file _me
1229	_me="devfs_init_rulesets"
1230
1231	# Go through this only once
1232	if [ -n "$devfs_rulesets_init" ]; then
1233		debug "$_me: devfs rulesets already initialized"
1234		return
1235	fi
1236	for file in $devfs_rulesets; do
1237		devfs_rulesets_from_file $file || return 1
1238	done
1239	devfs_rulesets_init=1
1240	debug "$_me: devfs rulesets initialized"
1241	return 0
1242}
1243
1244# devfs_set_ruleset ruleset [dir]
1245#	Sets the default ruleset of dir to ruleset. The ruleset argument
1246#	must be a ruleset name as specified in devfs.rules(5) file.
1247#	Returns non-zero if it could not set it successfully.
1248#
1249devfs_set_ruleset()
1250{
1251	local devdir rs _me
1252	[ -n "$1" ] && eval rs=\$$1 || rs=
1253	[ -n "$2" ] && devdir="-m "$2"" || devdir=
1254	_me="devfs_set_ruleset"
1255
1256	if [ -z "$rs" ]; then
1257		warn "$_me: you must specify a ruleset number"
1258		return 1
1259	fi
1260	debug "$_me: setting ruleset ($rs) on mount-point (${devdir#-m })"
1261	if ! /sbin/devfs $devdir ruleset $rs; then
1262		warn "$_me: unable to set ruleset $rs to ${devdir#-m }"
1263		return 1
1264	fi
1265	return 0
1266}
1267
1268# devfs_apply_ruleset ruleset [dir]
1269#	Apply ruleset number $ruleset to the devfs mountpoint $dir.
1270#	The ruleset argument must be a ruleset name as specified
1271#	in a devfs.rules(5) file.  Returns 0 on success or non-zero
1272#	if it could not apply the ruleset.
1273#
1274devfs_apply_ruleset()
1275{
1276	local devdir rs _me
1277	[ -n "$1" ] && eval rs=\$$1 || rs=
1278	[ -n "$2" ] && devdir="-m "$2"" || devdir=
1279	_me="devfs_apply_ruleset"
1280
1281	if [ -z "$rs" ]; then
1282		warn "$_me: you must specify a ruleset"
1283		return 1
1284	fi
1285	debug "$_me: applying ruleset ($rs) to mount-point (${devdir#-m })"
1286	if ! /sbin/devfs $devdir rule -s $rs applyset; then
1287		warn "$_me: unable to apply ruleset $rs to ${devdir#-m }"
1288		return 1
1289	fi
1290	return 0
1291}
1292
1293# devfs_domount dir [ruleset]
1294#	Mount devfs on dir. If ruleset is specified it is set
1295#	on the mount-point. It must also be a ruleset name as specified
1296#	in a devfs.rules(5) file. Returns 0 on success.
1297#
1298devfs_domount()
1299{
1300	local devdir rs _me
1301	devdir="$1"
1302	[ -n "$2" ] && rs=$2 || rs=
1303	_me="devfs_domount()"
1304
1305	if [ -z "$devdir" ]; then
1306		warn "$_me: you must specify a mount-point"
1307		return 1
1308	fi
1309	debug "$_me: mount-point is ($devdir), ruleset is ($rs)"
1310	if ! mount -t devfs dev "$devdir"; then
1311		warn "$_me: Unable to mount devfs on $devdir"
1312		return 1
1313	fi
1314	if [ -n "$rs" ]; then
1315		devfs_init_rulesets
1316		devfs_set_ruleset $rs $devdir
1317		devfs -m $devdir rule applyset
1318	fi
1319	return 0
1320}
1321
1322# devfs_mount_jail dir [ruleset]
1323#	Mounts a devfs file system appropriate for jails
1324#	on the directory dir. If ruleset is specified, the ruleset
1325#	it names will be used instead.  If present, ruleset must
1326#	be the name of a ruleset as defined in a devfs.rules(5) file.
1327#	This function returns non-zero if an error occurs.
1328#
1329devfs_mount_jail()
1330{
1331	local jdev rs _me
1332	jdev="$1"
1333	[ -n "$2" ] && rs=$2 || rs="devfsrules_jail"
1334	_me="devfs_mount_jail"
1335
1336	devfs_init_rulesets
1337	if ! devfs_domount "$jdev" $rs; then
1338		warn "$_me: devfs was not mounted on $jdev"
1339		return 1
1340	fi
1341	return 0
1342}
1343
1344# Provide a function for normalizing the mounting of memory
1345# filesystems.  This should allow the rest of the code here to remain
1346# as close as possible between 5-current and 4-stable.
1347#   $1 = size
1348#   $2 = mount point
1349#   $3 = (optional) extra mdmfs flags
1350mount_md()
1351{
1352	if [ -n "$3" ]; then
1353		flags="$3"
1354	fi
1355	/sbin/mdmfs $flags -s $1 md $2
1356}
1357
1358# ltr str src dst
1359#	Change every $src in $str to $dst.
1360#	Useful when /usr is not yet mounted and we cannot use tr(1), sed(1) nor
1361#	awk(1).
1362ltr()
1363{
1364	local _str _src _dst _out _com
1365	_str=$1
1366	_src=$2
1367	_dst=$3
1368	_out=""
1369
1370	IFS=${_src}
1371	for _com in ${_str}; do
1372		if [ -z "${_out}" ]; then
1373			_out="${_com}"
1374		else
1375			_out="${_out}${_dst}${_com}"
1376		fi
1377	done
1378	echo "${_out}"
1379}
1380
1381# Creates a list of providers for GELI encryption.
1382geli_make_list()
1383{
1384	local devices devices2
1385	local provider mountpoint type options rest
1386
1387	# Create list of GELI providers from fstab.
1388	while read provider mountpoint type options rest ; do
1389		case ":${options}" in
1390		:*noauto*)
1391			noauto=yes
1392			;;
1393		*)
1394			noauto=no
1395			;;
1396		esac
1397
1398		case ":${provider}" in
1399		:#*)
1400			continue
1401			;;
1402		*.eli)
1403			# Skip swap devices.
1404			if [ "${type}" = "swap" -o "${options}" = "sw" -o "${noauto}" = "yes" ]; then
1405				continue
1406			fi
1407			devices="${devices} ${provider}"
1408			;;
1409		esac
1410	done < /etc/fstab
1411
1412	# Append providers from geli_devices.
1413	devices="${devices} ${geli_devices}"
1414
1415	for provider in ${devices}; do
1416		provider=${provider%.eli}
1417		provider=${provider#/dev/}
1418		devices2="${devices2} ${provider}"
1419	done
1420
1421	echo ${devices2}
1422}
1423
1424# Find scripts in local_startup directories that use the old syntax
1425#
1426find_local_scripts_old () {
1427	zlist=''
1428	slist=''
1429	for dir in ${local_startup}; do
1430		if [ -d "${dir}" ]; then
1431			for file in ${dir}/[0-9]*.sh; do
1432				grep '^# PROVIDE:' $file >/dev/null 2>&1 &&
1433				    continue
1434				zlist="$zlist $file"
1435			done
1436			for file in ${dir}/[^0-9]*.sh; do
1437				grep '^# PROVIDE:' $file >/dev/null 2>&1 &&
1438				    continue
1439				slist="$slist $file"
1440			done
1441		fi
1442	done
1443}
1444
1445find_local_scripts_new () {
1446	local_rc=''
1447	for dir in ${local_startup}; do
1448		if [ -d "${dir}" ]; then
1449			for file in `grep -l '^# PROVIDE:' ${dir}/* 2>/dev/null`; do
1450				case "$file" in
1451				*.sample) ;;
1452				*)	if [ -x "$file" ]; then
1453						local_rc="${local_rc} ${file}"
1454					fi
1455					;;
1456				esac
1457			done
1458		fi
1459	done
1460}
1461
1462fi
1463