tinyramfs/init

147 lines
3.7 KiB
Bash
Executable File

#!/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