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