1 1.149 lukem #!/bin/sh 2 1.149 lukem # 3 1.175 martin # $NetBSD: rc,v 1.175 2020/09/08 16:10:53 martin Exp $ 4 1.149 lukem # 5 1.154 lukem # rc -- 6 1.164 apb # Run the scripts in /etc/rc.d with rcorder, and log output 7 1.164 apb # to /var/run/rc.log. 8 1.149 lukem 9 1.154 lukem # System startup script run by init(8) on autoboot or after single-user. 10 1.149 lukem # Output and error are redirected to console by init, and the console 11 1.149 lukem # is the controlling terminal. 12 1.1 cgd 13 1.149 lukem export HOME=/ 14 1.149 lukem export PATH=/sbin:/bin:/usr/sbin:/usr/bin 15 1.157 lukem umask 022 16 1.1 cgd 17 1.164 apb if [ -e ./rc.subr ] ; then 18 1.164 apb . ./rc.subr # for testing 19 1.164 apb else 20 1.164 apb . /etc/rc.subr 21 1.164 apb fi 22 1.149 lukem . /etc/rc.conf 23 1.162 lukem _rc_conf_loaded=true 24 1.150 enami 25 1.164 apb : ${RC_LOG_FILE:="/var/run/rc.log"} 26 1.164 apb 27 1.166 apb # rc.subr redefines echo and printf. Undo that here. 28 1.166 apb unset echo ; unalias echo 29 1.166 apb unset printf ; unalias printf 30 1.166 apb 31 1.150 enami if ! checkyesno rc_configured; then 32 1.150 enami echo "/etc/rc.conf is not configured. Multiuser boot aborted." 33 1.150 enami exit 1 34 1.150 enami fi 35 1.50 thorpej 36 1.149 lukem if [ "$1" = autoboot ]; then 37 1.149 lukem autoboot=yes 38 1.160 lukem rc_fast=yes # run_rc_command(): do fast booting 39 1.50 thorpej fi 40 1.107 tron 41 1.164 apb # 42 1.164 apb # Completely ignore INT and QUIT at the outer level. The rc_real_work() 43 1.164 apb # function should do something different. 44 1.164 apb # 45 1.164 apb trap '' INT QUIT 46 1.1 cgd 47 1.149 lukem # 48 1.164 apb # This string will be used to mark lines of meta-data sent over the pipe 49 1.164 apb # from the rc_real_work() function to the rc_postprocess() function. Lines 50 1.164 apb # not so marked are assumed to be output from rc.d scripts. 51 1.164 apb # 52 1.164 apb # This string is long and unique to ensure that it does not accidentally 53 1.164 apb # appear in output from any rc.d script. It must not contain any 54 1.164 apb # characters that are special to glob expansion ('*', '?', '[', or ']'). 55 1.164 apb # 56 1.164 apb rc_metadata_prefix="$0:$$:metadata:"; 57 1.1 cgd 58 1.164 apb # Child scripts may sometimes want to print directly to the original 59 1.164 apb # stdout and stderr, bypassing the pipe to the postprocessor. These 60 1.164 apb # _rc_*_fd variables are private, shared with /etc/rc.subr, but not 61 1.164 apb # intended to be used directly by child scripts. (Child scripts 62 1.164 apb # may use rc.subr's no_rc_postprocess function.) 63 1.164 apb # 64 1.164 apb _rc_original_stdout_fd=7; export _rc_original_stdout_fd 65 1.164 apb _rc_original_stderr_fd=8; export _rc_original_stderr_fd 66 1.164 apb eval "exec ${_rc_original_stdout_fd}>&1" 67 1.164 apb eval "exec ${_rc_original_stderr_fd}>&2" 68 1.172 christos fdflags -s +cloexec 7 8 69 1.156 lukem 70 1.164 apb # 71 1.164 apb # rc_real_work 72 1.164 apb # Do the real work. Output from this function will be piped into 73 1.164 apb # rc_postprocess(), and some of the output will be marked as 74 1.164 apb # metadata. 75 1.164 apb # 76 1.164 apb # The body of this function is defined using (...), not {...}, to force 77 1.164 apb # it to run in a subshell. 78 1.164 apb # 79 1.164 apb rc_real_work() 80 1.164 apb ( 81 1.164 apb stty status '^T' 82 1.164 apb 83 1.164 apb # print_rc_metadata() wants to be able to print to the pipe 84 1.164 apb # that goes to our postprocessor, even if its in a context 85 1.164 apb # with redirected output. 86 1.164 apb # 87 1.164 apb _rc_postprocessor_fd=9 ; export _rc_postprocessor_fd 88 1.169 apb _rc_pid=$$ ; export _rc_pid 89 1.164 apb eval "exec ${_rc_postprocessor_fd}>&1" 90 1.173 christos fdflags -s +cloexec 9 91 1.164 apb 92 1.164 apb # Print a metadata line when we exit 93 1.164 apb # 94 1.164 apb trap 'es=$?; print_rc_metadata "exit:$es"; trap "" 0; exit $es' 0 95 1.164 apb 96 1.164 apb # Set shell to ignore SIGINT, but children will not ignore it. 97 1.164 apb # Shell catches SIGQUIT and returns to single user. 98 1.164 apb # 99 1.164 apb trap : INT 100 1.164 apb trap '_msg="Boot interrupted at $(date)"; 101 1.164 apb print_rc_metadata "interrupted:${_msg}"; 102 1.164 apb exit 1' QUIT 103 1.164 apb 104 1.164 apb print_rc_metadata "start:$(date)" 105 1.164 apb 106 1.164 apb # 107 1.164 apb # The stop_boot() function in rc.subr may kill $RC_PID. We want 108 1.164 apb # it to kill the subshell running this rc_real_work() function, 109 1.164 apb # rather than killing the parent shell, because we want the 110 1.164 apb # rc_postprocess() function to be able to log the error 111 1.164 apb # without being killed itself. 112 1.164 apb # 113 1.164 apb # "$$" is the pid of the top-level shell, not the pid of the 114 1.164 apb # subshell that's executing this function. The command below 115 1.164 apb # tentatively assumes that the parent of the "/bin/sh -c ..." 116 1.164 apb # process will be the current subshell, and then uses "kill -0 117 1.164 apb # ..." to check the result. If the "/bin/sh -c ..." process 118 1.164 apb # fails, or returns the pid of an ephemeral process that exits 119 1.164 apb # before the "kill" command, then we fall back to using "$$". 120 1.164 apb # 121 1.164 apb RC_PID=$(/bin/sh -c 'ps -p $$ -o ppid=') || RC_PID=$$ 122 1.164 apb kill -0 $RC_PID >/dev/null 2>&1 || RC_PID=$$ 123 1.164 apb 124 1.164 apb # 125 1.168 apb # As long as process $RC_PID is still running, send a "nop" 126 1.168 apb # metadata message to the postprocessor every few seconds. 127 1.168 apb # This should help flush partial lines that may appear when 128 1.168 apb # rc.d scripts that are NOT marked with "KEYWORD: interactive" 129 1.168 apb # nevertheless attempt to print prompts and wait for input. 130 1.168 apb # 131 1.168 apb ( 132 1.170 apb # First detach from tty, to avoid intercepting SIGINFO. 133 1.170 apb eval "exec ${_rc_original_stdout_fd}<&-" 134 1.170 apb eval "exec ${_rc_original_stderr_fd}<&-" 135 1.170 apb exec </dev/null >/dev/null 2>&1 136 1.168 apb while kill -0 $RC_PID ; do 137 1.168 apb print_rc_metadata "nop" 138 1.168 apb sleep 3 139 1.168 apb done 140 1.168 apb ) & 141 1.168 apb 142 1.168 apb # 143 1.164 apb # Get a list of all rc.d scripts, and use rcorder to choose 144 1.164 apb # what order to execute them. 145 1.164 apb # 146 1.164 apb # For testing, allow RC_FILES_OVERRIDE from the environment to 147 1.164 apb # override this. 148 1.164 apb # 149 1.164 apb print_rc_metadata "cmd-name:rcorder" 150 1.164 apb scripts=$(for rcd in ${rc_directories:-/etc/rc.d}; do 151 1.164 apb test -d ${rcd} && echo ${rcd}/*; 152 1.164 apb done) 153 1.164 apb files=$(rcorder -s nostart ${rc_rcorder_flags} ${scripts}) 154 1.164 apb print_rc_metadata "cmd-status:rcorder:$?" 155 1.164 apb 156 1.164 apb if [ -n "${RC_FILES_OVERRIDE}" ]; then 157 1.164 apb files="${RC_FILES_OVERRIDE}" 158 1.164 apb fi 159 1.164 apb 160 1.164 apb # 161 1.164 apb # Run the scripts in order. 162 1.164 apb # 163 1.164 apb for _rc_elem in $files; do 164 1.164 apb print_rc_metadata "cmd-name:$_rc_elem" 165 1.164 apb run_rc_script $_rc_elem start 166 1.164 apb print_rc_metadata "cmd-status:$_rc_elem:$?" 167 1.164 apb done 168 1.164 apb 169 1.164 apb print_rc_metadata "end:$(date)" 170 1.164 apb exit 0 171 1.164 apb ) 172 1.155 lukem 173 1.164 apb # 174 1.164 apb # rc_postprocess 175 1.164 apb # Post-process the output from the rc_real_work() function. For 176 1.164 apb # each line of input, we have to decide whether to print the line 177 1.164 apb # to the console, print a twiddle on the console, print a line to 178 1.164 apb # the log, or some combination of these. 179 1.164 apb # 180 1.164 apb # If rc_silent is true, then suppress most output, instead running 181 1.164 apb # rc_silent_cmd (typically "twiddle") for each line. 182 1.164 apb # 183 1.164 apb # The body of this function is defined using (...), not {...}, to force 184 1.164 apb # it to run in a subshell. 185 1.164 apb # 186 1.164 apb # We have to deal with the following constraints: 187 1.164 apb # 188 1.164 apb # * There may be no writable file systems early in the boot, so 189 1.164 apb # any use of temporary files would be problematic. 190 1.164 apb # 191 1.164 apb # * Scripts run during the boot may clear /tmp and/var/run, so even 192 1.164 apb # if they are writable, using those directories too early may be 193 1.164 apb # problematic. We assume that it's safe to write to our log file 194 1.175 martin # after the CRITLOCALMOUNTED script has run. 195 1.164 apb # 196 1.164 apb # * /usr/bin/tee cannot be used because the /usr file system may not 197 1.164 apb # be mounted early in the boot. 198 1.164 apb # 199 1.164 apb # * All calls to the rc_log_message and rc_log_flush functions must be 200 1.164 apb # from the same subshell, otherwise the use of a shell variable to 201 1.164 apb # buffer log messages will fail. 202 1.164 apb # 203 1.164 apb rc_postprocess() 204 1.164 apb ( 205 1.164 apb local line 206 1.164 apb local before after 207 1.164 apb local IFS='' 208 1.164 apb 209 1.164 apb # Try quite hard to flush the log to disk when we exit. 210 1.164 apb trap 'es=$?; rc_log_flush FORCE; trap "" 0; exit $es' 0 211 1.164 apb 212 1.164 apb yesno_to_truefalse rc_silent 2>/dev/null 213 1.164 apb 214 1.164 apb while read -r line ; do 215 1.164 apb case "$line" in 216 1.164 apb "${rc_metadata_prefix}"*) 217 1.164 apb after="${line#*"${rc_metadata_prefix}"}" 218 1.164 apb rc_postprocess_metadata "${after}" 219 1.164 apb ;; 220 1.164 apb *"${rc_metadata_prefix}"*) 221 1.164 apb # magic string is present, but not at the start of 222 1.166 apb # the line. Treat it as a partial line of 223 1.166 apb # ordinary data, followed by a line of metadata. 224 1.164 apb before="${line%"${rc_metadata_prefix}"*}" 225 1.166 apb rc_postprocess_partial_line "${before}" 226 1.164 apb after="${line#*"${rc_metadata_prefix}"}" 227 1.164 apb rc_postprocess_metadata "${after}" 228 1.164 apb ;; 229 1.164 apb *) 230 1.164 apb rc_postprocess_plain_line "${line}" 231 1.164 apb ;; 232 1.164 apb esac 233 1.164 apb done 234 1.164 apb 235 1.164 apb # If we get here, then the rc_real_work() function must have 236 1.164 apb # exited uncleanly. A clean exit would have been accompanied by 237 1.164 apb # a line of metadata that would have prevented us from getting 238 1.164 apb # here. 239 1.164 apb # 240 1.164 apb exit 1 241 1.164 apb ) 242 1.164 apb 243 1.164 apb # 244 1.164 apb # rc_postprocess_plain_line string 245 1.164 apb # $1 is a string representing a line of output from one of the 246 1.164 apb # rc.d scripts. Append the line to the log, and also either 247 1.164 apb # display the line on the console, or run $rc_silent_cmd, 248 1.164 apb # depending on the value of $rc_silent. 249 1.164 apb # 250 1.164 apb rc_postprocess_plain_line() 251 1.164 apb { 252 1.164 apb local line="$1" 253 1.164 apb rc_log_message "${line}" 254 1.164 apb if $rc_silent; then 255 1.164 apb eval "$rc_silent_cmd" 256 1.164 apb else 257 1.164 apb printf "%s\n" "${line}" 258 1.164 apb fi 259 1.164 apb } 260 1.164 apb 261 1.164 apb # 262 1.166 apb # rc_postprocess_partial_line string 263 1.166 apb # This is just like rc_postprocess_plain_line, except that 264 1.166 apb # a newline is not appended to the string. 265 1.166 apb # 266 1.166 apb rc_postprocess_partial_line() 267 1.166 apb { 268 1.166 apb local line="$1" 269 1.166 apb rc_log_message_n "${line}" 270 1.166 apb if $rc_silent; then 271 1.166 apb eval "$rc_silent_cmd" 272 1.166 apb else 273 1.166 apb printf "%s" "${line}" 274 1.166 apb fi 275 1.166 apb } 276 1.166 apb 277 1.166 apb # 278 1.164 apb # rc_postprocess_metadata string 279 1.164 apb # $1 is a string containing metadata from the rc_real_work() 280 1.164 apb # function. The rc_metadata_prefix marker should already 281 1.164 apb # have been removed before the string is passed to this function. 282 1.164 apb # Take appropriate action depending on the content of the string. 283 1.164 apb # 284 1.164 apb rc_postprocess_metadata() 285 1.164 apb { 286 1.164 apb local metadata="$1" 287 1.164 apb local keyword args 288 1.164 apb local msg 289 1.164 apb local IFS=':' 290 1.164 apb 291 1.164 apb # given metadata="bleep:foo bar:baz", 292 1.164 apb # set keyword="bleep", args="foo bar:baz", 293 1.164 apb # $1="foo bar", $2="baz" 294 1.164 apb # 295 1.164 apb keyword="${metadata%%:*}" 296 1.164 apb args="${metadata#*:}" 297 1.164 apb set -- $args 298 1.164 apb 299 1.164 apb case "$keyword" in 300 1.164 apb start) 301 1.167 apb # Marks the start of the entire /etc/rc script. 302 1.167 apb # $args contains a date/time. 303 1.164 apb rc_log_message "[$0 starting at $args]" 304 1.164 apb if ! $rc_silent; then 305 1.164 apb printf "%s\n" "$args" 306 1.164 apb fi 307 1.164 apb ;; 308 1.164 apb cmd-name) 309 1.167 apb # Marks the start of a child script (usually one of 310 1.167 apb # the /etc/rc.d/* scripts). 311 1.164 apb rc_log_message "[running $1]" 312 1.164 apb ;; 313 1.164 apb cmd-status) 314 1.167 apb # Marks the end of a child script. 315 1.164 apb # $1 is a command name, $2 is the command's exit status. 316 1.164 apb # If the command failed, report it, and add it to a list. 317 1.164 apb if [ "$2" != 0 ]; then 318 1.164 apb rc_failures="${rc_failures}${rc_failures:+ }$1" 319 1.165 christos msg="$1 $(human_exit_code $2)" 320 1.164 apb rc_log_message "$msg" 321 1.164 apb if ! $rc_silent; then 322 1.164 apb printf "%s\n" "$msg" 323 1.164 apb fi 324 1.164 apb fi 325 1.175 martin # After the CRITLOCALMOUNTED script has finished, it's 326 1.164 apb # OK to flush the log to disk 327 1.164 apb case "$1" in 328 1.175 martin */CRITLOCALMOUNTED) 329 1.164 apb rc_log_flush OK 330 1.164 apb ;; 331 1.164 apb esac 332 1.164 apb ;; 333 1.166 apb nop) 334 1.166 apb # Do nothing. 335 1.167 apb # This has the side effect of flushing partial lines, 336 1.167 apb # and the echo() and printf() functions in rc.subr take 337 1.167 apb # advantage of this. 338 1.166 apb ;; 339 1.164 apb note) 340 1.167 apb # Unlike most metadata messages, which should be used 341 1.167 apb # only by /etc/rc and rc.subr, the "note" message may be 342 1.167 apb # used directly by /etc.rc.d/* and similar scripts. 343 1.167 apb # It adds a note to the log file, without displaying 344 1.167 apb # it to stdout. 345 1.164 apb rc_log_message "[NOTE: $args]" 346 1.164 apb ;; 347 1.164 apb end) 348 1.167 apb # Marks the end of processing, after the last child script. 349 1.167 apb # If any child scripts (or other commands) failed, report them. 350 1.164 apb # 351 1.164 apb if [ -n "$rc_failures" ]; then 352 1.164 apb rc_log_message "[failures]" 353 1.164 apb msg="The following components reported failures:" 354 1.164 apb msg="${msg}${nl}$( echo " ${rc_failures}" | fmt )" 355 1.164 apb msg="${msg}${nl}See ${RC_LOG_FILE} for more information." 356 1.164 apb rc_log_message "${msg}" 357 1.164 apb printf "%s\n" "${msg}" 358 1.164 apb fi 359 1.164 apb # 360 1.164 apb # Report the end date/time, even in silent mode 361 1.164 apb # 362 1.164 apb rc_log_message "[$0 finished at $args]" 363 1.164 apb printf "%s\n" "$args" 364 1.164 apb ;; 365 1.164 apb exit) 366 1.167 apb # Marks an exit from the rc_real_work() function. 367 1.167 apb # This may be a normal or abnormal exit. 368 1.167 apb # 369 1.164 apb rc_log_message "[$0 exiting with status $1]" 370 1.164 apb exit $1 371 1.164 apb ;; 372 1.164 apb interrupted) 373 1.167 apb # Marks an interrupt trapped by the rc_real_work() function. 374 1.167 apb # $args is a human-readable message. 375 1.164 apb rc_log_message "$args" 376 1.164 apb printf "%s\n" "$args" 377 1.164 apb ;; 378 1.164 apb *) 379 1.164 apb # an unrecognised line of metadata 380 1.164 apb rc_log_message "[metadata:${metadata}]" 381 1.164 apb ;; 382 1.164 apb esac 383 1.164 apb } 384 1.164 apb 385 1.164 apb # 386 1.164 apb # rc_log_message string [...] 387 1.166 apb # Write a message to the log file, or buffer it for later. 388 1.166 apb # This function appends a newline to the message. 389 1.164 apb # 390 1.164 apb rc_log_message() 391 1.164 apb { 392 1.164 apb _rc_log_buffer="${_rc_log_buffer}${*}${nl}" 393 1.164 apb rc_log_flush 394 1.164 apb } 395 1.1 cgd 396 1.164 apb # 397 1.166 apb # rc_log_message_n string [...] 398 1.166 apb # Just like rc_log_message, except without appending a newline. 399 1.166 apb # 400 1.166 apb rc_log_message_n() 401 1.166 apb { 402 1.166 apb _rc_log_buffer="${_rc_log_buffer}${*}" 403 1.166 apb rc_log_flush 404 1.166 apb } 405 1.166 apb 406 1.166 apb # 407 1.164 apb # rc_log_flush [OK|FORCE] 408 1.164 apb # save outstanding messages from $_rc_log_buffer to $RC_LOG_FILE. 409 1.164 apb # 410 1.164 apb # The log file is expected to reside in the /var/run directory, which 411 1.164 apb # may not be writable very early in the boot sequence, and which is 412 1.164 apb # erased a little later in the boot sequence. We therefore avoid 413 1.164 apb # writing to the file until we believe it's safe to do so. We also 414 1.164 apb # assume that it's reasonable to always append to the file, never 415 1.164 apb # truncating it. 416 1.164 apb # 417 1.164 apb # Optional argument $1 may be "OK" to report that writing to the log 418 1.164 apb # file is expected to be safe from now on, or "FORCE" to force writing 419 1.164 apb # to the log file even if it may be unsafe. 420 1.164 apb # 421 1.164 apb # Returns a non-zero status if messages could not be written to the 422 1.164 apb # file. 423 1.164 apb # 424 1.164 apb rc_log_flush() 425 1.164 apb { 426 1.164 apb # 427 1.164 apb # If $_rc_log_flush_ok is false, then it's probably too early to 428 1.164 apb # write to the log file, so don't do it, unless $1 is "FORCE". 429 1.164 apb # 430 1.164 apb : ${_rc_log_flush_ok=false} 431 1.164 apb case "$1:$_rc_log_flush_ok" in 432 1.164 apb OK:*) 433 1.164 apb _rc_log_flush_ok=true 434 1.164 apb ;; 435 1.164 apb FORCE:*) 436 1.164 apb : OK just this once 437 1.164 apb ;; 438 1.164 apb *:true) 439 1.164 apb : OK 440 1.164 apb ;; 441 1.164 apb *) 442 1.164 apb # it's too early in the boot sequence, so don't flush 443 1.164 apb return 1 444 1.164 apb ;; 445 1.164 apb esac 446 1.164 apb 447 1.164 apb # 448 1.164 apb # Now append the buffer to the file. The buffer should already 449 1.164 apb # contain a trailing newline, so don't add an extra newline. 450 1.164 apb # 451 1.164 apb if [ -n "$_rc_log_buffer" ]; then 452 1.164 apb if { printf "%s" "${_rc_log_buffer}" >>"${RC_LOG_FILE}" ; } \ 453 1.164 apb 2>/dev/null 454 1.164 apb then 455 1.164 apb _rc_log_buffer="" 456 1.164 apb else 457 1.164 apb return 1 458 1.164 apb fi 459 1.164 apb fi 460 1.164 apb return 0 461 1.164 apb } 462 1.164 apb 463 1.164 apb # 464 1.164 apb # Most of the action is in the rc_real_work() and rc_postprocess() 465 1.164 apb # functions. 466 1.164 apb # 467 1.164 apb rc_real_work "$@" 2>&1 | rc_postprocess 468 1.164 apb exit $? 469