Re: 8.2 fails suddenly to boot - /etc/rc issues

Riccardo Mottola <[email protected]>
Newsgroups gmane.os.netbsd.ports.sparc
Message-ID <[email protected]>
Hello Robert,

sorry for the delay...
Before replacing rc.subr with the one from etc

>   | no, I did not change rc.subr
> 
> Good.   Most people don't - it is one of the trickier parts of
> the system.
> 

I try not to touch things without knowing... if not desperate :)

>   | Perhaps some files corrupted in my filesystem?
> 
> That's certainly a possibility - and could be caused by lots of
> thing, anything from a bug, to failing disk, RAM problems, or even
> power fluctuations.

given the diff, appears strange.


> 
>   | extract ./etc/rc.subr
>   | and comare it to my local one. It looks full of differences!
> 
> Differences as in some parts the same, but with changes, but still
> looking like sh code, or differences more like "these two things are
> nothing alike at all"?

To me it looks very different, but with the same file but in different time!
Especially the beninning is telling:

-# $NetBSD: rc.subr,v 1.18 2021/08/30 21:45:02 khorben Exp $
+# $NetBSD: rc.subr,v 1.100.4.1 2018/04/04 16:07:33 martin Exp $
 #
-# Copyright (c) 1997-2002 The NetBSD Foundation, Inc.
+# Copyright (c) 1997-2011 The NetBSD Foundation, Inc.
 # All rights reserved.


Do you recognize something?

Riccardo
rc-subr.diff (text/x-patch, 30.9 KB)
--- /etc/rc.subr.bak	2023-03-09 21:21:56.000000000 +0100
+++ /etc/rc.subr	2020-03-31 07:08:40.000000000 +0200
@@ -1,6 +1,6 @@
-# $NetBSD: rc.subr,v 1.18 2021/08/30 21:45:02 khorben Exp $
+# $NetBSD: rc.subr,v 1.100.4.1 2018/04/04 16:07:33 martin Exp $
 #
-# Copyright (c) 1997-2002 The NetBSD Foundation, Inc.
+# Copyright (c) 1997-2011 The NetBSD Foundation, Inc.
 # All rights reserved.
 #
 # This code is derived from software contributed to The NetBSD Foundation
@@ -31,122 +31,19 @@
 #	functions used by various rc scripts
 #
 
-#	global variables
-#	----------------
-
-sysconfbase=/etc
-_RCCMD_chown="/usr/sbin/chown"
-_RCCMD_chroot="/usr/sbin/chroot"
-_RCCMD_ci="/usr/bin/ci"
-_RCCMD_co="/usr/bin/co"
-_RCCMD_cp="/bin/cp"
-_RCCMD_echo="echo"			# use sh builtin
-_RCCMD_kill="/bin/kill"
-_RCCMD_logger="/usr/bin/logger"
-_RCCMD_mount="/sbin/mount"
-_RCCMD_mv="/bin/mv"
-_RCCMD_nice="/usr/bin/nice"
-_RCCMD_ps="/bin/ps"
-_RCCMD_rcs="/usr/bin/rcs"
-_RCCMD_rm="/bin/rm"
-_RCCMD_sh="/bin/sh"
-_RCCMD_su="/usr/bin/su"
-_RCCMD_systrace="/bin/systrace"
-_RCCMD_whoami="/usr/bin/whoami"
-
-_RCARG_psformat="-o pid,command"
-_RCARG_ps="-ax"
-_RCARG_su="-m"
-
-_osname=$(uname -s)
-_osrelease=$(uname -r)
-case $_osname in
-	Darwin)
-		_RCCMD_echo="/bin/echo"
-		;;
-	SunOS)
-		_RCCMD_chown="/bin/chown"
-#		_RCCMD_ci="/usr/bin/ci"			# not in Solaris 9
-#		_RCCMD_co="/usr/bin/co"			# not in Solaris 9
-#		_RCCMD_systrace="/bin/systrace"		# not in Solaris 9
-		_RCARG_psformat="-o pid,comm"
-		_RCARG_ps="-ef"
-		_RCARG_su=""
-		if [ "$_osrelease" = "5.11" ]; then
-			bsd_echo () {
-				if [ "$1" = "-n" ]; then
-					shift; echo "$@\c"
-				else
-					echo "$@"
-				fi
-			}
-			_RCCMD_echo="bsd_echo"
-		else
-			_RCCMD_echo="/usr/ucb/echo"	# not in Solaris 11
-			_RCCMD_whoami="/usr/ucb/whoami"	# not in Solaris 11
-		fi
-		;;
-	Interix)
-		bsd_echo () {
-			if [ "$1" = "-n" ]; then
-				shift; echo "$@\c"
-			else
-				echo "$@"
-			fi
-		}
-		_RCCMD_chown="/usr/bin/chown"
-		_RCCMD_ci="/usr/contrib/bin/ci"
-		_RCCMD_co="/usr/contrib/bin/co"
-		_RCCMD_echo="bsd_echo"
-		_RCCMD_rcs="/usr/contrib/bin/rcs"
-		_RCARG_psformat="-wwo pid,comm"
-		;;
-	IRIX*)
-		_RCCMD_chown="/sbin/chown"
-		_RCCMD_ci="/usr/sbin/ci"
-		_RCCMD_co="/usr/sbin/co"
-		_RCCMD_cp="/sbin/cp"
-		_RCCMD_kill="kill"			# use sh builtin
-#		_RCCMD_logger="/usr/bin/logger"		# not in IRIX
-		_RCCMD_mv="/sbin/mv"
-		_RCCMD_nice="/sbin/nice"
-		_RCCMD_ps="/sbin/ps"
-		_RCCMD_rcs="/usr/sbin/rcs"
-		_RCCMD_rm="/sbin/rm"
-		_RCCMD_sh="/sbin/sh"
-		_RCCMD_su="/sbin/su"
-#		_RCCMD_systrace="/bin/systrace"		# not in IRIX
-		_RCCMD_whoami="/bin/whoami"
-		_RCARG_psformat="-o pid,comm=12345678901234567890"
-			# widen column to try to get full command-name
-		_RCARG_ps="-ef"
-		_RCARG_su=""
-		;;
-	Linux)
-		_RCCMD_chown="/bin/chown"
-#		_RCCMD_ci="/usr/bin/ci"			# not in Slackware 8.1
-#		_RCCMD_co="/usr/bin/co"			# not in Slackware 8.1
-		if [ ! -f /etc/arch-release ] ; then	# which not in Arch
-			_RCCMD_nice=$(which nice)
-		fi
-#		_RCCMD_rcs="/usr/bin/rcs"		# not in Slackware 8.1
-#		_RCCMD_systrace="/bin/systrace"		# not in Slackware 8.1
-		_RCARG_ps="ax"
-		if [ -e /etc/redhat-release ]; then
-			_RCCMD_su="/bin/su"		# for CentOS/RHEL
-		fi
-		;;
-	QNX)
-		_RCCMD_chown="/bin/chown"
-#		_RCCMD_ci="/usr/bin/ci"			# not in QNX
-#		_RCCMD_co="/usr/bin/co"			# not in QNX
-#		_RCCMD_systrace="/bin/systrace"		# not in QNX
-		_RCCMD_whoami="/usr/bin/id -un"
-		_RCARG_psformat="-o pid,comm"
-		_RCARG_ps="-ef"
-		_RCARG_su=""
-		;;
-esac
+: ${rcvar_manpage:='rc.conf(5)'}
+: ${RC_PID:=$$} ; export RC_PID
+nl='
+' # a literal newline
+
+# RC variables to clear on start.
+_env_clear_rc_vars="
+RC_PID=
+_rc_pid=
+_rc_original_stdout_fd=
+_rc_original_stderr_fd=
+_rc_postprocessor_fd=
+"
 
 #
 #	functions
