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