[BACK]Return to rc CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / etc

Annotation of src/etc/rc, Revision 1.173

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

CVSweb <webmaster@jp.NetBSD.org>