@@ -154,10 +51,10 @@
 
 #
 # checkyesno var
-#	Test $1 variable, and warn if not set to YES or NO.
-#	Return 0 if it's "yes" (et al), nonzero otherwise.
+#	Test $1 variable.
+#	Return 0 if it's "yes" (et al), 1 if it's "no" (et al), 2 otherwise.
 #
-checkyesno()
+checkyesnox()
 {
 	eval _value=\$${1}
 	case $_value in
@@ -172,12 +69,45 @@
 		return 1
 		;;
 	*)
-		warn "\$${1} is not set properly - see rc.conf(5)."
-		return 1
+		return 2
 		;;
 	esac
 }
 
+#
+# checkyesno var
+#	Test $1 variable, and warn if not set to YES or NO.
+#	Return 0 if it's "yes" (et al), nonzero otherwise.
+#
+checkyesno()
+{
+	local var
+
+	checkyesnox $1
+	var=$?
+	[ $var = 0 -o $var = 1 ] && return $var
+	warn "\$${1} is not set properly - see ${rcvar_manpage}."
+	return 1
+}
+
+#
+# yesno_to_truefalse var
+#	Convert the value of a variable from any of the values
+#	understood by checkyesno() to "true" or "false".
+#
+yesno_to_truefalse()
+{
+	local var=$1
+	if checkyesno $var; then
+		eval $var=true
+		return 0
+	else
+		eval $var=false
+		return 1
+	fi
+}
+
+#
 # reverse_list list
 #	print the list in reverse order
 #
@@ -187,33 +117,84 @@
 	for _revfile; do
 		_revlist="$_revfile $_revlist"
 	done
-	${_RCCMD_echo} $_revlist
+	echo $_revlist
+}
+
+#
+# If booting directly to multiuser, send SIGTERM to
+# the parent (/etc/rc) to abort the boot.
+# Otherwise just exit.
+#
+stop_boot()
+{
+	if [ "$autoboot" = yes ]; then
+		echo "ERROR: ABORTING BOOT (sending SIGTERM to parent)!"
+		kill -TERM ${RC_PID}
+	fi
+	exit 1
 }
 
 #
 # mount_critical_filesystems type
-#	Go through the list of critical filesystems as provided in
+#	Go through the list of critical file systems as provided in
 #	the rc.conf(5) variable $critical_filesystems_${type}, checking
 #	each one to see if it is mounted, and if it is not, mounting it.
+#	It's not an error if file systems prefixed with "OPTIONAL:"
+#	are not mentioned in /etc/fstab.
 #
 mount_critical_filesystems()
 {
 	eval _fslist=\$critical_filesystems_${1}
+	_mountcrit_es=0
 	for _fs in $_fslist; do
-		${_RCCMD_mount} | (
-			_ismounted=false
-			while read what _on on _type type; do
-				if [ $on = $_fs ]; then
-					_ismounted=true
+		_optional=false
+		case "$_fs" in
+		OPTIONAL:*)
+			_optional=true
+			_fs="${_fs#*:}"
+			;;
+		esac
+		_ismounted=false
+		# look for a line like "${fs} on * type *"
+		# or "* on ${fs} type *" in the output from mount.
+		case "${nl}$( mount )${nl}" in
+		*" on ${_fs} type "*)
+			_ismounted=true
+			;;
+		*"${nl}${_fs} on "*)
+			_ismounted=true
+			;;
+		esac
+		if $_ismounted; then
+			print_rc_metadata \
+			"note:File system ${_fs} was already mounted"
+		else
+			_mount_output=$( mount $_fs 2>&1 )
+			_mount_es=$?
+			case "$_mount_output" in
+			*"${nl}"*)
+				# multiple lines can't be good,
+				# not even if $_optional is true
+				;;
+			*[uU]'nknown special file or file system'*)
+				if $_optional; then
+					# ignore this error
+					print_rc_metadata \
+			"note:Optional file system ${_fs} is not present"
+					_mount_es=0
+					_mount_output=""
 				fi
