Subject: bin/27393: Logging capability for rc and rc.shutdown
To: None <gnats-bugs@gnats.NetBSD.org>
From: None <mishka@apk.od.ua>
List: netbsd-bugs
Date: 10/23/2004 18:37:16
>Number:         27393
>Category:       bin
>Synopsis:       Logging capability for rc and rc.shutdown
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Oct 23 15:38:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Mike M. Volokhov
>Release:        NetBSD 2.99.x, others too
>Organization:
Insakharprom-K, Ltd.
>Environment:
	Boot and shutdown framework on NetBSD
>Description:

	Greetings!

	It sometimes very covenient to see how your NetBSD boots
	and how it halts. Unfortunately, if hardware have no monitor
	installed or by any other reason you are missing something
	in boot/shutdown messages, it is not possible to revert
	logs back and detaily analyse all contained info.

	Of course, your output may be directed to serial console
	with logging capabilities but often it not done in such
	way (or at least too hard to implement).

>How-To-Repeat:

	Reboot the system and watch all output. If messages directed
	to device without logging capabilities (such as plain CRT
	monitor) information will be quickly pushed by new data.
	Login to system and see you have not any boot/shutdown
	output stored anywhere.

>Fix:

	Having discussed at tech-userlevel@ this issue (see topic
	"Re: rc.d boot log [was: Re: Cosmetic changes to rc.d
	scripts]") I had reworked my latest patch with fault
	tolerance in mind and suppose the following way:

	1) Logging available if rc_log_output is set to YES;
	otherwise rc works just like before a patch.

	2) By default we use tee(1). But if log inavailable (for
	example, on read-only mounted filesystem) as well as tee
	itself, we falling back to buffering (in shell variables)
	all output until log may be written back to disk.

	3) We will write new log to disk in case only if previous
	one has been backed up (rotated).

	4) Logs are rotated once per /etc/rc or /etc/rc.shutdown
	execution, and stored to /var/log/{boot,shutdown}.{log,prev}
	files.

	5) Buffers are flushed every time after logs was wrote
	(irrelevant on what way was - echo or tee).  If buffers
	still contain any data after very last rclog() call, this
	really means that logfile is no more available.  In this
	case we try to save log in /etc/rclog.tmp file.

	6) Output from the verbose commands will be delayed for a
	one line only, but no more.

	Please see a patch below to fix this issue. It diffed
	against rc, rc.shutdown, and rc.subr files. Thus, to patch
	your system use the following command:

		cd /etc && patch < /path/to/saved/rclog.patch

	Don't forget to set rc_log_output variable in your /etc/rc.conf
	file.

	--
	Best regards,
	Mishka.


--- /build/netbsd/src-current/etc/rc	2003-01-08 19:46:19.000000000 +0200
+++ rc	2004-10-21 09:47:26.000000000 +0300
@@ -17,6 +17,8 @@
 . /etc/rc.conf
 _rc_conf_loaded=true
 
+_rc_state=boot
+
 if ! checkyesno rc_configured; then
 	echo "/etc/rc.conf is not configured.  Multiuser boot aborted."
 	exit 1
@@ -35,13 +37,19 @@
 trap : INT
 trap "echo 'Boot interrupted.'; exit 1" QUIT
 
