Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src Add a postprocessor to /etc/rc, which logs messages to /var/...



details:   https://anonhg.NetBSD.org/src/rev/e3dec08bb5df
branches:  trunk
changeset: 747331:e3dec08bb5df
user:      apb <apb%NetBSD.org@localhost>
date:      Fri Sep 11 18:17:04 2009 +0000

description:
Add a postprocessor to /etc/rc, which logs messages to /var/run/rc.log,
and which can suppress output in silent mode.  Silent mode is enabled
via the new rc_silent variable, which defaults to a value that depends
on the kern.boothowto sysctl.

Part of the /etc/rc silent changes requested in PR 41946
and proposed in tech-userlevel.

diffstat:

 doc/CHANGES              |    5 +-
 etc/defaults/rc.conf     |   13 +-
 etc/rc                   |  382 ++++++++++++++++++++++++++++++++++++++++++++--
 etc/rc.subr              |  175 +++++++++++++++++++++-
 share/man/man5/rc.conf.5 |   30 +++-
 share/man/man8/rc.8      |   32 +++-
 share/man/man8/rc.subr.8 |  108 +++++++++++++-
 7 files changed, 719 insertions(+), 26 deletions(-)

diffs (truncated from 948 to 300 lines):

diff -r a27194d60069 -r e3dec08bb5df doc/CHANGES
--- a/doc/CHANGES       Fri Sep 11 18:14:58 2009 +0000
+++ b/doc/CHANGES       Fri Sep 11 18:17:04 2009 +0000
@@ -1,4 +1,4 @@
-# LIST OF CHANGES FROM LAST RELEASE:                   <$Revision: 1.1287 $>
+# LIST OF CHANGES FROM LAST RELEASE:                   <$Revision: 1.1288 $>
 #
 #
 # [Note: This file does not mention every change made to the NetBSD source tree.
@@ -389,3 +389,6 @@
        build.sh: Add a modules operation which builds kernel modules and
                installs them into DESTDIR. [jnemeth 20090907]
        sysctl(7): Add kern.boothowto variable.  [apb 20090911]
+       rc(8): Output is now logged to /var/run/rc.log.  A new rc_silent
+               option, enabled if the kernel is booted in silent mode,
+               suppresses output to the console.  [apb 20090911]
diff -r a27194d60069 -r e3dec08bb5df etc/defaults/rc.conf
--- a/etc/defaults/rc.conf      Fri Sep 11 18:14:58 2009 +0000
+++ b/etc/defaults/rc.conf      Fri Sep 11 18:17:04 2009 +0000
@@ -1,4 +1,4 @@
-#      $NetBSD: rc.conf,v 1.104 2009/07/25 16:20:10 mbalmer Exp $
+#      $NetBSD: rc.conf,v 1.105 2009/09/11 18:17:04 apb Exp $
 #
 # /etc/defaults/rc.conf --
 #      default configuration of /etc/rc.conf
@@ -26,6 +26,17 @@
 #
 #rc_fast_and_loose=YES
 
+# If rc_silent is true then /etc/rc will suppress most output to
+# the console.  The default is taken from the AB_SILENT flag passed
+# from the boot loader to the kernel in the boothowto(9) variable.
+#
+# rc_silent_cmd is executed once for each suppressed line of output.
+# Useful values are ":" and "twiddle".
+#
+rc_silent=$( [ "$(( $(/sbin/sysctl -n kern.boothowto 2>/dev/null || echo 0) \
+                   & 0x40000 ))" != 0 ] && echo true || echo false )
+rc_silent_cmd=twiddle
+
 # Additional flags to the rcorder(8) that's run by /etc/rc.
 #
 rc_rcorder_flags=""
diff -r a27194d60069 -r e3dec08bb5df etc/rc
--- a/etc/rc    Fri Sep 11 18:14:58 2009 +0000
+++ b/etc/rc    Fri Sep 11 18:17:04 2009 +0000
@@ -1,9 +1,10 @@
 #!/bin/sh
 #
-# $NetBSD: rc,v 1.163 2009/04/10 16:18:04 joerg Exp $
+# $NetBSD: rc,v 1.164 2009/09/11 18:17:04 apb Exp $
 #
 # rc --
-#      Run the scripts in /etc/rc.d with rcorder.
+#      Run the scripts in /etc/rc.d with rcorder, and log output
+#      to /var/run/rc.log.
 
 #      System startup script run by init(8) on autoboot or after single-user.
 #      Output and error are redirected to console by init, and the console