-			done
-			if $_ismounted; then
-				:
-			else
-				${_RCCMD_mount} $_fs >/dev/null 2>&1
+				;;
+			esac
+			if [ -n "$_mount_output" ]; then
+				printf >&2 "%s\n" "$_mount_output"
+			fi
+			if [ "$_mount_es" != 0 ]; then
+				_mountcrit_es="$_mount_es"
 			fi
-		)
+		fi
 	done
+	return $_mountcrit_es
 }
 
 #
@@ -254,7 +235,7 @@
 	if [ -z "$_procname" ]; then
 		err 3 'USAGE: check_process procname [interpreter]'
 	fi
-	_find_processes $_procname ${_interpreter:-.} ${_RCARG_ps}
+	_find_processes $_procname ${_interpreter:-.} '-A'
 }
 
 #
@@ -272,7 +253,9 @@
 #	If interpreter != ".", read the first line of procname, remove the
 #	leading #!, normalise whitespace, append procname, and attempt to
 #	match that against each command, either as is, or with extra words
-#	at the end.
+#	at the end.  As an alternative, to deal with interpreted daemons
+#	using perl, the basename of the interpreter plus a colon is also
+#	tried as the prefix to procname.
 #
 _find_processes()
 {
@@ -284,37 +267,46 @@
 	_psargs=$3
 
 	_pref=
+	_procnamebn=${_procname##*/}
 	if [ $_interpreter != "." ]; then	# an interpreted script
-		read _interp < $_procname	# read interpreter name
+		read _interp < ${_chroot:-}/$_procname	# read interpreter name
 		_interp=${_interp#\#!}		# strip #!
 		set -- $_interp
+		if [ $1 = "/usr/bin/env" ]; then
+			shift
+			set -- $(type $1)
+			shift $(($# - 1))
+			_interp="${1##*/} $_procname"
+		else
+			_interp="$* $_procname"
+		fi
 		if [ $_interpreter != $1 ]; then
 			warn "\$command_interpreter $_interpreter != $1"
 		fi
-		_interp="$* $_procname"		# cleanup spaces, add _procname
+		_interpbn=${1##*/}
 		_fp_args='_argv'
 		_fp_match='case "$_argv" in
-		    ${_interp}|"${_interp} "*)'
+		    ${_interp}|"${_interp} "*|"${_interpbn}: "*${_procnamebn}*)'
 	else					# a normal daemon
-		_procnamebn=${_procname##*/}
 		_fp_args='_arg0 _argv'
 		_fp_match='case "$_arg0" in
-		    $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})"|"[${_procnamebn}]")'
+		    $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})")'
 	fi
 
 	_proccheck='
-		${_RCCMD_ps} ${_RCARG_psformat} '"$_psargs"' |
+		ps -o "pid,args" '"$_psargs"' |
 		while read _npid '"$_fp_args"'; do
 			case "$_npid" in
 			    PID)
 				continue ;;
 			esac ; '"$_fp_match"'
-				${_RCCMD_echo} -n "$_pref$_npid" ;
+				echo -n "$_pref$_npid" ;
 				_pref=" "
 				;;
 			esac
 		done'
 
+#echo 1>&2 "proccheck is :$_proccheck:"
 	eval $_proccheck
 }
 
@@ -332,35 +324,39 @@
 	while true; do
 		_nlist="";
 		for _j in $_list; do
-			if ${_RCCMD_kill} -0 $_j 2>/dev/null; then
+			if kill -0 $_j 2>/dev/null; then
 				_nlist="${_nlist}${_nlist:+ }$_j"
 			fi
 		done
 		if [ -z "$_nlist" ]; then
 			break
 		fi
-		_list=$_nlist
-		${_RCCMD_echo} -n ${_prefix:-"Waiting for PIDS: "}$_list
-		_prefix=", "
-		sleep 2
+		if [ "$_list" != "$_nlist" ]; then
+			_list=$_nlist
+			echo -n ${_prefix:-"Waiting for PIDS: "}$_list
+			_prefix=", "
+		fi
+		# We want this to be a tight loop for a fast exit
+		sleep 0.05
 	done
 	if [ -n "$_prefix" ]; then
-		${_RCCMD_echo} "."
+		echo "."
 	fi
 }
 
 #
-# run_rc_command argument
+# run_rc_command argument [parameters]
 #	Search for argument in the list of supported commands, which is:
 #		"start stop restart rcvar status poll ${extra_commands}"
 #	If there's a match, run ${argument}_cmd or the default method
-#	(see below).
+#	(see below), and pass the optional list of parameters to it.
 #
 #	If argument has a given prefix, then change the operation as follows:
 #		Prefix	Operation
 #		------	---------
 #		fast	Skip the pid check, and set rc_fast=yes
 #		force	Set ${rcvar} to YES, and set rc_force=yes
+#		one	Set ${rcvar} to YES
 #
 #	The following globals are used:
 #