-date
+rclog "echo Starting up at `date`"
 
 files=$(rcorder -s nostart ${rc_rcorder_flags} /etc/rc.d/*)
 
 for _rc_elem in $files; do
-	run_rc_script $_rc_elem start
+	rclog "run_rc_script $_rc_elem start"
 done
 
-date
+rclog "echo System was started at `date`"
+
+if checkyesno rc_log_output && [ -n "$_rclogbuf" ]; then
+	warn "stale log found in buffer; saving to /etc/rclog.tmp"
+	echo "$_rclogbuf" >> /etc/rclog.tmp
+fi
+
 exit 0
--- /build/netbsd/src-current/etc/rc.shutdown	2002-06-10 02:38:05.000000000 +0300
+++ rc.shutdown	2004-10-19 17:06:04.000000000 +0300
@@ -14,6 +14,8 @@
 . /etc/rc.subr
 . /etc/rc.conf
 
+_rc_state=shutdown
+
 if ! checkyesno do_rcshutdown; then
 	echo "Skipping shutdown hooks."
 	exit 0
@@ -43,6 +45,7 @@
 	_rcshutdown_watchdog=$!
 fi
 
+rclog "echo System going down at `date`"
 
 #	Determine the shutdown order of the /etc/rc.d scripts,
 #	and perform the operation
@@ -50,7 +53,7 @@
 files=$(rcorder -k shutdown ${rcshutdown_rcorder_flags} /etc/rc.d/*)
 
 for _rc_elem in $(reverse_list $files); do
-	run_rc_script $_rc_elem stop
+	rclog "run_rc_script $_rc_elem stop"
 done
 
 
@@ -60,5 +63,11 @@
 	kill -TERM $_rcshutdown_watchdog >/dev/null 2>&1
 fi
 
-date
+rclog "echo Running shutdown scripts finished at `date`"
+
+if checkyesno rc_log_output && [ -n "$_rclogbuf" ]; then
+	warn "stale log found in buffer; saving to /etc/rclog.tmp"
+	echo "$_rclogbuf" >> /etc/rclog.tmp
+fi
+
 exit 0
--- /build/netbsd/src-current/etc/rc.subr	2004-10-13 12:50:44.000000000 +0300
+++ rc.subr	2004-10-21 12:24:04.000000000 +0300
@@ -819,6 +819,70 @@
 }
 
 #
+# rclog command
+#	Run "command" and append log all output to $_rclogfile. If it cannot
+#	be saved by any reason, accumulate log in _rclogbuf variable.
+#
+rclog()
+{
+	_rclogcmd=$1
+	if checkyesno rc_log_output && [ -n "${_rc_state}" ]; then
+		: ${_rclogfile:=/var/log/${_rc_state}.log}
+		: ${_rclogfile_prev:=/var/log/${_rc_state}.prev}
+
+		if [ -z "$_rclog_rotated" -a -d /var/log ]; then
+			if [ -f ${_rclogfile} ]; then
+				mv -f $_rclogfile $_rclogfile_prev 2>/dev/null
+			fi
+			if [ ! -f ${_rclogfile} ] && \
+				echo "Log file turned over at `date`" \
+					> $_rclogfile 2>/dev/null; then
+				echo "The rc $_rc_state log was rotated."
+				_rclog_rotated=$_rc_state
+			fi
+		fi
+
+		if [ -f ${_rclogfile} -a -n "$_rclog_rotated" -a \
+				-x /usr/bin/tee -a -x /usr/bin/touch ] && \
+				/usr/bin/touch $_rclogfile 2>/dev/null; then
+			if [ -n "$_rclogbuf" ]; then
+				echo -n "$_rclogbuf" >> ${_rclogfile}
+				_rclogbuf=
+			fi
+			${_rclogcmd} 2>&1 | /usr/bin/tee -a ${_rclogfile}
+		else
+			_rclogcmdbuf=$( ${_rclogcmd} 2>&1 | _rctee_output )
+			if [ -n "$_rclogcmdbuf" ]; then
+				_rclogcmdbuf="$_rclogcmdbuf
+"
+				_rclogbuf="${_rclogbuf}${_rclogcmdbuf}"
+				_rclogcmdbuf=
+			fi
+			if [ -n "$_rclogbuf" -a -f ${_rclogfile} -a \
+					-n "$_rclog_rotated" ] && \
+					echo -n "$_rclogbuf" >> $_rclogfile \
+						2>/dev/null; then
+				_rclogbuf=
+			fi
+		fi
+	else
+		$_rclogcmd
+	fi
+}
+
+#
+# _rctee_output
+#	Take stdin line by line and send it to stdout and stderr
+#
+_rctee_output()
+{
+	while read _rctee_buffer; do
+		echo "$_rctee_buffer"
+		echo "$_rctee_buffer" 1>&2
+	done
+}
+
+#
 # backup_file action file cur backup
 #	Make a backup copy of `file' into `cur', and save the previous
 #	version of `cur' as `backup' or use rcs for archiving.

>Release-Note:
>Audit-Trail:
>Unformatted: