18f8c6d6bSlukem#!/bin/sh 28f8c6d6bSlukem# 3*6cb68e44Smartin# $NetBSD: rc,v 1.175 2020/09/08 16:10:53 martin Exp $ 48f8c6d6bSlukem# 56eef11f2Slukem# rc -- 689fd5357Sapb# Run the scripts in /etc/rc.d with rcorder, and log output 789fd5357Sapb# to /var/run/rc.log. 861f28255Scgd 96eef11f2Slukem# System startup script run by init(8) on autoboot or after single-user. 108f8c6d6bSlukem# Output and error are redirected to console by init, and the console 118f8c6d6bSlukem# is the controlling terminal. 128f8c6d6bSlukem 138f8c6d6bSlukemexport HOME=/ 148f8c6d6bSlukemexport PATH=/sbin:/bin:/usr/sbin:/usr/bin 1528d6dbb5Slukemumask 022 168f8c6d6bSlukem 1789fd5357Sapbif [ -e ./rc.subr ] ; then 1889fd5357Sapb . ./rc.subr # for testing 1989fd5357Sapbelse 208f8c6d6bSlukem . /etc/rc.subr 2189fd5357Sapbfi 228f8c6d6bSlukem. /etc/rc.conf 2315388316Slukem_rc_conf_loaded=true 248f8c6d6bSlukem 2589fd5357Sapb: ${RC_LOG_FILE:="/var/run/rc.log"} 2689fd5357Sapb 2775350585Sapb# rc.subr redefines echo and printf. Undo that here. 2875350585Sapbunset echo ; unalias echo 2975350585Sapbunset printf ; unalias printf 3075350585Sapb 31e8b27ae5Senamiif ! checkyesno rc_configured; then 32e8b27ae5Senami echo "/etc/rc.conf is not configured. Multiuser boot aborted." 33e8b27ae5Senami exit 1 34e8b27ae5Senamifi 35e8b27ae5Senami 368f8c6d6bSlukemif [ "$1" = autoboot ]; then 378f8c6d6bSlukem autoboot=yes 38892c0453Slukem rc_fast=yes # run_rc_command(): do fast booting 398f8c6d6bSlukemfi 4061f28255Scgd 4189fd5357Sapb# 4289fd5357Sapb# Completely ignore INT and QUIT at the outer level. The rc_real_work() 4389fd5357Sapb# function should do something different. 4489fd5357Sapb# 4589fd5357Sapbtrap '' INT QUIT 4689fd5357Sapb 4789fd5357Sapb# 4889fd5357Sapb# This string will be used to mark lines of meta-data sent over the pipe 4989fd5357Sapb# from the rc_real_work() function to the rc_postprocess() function. Lines 5089fd5357Sapb# not so marked are assumed to be output from rc.d scripts. 5189fd5357Sapb# 5289fd5357Sapb# This string is long and unique to ensure that it does not accidentally 5389fd5357Sapb# appear in output from any rc.d script. It must not contain any 5489fd5357Sapb# characters that are special to glob expansion ('*', '?', '[', or ']'). 5589fd5357Sapb# 5689fd5357Sapbrc_metadata_prefix="$0:$$:metadata:"; 5789fd5357Sapb 5889fd5357Sapb# Child scripts may sometimes want to print directly to the original 5989fd5357Sapb# stdout and stderr, bypassing the pipe to the postprocessor. These 6089fd5357Sapb# _rc_*_fd variables are private, shared with /etc/rc.subr, but not 6189fd5357Sapb# intended to be used directly by child scripts. (Child scripts 6289fd5357Sapb# may use rc.subr's no_rc_postprocess function.) 6389fd5357Sapb# 6489fd5357Sapb_rc_original_stdout_fd=7; export _rc_original_stdout_fd 6589fd5357Sapb_rc_original_stderr_fd=8; export _rc_original_stderr_fd 6689fd5357Sapbeval "exec ${_rc_original_stdout_fd}>&1" 6789fd5357Sapbeval "exec ${_rc_original_stderr_fd}>&2" 687e6c5426Schristosfdflags -s +cloexec 7 8 6989fd5357Sapb 7089fd5357Sapb# 7189fd5357Sapb# rc_real_work 7289fd5357Sapb# Do the real work. Output from this function will be piped into 7389fd5357Sapb# rc_postprocess(), and some of the output will be marked as 7489fd5357Sapb# metadata. 7589fd5357Sapb# 7689fd5357Sapb# The body of this function is defined using (...), not {...}, to force 7789fd5357Sapb# it to run in a subshell. 7889fd5357Sapb# 7989fd5357Sapbrc_real_work() 8089fd5357Sapb( 8161f28255Scgd stty status '^T' 8261f28255Scgd 8389fd5357Sapb # print_rc_metadata() wants to be able to print to the pipe 8489fd5357Sapb # that goes to our postprocessor, even if its in a context 8589fd5357Sapb # with redirected output. 8689fd5357Sapb # 8789fd5357Sapb _rc_postprocessor_fd=9 ; export _rc_postprocessor_fd 887bc38475Sapb _rc_pid=$$ ; export _rc_pid 8989fd5357Sapb eval "exec ${_rc_postprocessor_fd}>&1" 90476172bcSchristos fdflags -s +cloexec 9 9189fd5357Sapb 9289fd5357Sapb # Print a metadata line when we exit 9389fd5357Sapb # 9489fd5357Sapb trap 'es=$?; print_rc_metadata "exit:$es"; trap "" 0; exit $es' 0 9589fd5357Sapb 9689fd5357Sapb # Set shell to ignore SIGINT, but children will not ignore it. 9789fd5357Sapb # Shell catches SIGQUIT and returns to single user. 988f8c6d6bSlukem # 99bd7fad6cSlukem trap : INT 10089fd5357Sapb trap '_msg="Boot interrupted at $(date)"; 10189fd5357Sapb print_rc_metadata "interrupted:${_msg}"; 10289fd5357Sapb exit 1' QUIT 10361f28255Scgd 10489fd5357Sapb print_rc_metadata "start:$(date)" 105c47a8060Slukem 10689fd5357Sapb # 10789fd5357Sapb # The stop_boot() function in rc.subr may kill $RC_PID. We want 10889fd5357Sapb # it to kill the subshell running this rc_real_work() function, 10989fd5357Sapb # rather than killing the parent shell, because we want the 11089fd5357Sapb # rc_postprocess() function to be able to log the error 11189fd5357Sapb # without being killed itself. 11289fd5357Sapb # 11389fd5357Sapb # "$$" is the pid of the top-level shell, not the pid of the 11489fd5357Sapb # subshell that's executing this function. The command below 11589fd5357Sapb # tentatively assumes that the parent of the "/bin/sh -c ..." 11689fd5357Sapb # process will be the current subshell, and then uses "kill -0 11789fd5357Sapb # ..." to check the result. If the "/bin/sh -c ..." process 11889fd5357Sapb # fails, or returns the pid of an ephemeral process that exits 11989fd5357Sapb # before the "kill" command, then we fall back to using "$$". 12089fd5357Sapb # 12189fd5357Sapb RC_PID=$(/bin/sh -c 'ps -p $$ -o ppid=') || RC_PID=$$ 12289fd5357Sapb kill -0 $RC_PID >/dev/null 2>&1 || RC_PID=$$ 12389fd5357Sapb 12489fd5357Sapb # 125288496e5Sapb # As long as process $RC_PID is still running, send a "nop" 126288496e5Sapb # metadata message to the postprocessor every few seconds. 127288496e5Sapb # This should help flush partial lines that may appear when 128288496e5Sapb # rc.d scripts that are NOT marked with "KEYWORD: interactive" 129288496e5Sapb # nevertheless attempt to print prompts and wait for input. 130288496e5Sapb # 131288496e5Sapb ( 1328a948b43Sapb # First detach from tty, to avoid intercepting SIGINFO. 1338a948b43Sapb eval "exec ${_rc_original_stdout_fd}<&-" 1348a948b43Sapb eval "exec ${_rc_original_stderr_fd}<&-" 1358a948b43Sapb exec </dev/null >/dev/null 2>&1 136288496e5Sapb while kill -0 $RC_PID ; do 137288496e5Sapb print_rc_metadata "nop" 138288496e5Sapb sleep 3 139288496e5Sapb done 140288496e5Sapb ) & 141288496e5Sapb 142288496e5Sapb # 14389fd5357Sapb # Get a list of all rc.d scripts, and use rcorder to choose 14489fd5357Sapb # what order to execute them. 14589fd5357Sapb # 14689fd5357Sapb # For testing, allow RC_FILES_OVERRIDE from the environment to 14789fd5357Sapb # override this. 14889fd5357Sapb # 14989fd5357Sapb print_rc_metadata "cmd-name:rcorder" 150760b4799Sjoerg scripts=$(for rcd in ${rc_directories:-/etc/rc.d}; do 151760b4799Sjoerg test -d ${rcd} && echo ${rcd}/*; 152760b4799Sjoerg done) 153760b4799Sjoerg files=$(rcorder -s nostart ${rc_rcorder_flags} ${scripts}) 15489fd5357Sapb print_rc_metadata "cmd-status:rcorder:$?" 155b6742129Slukem 15689fd5357Sapb if [ -n "${RC_FILES_OVERRIDE}" ]; then 15789fd5357Sapb files="${RC_FILES_OVERRIDE}" 15889fd5357Sapb fi 15989fd5357Sapb 16089fd5357Sapb # 16189fd5357Sapb # Run the scripts in order. 16289fd5357Sapb # 163c47a8060Slukem for _rc_elem in $files; do 16489fd5357Sapb print_rc_metadata "cmd-name:$_rc_elem" 165c47a8060Slukem run_rc_script $_rc_elem start 16689fd5357Sapb print_rc_metadata "cmd-status:$_rc_elem:$?" 167e4634682Sdarrenr done 16861f28255Scgd 16989fd5357Sapb print_rc_metadata "end:$(date)" 17061f28255Scgd exit 0 17189fd5357Sapb) 17289fd5357Sapb 17389fd5357Sapb# 17489fd5357Sapb# rc_postprocess 17589fd5357Sapb# Post-process the output from the rc_real_work() function. For 17689fd5357Sapb# each line of input, we have to decide whether to print the line 17789fd5357Sapb# to the console, print a twiddle on the console, print a line to 17889fd5357Sapb# the log, or some combination of these. 17989fd5357Sapb# 18089fd5357Sapb# If rc_silent is true, then suppress most output, instead running 18189fd5357Sapb# rc_silent_cmd (typically "twiddle") for each line. 18289fd5357Sapb# 18389fd5357Sapb# The body of this function is defined using (...), not {...}, to force 18489fd5357Sapb# it to run in a subshell. 18589fd5357Sapb# 18689fd5357Sapb# We have to deal with the following constraints: 18789fd5357Sapb# 18889fd5357Sapb# * There may be no writable file systems early in the boot, so 18989fd5357Sapb# any use of temporary files would be problematic. 19089fd5357Sapb# 19189fd5357Sapb# * Scripts run during the boot may clear /tmp and/var/run, so even 19289fd5357Sapb# if they are writable, using those directories too early may be 19389fd5357Sapb# problematic. We assume that it's safe to write to our log file 194*6cb68e44Smartin# after the CRITLOCALMOUNTED script has run. 19589fd5357Sapb# 19689fd5357Sapb# * /usr/bin/tee cannot be used because the /usr file system may not 19789fd5357Sapb# be mounted early in the boot. 19889fd5357Sapb# 19989fd5357Sapb# * All calls to the rc_log_message and rc_log_flush functions must be 20089fd5357Sapb# from the same subshell, otherwise the use of a shell variable to 20189fd5357Sapb# buffer log messages will fail. 20289fd5357Sapb# 20389fd5357Sapbrc_postprocess() 20489fd5357Sapb( 20589fd5357Sapb local line 20689fd5357Sapb local before after 20789fd5357Sapb local IFS='' 20889fd5357Sapb 20989fd5357Sapb # Try quite hard to flush the log to disk when we exit. 21089fd5357Sapb trap 'es=$?; rc_log_flush FORCE; trap "" 0; exit $es' 0 21189fd5357Sapb 21289fd5357Sapb yesno_to_truefalse rc_silent 2>/dev/null 21389fd5357Sapb 21489fd5357Sapb while read -r line ; do 21589fd5357Sapb case "$line" in 21689fd5357Sapb "${rc_metadata_prefix}"*) 21789fd5357Sapb after="${line#*"${rc_metadata_prefix}"}" 21889fd5357Sapb rc_postprocess_metadata "${after}" 21989fd5357Sapb ;; 22089fd5357Sapb *"${rc_metadata_prefix}"*) 22189fd5357Sapb # magic string is present, but not at the start of 22275350585Sapb # the line. Treat it as a partial line of 22375350585Sapb # ordinary data, followed by a line of metadata. 22489fd5357Sapb before="${line%"${rc_metadata_prefix}"*}" 22575350585Sapb rc_postprocess_partial_line "${before}" 22689fd5357Sapb after="${line#*"${rc_metadata_prefix}"}" 22789fd5357Sapb rc_postprocess_metadata "${after}" 22889fd5357Sapb ;; 22989fd5357Sapb *) 23089fd5357Sapb rc_postprocess_plain_line "${line}" 23189fd5357Sapb ;; 23289fd5357Sapb esac 23389fd5357Sapb done 23489fd5357Sapb 23589fd5357Sapb # If we get here, then the rc_real_work() function must have 23689fd5357Sapb # exited uncleanly. A clean exit would have been accompanied by 23789fd5357Sapb # a line of metadata that would have prevented us from getting 23889fd5357Sapb # here. 23989fd5357Sapb # 24089fd5357Sapb exit 1 24189fd5357Sapb) 24289fd5357Sapb 24389fd5357Sapb# 24489fd5357Sapb# rc_postprocess_plain_line string 24589fd5357Sapb# $1 is a string representing a line of output from one of the 24689fd5357Sapb# rc.d scripts. Append the line to the log, and also either 24789fd5357Sapb# display the line on the console, or run $rc_silent_cmd, 24889fd5357Sapb# depending on the value of $rc_silent. 24989fd5357Sapb# 25089fd5357Sapbrc_postprocess_plain_line() 25189fd5357Sapb{ 25289fd5357Sapb local line="$1" 25389fd5357Sapb rc_log_message "${line}" 25489fd5357Sapb if $rc_silent; then 25589fd5357Sapb eval "$rc_silent_cmd" 25689fd5357Sapb else 25789fd5357Sapb printf "%s\n" "${line}" 25889fd5357Sapb fi 25989fd5357Sapb} 26089fd5357Sapb 26189fd5357Sapb# 26275350585Sapb# rc_postprocess_partial_line string 26375350585Sapb# This is just like rc_postprocess_plain_line, except that 26475350585Sapb# a newline is not appended to the string. 26575350585Sapb# 26675350585Sapbrc_postprocess_partial_line() 26775350585Sapb{ 26875350585Sapb local line="$1" 26975350585Sapb rc_log_message_n "${line}" 27075350585Sapb if $rc_silent; then 27175350585Sapb eval "$rc_silent_cmd" 27275350585Sapb else 27375350585Sapb printf "%s" "${line}" 27475350585Sapb fi 27575350585Sapb} 27675350585Sapb 27775350585Sapb# 27889fd5357Sapb# rc_postprocess_metadata string 27989fd5357Sapb# $1 is a string containing metadata from the rc_real_work() 28089fd5357Sapb# function. The rc_metadata_prefix marker should already 28189fd5357Sapb# have been removed before the string is passed to this function. 28289fd5357Sapb# Take appropriate action depending on the content of the string. 28389fd5357Sapb# 28489fd5357Sapbrc_postprocess_metadata() 28589fd5357Sapb{ 28689fd5357Sapb local metadata="$1" 28789fd5357Sapb local keyword args 28889fd5357Sapb local msg 28989fd5357Sapb local IFS=':' 29089fd5357Sapb 29189fd5357Sapb # given metadata="bleep:foo bar:baz", 29289fd5357Sapb # set keyword="bleep", args="foo bar:baz", 29389fd5357Sapb # $1="foo bar", $2="baz" 29489fd5357Sapb # 29589fd5357Sapb keyword="${metadata%%:*}" 29689fd5357Sapb args="${metadata#*:}" 29789fd5357Sapb set -- $args 29889fd5357Sapb 29989fd5357Sapb case "$keyword" in 30089fd5357Sapb start) 30111bfaf8dSapb # Marks the start of the entire /etc/rc script. 30211bfaf8dSapb # $args contains a date/time. 30389fd5357Sapb rc_log_message "[$0 starting at $args]" 30489fd5357Sapb if ! $rc_silent; then 30589fd5357Sapb printf "%s\n" "$args" 30689fd5357Sapb fi 30789fd5357Sapb ;; 30889fd5357Sapb cmd-name) 30911bfaf8dSapb # Marks the start of a child script (usually one of 31011bfaf8dSapb # the /etc/rc.d/* scripts). 31189fd5357Sapb rc_log_message "[running $1]" 31289fd5357Sapb ;; 31389fd5357Sapb cmd-status) 31411bfaf8dSapb # Marks the end of a child script. 31589fd5357Sapb # $1 is a command name, $2 is the command's exit status. 31689fd5357Sapb # If the command failed, report it, and add it to a list. 31789fd5357Sapb if [ "$2" != 0 ]; then 31889fd5357Sapb rc_failures="${rc_failures}${rc_failures:+ }$1" 319520fd1e5Schristos msg="$1 $(human_exit_code $2)" 32089fd5357Sapb rc_log_message "$msg" 32189fd5357Sapb if ! $rc_silent; then 32289fd5357Sapb printf "%s\n" "$msg" 32389fd5357Sapb fi 32489fd5357Sapb fi 325*6cb68e44Smartin # After the CRITLOCALMOUNTED script has finished, it's 32689fd5357Sapb # OK to flush the log to disk 32789fd5357Sapb case "$1" in 328*6cb68e44Smartin */CRITLOCALMOUNTED) 32989fd5357Sapb rc_log_flush OK 33089fd5357Sapb ;; 33189fd5357Sapb esac 33289fd5357Sapb ;; 33375350585Sapb nop) 33475350585Sapb # Do nothing. 33511bfaf8dSapb # This has the side effect of flushing partial lines, 33611bfaf8dSapb # and the echo() and printf() functions in rc.subr take 33711bfaf8dSapb # advantage of this. 33875350585Sapb ;; 33989fd5357Sapb note) 34011bfaf8dSapb # Unlike most metadata messages, which should be used 34111bfaf8dSapb # only by /etc/rc and rc.subr, the "note" message may be 34211bfaf8dSapb # used directly by /etc.rc.d/* and similar scripts. 34311bfaf8dSapb # It adds a note to the log file, without displaying 34411bfaf8dSapb # it to stdout. 34589fd5357Sapb rc_log_message "[NOTE: $args]" 34689fd5357Sapb ;; 34789fd5357Sapb end) 34811bfaf8dSapb # Marks the end of processing, after the last child script. 34911bfaf8dSapb # If any child scripts (or other commands) failed, report them. 35089fd5357Sapb # 35189fd5357Sapb if [ -n "$rc_failures" ]; then 35289fd5357Sapb rc_log_message "[failures]" 35389fd5357Sapb msg="The following components reported failures:" 35489fd5357Sapb msg="${msg}${nl}$( echo " ${rc_failures}" | fmt )" 35589fd5357Sapb msg="${msg}${nl}See ${RC_LOG_FILE} for more information." 35689fd5357Sapb rc_log_message "${msg}" 35789fd5357Sapb printf "%s\n" "${msg}" 35889fd5357Sapb fi 35989fd5357Sapb # 36089fd5357Sapb # Report the end date/time, even in silent mode 36189fd5357Sapb # 36289fd5357Sapb rc_log_message "[$0 finished at $args]" 36389fd5357Sapb printf "%s\n" "$args" 36489fd5357Sapb ;; 36589fd5357Sapb exit) 36611bfaf8dSapb # Marks an exit from the rc_real_work() function. 36711bfaf8dSapb # This may be a normal or abnormal exit. 36811bfaf8dSapb # 36989fd5357Sapb rc_log_message "[$0 exiting with status $1]" 37089fd5357Sapb exit $1 37189fd5357Sapb ;; 37289fd5357Sapb interrupted) 37311bfaf8dSapb # Marks an interrupt trapped by the rc_real_work() function. 37411bfaf8dSapb # $args is a human-readable message. 37589fd5357Sapb rc_log_message "$args" 37689fd5357Sapb printf "%s\n" "$args" 37789fd5357Sapb ;; 37889fd5357Sapb *) 37989fd5357Sapb # an unrecognised line of metadata 38089fd5357Sapb rc_log_message "[metadata:${metadata}]" 38189fd5357Sapb ;; 38289fd5357Sapb esac 38389fd5357Sapb} 38489fd5357Sapb 38589fd5357Sapb# 38689fd5357Sapb# rc_log_message string [...] 38775350585Sapb# Write a message to the log file, or buffer it for later. 38875350585Sapb# This function appends a newline to the message. 38989fd5357Sapb# 39089fd5357Sapbrc_log_message() 39189fd5357Sapb{ 39289fd5357Sapb _rc_log_buffer="${_rc_log_buffer}${*}${nl}" 39389fd5357Sapb rc_log_flush 39489fd5357Sapb} 39589fd5357Sapb 39689fd5357Sapb# 39775350585Sapb# rc_log_message_n string [...] 39875350585Sapb# Just like rc_log_message, except without appending a newline. 39975350585Sapb# 40075350585Sapbrc_log_message_n() 40175350585Sapb{ 40275350585Sapb _rc_log_buffer="${_rc_log_buffer}${*}" 40375350585Sapb rc_log_flush 40475350585Sapb} 40575350585Sapb 40675350585Sapb# 40789fd5357Sapb# rc_log_flush [OK|FORCE] 40889fd5357Sapb# save outstanding messages from $_rc_log_buffer to $RC_LOG_FILE. 40989fd5357Sapb# 41089fd5357Sapb# The log file is expected to reside in the /var/run directory, which 41189fd5357Sapb# may not be writable very early in the boot sequence, and which is 41289fd5357Sapb# erased a little later in the boot sequence. We therefore avoid 41389fd5357Sapb# writing to the file until we believe it's safe to do so. We also 41489fd5357Sapb# assume that it's reasonable to always append to the file, never 41589fd5357Sapb# truncating it. 41689fd5357Sapb# 41789fd5357Sapb# Optional argument $1 may be "OK" to report that writing to the log 41889fd5357Sapb# file is expected to be safe from now on, or "FORCE" to force writing 41989fd5357Sapb# to the log file even if it may be unsafe. 42089fd5357Sapb# 42189fd5357Sapb# Returns a non-zero status if messages could not be written to the 42289fd5357Sapb# file. 42389fd5357Sapb# 42489fd5357Sapbrc_log_flush() 42589fd5357Sapb{ 42689fd5357Sapb # 42789fd5357Sapb # If $_rc_log_flush_ok is false, then it's probably too early to 42889fd5357Sapb # write to the log file, so don't do it, unless $1 is "FORCE". 42989fd5357Sapb # 43089fd5357Sapb : ${_rc_log_flush_ok=false} 43189fd5357Sapb case "$1:$_rc_log_flush_ok" in 43289fd5357Sapb OK:*) 43389fd5357Sapb _rc_log_flush_ok=true 43489fd5357Sapb ;; 43589fd5357Sapb FORCE:*) 43689fd5357Sapb : OK just this once 43789fd5357Sapb ;; 43889fd5357Sapb *:true) 43989fd5357Sapb : OK 44089fd5357Sapb ;; 44189fd5357Sapb *) 44289fd5357Sapb # it's too early in the boot sequence, so don't flush 44389fd5357Sapb return 1 44489fd5357Sapb ;; 44589fd5357Sapb esac 44689fd5357Sapb 44789fd5357Sapb # 44889fd5357Sapb # Now append the buffer to the file. The buffer should already 44989fd5357Sapb # contain a trailing newline, so don't add an extra newline. 45089fd5357Sapb # 45189fd5357Sapb if [ -n "$_rc_log_buffer" ]; then 45289fd5357Sapb if { printf "%s" "${_rc_log_buffer}" >>"${RC_LOG_FILE}" ; } \ 45389fd5357Sapb 2>/dev/null 45489fd5357Sapb then 45589fd5357Sapb _rc_log_buffer="" 45689fd5357Sapb else 45789fd5357Sapb return 1 45889fd5357Sapb fi 45989fd5357Sapb fi 46089fd5357Sapb return 0 46189fd5357Sapb} 46289fd5357Sapb 46389fd5357Sapb# 46489fd5357Sapb# Most of the action is in the rc_real_work() and rc_postprocess() 46589fd5357Sapb# functions. 46689fd5357Sapb# 46789fd5357Sapbrc_real_work "$@" 2>&1 | rc_postprocess 46889fd5357Sapbexit $? 469