@@ -398,6 +394,9 @@
 #				NOTE:	$flags from the parent environment
 #					can be used to override this.
 #
+#	${name}_env	n	Additional environment variable settings
+#				for running ${command}
+#
 #	${name}_nice	n	Nice level to run ${command} at.
 #
 #	${name}_user	n	User to run ${command} as, using su(1) if not
@@ -411,13 +410,6 @@
 #				to run the chrooted ${command} with.
 #				Requires /usr to be mounted.
 #
-#	${name}_systrace n	Flags passed to systrace(1) if it is used.
-#				Setting this variable enables systracing
-# 				of the given program.  The use of "-a" is
-#				recommended so that the boot process is not
-#				stalled.  In order to pass no flags to
-#				systrace, set this variable to "--".
-#
 #	${rc_arg}_cmd	n	If set, use this as the method when invoked;
 #				Otherwise, use default command (see below)
 #
@@ -477,7 +469,7 @@
 #
 #	Variable	Purpose
 #	--------	-------
-#	rc_arg		Argument to command, after fast/force processing
+#	rc_arg		Argument to command, after fast/force/one processing
 #			performed
 #
 #	rc_flags	Flags to start the default command with.
@@ -499,21 +491,33 @@
 		err 3 'run_rc_command: $name is not set.'
 	fi
 
+	_rc_prefix=
 	case "$rc_arg" in
 	fast*)				# "fast" prefix; don't check pid
 		rc_arg=${rc_arg#fast}
 		rc_fast=yes
 		;;
-	force*)				# "force prefix; always start
-		rc_arg=${rc_arg#force}
+	force*)				# "force" prefix; always run
 		rc_force=yes
+		_rc_prefix=force
+		rc_arg=${rc_arg#${_rc_prefix}}
+		if [ -n "${rcvar}" ]; then
+			eval ${rcvar}=YES
+		fi
+		;;
+	one*)				# "one" prefix; set ${rcvar}=yes
+		_rc_prefix=one
+		rc_arg=${rc_arg#${_rc_prefix}}
 		if [ -n "${rcvar}" ]; then
 			eval ${rcvar}=YES
 		fi
 		;;
 	esac
 
-	_keywords="start stop restart rcvar $extra_commands"
+	_keywords="start stop restart rcvar"
+	if [ -n "$extra_commands" ]; then
+		_keywords="${_keywords} ${extra_commands}"
+	fi
 	rc_pid=
 	_pidcmd=
 	_procname=${procname:-${command}}
@@ -533,6 +537,7 @@
 	if [ -z "$rc_arg" ]; then
 		rc_usage "$_keywords"
 	fi
+	shift	# remove $rc_arg from the positional parameters
 
 	if [ -n "$flags" ]; then	# allow override from environment
 		rc_flags=$flags
@@ -542,10 +547,10 @@
 	eval _chdir=\$${name}_chdir	_chroot=\$${name}_chroot \
 	    _nice=\$${name}_nice	_user=\$${name}_user \
 	    _group=\$${name}_group	_groups=\$${name}_groups \
-	    _systrace=\$${name}_systrace
+	    _env=\"\$${name}_env\"
 
 	if [ -n "$_user" ]; then	# unset $_user if running as that user
-		if [ "$_user" = "$($_RCCMD_whoami)" ]; then
+		if [ "$_user" = "$(id -un)" ]; then
 			unset _user
 		fi
 	fi
@@ -553,11 +558,27 @@
 					# if ${rcvar} is set, and $1 is not
 					# "rcvar", then run
 					#	checkyesno ${rcvar}
-					# and return if that failed
+					# and return if that failed or warn
+					# user and exit when interactive
 					#
 	if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" ]; then
 		if ! checkyesno ${rcvar}; then
-			return 0
+					# check whether interactive or not
+			if [ -n "$_run_rc_script" ]; then
+				return 0
+			fi
+			for _elem in $_keywords; do
+				if [ "$_elem" = "$rc_arg" ]; then
+					cat 1>&2 <<EOF
+\$${rcvar} is not enabled - see ${rcvar_manpage}.
+Use the following if you wish to perform the operation:
+  $0 one${rc_arg}
+EOF
+					exit 1
+				fi
+			done
+			echo 1>&2 "$0: unknown directive '$rc_arg'."
+			rc_usage "$_keywords"
 		fi
 	fi
 
@@ -581,29 +602,31 @@
 				return 1
 			fi
 
-			if ! eval $_cmd && [ -z "$rc_force" ]; then
+			if ! eval $_cmd \"\${@}\" && [ -z "$rc_force" ]; then
 				return 1
 			fi
 			eval $_postcmd
 			return 0
 		fi
 
+		if [ ${#} -gt 0 ]; then
+			err 1 "the $rc_arg command does not take any parameters"
+		fi
+
 		case "$rc_arg" in	# default operations...
 
 		status)
 			if [ -n "$rc_pid" ]; then
-				${_RCCMD_echo} \
-				    "${name} is running as pid $rc_pid."
+				echo "${name} is running as pid $rc_pid."
 			else
-				${_RCCMD_echo} "${name} is not running."
+				echo "${name} is not running."
 				return 1
 			fi
 			;;
 
 		start)
 			if [ -n "$rc_pid" ]; then
-				${_RCCMD_echo} \
-				    "${name} already running? (pid=$rc_pid)."
+				echo 1>&2 "${name} already running? (pid=$rc_pid)."
 				exit 1
 			fi
 
@@ -616,7 +639,7 @@
 					#
 			for _f in $required_vars; do
 				if ! checkyesno $_f; then
-					warn "\$${_f} is not set."
+					warn "\$${_f} is not enabled."
 					if [ -z "$rc_force" ]; then
 						return 1
 					fi
@@ -648,35 +671,21 @@
 
 					# setup the command to run, and run it
 					#
-			${_RCCMD_echo} "Starting ${name}."
+			echo "Starting ${name}."
 			if [ -n "$_chroot" ]; then
-				if [ "$_osname" = "SunOS" ]; then
-					_doit="\
-${_nice:+${_RCCMD_nice} -n $_nice }\
-${_RCCMD_chroot} $_chroot"
-					if [ -n "$_user" ]; then
-						_doit="\
-$_doit ${_RCCMD_su} ${_RCARG_su} $_user \
--c '${_RCCMD_sh} -c'"
-					fi
-					_doit="\
-$_doit $command $rc_flags $command_args"
-				else
-					_doit="\
-${_nice:+${_RCCMD_nice} -n $_nice }\
-${_systrace:+${_RCCMD_systrace} $_systrace }\
-${_RCCMD_chroot} ${_user:+-u $_user }${_group:+-g $_group }\
-${_groups:+-G $_groups } $_chroot $command $rc_flags $command_args"
-				fi
+				_doit="\
+$_env_clear_rc_vars $_env \
+${_nice:+nice -n $_nice }\
+chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\
+$_chroot $command $rc_flags $command_args"
 			else
 				_doit="\
 ${_chdir:+cd $_chdir; }\
-${_nice:+${_RCCMD_nice} -n $_nice }\
-${_systrace:+${_RCCMD_systrace} $_systrace }\
+$_env_clear_rc_vars $_env \
+${_nice:+nice -n $_nice }\
 $command $rc_flags $command_args"
 				if [ -n "$_user" ]; then
-				    _doit="${_RCCMD_su} ${_RCARG_su} $_user \
-					-c '${_RCCMD_sh} -c \"$_doit\"'"
+				    _doit="su -m $_user -c 'sh -c \"$_doit\"'"
 				fi
 			fi
 
@@ -695,10 +704,10 @@
 		stop)
 			if [ -z "$rc_pid" ]; then
 				if [ -n "$pidfile" ]; then
