#!/bin/sh # # Tiny init # # https://www.shellcheck.net/wiki/SC2154 # shellcheck disable=2154 print() { printf "%b %s\n" "${2:-"\033[1;37m>>\033[m"}" "$1" } panic() { print "${1:-unexpected error occurred}" \ "\033[1;31m!!\033[m" >&2 sh } resolve_device() { count=0; device="$1" case "${device%%=*}" in UUID) device="/dev/disk/by-uuid/${device#*=}" ;; LABEL) device="/dev/disk/by-label/${device#*=}" ;; PARTUUID) device="/dev/disk/by-partuuid/${device#*=}" ;; /dev/*) ;; *) return 0 ;; esac # Race condition may occur if device manager is not yet initialized device. # To fix this, we simply waiting until device is available. If device # didn't appear in specified time, we panic. while :; do if [ -b "$device" ]; then return 0 elif [ "$((count += 1))" = "${rootdelay:=30}" ]; then break else sleep 1 fi done panic "failed to lookup partition" } run_hook() { type="$1" # Run hooks if any exist. # # https://www.shellcheck.net/wiki/SC1090 # shellcheck disable=1090 for hook in $hooks; do [ -f "/usr/share/tinyramfs/hooks/${hook}/${hook}.${type}" ] || continue . "/usr/share/tinyramfs/hooks/${hook}/${hook}.${type}" done } prepare_environment() { # https://www.shellcheck.net/wiki/SC1091 # shellcheck disable=1091 . /etc/tinyramfs/config export \ PATH=/bin TERM=linux SHELL=/bin/sh \ LANG=C LC_ALL=C PS1="# " HOME=/root mount -t proc -o nosuid,noexec,nodev proc /proc mount -t sysfs -o nosuid,noexec,nodev sys /sys mount -t tmpfs -o nosuid,nodev,mode=0755 run /run mount -t devtmpfs -o nosuid,noexec,mode=0755 dev /dev ln -s /proc/self/fd /dev/fd ln -s fd/0 /dev/stdin ln -s fd/1 /dev/stdout ln -s fd/2 /dev/stderr } parse_cmdline() { read -r cmdline < /proc/cmdline for line in $cmdline; do case "$line" in rootfstype=*) root_type="${line#*=}" ;; rootflags=*) root_opts="${line#*=}" ;; debug=1) set -x ;; ro | rw) rorw="$line" ;; --*) init_args="${cmdline#*-- }"; break ;; *=*) command export "$line" ;; *) command export "${line}=1" ;; esac 2> /dev/null || :; done } mount_root() { [ "$break" = root ] && { print "break before mount_root()"; sh; } resolve_device "$root" # https://www.shellcheck.net/wiki/SC2086 # shellcheck disable=2086 mount \ -o "${rorw:-ro}${root_opts:+,$root_opts}" ${root_type:+-t $root_type} \ -- "$device" /mnt/root || panic "failed to mount root" } boot_system() { [ "$break" = boot ] && { print "break before boot_system()"; sh; } for dir in run dev sys proc; do mount -o move "$dir" "/mnt/root/${dir}" done # POSIX 'exec' has no '-c' flag to execute command with empty environment. # Using 'env -i' instead to prevent leaking exported variables. # # Some implementations of 'switch_root' doesn't conform to POSIX utility # guidelines and doesn't support '--'. This means that we can't guarantee # safety of init_args. # shellcheck disable=2086 exec \ env -i TERM=linux PATH=/bin:/sbin:/usr/bin:/usr/sbin \ switch_root /mnt/root "${init-/sbin/init}" $init_args || panic "failed to boot system" } # Exit if command fails and disable globbing. set -ef # Run emergency shell if init unexpectedly exiting due to error. trap panic EXIT # TODO display fancy colored status info prepare_environment parse_cmdline run_hook init mount_root run_hook init.late boot_system