123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- # 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 $@
- }
- err () {
- echo "ERROR: $@"
- }
- log_kmsg() {
- local fmt=$1; shift
- printf "<31>initramfs: $fmt\n" "$@"
- }
- poll_device() {
- local device=$1 seconds=${2//[!0-9]}
- [ "${seconds:-x}" = x ] && seconds=10
- deciseconds=$(( seconds * 10 ))
- # tenths of a second
- sleepinterval=1
- [ -b "$device" ] && return 0
- if [ "$udevd_running" -eq 1 ]; then
- msg "Waiting $seconds seconds for device $device ..." >&2
- while [ ! -b "$device" -a "$deciseconds" -gt 0 ]; do
- if [ "$sleepinterval" -ge 10 ]; then
- sleep 1
- deciseconds=$(( deciseconds - 10 ))
- else
- sleep .$sleepinterval
- deciseconds=$(( deciseconds - sleepinterval ))
- sleepinterval=$(( sleepinterval * 2 ))
- fi
- done
- fi
- [ -b "$device" ]
- }
- launch_interactive_shell() {
- export PS1='[rootfs \W]\$ '
- # 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() {
- local dev
- [ -e "/sys/dev/block/$1:$2" ] || return 1
- if dev=$(readlink -f "/sys/dev/block/$1:$2" 2>/dev/null); then
- echo "/dev/${dev##*/}"
- return 0
- fi
- return 1
- }
- run_hookfunctions() {
- local hook fn=$1 desc=$2
- shift 2
- for hook in "$@"; do
- [ -x "/hooks/$hook" ] || continue
- unset "$fn"
- . "/hooks/$hook"
- type "$fn" >/dev/null || continue
- msg ":: running $desc [$hook]"
- "$fn"
- 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
- for _w in $_cmdline; do
- if [ -z "$_quoted" ]; then
- case $_w in
- # ignore everything after a # in the commandline
- \#*) break ;;
- # special cases
- rw|ro) rwopt=$_w ;;
- fsck.mode=*)
- case ${_w#*=} in
- force)
- forcefsck=y
- ;;
- skip)
- fastboot=y
- ;;
- *)
- err "unknown fsck.mode parameter: '${_w#*=}'"
- ;;
- 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#*=}
- _lhs=${_w%%=*}
- _lhs=${_lhs//[-.]/_}
- if [ '"' = "${_rhs:0:1}" ]; then
- if [ '"' = "${_rhs:$((${#_rhs}-1))}" ]; then
- _rhs="${_rhs:1:$((${#_rhs}-2))}"
- else
- _rhs=${_rhs:1}
- _quoted=1
- continue
- fi
- fi
- eval $_lhs=\$_rhs
- ;;
- [[:alpha:]_]*)
- _lhs=${_w//[-.]/_}
- eval $_lhs=y
- ;;
- esac
- else
- if [ '"' = "${_w:$((${#_w}-1))}" ]; then
- _rhs="$_rhs ${_w%\"}"
- unset _quoted
- eval $_lhs=\$_rhs
- else
- _rhs="$_rhs $_w"
- fi
- fi
- done
- }
- fsck_device() {
- [ -x /sbin/fsck ] || return 255
- if [ ! -b "$1" ]; then
- err "device '$1' not found. Skipping fsck."
- return 255
- fi
- if [ -n "$fastboot" ]; then
- msg ":: skipping fsck on '$1'"
- return
- fi
- msg ":: performing fsck on '$1'"
- fsck -Ta -C"$FSCK_FD" "$1" -- ${forcefsck+-f}
- }
- fsck_root() {
- fsck_device "$root"
- fsckret=$?
- if [ -n "$fsckret" ] && [ "$fsckret" -ne 255 ]; then
- # handle error conditions; do nothing on success.
- if bitfield_has_bit "$fsckret" 4; then
- err "Bailing out. Run 'fsck $root' manually"
- printf '%s\n' \
- "********** FILESYSTEM CHECK FAILED **********" \
- "* *" \
- "* Please run fsck manually. After leaving *" \
- "* this maintenance shell, the system will *" \
- "* reboot automatically. *" \
- "* *" \
- "*********************************************"
- launch_interactive_shell
- echo ":: Automatic reboot in progress"
- sleep 2
- reboot -f
- elif bitfield_has_bit "$fsckret" 2; then
- printf '%s\n' \
- "************** REBOOT REQUIRED **************" \
- "* *" \
- "* automatically restarting in 10 seconds *" \
- "* *" \
- "*********************************************"
- sleep 10
- reboot -f
- elif bitfield_has_bit "$fsckret" 8; then
- err "fsck failed on '$root'"
- elif bitfield_has_bit "$fsckret" 16; then
- err "Failed to invoke fsck: usage or syntax error"
- elif bitfield_has_bit "$fsckret" 32; then
- echo ":: fsck cancelled on user request"
- elif bitfield_has_bit "$fsckret" 128; then
- err "fatal error invoking fsck"
- fi
- # ensure that root is going to be mounted rw. Otherwise, systemd
- # might fsck the device again. Annoy the user so that they fix this.
- if [ "${rwopt:-ro}" != 'rw' ]; then
- echo "********************** WARNING **********************"
- echo "* *"
- echo "* The root device is not configured to be mounted *"
- echo "* read-write! It may be fsck'd again later. *"
- echo "* *"
- echo "*****************************************************"
- fi
- fi
- }
- # TODO: this really needs to follow the logic of systemd's encode_devnode_name
- # function more closely.
- tag_to_udev_path() {
- awk -v "tag=$1" -v "value=$2" '
- BEGIN {
- gsub(/\//, "\\x2f", value)
- printf "/dev/disk/by-%s/%s\n", tolower(tag), value
- }'
- }
- resolve_device() {
- local major minor dev tag device=$1
- # attempt to resolve devices immediately. if this fails
- # and udev is running, fall back on lazy resolution using
- # /dev/disk/by-* symlinks. this is flexible enough to support
- # usage of tags without udev and "slow" devices like root on
- # USB, which might not immediately show up.
- case $device in
- UUID=*|LABEL=*|PARTUUID=*|PARTLABEL=*)
- dev=$(blkid -lt "$device" -o device)
- if [ -z "$dev" -a "$udevd_running" -eq 1 ]; then
- dev=$(tag_to_udev_path "${device%%=*}" "${device#*=}")
- fi
- esac
- [ -n "$dev" ] && device=$dev
- case $device in
- # path to kernel named block device
- /dev/*)
- if poll_device "$device" "$rootdelay"; then
- echo "$device"
- return 0
- fi
- ;;
- # hex encoded major/minor, such as from LILO
- [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]|[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
- major=$(( 0x0$device >> 8 ))
- minor=$(( 0x0$device & 0xff ))
- ;;
- 0x[0-9a-fA-F][0-9a-fA-F]*)
- major=$(( device >> 8 ))
- minor=$(( device & 0xff ))
- ;;
- esac
- if [ -n "$major" -a -n "$minor" ]; then
- device=$(major_minor_to_device "$major" "$minor" || echo '/dev/root')
- if [ ! -b "$device" ]; then
- msg "Creating device node with major $major and minor $minor." >&2
- mknod "$device" b "$major" "$minor"
- fi
- echo "$device"
- return 0
- fi
- return 1
- }
- default_mount_handler() {
- if [ ! -b "$root" ]; then
- err "Unable to find root device '$root'."
- echo "You are being dropped to a recovery shell"
- echo " Type 'exit' to try and continue booting"
- launch_interactive_shell
- msg "Trying to continue (this will most likely fail) ..."
- fi
- msg ":: mounting '$root' on real root"
- if ! mount ${fstype:+-t $fstype} -o ${rwopt:-ro}${rootflags:+,$rootflags} "$root" "$1"; then
- echo "You are now being dropped into an emergency shell."
- launch_interactive_shell
- msg "Trying to continue (this will most likely fail) ..."
- 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
- # messages would be otherwise lost if we don't unset quiet. this does,
- # however, mean that passing rd.log=console will negate the effects of
- # 'quiet' for initramfs console output.
- unset quiet
- }
- 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 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:
|