-					${_RCCMD_echo} \
+					echo 1>&2 \
 				    "${name} not running? (check $pidfile)."
 				else
-					${_RCCMD_echo} "${name} not running?"
+					echo 1>&2 "${name} not running?"
 				fi
 				exit 1
 			fi
@@ -712,11 +721,10 @@
 
 					# send the signal to stop
 					#
-			${_RCCMD_echo} "Stopping ${name}."
-			_doit="${_RCCMD_kill} -${sig_stop:-TERM} $rc_pid"
+			echo "Stopping ${name}."
+			_doit="kill -${sig_stop:-TERM} $rc_pid"
 			if [ -n "$_user" ]; then
-				_doit="${_RCCMD_su} ${_RCARG_su} $_user \
-				    -c '${_RCCMD_sh} -c \"$_doit\"'"
+				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
 			fi
 
 					# if the stop cmd failed and force
@@ -735,20 +743,20 @@
 		reload)
 			if [ -z "$rc_pid" ]; then
 				if [ -n "$pidfile" ]; then
-					${_RCCMD_echo} \
+					echo 1>&2 \
 				    "${name} not running? (check $pidfile)."
 				else
-					${_RCCMD_echo} "${name} not running?"
+					echo 1>&2 "${name} not running?"
 				fi
 				exit 1
 			fi
-			${_RCCMD_echo} "Reloading ${name} config files."
+			echo "Reloading ${name} config files."
 			if ! eval $_precmd && [ -z "$rc_force" ]; then
 				return 1
 			fi
-			_doit="${_RCCMD_kill} -${sig_reload:-HUP} $rc_pid"
+			_doit="kill -${sig_reload:-HUP} $rc_pid"
 			if [ -n "$_user" ]; then
-				_doit="${_RCCMD_su} ${_RCARG_su} $_user -c '${_RCCMD_sh} -c \"$_doit\"'"
+				_doit="su -m $_user -c 'sh -c \"$_doit\"'"
 			fi
 			if ! eval $_doit && [ -z "$rc_force" ]; then
 				return 1
@@ -768,8 +776,8 @@
 			fi
 			_rc_restart_done=true
 
-			( $0 ${rc_force:+force}stop )
-			$0 ${rc_force:+force}start
+			( $0 ${_rc_prefix}stop )
+			$0 ${_rc_prefix}start
 
 			eval $_postcmd
 			;;
@@ -781,12 +789,12 @@
 			;;
 
 		rcvar)
-			${_RCCMD_echo} "# $name"
+			echo "# $name"
 			if [ -n "$rcvar" ]; then
 				if checkyesno ${rcvar}; then
-					${_RCCMD_echo} "\$${rcvar}=YES"
+					echo "\$${rcvar}=YES"
 				else
-					${_RCCMD_echo} "\$${rcvar}=NO"
+					echo "\$${rcvar}=NO"
 				fi
 			fi
 			;;
@@ -799,12 +807,31 @@
 		return 0
 	done
 
-	${_RCCMD_echo} 1>&2 "$0: unknown directive '$rc_arg'."
+	echo 1>&2 "$0: unknown directive '$rc_arg'."
 	rc_usage "$_keywords"
 	exit 1
 }
 
 #
