loongbedrocklinux-userlandd/src/init/init
2022-07-28 17:31:45 +08:00

476 lines
14 KiB
Plaintext
Executable File

#!/bedrock/libexec/busybox sh
#
# init
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# Copyright (c) 2014-2019 Daniel Thau <danthau@bedrocklinux.org>
#
# Bedrock Linux meta-init system
. /bedrock/share/common-code
# Remove typical runtime sanity checks, as crashing during init is going to
# just cause difficult to debug headaches for most users.
set +eu
trap '' EXIT
fatal_error() {
echo ""
printf "${color_alert}${*}${color_norm}\\n"
echo ""
echo "Starting emergency shell"
echo "Good luck"
echo ""
exec sh
}
setup_term() {
# Ensure plymouth is not running, as it will fight with us over control
# of the terminal.
/bedrock/libexec/plymouth-quit
# Plymouth will lock the terminal settings. This causes the following
# commands to fail if not unlocked.
/bedrock/libexec/manage_tty_lock unlock
# Ensure sane terminal settings. This is probably overkill.
reset
stty sane
stty cooked
reset
}
ensure_essential_environment() {
if ! [ -r /proc/mounts ] || ! grep -q "^\\w* /proc proc" /proc/mounts 2>&1 | head -n0; then
mkdir -p /proc
mount -t proc proc /proc
fi
mount -o remount,rw / 2>&1 | head -n0
if ! grep -q "^\\w* /sys sysfs" /proc/mounts; then
mkdir -p /sys
mount -t sysfs sysfs /sys
fi
if ! grep -q "^\\w* /dev devtmpfs" /proc/mounts; then
mkdir -p /dev
mount -t devtmpfs devtmpfs /dev
mdev -s
fi
if ! grep -q "^\\w* /dev/pts devpts" /proc/mounts; then
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
fi
if ! grep -q "^\\w* /run tmpfs" /proc/mounts; then
mkdir -p /run
mount -t tmpfs tmpfs /run
fi
# If the user runs a non-zstd-aware depmod with a zstd-based module
# set, it may clear modules.dep. If so, fix modules.dep.
if [ -e /lib/modules/$(uname -r)/modules.dep ] && \
[ "$(stat -c%s "/lib/modules/$(uname -r)/modules.dep")" -eq 0 ]; then
printf "Regenerating modules.dep... "
/sbin/depmod
echo "done"
fi
if ! grep -q "\\<fuse\\>" /proc/filesystems; then
/sbin/modprobe fuse
fi
if ! [ -e /dev/fuse ]; then
mkdir -p /dev
mknod -m 660 /dev/fuse c 10 229
fi
if ! grep -q "\\<binfmt_misc\\>" /proc/filesystems; then
/sbin/modprobe binfmt_misc >/dev/null 2>&1 || true
fi
# Systemd seems confused when an initrd-systemd launches Bedrock's init
# which launches userland-systemd. Kill /run/systemd to break the
# relationship so systemd thinks it is starting fresh.
if [ -d /run/systemd ]; then
rm -r /run/systemd
fi
# Some networking software make /etc/resolv.conf into a symlink. Other
# networking software seems confused by this. This can cause problems
# when switching init-providing strata (and thus their networking
# stacks) during a reboot. To avoid this, remove /etc/resolv.conf
# symlinks here. Network software should re-recreate this file as
# needed.
if [ -h /etc/resolv.conf ]; then
rm /etc/resolv.conf
fi
}
complete_hijack() {
step_init "6"
notice "Completing ${color_term}hijack install${color_norm}"
hijacked="$(deref hijacked)"
step "Moving ${color_strat}${hijacked}${color_norm} files to ${color_file}/bedrock/strata/${hijacked}${color_norm}"
mkdir -p "/bedrock/strata/${hijacked}"
set_attr "/bedrock/strata/${hijacked}" "stratum" "${hijacked}"
cd /
for item in *; do
case "${item}" in
"proc" | "sys" | "dev" | "run" | "boot" | bedrock*)
continue
;;
esac
mv "${item}" "/bedrock/strata/${hijacked}/${item}"
done
step "Moving ${color_strat}global${color_norm} items to ${color_file}/${color_norm}"
set_attr "/" "stratum" "bedrock"
for global in $(cfg_values "global" "share") $(cfg_values "global" "bind"); do
case "${global}" in
"/proc" | "/sys" | "/dev" | "/run" | "/boot" | /bedrock*)
continue
;;
esac
if [ -e "/bedrock/strata/${hijacked}${global}" ] ||
[ -h "/bedrock/strata/${hijacked}${global}" ]; then
mkdir -p "$(dirname "${global}")"
mv "/bedrock/strata/${hijacked}${global}" "${global}"
mkdir -p "/bedrock/strata/${hijacked}${global}"
else
mkdir -p "${global}"
fi
done
for global in $(cfg_values "global" "etc"); do
mkdir -p "$(dirname "/etc/${global}")"
if [ -e "/bedrock/strata/${hijacked}/etc/${global}" ] ||
[ -h "/bedrock/strata/${hijacked}/etc/${global}" ]; then
mv "/bedrock/strata/${hijacked}/etc/${global}" "/etc/${global}"
fi
done
step "Creating root files and directories"
for dir in /bin /dev /etc /lib/systemd /mnt /proc /root /run /sbin /sys /tmp /usr/bin /usr/sbin /usr/share/info /var; do
mkdir -p "${dir}"
done
ln -s ../bedrock/etc/bedrock-release /etc/bedrock-release
ln -s ../bedrock/etc/os-release /etc/os-release
ln -s /bedrock/libexec/kmod /sbin/depmod
ln -s /bedrock/libexec/kmod /sbin/insmod
ln -s /bedrock/libexec/kmod /sbin/lsmod
ln -s /bedrock/libexec/kmod /sbin/modinfo
ln -s /bedrock/libexec/kmod /sbin/modprobe
ln -s /bedrock/libexec/kmod /sbin/rmmod
ln -s /bedrock/strata/hijacked/usr/share/grub /usr/share/grub
/bedrock/libexec/busybox --install -s
touch /usr/share/info/.keepinfodir
mv "/bedrock/strata/${hijacked}/sbin/init" "/sbin/init"
step "Restoring ${color_term}hijacked${color_norm} init systems"
prefix="/bedrock/strata/${hijacked}"
for init in /bin/init /sbin/init /usr/bin/init /usr/sbin/init /lib/systemd/systemd /usr/lib/systemd/systemd; do
if [ -h "${prefix}${init}-bedrock-backup" ] || [ -e "${prefix}${init}-bedrock-backup" ]; then
mv "${prefix}${init}-bedrock-backup" "${prefix}${init}"
fi
done
step "Granting ${color_cmd}strat${color_norm} necessary capabilities"
/bedrock/libexec/setcap cap_sys_chroot=ep /bedrock/bin/strat
step "Completing ${color_term}hijack install${color_norm}"
rm /bedrock/complete-hijack-install
echo ""
}
complete_upgrade() {
# All crossfs builds prior to 0.7.8 became confused if bouncer changed
# out from under them. If upgrading such a version, bouncer was not
# upgraded in-place. Complete the upgrade now while crossfs is not
# running.
if [ -e /bedrock/libexec/bouncer-0.7.9 ]; then
mv /bedrock/libexec/bouncer-0.7.9 /bedrock/libexec/bouncer
fi
}
wait_for_keyboard() {
# Users with keyboards that take a while to initialize have reported
# surprise at the inability to type in the init selection menu.
# Vocally wait for a keyboard to be available to assuage these
# concerns.
for module in $(cfg_values "init" "modules"); do
/sbin/modprobe "${module}" 2>/dev/null
done
if /bedrock/libexec/keyboard_is_present; then
return
fi
printf "Waiting up to %s seconds for keyboard initialization..." "${init_timeout}"
start="$(date +%s)"
end="$((init_timeout + start))"
while [ "$(date +%s)" -lt "${end}" ] && ! /bedrock/libexec/keyboard_is_present; do
usleep 250000 # quarter second
done
printf "\r"
printf "Waiting up to %s seconds for keyboard initialization..." "${init_timeout}" | sed 's/./ /g'
printf "\r"
if ! /bedrock/libexec/keyboard_is_present; then
notice "${color_warn}WARNING: unable to detect keyboard${color_norm}"
fi
}
list_init_options() {
for stratum in $(list_strata); do
if [ "${stratum}" = "bedrock" ]; then
continue
fi
if ! has_attr "/bedrock/strata/${stratum}" "show_init"; then
continue
fi
for cmd in $(cfg_values "init" "paths"); do
sproc="/bedrock/strata/${stratum}/proc"
mkdir -p "${sproc}" 2>/dev/null || true
mount -t proc proc "${sproc}"
link="$(chroot "/bedrock/strata/${stratum}" "/proc/1/root/bedrock/libexec/busybox" realpath "${cmd}" 2>/dev/null)" || true
path="/bedrock/strata/${stratum}${link}"
if [ -n "${link:-}" ] && [ -x "${path}" ]; then
echo "${stratum} ${path} ${cmd} ${link}"
fi
umount "${sproc}"
done
done | awk '
{
if (!($2 in cmds)) {
cmds[$2] = $0
next
}
split($0, a)
if (a[3] == a[4] && $3 != $4) {
cmds[$2] = $0
}
}
END {
for (i in cmds) {
print cmds[i]
}
}
' | sort | uniq
}
pretty_print_options() {
i=0
IFS="
"
for option in $(list_init_options); do
i=$((i + 1))
stratum="$(echo "${option}" | cut -d" " -f1)"
path="$(echo "${option}" | cut -d" " -f2)"
cmd="$(echo "${option}" | cut -d" " -f3)"
link="$(echo "${option}" | cut -d" " -f4)"
if [ "${path}" = "${def_path}" ]; then
star="${color_glue}*${color_norm}"
else
star=" "
fi
if [ "${cmd}" != "${link}" ]; then
res="${color_glue} -> ${color_cmd}${link}"
else
res=""
fi
printf "${star}${color_sub}%2s${color_norm}. ${color_strat}${stratum}${color_glue}:${color_cmd}${cmd}${res}${color_norm}\\n" "${i}"
done
}
get_init_choice() {
echo "Select init number to use for this session" >&2
echo "See /bedrock/etc/bedrock.conf [init] to change default init and timeout" >&2
echo "" >&2
max="$(list_init_options | wc -l)"
wait_for_keyboard >&2
while true; do
pretty_print_options >&2
printf "\\nInit number: " >&2
if [ "${init_timeout}" -eq 0 ] && [ -n "${def_path}" ] && [ -x "${def_path}" ]; then
selected_num=0
elif [ "${init_timeout}" -gt 0 ] && [ -n "${def_path}" ] && [ -x "${def_path}" ]; then
printf "(${color_sub}${init_timeout}s${color_norm}): " >&2
read -r -t "${init_timeout}" selected_num
else
read -r selected_num
fi
if [ -z "${selected_num}" ]; then
selected_num=0
fi
if [ -n "${def_path}" ] && [ -x "${def_path}" ] && [ "${selected_num}" -eq 0 ]; then
echo "${def_stratum}:${def_cmd}"
return
elif echo "${selected_num}" | grep -q "[^0-9]"; then
true
elif [ "${selected_num}" -gt "${max}" ]; then
true
elif [ "${selected_num}" -gt 0 ]; then
list_init_options | awk -v"n=${selected_num}" 'NR==n {print $1":"$3}'
return
fi
printf "${color_alert}Unrecognized choice, try again.${color_norm}\\n" >&2
done
}
pivot() {
# Can only pivot_root with mount points. Ensure init_stratum root is a mount point.
mount --bind "/bedrock/strata/${init_stratum}" "/bedrock/strata/${init_stratum}"
# Ensure /bedrock is in the stratum so we can continue to utilize it post pivot
mkdir -p "/bedrock/strata/${init_stratum}/bedrock"
mount --bind "/bedrock" "/bedrock/strata/${init_stratum}/bedrock"
# Pivot
cd "/bedrock/strata/${init_stratum}"
pivot_root "." "bedrock/strata/${init_stratum}"
cd /
mount --move "/bedrock/strata/${init_stratum}" "/bedrock/strata/bedrock"
# `man 8 pivot_root` says to `chroot .` here, but in practice this was
# found to make mount points inaccessible, and no problems were found when
# skipping this step.
}
preenable() {
# Preemptively share key directories that are needed to enable
mkdir -p "/bedrock/strata/bedrock/proc"
mount --make-shared "/bedrock/strata/bedrock/proc"
mkdir -p "/proc"
mount --rbind "/bedrock/strata/bedrock/proc" "/proc"
mkdir -p "/bedrock/strata/bedrock/dev"
mount --make-shared "/bedrock/strata/bedrock/dev"
mkdir -p "/dev"
mount --rbind "/bedrock/strata/bedrock/dev" "/dev"
mkdir -p "/bedrock/strata/bedrock/sys"
mount --make-shared "/bedrock/strata/bedrock/sys"
mkdir -p "/sys"
mount --rbind "/bedrock/strata/bedrock/sys" "/sys"
mkdir -p "/bedrock/strata/bedrock/bedrock/run"
mount -t tmpfs bedrock_run "/bedrock/strata/bedrock/bedrock/run"
chmod go-w "/bedrock/strata/bedrock/bedrock"
chmod go-w "/bedrock/strata/bedrock/bedrock/run"
mkdir -p "/bedrock/run"
mount --rbind "/bedrock/strata/bedrock/bedrock/run" "/bedrock/run"
ensure_essential_environment
}
if [ "${$}" -ne 1 ]; then
exec "/sbin/init-bedrock-backup" "${@}"
fi
ensure_essential_environment
setup_term
clear
print_logo "$(cat /bedrock/etc/bedrock-release)"
if [ -e "/bedrock/complete-hijack-install" ]; then
complete_hijack
fi
complete_upgrade
init_timeout="$(cfg_values "init" "timeout")"
def_tuple="$(cfg_values "init" "default")"
def_stratum="$(deref "$(echo "${def_tuple}" | cut -d: -f1)")"
def_cmd="$(echo "${def_tuple}" | cut -d: -f2-)"
def_path=""
if [ -n "${def_tuple}" ] && [ -d "/bedrock/strata/${def_stratum}" ]; then
sproc="/bedrock/strata/${def_stratum}/proc"
mkdir -p "${sproc}" 2>/dev/null || true
mount -t proc proc "${sproc}"
if def_link="$(chroot "/bedrock/strata/${def_stratum}" "/proc/1/root/bedrock/libexec/busybox" realpath "${def_cmd}" 2>/dev/null)"; then
def_path="/bedrock/strata/${def_stratum}${def_link}"
fi
umount "${sproc}"
fi
if [ -n "${def_tuple}" ] && [ -z "${def_path:-}" ]; then
notice "${color_warn}WARNING: ${color_file}/bedrock/etc/bedrock.conf [init]/default ${color_warn}value does not describe a valid stratum:init pair.${color_norm}"
fi
if [ -z "${def_path:-}" ]; then
init_timeout="-1"
def_stratum=""
def_cmd=""
def_link=""
def_path=""
fi
if ! init_tuple="$(grep -q '\<bedrock_init=' /proc/cmdline && sed -e 's/^.*bedrock_init=//' -e 's/[ \t].*$//' /proc/cmdline)"; then
init_tuple="$(get_init_choice)"
fi
init_stratum="$(echo "${init_tuple}" | cut -d: -f1)"
init_cmd="$(echo "${init_tuple}" | cut -d: -f2-)"
echo ""
step_init "6"
step "Mounting ${color_file}fstab${color_norm}"
/bedrock/libexec/dmsetup mknodes
/bedrock/libexec/lvm vgscan --ignorelockingfailure
/bedrock/libexec/lvm vgchange -ay --ignorelockingfailure
mount -a
step "Pivoting to ${color_strat}${init_stratum}${color_norm}"
pivot
step "Preparing to enable"
preenable
step "Enabling ${color_term}strata${color_norm}"
notice "Enabling ${color_strat}bedrock${color_norm}"
# Cannot brl-enable bedrock or init stratum, and cannot brl-repair disabled
# strata. Thus, manually set up minimum required to mark strata as enabled
# then run brl-repair on bedrock and init strata.
mkdir -p /bedrock/strata/bedrock/bedrock/run/enabled_strata
touch /bedrock/strata/bedrock/bedrock/run/enabled_strata/bedrock
ln -s "/bedrock/strata/${init_stratum}" "/bedrock/strata/bedrock/bedrock/run/init-alias"
touch "/bedrock/strata/bedrock/bedrock/run/enabled_strata/${init_stratum}"
/bedrock/libexec/brl-repair --skip-crossfs "bedrock"
notice "Enabling ${color_strat}${init_stratum}${color_norm}"
/bedrock/libexec/brl-repair --skip-crossfs "${init_stratum}"
for stratum in $(list_strata); do
if is_bedrock "${stratum}" ||
is_init "${stratum}" ||
! has_attr "/bedrock/strata/${stratum}" "show_boot"; then
continue
fi
notice "Enabling ${color_strat}${stratum}${color_norm}"
/bedrock/libexec/brl-enable --skip-crossfs "${stratum}"
done
step "Applying configuration"
/bedrock/libexec/brl-apply --skip-repair
step "Handing control off to ${color_strat}${init_stratum}${color_glue}:${color_cmd}${init_cmd}${color_norm}"
if ! [ -x "${init_cmd}" ]; then
fatal_error "Specified (${init_cmd}) is not executable"
fi
# Shellcheck warns about `exec` usage. It is explicitly desired here.
#
# shellcheck disable=SC2093
exec "${init_cmd}" "${@}"
# We should never get here.
# If exec above succeeds, that takes over.
# If exec above fails, we get a kernel panic.
fatal_error "Unable to execute ${init_stratum}:${init_cmd}"