@@ -13,10 +14,16 @@
 export PATH=/sbin:/bin:/usr/sbin:/usr/bin
 umask 022
 
-. /etc/rc.subr
+if [ -e ./rc.subr ] ; then
+       . ./rc.subr # for testing
+else
+       . /etc/rc.subr
+fi
 . /etc/rc.conf
 _rc_conf_loaded=true
 
+: ${RC_LOG_FILE:="/var/run/rc.log"}
+
 if ! checkyesno rc_configured; then
        echo "/etc/rc.conf is not configured.  Multiuser boot aborted."
        exit 1
@@ -27,24 +34,363 @@
        rc_fast=yes     # run_rc_command(): do fast booting
 fi
 
-stty status '^T'
+#
+# Completely ignore INT and QUIT at the outer level.  The rc_real_work()
+# function should do something different.
+#
+trap '' INT QUIT
+
+#
+# This string will be used to mark lines of meta-data sent over the pipe
+# from the rc_real_work() function to the rc_postprocess() function.  Lines
+# not so marked are assumed to be output from rc.d scripts.
+#
+# This string is long and unique to ensure that it does not accidentally
+# appear in output from any rc.d script.  It must not contain any
+# characters that are special to glob expansion ('*', '?', '[', or ']').
+#
+rc_metadata_prefix="$0:$$:metadata:";
 
-#      Set shell to ignore SIGINT, but not children;
-#      shell catches SIGQUIT and returns to single user.
+# Child scripts may sometimes want to print directly to the original
+# stdout and stderr, bypassing the pipe to the postprocessor.  These
+# _rc_*_fd variables are private, shared with /etc/rc.subr, but not
+# intended to be used directly by child scripts.  (Child scripts
+# may use rc.subr's no_rc_postprocess function.)
+#
+_rc_original_stdout_fd=7; export _rc_original_stdout_fd
+_rc_original_stderr_fd=8; export _rc_original_stderr_fd
+eval "exec ${_rc_original_stdout_fd}>&1"
+eval "exec ${_rc_original_stderr_fd}>&2"
+
+#
+# rc_real_work
+#      Do the real work.  Output from this function will be piped into
+#      rc_postprocess(), and some of the output will be marked as
+#      metadata.
+#
+# The body of this function is defined using (...), not {...}, to force
+# it to run in a subshell.
 #