+# _have_rc_postprocessor
+#	Test whether the current script is running in a context that
+#	was invoked from /etc/rc with a postprocessor.
+#
+#	If the test fails, some variables may be unset to make
+#	such tests more efficient in future.
+#
+_have_rc_postprocessor()
+{
+	# Cheap tests that fd and pid are set, fd is writable.
+	[ -n "${_rc_pid}" ] || { unset _rc_pid; return 1; }
+	[ -n "${_rc_postprocessor_fd}" ] || { unset _rc_pid; return 1; }
+	eval ": >&${_rc_postprocessor_fd}" 2>/dev/null \
+	|| { unset _rc_pid; return 1; }
+
+	return 0
+}
+
+#
 # run_rc_script file arg
 #	Start the script `file' with `arg', and correctly handle the
 #	return value from the script.  If `file' ends with `.sh', it's
@@ -812,6 +839,12 @@
 #	a backup or scratch file, ignore it.  Otherwise if it's
 #	executable run as a child process.
 #
+#	If `file' contains "KEYWORD: interactive" and if we are
+#	running inside /etc/rc with postprocessing, then the script's
+#	stdout and stderr are redirected to $_rc_original_stdout_fd and
+#	$_rc_original_stderr_fd, so the output will be displayed on the
+#	console but not intercepted by /etc/rc's postprocessor.
+#
 run_rc_script()
 {
 	_file=$1
@@ -820,32 +853,60 @@
 		err 3 'USAGE: run_rc_script file arg'
 	fi
 
+	_run_rc_script=true
+
 	unset	name command command_args command_interpreter \
 		extra_commands pidfile procname \
 		rcvar required_dirs required_files required_vars
 	eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd
 
+	_must_redirect=false
+	if _have_rc_postprocessor \
+	    && _has_rcorder_keyword interactive $_file
+	then
+		_must_redirect=true
+	fi
+
 	case "$_file" in
 	*.sh)				# run in current shell
-		set $_arg ; . $_file
+		if $_must_redirect; then
+			print_rc_metadata \
+			    "note:Output from ${_file} is not logged"
+			no_rc_postprocess eval \
+			    'set $_arg ; . $_file'
+		else
+			set $_arg ; . $_file
+		fi
 		;;
-	*[~#]|*.OLD|*.orig)		# scratch file; skip
+	*[~#]|*.OLD|*.orig|*,v)		# scratch file; skip
 		warn "Ignoring scratch file $_file"
 		;;
 	*)				# run in subshell
-		if [ -x $_file ]; then
+		if [ -x $_file ] && $_must_redirect; then
+			print_rc_metadata \
+			    "note:Output from ${_file} is not logged"
+			if [ -n "$rc_fast_and_loose" ]; then
+				no_rc_postprocess eval \
+				    'set $_arg ; . $_file'
+			else
+				no_rc_postprocess eval \
+				    '( set $_arg ; . $_file )'
+			fi
+		elif [ -x $_file ]; then
 			if [ -n "$rc_fast_and_loose" ]; then
 				set $_arg ; . $_file
 			else
 				( set $_arg ; . $_file )
 			fi
+		else
+			warn "Ignoring non-executable file $_file"
 		fi
 		;;
 	esac
 }
 
 #
-# load_rc_config
+# load_rc_config command
 #	Source in the configuration file for a given command.
 #
 load_rc_config()
@@ -858,14 +919,11 @@
 	if ${_rc_conf_loaded:-false}; then
 		:
 	else
-		. ${sysconfbase}/rc.conf
+		. /etc/rc.conf
 		_rc_conf_loaded=true
 	fi
-	if [ "$_osname" = "OpenBSD" ] && [ -f ${sysconfbase}/rc.conf.local ]; then
-		. ${sysconfbase}/rc.conf.local
-	fi
-	if [ -f ${sysconfbase}/rc.conf.d/"$_command" ]; then
-		. ${sysconfbase}/rc.conf.d/"$_command"
+	if [ -f /etc/rc.conf.d/"$_command" ]; then
+		. /etc/rc.conf.d/"$_command"
 	fi
 }
 
@@ -882,13 +940,12 @@
 	fi
 	eval $(eval '(
 		load_rc_config '$1' >/dev/null;
-		if [ -n "${'$2'}" ] || [ "${'$2'-UNSET}" != "UNSET" ]; then
+		if [ -n "${'$2'}" -o "${'$2'-UNSET}" != "UNSET" ]; then
 			echo '$2'=\'\''${'$2'}\'\'';
 		fi
 	)' )
 }
 
-
 #
 # rc_usage commands
 #	Print a usage string for $0, with `commands' being a list of
@@ -896,14 +953,14 @@
 #
 rc_usage()
 {
-	${_RCCMD_echo} -n 1>&2 "Usage: $0 [fast|force]("
+	echo -n 1>&2 "Usage: $0 [fast|force|one]("
 
 	_sep=
 	for _elem; do
-		${_RCCMD_echo} -n 1>&2 "$_sep$_elem"
+		echo -n 1>&2 "$_sep$_elem"
 		_sep="|"
 	done
-	${_RCCMD_echo} 1>&2 ")"
+	echo 1>&2 ")"
 	exit 1
 }
 
@@ -916,10 +973,10 @@
 	exitval=$1
 	shift
 
-	if [ -x ${_RCCMD_logger} ]; then
-		${_RCCMD_logger} "$0: ERROR: $*"
+	if [ -x /usr/bin/logger ]; then
+		logger "$0: ERROR: $*"
 	fi
-	${_RCCMD_echo} 1>&2 "$0: ERROR: $*"
+	echo 1>&2 "$0: ERROR: $*"
 	exit $exitval
 }
 
