rc.subr revision 1.90
1# $NetBSD: rc.subr,v 1.90 2012/04/07 17:22:10 christos Exp $
2#
3# Copyright (c) 1997-2011 The NetBSD Foundation, Inc.
4# All rights reserved.
5#
6# This code is derived from software contributed to The NetBSD Foundation
7# by Luke Mewburn.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions
11# are met:
12# 1. Redistributions of source code must retain the above copyright
13#    notice, this list of conditions and the following disclaimer.
14# 2. Redistributions in binary form must reproduce the above copyright
15#    notice, this list of conditions and the following disclaimer in the
16#    documentation and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30# rc.subr
31#	functions used by various rc scripts
32#
33
34: ${rcvar_manpage:='rc.conf(5)'}
35: ${RC_PID:=$$} ; export RC_PID
36nl='
37' # a literal newline
38
39#
40#	functions
41#	---------
42
43#
44# checkyesno var
45#	Test $1 variable, and warn if not set to YES or NO.
46#	Return 0 if it's "yes" (et al), nonzero otherwise.
47#
48checkyesno()
49{
50	case $_rc_prefix in
51	one)	eval \$${1}=YES; return 0;;
52	esac
53	eval _value=\$${1}
54	case $_value in
55
56		#	"yes", "true", "on", or "1"
57	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
58		return 0
59		;;
60
61		#	"no", "false", "off", or "0"
62	[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
63		return 1
64		;;
65	*)
66		warn "\$${1} is not set properly - see ${rcvar_manpage}."
67		return 1
68		;;
69	esac
70}
71
72#
73# yesno_to_truefalse var
74#	Convert the value of a variable from any of the values
75#	understood by checkyesno() to "true" or "false".
76#
77yesno_to_truefalse()
78{
79	local var=$1
80	if checkyesno $var; then
81		eval $var=true
82		return 0
83	else
84		eval $var=false
85		return 1
86	fi
87}
88
89#
90# reverse_list list
91#	print the list in reverse order
92#
93reverse_list()
94{
95	_revlist=
96	for _revfile; do
97		_revlist="$_revfile $_revlist"
98	done
99	echo $_revlist
100}
101
102#
103# If booting directly to multiuser, send SIGTERM to
104# the parent (/etc/rc) to abort the boot.
105# Otherwise just exit.
106#
107stop_boot()
108{
109	if [ "$autoboot" = yes ]; then
110		echo "ERROR: ABORTING BOOT (sending SIGTERM to parent)!"
111		kill -TERM ${RC_PID}
112	fi
113	exit 1
114}
115
116#
117# mount_critical_filesystems type
118#	Go through the list of critical filesystems as provided in
119#	the rc.conf(5) variable $critical_filesystems_${type}, checking
120#	each one to see if it is mounted, and if it is not, mounting it.
121#	It's not an error if file systems prefixed with "OPTIONAL:"
122#	are not mentioned in /etc/fstab.
123#
124mount_critical_filesystems()
125{
126	eval _fslist=\$critical_filesystems_${1}
127	_mountcrit_es=0
128	for _fs in $_fslist; do
129		_optional=false
130		case "$_fs" in
131		OPTIONAL:*)
132			_optional=true
133			_fs="${_fs#*:}"
134			;;
135		esac
136		_ismounted=false
137		# look for a line like "${fs} on * type *"
138		# or "* on ${fs} type *" in the output from mount.
139		case "${nl}$( mount )${nl}" in
140		*" on ${_fs} type "*)
141			_ismounted=true
142			;;
143		*"${nl}${_fs} on "*)
144			_ismounted=true
145			;;
146		esac
147		if $_ismounted; then
148			print_rc_metadata \
149			"note:File system ${_fs} was already mounted"
150		else
151			_mount_output=$( mount $_fs 2>&1 )
152			_mount_es=$?
153			case "$_mount_output" in
154			*"${nl}"*)
155				# multiple lines can't be good,
156				# not even if $_optional is true
157				;;
158			*[uU]'nknown special file or file system'*)
159				if $_optional; then
160					# ignore this error
161					print_rc_metadata \
162			"note:Optional file system ${_fs} is not present"
163					_mount_es=0
164					_mount_output=""
165				fi
166				;;
167			esac
168			if [ -n "$_mount_output" ]; then
169				printf >&2 "%s\n" "$_mount_output"
170			fi
171			if [ "$_mount_es" != 0 ]; then
172				_mountcrit_es="$_mount_es"
173			fi
174		fi
175	done
176	return $_mountcrit_es
177}
178
179#
180# check_pidfile pidfile procname [interpreter]
181#	Parses the first line of pidfile for a PID, and ensures
182#	that the process is running and matches procname.
183#	Prints the matching PID upon success, nothing otherwise.
184#	interpreter is optional; see _find_processes() for details.
185#
186check_pidfile()
187{
188	_pidfile=$1
189	_procname=$2
190	_interpreter=$3
191	if [ -z "$_pidfile" -o -z "$_procname" ]; then
192		err 3 'USAGE: check_pidfile pidfile procname [interpreter]'
193	fi
194	if [ ! -f $_pidfile ]; then
195		return
196	fi
197	read _pid _junk < $_pidfile
198	if [ -z "$_pid" ]; then
199		return
200	fi
201	_find_processes $_procname ${_interpreter:-.} '-p '"$_pid"
202}
203
204#
205# check_process procname [interpreter]
206#	Ensures that a process (or processes) named procname is running.
207#	Prints a list of matching PIDs.
208#	interpreter is optional; see _find_processes() for details.
209#
210check_process()
211{
212	_procname=$1
213	_interpreter=$2
214	if [ -z "$_procname" ]; then
215		err 3 'USAGE: check_process procname [interpreter]'
216	fi
217	_find_processes $_procname ${_interpreter:-.} '-ax'
218}
219
220#
221# _find_processes procname interpreter psargs
222#	Search for procname in the output of ps generated by psargs.
223#	Prints the PIDs of any matching processes, space separated.
224#
225#	If interpreter == ".", check the following variations of procname
226#	against the first word of each command:
227#		procname
228#		`basename procname`
229#		`basename procname` + ":"
230#		"(" + `basename procname` + ")"
231#
232#	If interpreter != ".", read the first line of procname, remove the
233#	leading #!, normalise whitespace, append procname, and attempt to
234#	match that against each command, either as is, or with extra words
235#	at the end.  As an alternative, to deal with interpreted daemons
236#	using perl, the basename of the interpreter plus a colon is also
237#	tried as the prefix to procname.
238#
239_find_processes()
240{
241	if [ $# -ne 3 ]; then
242		err 3 'USAGE: _find_processes procname interpreter psargs'
243	fi
244	_procname=$1
245	_interpreter=$2
246	_psargs=$3
247
248	_pref=
249	_procnamebn=${_procname##*/}
250	if [ $_interpreter != "." ]; then	# an interpreted script
251		read _interp < ${_chroot:-}/$_procname	# read interpreter name
252		_interp=${_interp#\#!}		# strip #!
253		set -- $_interp
254		if [ $1 = "/usr/bin/env" ]; then
255			shift
256			set -- $(type $1)
257			shift $(($# - 1))
258			_interp="${1##*/} $_procname"
259		else
260			_interp="$* $_procname"
261		fi
262		if [ $_interpreter != $1 ]; then
263			warn "\$command_interpreter $_interpreter != $1"
264		fi
265		_interpbn=${1##*/}
266		_fp_args='_argv'
267		_fp_match='case "$_argv" in
268		    ${_interp}|"${_interp} "*|"${_interpbn}: "*${_procnamebn}*)'
269	else					# a normal daemon
270		_fp_args='_arg0 _argv'
271		_fp_match='case "$_arg0" in
272		    $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})")'
273	fi
274
275	_proccheck='
276		ps -o "pid,command" '"$_psargs"' |
277		while read _npid '"$_fp_args"'; do
278			case "$_npid" in
279			    PID)
280				continue ;;
281			esac ; '"$_fp_match"'
282				echo -n "$_pref$_npid" ;
283				_pref=" "
284				;;
285			esac
286		done'
287
288#echo 1>&2 "proccheck is :$_proccheck:"
289	eval $_proccheck
290}
291
292#
293# wait_for_pids pid [pid ...]
294#	spins until none of the pids exist
295#
296wait_for_pids()
297{
298	_list="$@"
299	if [ -z "$_list" ]; then
300		return
301	fi
302	_prefix=
303	while true; do
304		_nlist="";
305		for _j in $_list; do
306			if kill -0 $_j 2>/dev/null; then
307				_nlist="${_nlist}${_nlist:+ }$_j"
308			fi
309		done
310		if [ -z "$_nlist" ]; then
311			break
312		fi
313		_list=$_nlist
314		echo -n ${_prefix:-"Waiting for PIDS: "}$_list
315		_prefix=", "
316		sleep 2
317	done
318	if [ -n "$_prefix" ]; then
319		echo "."
320	fi
321}
322
323#
324# run_rc_command argument [parameters]
325#	Search for argument in the list of supported commands, which is:
326#		"start stop restart rcvar status poll ${extra_commands}"
327#	If there's a match, run ${argument}_cmd or the default method
328#	(see below), and pass the optional list of parameters to it.
329#
330#	If argument has a given prefix, then change the operation as follows:
331#		Prefix	Operation
332#		------	---------
333#		fast	Skip the pid check, and set rc_fast=yes
334#		force	Set ${rcvar} to YES, and set rc_force=yes
335#		one	Set ${rcvar} to YES
336#
337#	The following globals are used:
338#
339#	Name		Needed	Purpose
340#	----		------	-------
341#	name		y	Name of script.
342#
343#	command		n	Full path to command.
344#				Not needed if ${rc_arg}_cmd is set for
345#				each keyword.
346#
347#	command_args	n	Optional args/shell directives for command.
348#
349#	command_interpreter n	If not empty, command is interpreted, so
350#				call check_{pidfile,process}() appropriately.
351#
352#	extra_commands	n	List of extra commands supported.
353#
354#	pidfile		n	If set, use check_pidfile $pidfile $command,
355#				otherwise use check_process $command.
356#				In either case, only check if $command is set.
357#
358#	procname	n	Process name to check for instead of $command.
359#
360#	rcvar		n	This is checked with checkyesno to determine
361#				if the action should be run.
362#
363#	${name}_chroot	n	Directory to chroot to before running ${command}
364#				Requires /usr to be mounted.
365#
366#	${name}_chdir	n	Directory to cd to before running ${command}
367#				(if not using ${name}_chroot).
368#
369#	${name}_flags	n	Arguments to call ${command} with.
370#				NOTE:	$flags from the parent environment
371#					can be used to override this.
372#
373#	${name}_env	n	Additional environment variable settings
374#				for running ${command}
375#
376#	${name}_nice	n	Nice level to run ${command} at.
377#
378#	${name}_user	n	User to run ${command} as, using su(1) if not
379#				using ${name}_chroot.
380#				Requires /usr to be mounted.
381#
382#	${name}_group	n	Group to run chrooted ${command} as.
383#				Requires /usr to be mounted.
384#
385#	${name}_groups	n	Comma separated list of supplementary groups
386#				to run the chrooted ${command} with.
387#				Requires /usr to be mounted.
388#
389#	${rc_arg}_cmd	n	If set, use this as the method when invoked;
390#				Otherwise, use default command (see below)
391#
392#	${rc_arg}_precmd n	If set, run just before performing the
393#				${rc_arg}_cmd method in the default
394#				operation (i.e, after checking for required
395#				bits and process (non)existence).
396#				If this completes with a non-zero exit code,
397#				don't run ${rc_arg}_cmd.
398#
399#	${rc_arg}_postcmd n	If set, run just after performing the
400#				${rc_arg}_cmd method, if that method
401#				returned a zero exit code.
402#
403#	required_dirs	n	If set, check for the existence of the given
404#				directories before running the default
405#				(re)start command.
406#
407#	required_files	n	If set, check for the readability of the given
408#				files before running the default (re)start
409#				command.
410#
411#	required_vars	n	If set, perform checkyesno on each of the
412#				listed variables before running the default
413#				(re)start command.
414#
415#	Default behaviour for a given argument, if no override method is
416#	provided:
417#
418#	Argument	Default behaviour
419#	--------	-----------------
420#	start		if !running && checkyesno ${rcvar}
421#				${command}
422#
423#	stop		if ${pidfile}
424#				rc_pid=$(check_pidfile $pidfile $command)
425#			else
426#				rc_pid=$(check_process $command)
427#			kill $sig_stop $rc_pid
428#			wait_for_pids $rc_pid
429#			($sig_stop defaults to TERM.)
430#
431#	reload		Similar to stop, except use $sig_reload instead,
432#			and doesn't wait_for_pids.
433#			$sig_reload defaults to HUP.
434#
435#	restart		Run `stop' then `start'.
436#
437#	status		Show if ${command} is running, etc.
438#
439#	poll		Wait for ${command} to exit.
440#
441#	rcvar		Display what rc.conf variable is used (if any).
442#
443#	Variables available to methods, and after run_rc_command() has
444#	completed:
445#
446#	Variable	Purpose
447#	--------	-------
448#	rc_arg		Argument to command, after fast/force/one processing
449#			performed
450#
451#	rc_flags	Flags to start the default command with.
452#			Defaults to ${name}_flags, unless overridden
453#			by $flags from the environment.
454#			This variable may be changed by the precmd method.
455#
456#	rc_pid		PID of command (if appropriate)
457#
458#	rc_fast		Not empty if "fast" was provided (q.v.)
459#
460#	rc_force	Not empty if "force" was provided (q.v.)
461#
462#
463run_rc_command()
464{
465	rc_arg=$1
466	if [ -z "$name" ]; then
467		err 3 'run_rc_command: $name is not set.'
468	fi
469
470	_rc_prefix=
471	case "$rc_arg" in
472	fast*)				# "fast" prefix; don't check pid
473		rc_arg=${rc_arg#fast}
474		rc_fast=yes
475		;;
476	force*)				# "force" prefix; always run
477		rc_force=yes
478		_rc_prefix=force
479		rc_arg=${rc_arg#${_rc_prefix}}
480		if [ -n "${rcvar}" ]; then
481			eval ${rcvar}=YES
482		fi
483		;;
484	one*)				# "one" prefix; set ${rcvar}=yes
485		_rc_prefix=one
486		rc_arg=${rc_arg#${_rc_prefix}}
487		if [ -n "${rcvar}" ]; then
488			eval ${rcvar}=YES
489		fi
490		;;
491	esac
492
493	_keywords="start stop restart rcvar"
494	if [ -n "$extra_commands" ]; then
495		_keywords="${_keywords} ${extra_commands}"
496	fi
497	rc_pid=
498	_pidcmd=
499	_procname=${procname:-${command}}
500
501					# setup pid check command if not fast
502	if [ -z "$rc_fast" -a -n "$_procname" ]; then
503		if [ -n "$pidfile" ]; then
504			_pidcmd='rc_pid=$(check_pidfile '"$pidfile $_procname $command_interpreter"')'
505		else
506			_pidcmd='rc_pid=$(check_process '"$_procname $command_interpreter"')'
507		fi
508		if [ -n "$_pidcmd" ]; then
509			_keywords="${_keywords} status poll"
510		fi
511	fi
512
513	if [ -z "$rc_arg" ]; then
514		rc_usage "$_keywords"
515	fi
516	shift	# remove $rc_arg from the positional parameters
517
518	if [ -n "$flags" ]; then	# allow override from environment
519		rc_flags=$flags
520	else
521		eval rc_flags=\$${name}_flags
522	fi
523	eval _chdir=\$${name}_chdir	_chroot=\$${name}_chroot \
524	    _nice=\$${name}_nice	_user=\$${name}_user \
525	    _group=\$${name}_group	_groups=\$${name}_groups \
526	    _env=\"\$${name}_env\"
527
528	if [ -n "$_user" ]; then	# unset $_user if running as that user
529		if [ "$_user" = "$(id -un)" ]; then
530			unset _user
531		fi
532	fi
533
534					# if ${rcvar} is set, and $1 is not
535					# "rcvar", then run
536					#	checkyesno ${rcvar}
537					# and return if that failed or warn
538					# user and exit when interactive
539					#
540	if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" ]; then
541		if ! checkyesno ${rcvar}; then
542					# check whether interactive or not
543			if [ -n "$_run_rc_script" ]; then
544				return 0
545			fi
546			for _elem in $_keywords; do
547				if [ "$_elem" = "$rc_arg" ]; then
548					cat 1>&2 <<EOF
549\$${rcvar} is not enabled - see ${rcvar_manpage}.
550Use the following if you wish to perform the operation:
551  $0 one${rc_arg}
552EOF
553					exit 1
554				fi
555			done
556			echo 1>&2 "$0: unknown directive '$rc_arg'."
557			rc_usage "$_keywords"
558		fi
559	fi
560
561	eval $_pidcmd			# determine the pid if necessary
562
563	for _elem in $_keywords; do
564		if [ "$_elem" != "$rc_arg" ]; then
565			continue
566		fi
567
568					# if there's a custom ${XXX_cmd},
569					# run that instead of the default
570					#
571		eval _cmd=\$${rc_arg}_cmd _precmd=\$${rc_arg}_precmd \
572		    _postcmd=\$${rc_arg}_postcmd
573		if [ -n "$_cmd" ]; then
574					# if the precmd failed and force
575					# isn't set, exit
576					#
577			if ! eval $_precmd && [ -z "$rc_force" ]; then
578				return 1
579			fi
580
581			if ! eval $_cmd \"\${@}\" && [ -z "$rc_force" ]; then
582				return 1
583			fi
584			eval $_postcmd
585			return 0
586		fi
587
588		if [ ${#} -gt 0 ]; then
589			err 1 "the $rc_arg command does not take any parameters"
590		fi
591
592		case "$rc_arg" in	# default operations...
593
594		status)
595			if [ -n "$rc_pid" ]; then
596				echo "${name} is running as pid $rc_pid."
597			else
598				echo "${name} is not running."
599				return 1
600			fi
601			;;
602
603		start)
604			if [ -n "$rc_pid" ]; then
605				echo 1>&2 "${name} already running? (pid=$rc_pid)."
606				exit 1
607			fi
608
609			if [ ! -x ${_chroot}${command} ]; then
610				return 0
611			fi
612
613					# check for required variables,
614					# directories, and files
615					#
616			for _f in $required_vars; do
617				if ! checkyesno $_f; then
618					warn "\$${_f} is not enabled."
619					if [ -z "$rc_force" ]; then
620						return 1
621					fi
622				fi
623			done
624			for _f in $required_dirs; do
625				if [ ! -d "${_f}/." ]; then
626					warn "${_f} is not a directory."
627					if [ -z "$rc_force" ]; then
628						return 1
629					fi
630				fi
631			done
632			for _f in $required_files; do
633				if [ ! -r "${_f}" ]; then
634					warn "${_f} is not readable."
635					if [ -z "$rc_force" ]; then
636						return 1
637					fi
638				fi
639			done
640
641					# if the precmd failed and force
642					# isn't set, exit
643					#
644			if ! eval $_precmd && [ -z "$rc_force" ]; then
645				return 1
646			fi
647
648					# setup the command to run, and run it
649					#
650			echo "Starting ${name}."
651			if [ -n "$_chroot" ]; then
652				_doit="\
653${_env:+env $_env }\
654${_nice:+nice -n $_nice }\
655chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\
656$_chroot $command $rc_flags $command_args"
657			else
658				_doit="\
659${_chdir:+cd $_chdir; }\
660${_env:+env $_env }\
661${_nice:+nice -n $_nice }\
662$command $rc_flags $command_args"
663				if [ -n "$_user" ]; then
664				    _doit="su -m $_user -c 'sh -c \"$_doit\"'"
665				fi
666			fi
667
668					# if the cmd failed and force
669					# isn't set, exit
670					#
671			if ! eval $_doit && [ -z "$rc_force" ]; then
672				return 1
673			fi
674
675					# finally, run postcmd
676					#
677			eval $_postcmd
678			;;
679
680		stop)
681			if [ -z "$rc_pid" ]; then
682				if [ -n "$pidfile" ]; then
683					echo 1>&2 \
684				    "${name} not running? (check $pidfile)."
685				else
686					echo 1>&2 "${name} not running?"
687				fi
688				exit 1
689			fi
690
691					# if the precmd failed and force
692					# isn't set, exit
693					#
694			if ! eval $_precmd && [ -z "$rc_force" ]; then
695				return 1
696			fi
697
698					# send the signal to stop
699					#
700			echo "Stopping ${name}."
701			_doit="kill -${sig_stop:-TERM} $rc_pid"
702			if [ -n "$_user" ]; then
703				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
704			fi
705
706					# if the stop cmd failed and force
707					# isn't set, exit
708					#
709			if ! eval $_doit && [ -z "$rc_force" ]; then
710				return 1
711			fi
712
713					# wait for the command to exit,
714					# and run postcmd.
715			wait_for_pids $rc_pid
716			eval $_postcmd
717			;;
718
719		reload)
720			if [ -z "$rc_pid" ]; then
721				if [ -n "$pidfile" ]; then
722					echo 1>&2 \
723				    "${name} not running? (check $pidfile)."
724				else
725					echo 1>&2 "${name} not running?"
726				fi
727				exit 1
728			fi
729			echo "Reloading ${name} config files."
730			if ! eval $_precmd && [ -z "$rc_force" ]; then
731				return 1
732			fi
733			_doit="kill -${sig_reload:-HUP} $rc_pid"
734			if [ -n "$_user" ]; then
735				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
736			fi
737			if ! eval $_doit && [ -z "$rc_force" ]; then
738				return 1
739			fi
740			eval $_postcmd
741			;;
742
743		restart)
744			if ! eval $_precmd && [ -z "$rc_force" ]; then
745				return 1
746			fi
747					# prevent restart being called more
748					# than once by any given script
749					#
750			if ${_rc_restart_done:-false}; then
751				return 0
752			fi
753			_rc_restart_done=true
754
755			( $0 ${_rc_prefix}stop )
756			$0 ${_rc_prefix}start
757
758			eval $_postcmd
759			;;
760
761		poll)
762			if [ -n "$rc_pid" ]; then
763				wait_for_pids $rc_pid
764			fi
765			;;
766
767		rcvar)
768			echo "# $name"
769			if [ -n "$rcvar" ]; then
770				if checkyesno ${rcvar}; then
771					echo "\$${rcvar}=YES"
772				else
773					echo "\$${rcvar}=NO"
774				fi
775			fi
776			;;
777
778		*)
779			rc_usage "$_keywords"
780			;;
781
782		esac
783		return 0
784	done
785
786	echo 1>&2 "$0: unknown directive '$rc_arg'."
787	rc_usage "$_keywords"
788	exit 1
789}
790
791#
792# run_rc_script file arg
793#	Start the script `file' with `arg', and correctly handle the
794#	return value from the script.  If `file' ends with `.sh', it's
795#	sourced into the current environment.  If `file' appears to be
796#	a backup or scratch file, ignore it.  Otherwise if it's
797#	executable run as a child process.
798#
799#	If `file' contains "KEYWORD: interactive" and if we are
800#	running inside /etc/rc with postprocessing (as signified by
801#	_rc_postprocessor_fd being defined) then the script's stdout
802#	and stderr are redirected to $_rc_original_stdout_fd and
803#	$_rc_original_stderr_fd, so the output will be displayed on the
804#	console but not intercepted by /etc/rc's postprocessor.
805#
806run_rc_script()
807{
808	_file=$1
809	_arg=$2
810	if [ -z "$_file" -o -z "$_arg" ]; then
811		err 3 'USAGE: run_rc_script file arg'
812	fi
813
814	_run_rc_script=true
815
816	unset	name command command_args command_interpreter \
817		extra_commands pidfile procname \
818		rcvar required_dirs required_files required_vars
819	eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd
820
821	_must_redirect=false
822	if [ -n "${_rc_postprocessor_fd}" ] \
823	    && _has_rcorder_keyword interactive $_file
824	then
825		_must_redirect=true
826	fi
827
828	case "$_file" in
829	*.sh)				# run in current shell
830		if $_must_redirect; then
831			print_rc_metadata \
832			    "note:Output from ${_file} is not logged"
833			no_rc_postprocess eval \
834			    'set $_arg ; . $_file'
835		else
836			set $_arg ; . $_file
837		fi
838		;;
839	*[~#]|*.OLD|*.orig|*,v)		# scratch file; skip
840		warn "Ignoring scratch file $_file"
841		;;
842	*)				# run in subshell
843		if [ -x $_file ] && $_must_redirect; then
844			print_rc_metadata \
845			    "note:Output from ${_file} is not logged"
846			if [ -n "$rc_fast_and_loose" ]; then
847				no_rc_postprocess eval \
848				    'set $_arg ; . $_file'
849			else
850				no_rc_postprocess eval \
851				    '( set $_arg ; . $_file )'
852			fi
853		elif [ -x $_file ]; then
854			if [ -n "$rc_fast_and_loose" ]; then
855				set $_arg ; . $_file
856			else
857				( set $_arg ; . $_file )
858			fi
859		else
860			warn "Ignoring non-executable file $_file"
861		fi
862		;;
863	esac
864}
865
866#
867# load_rc_config command
868#	Source in the configuration file for a given command.
869#
870load_rc_config()
871{
872	_command=$1
873	if [ -z "$_command" ]; then
874		err 3 'USAGE: load_rc_config command'
875	fi
876
877	if ${_rc_conf_loaded:-false}; then
878		:
879	else
880		. /etc/rc.conf
881		_rc_conf_loaded=true
882	fi
883	if [ -f /etc/rc.conf.d/"$_command" ]; then
884		. /etc/rc.conf.d/"$_command"
885	fi
886}
887
888#
889# load_rc_config_var cmd var
890#	Read the rc.conf(5) var for cmd and set in the
891#	current shell, using load_rc_config in a subshell to prevent
892#	unwanted side effects from other variable assignments.
893#
894load_rc_config_var()
895{
896	if [ $# -ne 2 ]; then
897		err 3 'USAGE: load_rc_config_var cmd var'
898	fi
899	eval $(eval '(
900		load_rc_config '$1' >/dev/null;
901		if [ -n "${'$2'}" -o "${'$2'-UNSET}" != "UNSET" ]; then
902			echo '$2'=\'\''${'$2'}\'\'';
903		fi
904	)' )
905}
906
907#
908# rc_usage commands
909#	Print a usage string for $0, with `commands' being a list of
910#	valid commands.
911#
912rc_usage()
913{
914	echo -n 1>&2 "Usage: $0 [fast|force|one]("
915
916	_sep=
917	for _elem; do
918		echo -n 1>&2 "$_sep$_elem"
919		_sep="|"
920	done
921	echo 1>&2 ")"
922	exit 1
923}
924
925#
926# err exitval message
927#	Display message to stderr and log to the syslog, and exit with exitval.
928#
929err()
930{
931	exitval=$1
932	shift
933
934	if [ -x /usr/bin/logger ]; then
935		logger "$0: ERROR: $*"
936	fi
937	echo 1>&2 "$0: ERROR: $*"
938	exit $exitval
939}
940
941#
942# warn message
943#	Display message to stderr and log to the syslog.
944#
945warn()
946{
947	if [ -x /usr/bin/logger ]; then
948		logger "$0: WARNING: $*"
949	fi
950	echo 1>&2 "$0: WARNING: $*"
951}
952
953#
954# backup_file action file cur backup
955#	Make a backup copy of `file' into `cur', and save the previous
956#	version of `cur' as `backup' or use rcs for archiving.
957#
958#	This routine checks the value of the backup_uses_rcs variable,
959#	which can be either YES or NO.
960#
961#	The `action' keyword can be one of the following:
962#
963#	add		`file' is now being backed up (and is possibly
964#			being reentered into the backups system).  `cur'
965#			is created and RCS files, if necessary, are
966#			created as well.
967#
968#	update		`file' has changed and needs to be backed up.
969#			If `cur' exists, it is copied to to `back' or
970#			checked into RCS (if the repository file is old),
971#			and then `file' is copied to `cur'.  Another RCS
972#			check in done here if RCS is being used.
973#
974#	remove		`file' is no longer being tracked by the backups
975#			system.  If RCS is not being used, `cur' is moved
976#			to `back', otherwise an empty file is checked in,
977#			and then `cur' is removed.
978#
979#
980backup_file()
981{
982	_action=$1
983	_file=$2
984	_cur=$3
985	_back=$4
986
987	if checkyesno backup_uses_rcs; then
988		_msg0="backup archive"
989		_msg1="update"
990
991		# ensure that history file is not locked
992		if [ -f $_cur,v ]; then
993			rcs -q -u -U -M $_cur
994		fi
995
996		# ensure after switching to rcs that the
997		# current backup is not lost
998		if [ -f $_cur ]; then
999			# no archive, or current newer than archive
1000			if [ ! -f $_cur,v -o $_cur -nt $_cur,v ]; then
1001				ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1002				rcs -q -kb -U $_cur
1003				co -q -f -u $_cur
1004			fi
1005		fi
1006
1007		case $_action in
1008		add|update)
1009			cp -p $_file $_cur
1010			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1011			rcs -q -kb -U $_cur
1012			co -q -f -u $_cur
1013			chown root:wheel $_cur $_cur,v
1014			;;
1015		remove)
1016			cp /dev/null $_cur
1017			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
1018			rcs -q -kb -U $_cur
1019			chown root:wheel $_cur $_cur,v
1020			rm $_cur
1021			;;
1022		esac
1023	else
1024		case $_action in
1025		add|update)
1026			if [ -f $_cur ]; then
1027				cp -p $_cur $_back
1028			fi
1029			cp -p $_file $_cur
1030			chown root:wheel $_cur
1031			;;
1032		remove)
1033			mv -f $_cur $_back
1034			;;
1035		esac
1036	fi
1037}
1038
1039#
1040# handle_fsck_error fsck_exit_code
1041#	Take action depending on the return code from fsck.
1042#
1043handle_fsck_error()
1044{
1045	case $1 in
1046	0)	# OK
1047		return
1048		;;
1049	2)	# Needs re-run, still fs errors
1050		echo "File system still has errors; re-run fsck manually!"
1051		;;
1052	4)	# Root modified
1053		echo "Root filesystem was modified, rebooting ..."
1054		reboot -n
1055		echo "Reboot failed; help!"
1056		;;
1057	8)	# Check failed
1058		echo "Automatic file system check failed; help!"
1059		;;
1060	12)	# Got signal
1061		echo "Boot interrupted."
1062		;;
1063	*)
1064		echo "Unknown error $1; help!"
1065		;;
1066	esac
1067	stop_boot
1068}
1069
1070#
1071# _has_rcorder_keyword word file
1072#	Check whether a file contains a "# KEYWORD:" comment with a
1073#	specified keyword in the style used by rcorder(8).
1074#
1075_has_rcorder_keyword()
1076{
1077	local word="$1"
1078	local file="$2"
1079	local line
1080
1081	[ -r "$file" ] || return 1
1082	while read line; do
1083		case "${line} " in
1084		"# KEYWORD:"*[\ \	]"${word}"[\ \	]*)
1085			return 0
1086			;;
1087		"#"*)
1088			continue
1089			;;
1090		*[A-Za-z0-9]*)
1091			# give up at the first non-empty non-comment line
1092			return 1
1093			;;
1094		esac
1095	done <"$file"
1096	return 1
1097}
1098
1099#
1100# print_rc_metadata string
1101#	Print the specified string in such a way that the post-processor
1102#	inside /etc/rc will treat it as meta-data.
1103#
1104#	If we are not running inside /etc/rc, do nothing.
1105#
1106#	For public use by any rc.d script, the string must begin with
1107#	"note:", followed by arbitrary text.  The intent is that the text
1108#	will appear in a log file but not on the console.
1109#
1110#	For private use within /etc/rc, the string must contain a
1111#	keyword recognised by the rc_postprocess_metadata() function
1112#	defined in /etc/rc, followed by a colon, followed by one or more
1113#	colon-separated arguments associated with the keyword.
1114#
1115print_rc_metadata()
1116{
1117	# _rc_postprocessor fd, if defined, is the fd to which we must
1118	# print, prefixing the output with $_rc_metadata_prefix.
1119	#
1120	if [ -n "$_rc_postprocessor_fd" ]; then
1121		command printf "%s%s\n" "$rc_metadata_prefix" "$1" \
1122			>&${_rc_postprocessor_fd}
1123	fi
1124}
1125
1126#
1127# _flush_rc_output
1128#	Arrange for output to be flushed, if we are running
1129#	inside /etc/rc with postprocessing.
1130#
1131_flush_rc_output()
1132{
1133	print_rc_metadata "nop"
1134}
1135
1136#
1137# print_rc_normal [-n] string
1138#	Print the specified string in such way that it is treated as
1139#	normal output, regardless of whether or not we are running
1140#	inside /etc/rc with post-processing.
1141#
1142#	If "-n" is specified in $1, then the string in $2 is printed
1143#	without a newline; otherwise, the string in $1 is printed
1144#	with a newline.
1145#
1146#	Intended use cases include:
1147#
1148#	o   An rc.d script can use ``print_rc_normal -n'' to print a
1149#	    partial line in such a way that it appears immediately
1150#	    instead of being buffered by rc(8)'s post-processor.
1151#
1152#	o   An rc.d script that is run via the no_rc_postprocess
1153#	    function (so most of its output is invisible to rc(8)'s
1154#	    post-processor) can use print_rc_normal to force some of its
1155#	    output to be seen by the post-processor.
1156#
1157#
1158print_rc_normal()
1159{
1160	# If _rc_postprocessor_fd is defined, then it is the fd
1161	# to which we must print; otherwise print to stdout.
1162	#
1163	local fd="${_rc_postprocessor_fd:-1}"
1164	case "$1" in
1165	"-n")
1166		command printf "%s" "$2" >&${fd}
1167		_flush_rc_output
1168		;;
1169	*)
1170		command printf "%s\n" "$1" >&${fd}
1171		;;
1172	esac
1173}
1174
1175#
1176# no_rc_postprocess cmd...
1177#	Execute the specified command in such a way that its output
1178#	bypasses the post-processor that handles the output from
1179#	most commands that are run inside /etc/rc.  If we are not
1180#	inside /etc/rc, then just execute the command without special
1181#	treatment.
1182#
1183#	The intent is that interactive commands can be run via
1184#	no_rc_postprocess(), and their output will apear immediately
1185#	on the console instead of being hidden or delayed by the
1186#	post-processor.	 An unfortunate consequence of the output
1187#	bypassing the post-processor is that the output will not be
1188#	logged.
1189#
1190no_rc_postprocess()
1191{
1192	if [ -n "${_rc_postprocessor_fd}" ]; then
1193		"$@" >&${_rc_original_stdout_fd} 2>&${_rc_original_stderr_fd}
1194	else
1195		"$@"
1196	fi
1197}
1198
1199#
1200# twiddle
1201#	On each call, print a different one of "/", "-", "\\", "|",
1202#	followed by a backspace.  The most recently printed value is
1203#	saved in $_twiddle_state.
1204#
1205#	Output is to /dev/tty, so this function may be useful even inside
1206#	a script whose output is redirected.
1207#
1208twiddle()
1209{
1210	case "$_twiddle_state" in
1211	'/')	_next='-' ;;
1212	'-')	_next='\' ;;
1213	'\')	_next='|' ;;
1214	*)	_next='/' ;;
1215	esac
1216	command printf "%s\b" "$_next" >/dev/tty
1217	_twiddle_state="$_next"
1218}
1219
1220#
1221# human_exit_code
1222#	Print the a human version of the exit code.
1223#
1224human_exit_code()
1225{
1226	if [ "$1" -lt 127 ]
1227	then
1228		echo "exited with code $1"
1229	elif [ "$(expr $1 % 256)" -eq 127 ]
1230	then
1231		# This cannot really happen because the shell will not
1232		# pass stopped job status out and the exit code is limited
1233		# to 8 bits. This code is here just for completeness.
1234		echo "stopped with signal $(expr $1 / 256)"
1235	else
1236		echo "terminated with signal $(expr $1 - 128)"
1237	fi
1238}
1239
1240#
1241# collapse_backslash_newline
1242#	Copy input to output, collapsing <backslash><newline>
1243#	to nothing, but leaving other backslashes alone.
1244#
1245collapse_backslash_newline()
1246{
1247	local line
1248	while read -r line ; do
1249		case "$line" in
1250		*\\)
1251			# print it, without the backslash or newline
1252			command printf "%s" "${line%?}"
1253			;;
1254		*)
1255			# print it, with a newline
1256			command printf "%s\n" "${line}"
1257			;;
1258		esac
1259	done
1260}
1261
1262# Override the normal "echo" and "printf" commands, so that
1263# partial lines printed by rc.d scripts appear immediately,
1264# instead of being buffered by rc(8)'s post-processor.
1265#
1266# Naive use of the echo or printf commands from rc.d scripts,
1267# elsewhere in rc.subr, or anything else that sources rc.subr,
1268# will call these functions.  To call the real echo and printf
1269# commands, use "command echo" or "command printf".
1270#
1271echo()
1272{
1273	command echo "$@"
1274	case "$1" in
1275	'-n')	_flush_rc_output ;;
1276	esac
1277}
1278printf()
1279{
1280	command printf "$@"
1281	case "$1" in
1282	*'\n')	: ;;
1283	*)	_flush_rc_output ;;
1284	esac
1285}
1286
1287_rc_subr_loaded=:
1288