diff options
author | Dave Reisner <dreisner@archlinux.org> | 2013-06-17 16:06:58 +0200 |
---|---|---|
committer | Dave Reisner <dreisner@archlinux.org> | 2013-06-25 23:16:41 +0200 |
commit | c61d4791d34591bb0fed558494a52d517d900b44 (patch) | |
tree | 297628ef2cece133a65981480d0ae5b99bc2abcb /init_functions | |
parent | d6e92394a4c133eb652714bb9aa13008c808301b (diff) | |
download | mkinitcpio-c61d4791d34591bb0fed558494a52d517d900b44.tar.gz mkinitcpio-c61d4791d34591bb0fed558494a52d517d900b44.tar.xz |
init: allow for logging of early userspace
This introduces support for the rd.log and rd.debug kernel command line
options, which log early userspace activity to /run/initramfs/init.log.
Code is largely inspired by Dracut's implementation of early userspace
logging, but without needless complexity and redundancies.
Signed-off-by: Dave Reisner <dreisner@archlinux.org>
Diffstat (limited to 'init_functions')
-rw-r--r-- | init_functions | 147 |
1 files changed, 145 insertions, 2 deletions
diff --git a/init_functions b/init_functions index 98bf354..7e591fc 100644 --- a/init_functions +++ b/init_functions @@ -1,5 +1,11 @@ # This file contains common functions used in init and in hooks +# logging targets +_rdlog_file=$(( 1 << 0 )) +_rdlog_kmsg=$(( 1 << 1 )) +_rdlog_cons=$(( 1 << 2 )) +_rdlog_all=$(( (1 << 3) - 1 )) + msg () { [ "${quiet}" != "y" ] && echo $@ } @@ -7,6 +13,11 @@ err () { echo "ERROR: $@" } +log_kmsg() { + local fmt=$1; shift + printf "<31>initramfs: $fmt\n" "$@" +} + poll_device() { local device=$1 seconds=${2//[!0-9]} @@ -37,8 +48,17 @@ poll_device() { launch_interactive_shell() { export PS1='[rootfs \W]\$ ' - [ "$1" = "--exec" ] && exec sh -i - sh -i + + # explicitly redirect to /dev/console in case we're logging. note that + # anything done in the rescue shell will NOT be logged. + { + [ "$1" = "--exec" ] && exec sh -i + sh -i + } 0</dev/console 1>/dev/console 2>/dev/console +} + +bitfield_has_bit() { + [ $(( $1 & $2 )) -eq $2 ] } major_minor_to_device() { @@ -70,6 +90,30 @@ run_hookfunctions() { done } +set_log_option() { + local opt + + for opt in ${1//|/ }; do + case $opt in + all) + rd_logmask=$_rdlog_all + ;; + kmsg) + rd_logmask=$(( rd_logmask | _rdlog_kmsg )) + ;; + file) + rd_logmask=$(( rd_logmask | _rdlog_file )) + ;; + console) + rd_logmask=$(( rd_logmask | _rdlog_cons )) + ;; + *) + err "unknown rd.log parameter: '$opt'" + ;; + esac + done +} + parse_cmdline() { local _w _quoted _lhs _rhs _cmdline read -r _cmdline @@ -93,6 +137,19 @@ parse_cmdline() { ;; esac ;; + rd.*) + case ${_w#rd.} in + debug) + rd_debug=y + ;; + log) + rd_logmask=$(( _rdlog_kmsg | _rdlog_cons )) + ;; + log=*) + set_log_option "${_w#rd.log=}" + ;; + esac + ;; # abide by shell variable naming rules [[:alpha:]_]*=*) _rhs=${_w#*=} @@ -261,4 +318,90 @@ default_mount_handler() { fi } +rdlogger_start() { + # rd.debug implies rd.log=console if rd.log(=.*)? isn't present + if [ "$rd_logmask" -eq 0 ] && [ -n "$rd_debug" ]; then + rd_logmask=$_rdlog_cons + fi + + [ "$rd_logmask" -gt 0 ] || return + + mkfifo /run/initramfs/rdlogger.pipe + + rdlogger </run/initramfs/rdlogger.pipe >/dev/console 2>&1 & + printf %s $! >/run/initramfs/rdlogger.pid + + exec >/run/initramfs/rdlogger.pipe 2>&1 + [ -n "$rd_debug" ] && set -x +} + +rdlogger_stop() { + local i=0 pid + + [ -e /run/initramfs/rdlogger.pipe ] || return + + [ -n "$rd_debug" ] && { set +x; } 2>/dev/null + + # this nudges rdlogger to exit + exec 0<>/dev/console 1<>/dev/console 2<>/dev/console + + # wait up to 1 second for a graceful shutdown + until [ ! -e /run/initramfs/rdlogger.pipe ] || [ $i -eq 10 ]; do + sleep 0.1 + i=$(( i + 1 )) + done + + if [ $i -eq 10 ]; then + # racy! the logger might still go away on its own + read -r pid </run/initramfs/rdlogger.pid 2>/dev/null + if [ -n "$pid" ]; then + kill "$pid" 2>/dev/null + fi + fi +} + +rdlogger() { + local line + + # establish logging FDs. Either redirect to an appropriate target or to + # /dev/null. This way, log methods can simply write and the associated FD + # will Do The Right Thing™. + + # rd.log=console + if [ -z "$quiet" ] && bitfield_has_bit "$rd_logmask" "$_rdlog_cons"; then + exec 4>/dev/console + else + exec 4>/dev/null + fi + + # rd.log=kmsg + if [ -c /dev/kmsg ] && bitfield_has_bit "$rd_logmask" "$_rdlog_kmsg"; then + exec 5>/dev/kmsg + else + exec 5>/dev/null + fi + + # rd.log=file + if bitfield_has_bit "$rd_logmask" "$_rdlog_file"; then + exec 6>/run/initramfs/init.log + else + exec 6>/dev/null + fi + + while read -r line; do + # rd.log=console + printf '%s\n' "$line" >&4 + + # rd.log=kmsg + log_kmsg '%s' "$line" >&5 + + # rd.log=file + printf '%s\n' "$line" >&6 + done + + # EOF, shutting down... + exec 4>&- 5>&- 6>&- + rm -f /run/initramfs/rdlogger.pipe /run/initramfs/rdlogger.pid +} + # vim: set ft=sh ts=4 sw=4 et: |