@@ -929,10 +986,10 @@
 #
 warn()
 {
-	if [ -x ${_RCCMD_logger} ]; then
-		${_RCCMD_logger} "$0: WARNING: $*"
+	if [ -x /usr/bin/logger ]; then
+		logger "$0: WARNING: $*"
 	fi
-	${_RCCMD_echo} 1>&2 "$0: WARNING: $*"
+	echo 1>&2 "$0: WARNING: $*"
 }
 
 #
@@ -975,7 +1032,7 @@
 
 		# ensure that history file is not locked
 		if [ -f $_cur,v ]; then
-			${_RCCMD_rcs} -q -u -U -M $_cur
+			rcs -q -u -U -M $_cur
 		fi
 
 		# ensure after switching to rcs that the
@@ -983,41 +1040,333 @@
 		if [ -f $_cur ]; then
 			# no archive, or current newer than archive
 			if [ ! -f $_cur,v -o $_cur -nt $_cur,v ]; then
-				${_RCCMD_ci} -q -f -u -t-"$_msg0" \
-				    -m"$_msg1" $_cur
-				${_RCCMD_rcs} -q -kb -U $_cur
-				${_RCCMD_co} -q -f -u $_cur
+				ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
+				rcs -q -kb -U $_cur
+				co -q -f -u $_cur
 			fi
 		fi
 
 		case $_action in
 		add|update)
-			${_RCCMD_cp} -p $_file $_cur
-			${_RCCMD_ci} -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
-			${_RCCMD_rcs} -q -kb -U $_cur
-			${_RCCMD_co} -q -f -u $_cur
-			${_RCCMD_chown} root:wheel $_cur $_cur,v
+			cp -p $_file $_cur
+			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
+			rcs -q -kb -U $_cur
+			co -q -f -u $_cur
+			chown root:wheel $_cur $_cur,v
 			;;
 		remove)
-			${_RCCMD_cp} /dev/null $_cur
-			${_RCCMD_ci} -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
-			${_RCCMD_rcs} -q -kb -U $_cur
-			${_RCCMD_chown} root:wheel $_cur $_cur,v
-			${_RCCMD_rm} $_cur
+			cp /dev/null $_cur
+			ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur
+			rcs -q -kb -U $_cur
+			chown root:wheel $_cur $_cur,v
+			rm $_cur
 			;;
 		esac
 	else
 		case $_action in
 		add|update)
 			if [ -f $_cur ]; then
-				${_RCCMD_cp} -p $_cur $_back
+				cp -p $_cur $_back
 			fi
-			${_RCCMD_cp} -p $_file $_cur
-			${_RCCMD_chown} root:wheel $_cur
+			cp -p $_file $_cur
+			chown root:wheel $_cur
 			;;
 		remove)
-			${_RCCMD_mv} -f $_cur $_back
+			mv -f $_cur $_back
 			;;
 		esac
 	fi
 }