-trap : INT
-trap "echo 'Boot interrupted.'; exit 1" QUIT
+rc_real_work()
+(
+       stty status '^T'
+
+       # print_rc_metadata() wants to be able to print to the pipe
+       # that goes to our postprocessor, even if its in a context
+       # with redirected output.
+       #
+       _rc_postprocessor_fd=9 ; export _rc_postprocessor_fd
+       eval "exec ${_rc_postprocessor_fd}>&1"
+
+       # Print a metadata line when we exit
+       #
+       trap 'es=$?; print_rc_metadata "exit:$es"; trap "" 0; exit $es' 0
+
+       #       Set shell to ignore SIGINT, but children will not ignore it.
+       #       Shell catches SIGQUIT and returns to single user.
+       #
+       trap : INT
+       trap '_msg="Boot interrupted at $(date)";
+             print_rc_metadata "interrupted:${_msg}";
+             exit 1' QUIT
+
+       print_rc_metadata "start:$(date)"
+
+       #
+       # The stop_boot() function in rc.subr may kill $RC_PID.  We want
+       # it to kill the subshell running this rc_real_work() function,
+       # rather than killing the parent shell, because we want the
+       # rc_postprocess() function to be able to log the error
+       # without being killed itself.
+       #
+       # "$$" is the pid of the top-level shell, not the pid of the
+       # subshell that's executing this function.  The command below
+       # tentatively assumes that the parent of the "/bin/sh -c ..."
+       # process will be the current subshell, and then uses "kill -0
+       # ..." to check the result.  If the "/bin/sh -c ..." process
+       # fails, or returns the pid of an ephemeral process that exits
+       # before the "kill" command, then we fall back to using "$$".
+       #
+       RC_PID=$(/bin/sh -c 'ps -p $$ -o ppid=') || RC_PID=$$
+       kill -0 $RC_PID >/dev/null 2>&1 || RC_PID=$$
+
+       #
+       # Get a list of all rc.d scripts, and use rcorder to choose
+       # what order to execute them.
+       #
+       # For testing, allow RC_FILES_OVERRIDE from the environment to
+       # override this.
+       #
+       print_rc_metadata "cmd-name:rcorder"
+       scripts=$(for rcd in ${rc_directories:-/etc/rc.d}; do
+               test -d ${rcd} && echo ${rcd}/*;
+       done)
+       files=$(rcorder -s nostart ${rc_rcorder_flags} ${scripts})
+       print_rc_metadata "cmd-status:rcorder:$?"
+
+       if [ -n "${RC_FILES_OVERRIDE}" ]; then
+               files="${RC_FILES_OVERRIDE}"
+       fi
+
+       #
+       # Run the scripts in order.
+       #
+       for _rc_elem in $files; do
+               print_rc_metadata "cmd-name:$_rc_elem"
+               run_rc_script $_rc_elem start
+               print_rc_metadata "cmd-status:$_rc_elem:$?"
+       done
 
-date
+       print_rc_metadata "end:$(date)"
+       exit 0
+)
+
+#
+# rc_postprocess
+#      Post-process the output from the rc_real_work() function.  For
+#      each line of input, we have to decide whether to print the line
+#      to the console, print a twiddle on the console, print a line to
+#      the log, or some combination of these.
+#
+#      If rc_silent is true, then suppress most output, instead running
+#      rc_silent_cmd (typically "twiddle") for each line.
+#
+# The body of this function is defined using (...), not {...}, to force
+# it to run in a subshell.
+#
+# We have to deal with the following constraints:
+#
+#  * There may be no writable file systems early in the boot, so
+#    any use of temporary files would be problematic.
+#
+#  * Scripts run during the boot may clear /tmp and/var/run, so even
+#    if they are writable, using those directories too early may be
+#    problematic.  We assume that it's safe to write to our log file
+#    after the mountcritlocal script has run.
+#
+#  * /usr/bin/tee cannot be used because the /usr file system may not
+#    be mounted early in the boot.
+#
+#  * All calls to the rc_log_message and rc_log_flush functions must be
+#    from the same subshell, otherwise the use of a shell variable to
+#    buffer log messages will fail.
+#
+rc_postprocess()
+(
+       local line
+       local before after
+       local IFS=''
+
+       # Try quite hard to flush the log to disk when we exit.
+       trap 'es=$?; rc_log_flush FORCE; trap "" 0; exit $es' 0
+
+       yesno_to_truefalse rc_silent 2>/dev/null
+
+       while read -r line ; do
+               case "$line" in
+               "${rc_metadata_prefix}"*)
+                       after="${line#*"${rc_metadata_prefix}"}"
+                       rc_postprocess_metadata "${after}"
+                       ;;
+               *"${rc_metadata_prefix}"*)
+                       # magic string is present, but not at the start of
+                       # the line.  Treat it like two separate lines.
+                       before="${line%"${rc_metadata_prefix}"*}"
+                       rc_postprocess_plain_line "${before}"
+                       after="${line#*"${rc_metadata_prefix}"}"
+                       rc_postprocess_metadata "${after}"
+                       ;;
+               *)
+                       rc_postprocess_plain_line "${line}"
+                       ;;
+               esac
+       done
+
+       # If we get here, then the rc_real_work() function must have
+       # exited uncleanly.  A clean exit would have been accompanied by
+       # a line of metadata that would have prevented us from getting
+       # here.
+       #
+       exit 1
+)
 
-scripts=$(for rcd in ${rc_directories:-/etc/rc.d}; do
-       test -d ${rcd} && echo ${rcd}/*;
-done)
-files=$(rcorder -s nostart ${rc_rcorder_flags} ${scripts})
+#
+# rc_postprocess_plain_line string
+#      $1 is a string representing a line of output from one of the
+#      rc.d scripts.  Append the line to the log, and also either
+#      display the line on the console, or run $rc_silent_cmd,
+#      depending on the value of $rc_silent.
+#
+rc_postprocess_plain_line()
+{
+       local line="$1"
+       rc_log_message "${line}"
+       if $rc_silent; then
+               eval "$rc_silent_cmd"
+       else
+               printf "%s\n" "${line}"
+       fi
+}
+
+#
+# rc_postprocess_metadata string
+#      $1 is a string containing metadata from the rc_real_work()
+#      function.  The rc_metadata_prefix marker should already
+#      have been removed before the string is passed to this function.
+#      Take appropriate action depending on the content of the string.
+#
+rc_postprocess_metadata()
+{
+       local metadata="$1"
+       local keyword args



Home | Main Index | Thread Index | Old Index