rc.subr revision 1.65
1# $NetBSD: rc.subr,v 1.65 2004/10/12 14:45:29 lukem Exp $
2#
3# Copyright (c) 1997-2004 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# 3. All advertising materials mentioning features or use of this software
18#    must display the following acknowledgement:
19#        This product includes software developed by the NetBSD
20#        Foundation, Inc. and its contributors.
21# 4. Neither the name of The NetBSD Foundation nor the names of its
22#    contributors may be used to endorse or promote products derived
23#    from this software without specific prior written permission.
24#
25# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35# POSSIBILITY OF SUCH DAMAGE.
36#
37# rc.subr
38#	functions used by various rc scripts
39#
40
41: ${rcvar_manpage:='rc.conf(5)'}
42
43#
44#	functions
45#	---------
46
47#
48# checkyesno var
49#	Test $1 variable, and warn if not set to YES or NO.
50#	Return 0 if it's "yes" (et al), nonzero otherwise.
51#
52checkyesno()
53{
54	eval _value=\$${1}
55	case $_value in
56
57		#	"yes", "true", "on", or "1"
58	[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
59		return 0
60		;;
61
62		#	"no", "false", "off", or "0"
63	[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
64		return 1
65		;;
66	*)
67		warn "\$${1} is not set properly - see ${rcvar_manpage}."
68		return 1
69		;;
70	esac
71}
72
73#
74# reverse_list list
75#	print the list in reverse order
76#
77reverse_list()
78{
79	_revlist=
80	for _revfile; do
81		_revlist="$_revfile $_revlist"
82	done
83	echo $_revlist
84}
85
86#
87# mount_critical_filesystems type
88#	Go through the list of critical filesystems as provided in
89#	the rc.conf(5) variable $critical_filesystems_${type}, checking
90#	each one to see if it is mounted, and if it is not, mounting it.
91#
92mount_critical_filesystems()
93{
94	eval _fslist=\$critical_filesystems_${1}
95	for _fs in $_fslist; do
96		mount | (
97			_ismounted=false
98			while read what _on on _type type; do
99				if [ $on = $_fs ]; then
100					_ismounted=true
101				fi
102			done
103			if $_ismounted; then
104				:
105			else
106				mount $_fs >/dev/null 2>&1
107			fi
108		)
109	done
110}
111
112#
113# check_pidfile pidfile procname [interpreter]
114#	Parses the first line of pidfile for a PID, and ensures
115#	that the process is running and matches procname.
116#	Prints the matching PID upon success, nothing otherwise.
117#	interpreter is optional; see _find_processes() for details.
118#
119check_pidfile()
120{
121	_pidfile=$1
122	_procname=$2
123	_interpreter=$3
124	if [ -z "$_pidfile" -o -z "$_procname" ]; then
125		err 3 'USAGE: check_pidfile pidfile procname [interpreter]'
126	fi
127	if [ ! -f $_pidfile ]; then
128		return
129	fi
130	read _pid _junk < $_pidfile
131	if [ -z "$_pid" ]; then
132		return
133	fi
134	_find_processes $_procname ${_interpreter:-.} '-p '"$_pid"
135}
136
137#
138# check_process procname [interpreter]
139#	Ensures that a process (or processes) named procname is running.
140#	Prints a list of matching PIDs.
141#	interpreter is optional; see _find_processes() for details.
142#
143check_process()
144{
145	_procname=$1
146	_interpreter=$2
147	if [ -z "$_procname" ]; then
148		err 3 'USAGE: check_process procname [interpreter]'
149	fi
150	_find_processes $_procname ${_interpreter:-.} '-ax'
151}
152
153#
154# _find_processes procname interpreter psargs
155#	Search for procname in the output of ps generated by psargs.
156#	Prints the PIDs of any matching processes, space separated.
157#
158#	If interpreter == ".", check the following variations of procname
159#	against the first word of each command:
160#		procname
161#		`basename procname`
162#		`basename procname` + ":"
163#		"(" + `basename procname` + ")"
164#
165#	If interpreter != ".", read the first line of procname, remove the
166#	leading #!, normalise whitespace, append procname, and attempt to
167#	match that against each command, either as is, or with extra words
168#	at the end.
169#
170_find_processes()
171{
172	if [ $# -ne 3 ]; then
173		err 3 'USAGE: _find_processes procname interpreter psargs'
174	fi
175	_procname=$1
176	_interpreter=$2
177	_psargs=$3
178
179	_pref=
180	if [ $_interpreter != "." ]; then	# an interpreted script
181		read _interp < $_procname	# read interpreter name
182		_interp=${_interp#\#!}		# strip #!
183		set -- $_interp
184		if [ $_interpreter != $1 ]; then
185			warn "\$command_interpreter $_interpreter != $1"
186		fi
187		_interp="$* $_procname"		# cleanup spaces, add _procname
188		_fp_args='_argv'
189		_fp_match='case "$_argv" in
190		    ${_interp}|"${_interp} "*)'
191	else					# a normal daemon
192		_procnamebn=${_procname##*/}
193		_fp_args='_arg0 _argv'
194		_fp_match='case "$_arg0" in
195		    $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})")'
196	fi
197
198	_proccheck='
199		ps -o "pid,command" '"$_psargs"' |
200		while read _npid '"$_fp_args"'; do
201			case "$_npid" in
202			    PID)
203				continue ;;
204			esac ; '"$_fp_match"'
205				echo -n "$_pref$_npid" ;
206				_pref=" "
207				;;
208			esac
209		done'
210
211#echo 1>&2 "proccheck is :$_proccheck:"
212	eval $_proccheck
213}
214
215#
216# wait_for_pids pid [pid ...]
217#	spins until none of the pids exist
218#
219wait_for_pids()
220{
221	_list="$@"
222	if [ -z "$_list" ]; then
223		return
224	fi
225	_prefix=
226	while true; do
227		_nlist="";
228		for _j in $_list; do
229			if kill -0 $_j 2>/dev/null; then
230				_nlist="${_nlist}${_nlist:+ }$_j"
231			fi
232		done
233		if [ -z "$_nlist" ]; then
234			break
235		fi
236		_list=$_nlist
237		echo -n ${_prefix:-"Waiting for PIDS: "}$_list
238		_prefix=", "
239		sleep 2
240	done
241	if [ -n "$_prefix" ]; then
242		echo "."
243	fi
244}
245
246#
247# run_rc_command argument
248#	Search for argument in the list of supported commands, which is:
249#		"start stop restart rcvar status poll ${extra_commands}"
250#	If there's a match, run ${argument}_cmd or the default method
251#	(see below).
252#
253#	If argument has a given prefix, then change the operation as follows:
254#		Prefix	Operation
255#		------	---------
256#		fast	Skip the pid check, and set rc_fast=yes
257#		force	Set ${rcvar} to YES, and set rc_force=yes
258#		one	Set ${rcvar} to YES
259#
260#	The following globals are used:
261#
262#	Name		Needed	Purpose
263#	----		------	-------
264#	name		y	Name of script.
265#
266#	command		n	Full path to command.
267#				Not needed if ${rc_arg}_cmd is set for
268#				each keyword.
269#
270#	command_args	n	Optional args/shell directives for command.
271#
272#	command_interpreter n	If not empty, command is interpreted, so
273#				call check_{pidfile,process}() appropriately.
274#
275#	extra_commands	n	List of extra commands supported.
276#
277#	pidfile		n	If set, use check_pidfile $pidfile $command,
278#				otherwise use check_process $command.
279#				In either case, only check if $command is set.
280#
281#	procname	n	Process name to check for instead of $command.
282#
283#	rcvar		n	This is checked with checkyesno to determine
284#				if the action should be run.
285#
286#	${name}_chroot	n	Directory to chroot to before running ${command}
287#				Requires /usr to be mounted.
288#
289#	${name}_chdir	n	Directory to cd to before running ${command}
290#				(if not using ${name}_chroot).
291#
292#	${name}_flags	n	Arguments to call ${command} with.
293#				NOTE:	$flags from the parent environment
294#					can be used to override this.
295#
296#	${name}_nice	n	Nice level to run ${command} at.
297#
298#	${name}_user	n	User to run ${command} as, using su(1) if not
299#				using ${name}_chroot.
300#				Requires /usr to be mounted.
301#
302#	${name}_group	n	Group to run chrooted ${command} as.
303#				Requires /usr to be mounted.
304#
305#	${name}_groups	n	Comma separated list of supplementary groups
306#				to run the chrooted ${command} with.
307#				Requires /usr to be mounted.
308#
309#	${name}_systrace n	Flags passed to systrace(1) if it is used.
310#				Setting this variable enables systracing
311# 				of the given program.  The use of "-a" is
312#				recommended so that the boot process is not
313#				stalled.  In order to pass no flags to
314#				systrace, set this variable to "--".
315#
316#	${rc_arg}_cmd	n	If set, use this as the method when invoked;
317#				Otherwise, use default command (see below)
318#
319#	${rc_arg}_precmd n	If set, run just before performing the
320#				${rc_arg}_cmd method in the default
321#				operation (i.e, after checking for required
322#				bits and process (non)existence).
323#				If this completes with a non-zero exit code,
324#				don't run ${rc_arg}_cmd.
325#
326#	${rc_arg}_postcmd n	If set, run just after performing the
327#				${rc_arg}_cmd method, if that method
328#				returned a zero exit code.
329#
330#	required_dirs	n	If set, check for the existence of the given
331#				directories before running the default
332#				(re)start command.
333#
334#	required_files	n	If set, check for the readability of the given
335#				files before running the default (re)start
336#				command.
337#
338#	required_vars	n	If set, perform checkyesno on each of the
339#				listed variables before running the default
340#				(re)start command.
341#
342#	Default behaviour for a given argument, if no override method is
343#	provided:
344#
345#	Argument	Default behaviour
346#	--------	-----------------
347#	start		if !running && checkyesno ${rcvar}
348#				${command}
349#
350#	stop		if ${pidfile}
351#				rc_pid=$(check_pidfile $pidfile $command)
352#			else
353#				rc_pid=$(check_process $command)
354#			kill $sig_stop $rc_pid
355#			wait_for_pids $rc_pid
356#			($sig_stop defaults to TERM.)
357#
358#	reload		Similar to stop, except use $sig_reload instead,
359#			and doesn't wait_for_pids.
360#			$sig_reload defaults to HUP.
361#
362#	restart		Run `stop' then `start'.
363#
364#	status		Show if ${command} is running, etc.
365#
366#	poll		Wait for ${command} to exit.
367#
368#	rcvar		Display what rc.conf variable is used (if any).
369#
370#	Variables available to methods, and after run_rc_command() has
371#	completed:
372#
373#	Variable	Purpose
374#	--------	-------
375#	rc_arg		Argument to command, after fast/force/one processing
376#			performed
377#
378#	rc_flags	Flags to start the default command with.
379#			Defaults to ${name}_flags, unless overridden
380#			by $flags from the environment.
381#			This variable may be changed by the precmd method.
382#
383#	rc_pid		PID of command (if appropriate)
384#
385#	rc_fast		Not empty if "fast" was provided (q.v.)
386#
387#	rc_force	Not empty if "force" was provided (q.v.)
388#
389#
390run_rc_command()
391{
392	rc_arg=$1
393	if [ -z "$name" ]; then
394		err 3 'run_rc_command: $name is not set.'
395	fi
396
397	_rc_prefix=
398	case "$rc_arg" in
399	fast*)				# "fast" prefix; don't check pid
400		rc_arg=${rc_arg#fast}
401		rc_fast=yes
402		;;
403	force*)				# "force" prefix; always run
404		rc_force=yes
405		_rc_prefix=force
406		rc_arg=${rc_arg#${_rc_prefix}}
407		if [ -n "${rcvar}" ]; then
408			eval ${rcvar}=YES
409		fi
410		;;
411	one*)				# "one" prefix; set ${rcvar}=yes
412		_rc_prefix=one
413		rc_arg=${rc_arg#${_rc_prefix}}
414		if [ -n "${rcvar}" ]; then
415			eval ${rcvar}=YES
416		fi
417		;;
418	esac
419
420	_keywords="start stop restart rcvar $extra_commands"
421	rc_pid=
422	_pidcmd=
423	_procname=${procname:-${command}}
424
425					# setup pid check command if not fast
426	if [ -z "$rc_fast" -a -n "$_procname" ]; then
427		if [ -n "$pidfile" ]; then
428			_pidcmd='rc_pid=$(check_pidfile '"$pidfile $_procname $command_interpreter"')'
429		else
430			_pidcmd='rc_pid=$(check_process '"$_procname $command_interpreter"')'
431		fi
432		if [ -n "$_pidcmd" ]; then
433			_keywords="${_keywords} status poll"
434		fi
435	fi
436
437	if [ -z "$rc_arg" ]; then
438		rc_usage "$_keywords"
439	fi
440
441	if [ -n "$flags" ]; then	# allow override from environment
442		rc_flags=$flags
443	else
444		eval rc_flags=\$${name}_flags
445	fi
446	eval _chdir=\$${name}_chdir	_chroot=\$${name}_chroot \
447	    _nice=\$${name}_nice	_user=\$${name}_user \
448	    _group=\$${name}_group	_groups=\$${name}_groups \
449	    _systrace=\$${name}_systrace
450
451	if [ -n "$_user" ]; then	# unset $_user if running as that user
452		if [ "$_user" = "$(id -un)" ]; then
453			unset _user
454		fi
455	fi
456
457					# if ${rcvar} is set, and $1 is not
458					# "rcvar", then run
459					#	checkyesno ${rcvar}
460					# and return if that failed
461					#
462	if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" ]; then
463		if ! checkyesno ${rcvar}; then
464			return 0
465		fi
466	fi
467
468	eval $_pidcmd			# determine the pid if necessary
469
470	for _elem in $_keywords; do
471		if [ "$_elem" != "$rc_arg" ]; then
472			continue
473		fi
474
475					# if there's a custom ${XXX_cmd},
476					# run that instead of the default
477					#
478		eval _cmd=\$${rc_arg}_cmd _precmd=\$${rc_arg}_precmd \
479		    _postcmd=\$${rc_arg}_postcmd
480		if [ -n "$_cmd" ]; then
481					# if the precmd failed and force
482					# isn't set, exit
483					#
484			if ! eval $_precmd && [ -z "$rc_force" ]; then
485				return 1
486			fi
487
488			if ! eval $_cmd && [ -z "$rc_force" ]; then
489				return 1
490			fi
491			eval $_postcmd
492			return 0
493		fi
494
495		case "$rc_arg" in	# default operations...
496
497		status)
498			if [ -n "$rc_pid" ]; then
499				echo "${name} is running as pid $rc_pid."
500			else
501				echo "${name} is not running."
502				return 1
503			fi
504			;;
505
506		start)
507			if [ -n "$rc_pid" ]; then
508				echo 1>&2 "${name} already running? (pid=$rc_pid)."
509				exit 1
510			fi
511
512			if [ ! -x ${_chroot}${command} ]; then
513				return 0
514			fi
515
516					# check for required variables,
517					# directories, and files
518					#
519			for _f in $required_vars; do
520				if ! checkyesno $_f; then
521					warn "\$${_f} is not enabled."
522					if [ -z "$rc_force" ]; then
523						return 1
524					fi
525				fi
526			done
527			for _f in $required_dirs; do
528				if [ ! -d "${_f}/." ]; then
529					warn "${_f} is not a directory."
530					if [ -z "$rc_force" ]; then
531						return 1
532					fi
533				fi
534			done
535			for _f in $required_files; do
536				if [ ! -r "${_f}" ]; then
537					warn "${_f} is not readable."
538					if [ -z "$rc_force" ]; then
539						return 1
540					fi
541				fi
542			done
543
544					# if the precmd failed and force
545					# isn't set, exit
546					#
547			if ! eval $_precmd && [ -z "$rc_force" ]; then
548				return 1
549			fi
550
551					# setup the command to run, and run it
552					#
553			echo "Starting ${name}."
554			if [ -n "$_chroot" ]; then
555				_doit="\
556${_nice:+nice -n $_nice }\
557${_systrace:+systrace $_systrace }\
558chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\
559$_chroot $command $rc_flags $command_args"
560			else
561				_doit="\
562${_chdir:+cd $_chdir; }\
563${_nice:+nice -n $_nice }\
564${_systrace:+systrace $_systrace }\
565$command $rc_flags $command_args"
566				if [ -n "$_user" ]; then
567				    _doit="su -m $_user -c 'sh -c \"$_doit\"'"
568				fi
569			fi
570
571					# if the cmd failed and force
572					# isn't set, exit
573					#
574			if ! eval $_doit && [ -z "$rc_force" ]; then
575				return 1
576			fi
577
578					# finally, run postcmd
579					#
580			eval $_postcmd
581			;;
582
583		stop)
584			if [ -z "$rc_pid" ]; then
585				if [ -n "$pidfile" ]; then
586					echo 1>&2 \
587				    "${name} not running? (check $pidfile)."
588				else
589					echo 1>&2 "${name} not running?"
590				fi
591				exit 1
592			fi
593
594					# if the precmd failed and force
595					# isn't set, exit
596					#
597			if ! eval $_precmd && [ -z "$rc_force" ]; then
598				return 1
599			fi
600
601					# send the signal to stop
602					#
603			echo "Stopping ${name}."
604			_doit="kill -${sig_stop:-TERM} $rc_pid"
605			if [ -n "$_user" ]; then
606				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
607			fi
608
609					# if the stop cmd failed and force
610					# isn't set, exit
611					#
612			if ! eval $_doit && [ -z "$rc_force" ]; then
613				return 1
614			fi
615
616					# wait for the command to exit,
617					# and run postcmd.
618			wait_for_pids $rc_pid
619			eval $_postcmd
620			;;
621
622		reload)
623			if [ -z "$rc_pid" ]; then
624				if [ -n "$pidfile" ]; then
625					echo 1>&2 \
626				    "${name} not running? (check $pidfile)."
627				else
628					echo 1>&2 "${name} not running?"
629				fi
630				exit 1
631			fi
632			echo "Reloading ${name} config files."
633			if ! eval $_precmd && [ -z "$rc_force" ]; then
634				return 1
635			fi
636			_doit="kill -${sig_reload:-HUP} $rc_pid"
637			if [ -n "$_user" ]; then
638				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
639			fi
640			if ! eval $_doit && [ -z "$rc_force" ]; then
641				return 1
642			fi
643			eval $_postcmd
644			;;
645
646		restart)
647			if ! eval $_precmd && [ -z "$rc_force" ]; then
648				return 1
649			fi
650					# prevent restart being called more
651					# than once by any given script
652					#
653			if ${_rc_restart_done:-false}; then
654				return 0
655			fi
656			_rc_restart_done=true
657
658			( $0 ${_rc_prefix}stop )
659			$0 ${_rc_prefix}start
660
661			eval $_postcmd
662			;;
663
664		poll)
665			if [ -n "$rc_pid" ]; then
666				wait_for_pids $rc_pid
667			fi
668			;;
669
670		rcvar)
671			echo "# $name"
672			if [ -n "$rcvar" ]; then
673				if checkyesno ${rcvar}; then
674					echo "\$${rcvar}=YES"
675				else
676					echo "\$${rcvar}=NO"
677				fi
678			fi
679			;;
680
681		*)
682			rc_usage "$_keywords"
683			;;
684
685		esac
686		return 0
687	done
688
689	echo 1>&2 "$0: unknown directive '$rc_arg'."
690	rc_usage "$_keywords"
691	exit 1
692}
693
694#
695# run_rc_script file arg
696#	Start the script `file' with `arg', and correctly handle the
697#	return value from the script.  If `file' ends with `.sh', it's
698#	sourced into the current environment.  If `file' appears to be
699#	a backup or scratch file, ignore it.  Otherwise if it's
700#	executable run as a child process.
701#
702run_rc_script()
703{
704	_file=$1
705	_arg=$2
706	if [ -z "$_file" -o -z "$_arg" ]; then
707		err 3 'USAGE: run_rc_script file arg'
708	fi
709
710	unset	name command command_args command_interpreter \
711		extra_commands pidfile procname \
712		rcvar required_dirs required_files required_vars
713	eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd
714
715	case "$_file" in
716	*.sh)				# run in current shell
717		set $_arg ; . $_file
718		;;
719	*[~#]|*.OLD|*.orig|*,v)		# scratch file; skip
720		warn "Ignoring scratch file $_file"
721		;;
722	*)				# run in subshell
723		if [ -x $_file ]; then
724			if [ -n "$rc_fast_and_loose" ]; then
725				set $_arg ; . $_file
726			else
727				( set $_arg ; . $_file )
728			fi
729		fi
730		;;
731	esac
732}
733
734#
735# load_rc_config command
736#	Source in the configuration file for a given command.
737#
738load_rc_config()
739{
740	_command=$1
741	if [ -z "$_command" ]; then
742		err 3 'USAGE: load_rc_config command'
743	fi
744
745	if ${_rc_conf_loaded:-false}; then
746		:
747	else
748		. /etc/rc.conf
749		_rc_conf_loaded=true
750	fi
751	if [ -f /etc/rc.conf.d/"$_command" ]; then
752		. /etc/rc.conf.d/"$_command"
753	fi
754}
755
756#
757# load_rc_config_var cmd var
758#	Read the rc.conf(5) var for cmd and set in the
759#	current shell, using load_rc_config in a subshell to prevent
760#	unwanted side effects from other variable assignments.
761#
762load_rc_config_var()
763{
764	if [ $# -ne 2 ]; then
765		err 3 'USAGE: load_rc_config_var cmd var'
766	fi
767	eval $(eval '(
768		load_rc_config '$1' >/dev/null;
769                if [ -n "${'$2'}" -o "${'$2'-UNSET}" != "UNSET" ]; then
770			echo '$2'=\'\''${'$2'}\'\'';
771		fi
772	)' )
773}
774
775#
776# rc_usage commands
777#	Print a usage string for $0, with `commands' being a list of
778#	valid commands.
779#
780rc_usage()
781{
782	echo -n 1>&2 "Usage: $0 [fast|force|one]("
783
784	_sep=
785	for _elem; do
786		echo -n 1>&2 "$_sep$_elem"
787		_sep="|"
788	done
789	echo 1>&2 ")"
790	exit 1
791}
792
793#
794# err exitval message
795#	Display message to stderr and log to the syslog, and exit with exitval.
796#
797err()
798{
799	exitval=$1
800	shift
801
802	if [ -x /usr/bin/logger ]; then
803		logger "$0: ERROR: $*"
804	fi
805	echo 1>&2 "$0: ERROR: $*"
806	exit $exitval
807}
808
809#
810# warn message
811#	Display message to stderr and log to the syslog.
812#
813warn()
814{
815	if [ -x /usr/bin/logger ]; then
816		logger "$0: WARNING: $*"
817	fi
818	echo 1>&2 "$0: WARNING: $*"
819}
820
821#
822# backup_file action file cur backup
823#	Make a backup copy of `file' into `cur', and save the previous
824#	version of `cur' as `backup' or use rcs for archiving.
825#
826#	This routine checks the value of the backup_uses_rcs variable,
827#	which can be either YES or NO.
828#
829#	The `action' keyword can be one of the following:
830#
831#	add		`file' is now being backed up (and is possibly
832#			being reentered into the backups system).  `cur'
833#			is created and RCS files, if necessary, are
834#			created as well.
835#
836#	update		`file' has changed and needs to be backed up.
837#			If `cur' exists, it is copied to to `back' or
838#			checked into RCS (if the repository file is old),
839#			and then `file' is copied to `cur'.  Another RCS
840#			check in done here if RCS is being used.
841#
842#	remove		`file' is no longer being tracked by the backups
843#			system.  If RCS is not being used, `cur' is moved
844#			to `back', otherwise an empty file is checked in,
845#			and then `cur' is removed.
846#
847#
848backup_file()
849{
850	_action=$1
851	_file=$2
852	_cur=$3
853	_back=$4
854
855	if checkyesno backup_uses_rcs; then
856		_msg0="backup archive"
857		_msg1="update"
858
859		# ensure that history file is not locked
860		if [ -f $_cur,v ]; then
861			rcs -q -u -U -M $_cur
862		fi
863
864		# ensure after switching to rcs that the
865		# current backup is not lost
866		if [ -f $_cur ]; then
867			# no archive, or current newer than archive
868			if [ ! -f $_cur,v -o $_cur -nt $_cur,v ]; then
869				ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
870				rcs -q -kb -U $_cur
871				co -q -f -u $_cur
872			fi
873		fi
874
875		case $_action in
876		add|update)
877			cp -p $_file $_cur
878			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
879			rcs -q -kb -U $_cur
880			co -q -f -u $_cur
881			chown root:wheel $_cur $_cur,v
882			;;
883		remove)
884			cp /dev/null $_cur
885			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
886			rcs -q -kb -U $_cur
887			chown root:wheel $_cur $_cur,v
888			rm $_cur
889			;;
890		esac
891	else
892		case $_action in
893		add|update)
894			if [ -f $_cur ]; then
895				cp -p $_cur $_back
896			fi
897			cp -p $_file $_cur
898			chown root:wheel $_cur
899			;;
900		remove)
901			mv -f $_cur $_back
902			;;
903		esac
904	fi
905}
906
907_rc_subr_loaded=:
908