+
+#
+# handle_fsck_error fsck_exit_code
+#	Take action depending on the return code from fsck.
+#
+handle_fsck_error()
+{
+	case $1 in
+	0)	# OK
+		return
+		;;
+	2)	# Needs re-run, still fs errors
+		echo "File system still has errors; re-run fsck manually!"
+		;;
+	4)	# Root modified
+		echo "Root file system was modified, rebooting ..."
+		reboot -n
+		echo "Reboot failed; help!"
+		;;
+	8)	# Check failed
+		echo "Automatic file system check failed; help!"
+		;;
+	12)	# Got signal
+		echo "Boot interrupted."
+		;;
+	*)
+		echo "Unknown error $1; help!"
+		;;
+	esac
+	stop_boot
+}
+
+#
+# _has_rcorder_keyword word file
+#	Check whether a file contains a "# KEYWORD:" comment with a
+#	specified keyword in the style used by rcorder(8).
+#
+_has_rcorder_keyword()
+{
+	local word="$1"
+	local file="$2"
+	local line
+
+	[ -r "$file" ] || return 1
+	while read line; do
+		case "${line} " in
+		"# KEYWORD:"*[\ \	]"${word}"[\ \	]*)
+			return 0
+			;;
+		"#"*)
+			continue
+			;;
+		*[A-Za-z0-9]*)
+			# give up at the first non-empty non-comment line
+			return 1
+			;;
+		esac
+	done <"$file"
+	return 1
+}
+
+#
+# print_rc_metadata string
+#	Print the specified string in such a way that the post-processor
+#	inside /etc/rc will treat it as meta-data.
+#
+#	If we are not running inside /etc/rc, do nothing.
+#
+#	For public use by any rc.d script, the string must begin with
+#	"note:", followed by arbitrary text.  The intent is that the text
+#	will appear in a log file but not on the console.
+#
+#	For private use within /etc/rc, the string must contain a
+#	keyword recognised by the rc_postprocess_metadata() function
+#	defined in /etc/rc, followed by a colon, followed by one or more
+#	colon-separated arguments associated with the keyword.
+#
+print_rc_metadata()
+{
+	# _rc_postprocessor fd, if defined, is the fd to which we must
+	# print, prefixing the output with $_rc_metadata_prefix.
+	#
+	if _have_rc_postprocessor; then
+		command printf "%s%s\n" "$rc_metadata_prefix" "$1" \
+			>&${_rc_postprocessor_fd}
+	fi
+}
+
+#
+# _flush_rc_output
+#	Arrange for output to be flushed, if we are running
+#	inside /etc/rc with postprocessing.
+#
+_flush_rc_output()
+{
+	print_rc_metadata "nop"
+}
+
+#
+# print_rc_normal [-n] string
+#	Print the specified string in such way that it is treated as
+#	normal output, regardless of whether or not we are running
+#	inside /etc/rc with post-processing.
+#
+#	If "-n" is specified in $1, then the string in $2 is printed
+#	without a newline; otherwise, the string in $1 is printed
+#	with a newline.
+#
+#	Intended use cases include:
+#
+#	o   An rc.d script can use ``print_rc_normal -n'' to print a
+#	    partial line in such a way that it appears immediately
+#	    instead of being buffered by rc(8)'s post-processor.
+#
+#	o   An rc.d script that is run via the no_rc_postprocess
+#	    function (so most of its output is invisible to rc(8)'s
+#	    post-processor) can use print_rc_normal to force some of its
+#	    output to be seen by the post-processor.
+#
+#
+print_rc_normal()
+{
+	# print to stdout or _rc_postprocessor_fd, depending on
+	# whether not we have an rc postprocessor.
+	#
+	local fd=1
+	_have_rc_postprocessor && fd="${_rc_postprocessor_fd}"
+	case "$1" in
+	"-n")
+		command printf "%s" "$2" >&${fd}
+		_flush_rc_output
+		;;
+	*)
+		command printf "%s\n" "$1" >&${fd}
+		;;
+	esac
+}
+
+#
+# no_rc_postprocess cmd...
+#	Execute the specified command in such a way that its output
+#	bypasses the post-processor that handles the output from
+#	most commands that are run inside /etc/rc.  If we are not
+#	inside /etc/rc, then just execute the command without special
+#	treatment.
+#
+#	The intent is that interactive commands can be run via
+#	no_rc_postprocess(), and their output will apear immediately
+#	on the console instead of being hidden or delayed by the
+#	post-processor.	 An unfortunate consequence of the output
+#	bypassing the post-processor is that the output will not be
+#	logged.
+#
+no_rc_postprocess()
+{
+	if _have_rc_postprocessor; then
+		"$@" >&${_rc_original_stdout_fd} 2>&${_rc_original_stderr_fd}
+	else
+		"$@"
+	fi
+}
+
+#
+# twiddle
+#	On each call, print a different one of "/", "-", "\\", "|",
+#	followed by a backspace.  The most recently printed value is
+#	saved in $_twiddle_state.
+#
+#	Output is to /dev/tty, so this function may be useful even inside
+#	a script whose output is redirected.
+#
+twiddle()
+{
+	case "$_twiddle_state" in
+	'/')	_next='-' ;;
+	'-')	_next='\' ;;
+	'\')	_next='|' ;;
+	*)	_next='/' ;;
+	esac
+	command printf "%s\b" "$_next" >/dev/tty
+	_twiddle_state="$_next"
+}
+
+#
+# human_exit_code
+#	Print the a human version of the exit code.
+#
+human_exit_code()
+{
+	if [ "$1" -lt 127 ]
+	then
+		echo "exited with code $1"
+	elif [ "$(expr $1 % 256)" -eq 127 ]
+	then
+		# This cannot really happen because the shell will not
+		# pass stopped job status out and the exit code is limited
+		# to 8 bits. This code is here just for completeness.
+		echo "stopped with signal $(expr $1 / 256)"
+	else
+		echo "terminated with signal $(expr $1 - 128)"
+	fi
+}
+
+#
+# collapse_backslash_newline
+#	Copy input to output, collapsing <backslash><newline>
+#	to nothing, but leaving other backslashes alone.
+#
+collapse_backslash_newline()
+{
+	local line
+	while read -r line ; do
+		case "$line" in
+		*\\)
+			# print it, without the backslash or newline
+			command printf "%s" "${line%?}"
+			;;
+		*)
+			# print it, with a newline
+			command printf "%s\n" "${line}"
+			;;
+		esac
+	done
+}
+
+# Shell implementations of basename and dirname, usable before
+# the /usr file system is mounted.
+#
+basename()
+{
+	local file="$1"
+	local suffix="$2"
+	local base
+
+	base="${file##*/}"		# remove up to and including last '/'
+	base="${base%${suffix}}"	# remove suffix, if any
+	command printf "%s\n" "${base}"
+}
+
+dirname()
+{
+	local file="$1"
+	local dir
+
+	case "$file" in
+	/*/*)	dir="${file%/*}" ;;	# common case: absolute path
+	/*)	dir="/" ;;		# special case: name in root dir
+	*/*)	dir="${file%/*}" ;;	# common case: relative path with '/'
+	*)	dir="." ;;		# special case: name without '/'
+	esac
+	command printf "%s\n" "${dir}"
+}
+
+# Override the normal "echo" and "printf" commands, so that
+# partial lines printed by rc.d scripts appear immediately,
+# instead of being buffered by rc(8)'s post-processor.
+#
+# Naive use of the echo or printf commands from rc.d scripts,
+# elsewhere in rc.subr, or anything else that sources rc.subr,
+# will call these functions.  To call the real echo and printf
+# commands, use "command echo" or "command printf".
+#
+echo()
+{
+	command echo "$@"
+	case "$1" in
+	'-n')	_flush_rc_output ;;
+	esac
+}
+printf()
+{
+	command printf "$@"
+	case "$1" in
+	*'\n')	: ;;
+	*)	_flush_rc_output ;;
+	esac
+}
+
+kat() {
+	local i
+	local v
+	for i; do
+		while read -r v; do
+			v="${v%%#*}"
+			if [ -z "$v" ]; then
+				continue
+			fi
+			echo "$v"
+		done < "$i"
+	done
+}
+
+_rc_subr_loaded=:
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.