#!/bin/sh # # altboot provides a simple bootmenu before init is beeing run. # There are two default menu entries: "Normal boot" and "Single user mode" # New menu entries can be created be putting files into /etc/altboot-menu. # test -e /etc/altboot.func && . /etc/altboot.func || die "ERROR: /etc/altboot.func not found. Check your installation!" CURRENT_ENV="`set`" VERSION="DEVELOPER SNAPSHOT" # Set some defaults in case altboot.cfg is missing REAL_INIT="/sbin/init.sysvinit" # Read default runlevel from inittab INIT_RUNLEVEL="`cat /etc/inittab | sed -n "/^id\:/s/id\:\([0-9]\)\:.*$/\1/p"`" test -z "$INIT_RUNLEVEL" && INIT_RUNLEVEL=5 case "`uname -r`" in 2.6*) ALTBOOT_CFG_FILE="/etc/altboot-2.6.cfg";; 2.4*) ALTBOOT_CFG_FILE="/etc/altboot-2.4.cfg";; *) echo "Warning: Unknown kernel [`uname -r`], using kernel 2.6 configuration!" ALTBOOT_CFG_FILE="/etc/altboot-2.6.cfg";; esac test -e "$ALTBOOT_CFG_FILE" && . "$ALTBOOT_CFG_FILE" || echo "WARNING: No $ALTBOOT_CFG_FILE found! Check your installation of Altboot!" > /dev/tty1 C_RED="\033[31m" C_YELLOW="\033[33m" C_BLUE="\033[34m" C_WHITE="\033[37m" C_RESET="\033[0m" die() { echo -e "${C_RED}ERROR: $1${C_RESET}" >/dev/tty0 exec $SH_SHELL /dev/tty0 2>&1 } debug_shell() { # VT 2 = Opie, VT 3 = GPE test -z "$1" && VT=4 || VT=$1 echo -e "\033c" > /dev/tty$VT echo -en "\nPress to activate the debug shell." > /dev/tty$VT read junk /dev/tty$VT /bin/sh /dev/tty$VT 2>&1 #openvt -lf -c$VT -- /bin/sh /dev/tty$VT 2>&1 } # This function prints the boot-menu # $1: Directory containing the scripts for the menu-items show_menu() { test -z "$1" && die "DEBUG: Parameter 1 is empty in show_menu" ! test -d "$1" && die "show_menu: [$1] not found or no directory." echo "" echo -e "altboot v$VERSION\n" m_entry="" # Build "m_entry" for scripts in /etc/altboot-menu cd $1 cnt=0 ; reset_pref "menu_filelist" for file in `ls -1` do if ! test -d "$1/$file" then M_TITLE="`$1/$file title`" if ! test -z "$M_TITLE" then let cnt=$cnt+1 # Keep a list of existing "modules" together with an index number # This sure is ugly, but Busybox sh doesn't do arrays.... #m_entry="`echo -e "$m_entry\n$cnt:$file\n"`" set_pref "menu_filelist" "$cnt" "$file" echo -e "\t\t[$cnt] $M_TITLE" fi M_TITLE="" fi done # Display directories below /etc/altboot-menu as menu-item # and add all scripts inside the directory to m_entry for dir in `ls -1` do if test -d "$1/$dir" then M_TITLE="`basename "$1/$dir"`" if ! test -z "$M_TITLE" then let cnt=$cnt+1 # Keep a list of existing "modules" together with an index number # This sure is ugly, but Busybox sh doesn't do arrays.... #m_entry="`echo -e "$m_entry\n$cnt:$dir:DIR\n"`" set_pref "menu_filelist" "$cnt" "$dir:DIR" echo -e "\t\t[$cnt] $M_TITLE" OLD_PWD="$PWD" cd "$1/$dir" for file in `ls -1` do if ! test -d "$1/$dir/$file" then M_TITLE="`$1/$dir/$file title`" if ! test -z "$M_TITLE" then let cnt=$cnt+1 # Keep a list of existing "modules" together with an index number # This sure is ugly, but Busybox sh doesn't do arrays.... #m_entry="`echo -e "$m_entry\n$cnt:$dir/$file\n"`" set_pref "menu_filelist" "$cnt" "$dir/$file" #echo -e "\t\t[$cnt] $M_TITLE" fi M_TITLE="" fi done cd "$OLD_PWD" fi M_TITLE="" fi done #echo_pref "menu_filelist" echo "" } # This function is used to display the content of directories below # /etc/altboot-menu as menu-items show_sub_menu() { dirname="`basename "$1"`" d_entries="`dump_pref "menu_filelist" | grep "$dirname/"`" #echo "[$d_entries]" echo -e "\naltboot v$VERSION: $dirname menu\n" for d_entry in $d_entries do d_entry_number="`echo "$d_entry"| sed -n "s/\(.*\)\#\#\(.*\)\#\#\#/\1/p"`" d_entry_file="`echo "$d_entry"| sed -n "s/\(.*\)\#\#\(.*\)\#\#\#/\2/p"`" d_entry_title="`$d_entry_file title`" # echo "number: [$d_entry_number]" # echo "file: [$d_entry_file]" # echo "title: [$d_entry_title]" echo -e "\t\t[$d_entry_number] $d_entry_title" done echo "" } # Shows the timer and sets $launch_altboot to yes if a keypress was detected run_timer() { if test "$TIMEOUT" != 0 then mount -t proc proc /proc >/dev/null 2>&1 case "`uname -r`" in 2.4*) key_ints="`cat /proc/interrupts | grep keyboard | awk '{print $2}'`";; 2.6*) key_ints="`cat /proc/interrupts | grep Spitzkbd`" test -z "$key_ints" && key_ints="`cat /proc/interrupts | grep -i corgikbd`";; esac stty -echo /dev/tty0 2>&1 echo -en "\n\nPlease press any key to launch altboot." > /dev/tty1 test -z "$TIMEOUT" && TIMEOUT="3" cnt=0 while test "$cnt" != "$TIMEOUT" do sleep 1 case "`uname -r`" in 2.4*) key_ints_now="`cat /proc/interrupts | grep keyboard | awk '{print $2}'`";; 2.6*) key_ints_now="`cat /proc/interrupts | grep Spitzkbd`" test -z "$key_ints_now" && key_ints_now="`cat /proc/interrupts | grep -i corgikbd`";; esac if test "$key_ints_now" != "$key_ints" -o -z "$key_ints_now" then launch_altboot=yes stty echo /dev/tty0 2>&1 break fi echo -n "." >/dev/tty1 let cnt=$cnt+1 done if test "$launch_altboot" != "yes" then AUTOBOOT=yes else rm -f /etc/.altboot*.last fi else launch_altboot=yes fi } # This function launches the selected menu item / script # $1: Directory containing the scripts for the menu-items launch_selection() { test -z "$1" && die "Parameter 1 of launch_selection is empty!" case "$junk" in *) #file="`echo "$m_entry"| sed -n "/$junk\:/s/^.*\:\(.*\)/\1/p"`" get_pref "menu_filelist" "$junk" file_ type="`echo "$file_" | sed -n "s/\(.*\)\:\(.*\)/\2/p"`" file="`echo "$file_" | sed -n "s/\(.*\)\:\(.*\)/\1/p"`" test -z "$file" && file="$file_" #echo "[$file_]: [$type] / [$file] ($junk)" # The selected menu-item points to a directory if test "$type" = DIR then show_sub_menu /etc/altboot-menu/$file >/dev/tty0 wait_for_input >/dev/tty0 launch_selection /etc/altboot-menu >/dev/tty0 fi if test "$type" = MAIN then show_sub_menu /etc/altboot-menu >/dev/tty0 wait_for_input >/dev/tty0 launch_selection /etc/altboot-menu >/dev/tty0 fi . $1/$file run "$file" >/dev/tty0 die "WARNING: Using failsafe shell" >/dev/tty0 ;; esac } wait_for_input() { while true do # Do _not_ change the next few lines! # # This is required to work around an annoying busybox bug. # Every key you press while this script runs will be # picked up by the next "read $junk". # So the next read would pick up the "any" key the user pressed # above to launch the altboot menu. # Bash throws an ugly error on kill if ! (readlink /bin/sh | grep -q bash) then # This filters an "" from the user as "any key" ( while :; do read x< /dev/tty0 2>&1; done; ) > /dev/null 2>&1 & sleep 1; kill $! >/dev/null 2>&1 fi echo -n "Please choose one of the above [$last_selection]: " /dev/tty0 2>&1 stty echo /dev/tty0 2>&1 read junk< /dev/tty0 2>&1 # This filters other chars the user may have used #echo "junk: [$junk]" junk="`echo "$junk" | sed "s/[a-zA-Z]//g"`" #echo "junk: [$junk]" if test "$junk" -lt "$cnt" -o "$junk" -eq "$cnt" then if test ! -z "$junk" then # Don't remount rw if the drive is already mounted rw # Only helpful for testing / debugging if test "`mount|sed -n "/\/dev\/root/s/.*(\(.*\))/\1/p"`" != "rw" then mount -o remount,rw / >/dev/null 2>&1 echo "$junk" > /etc/altboot.conf mount -o remount,ro / >/dev/null 2>&1 else echo "$junk" > /etc/altboot.conf fi else junk="$last_selection" break fi break fi done } # * * * * * * This is the main function * * * * * * # Note: this is positively ugly. If someone knows a better way to detect wheter # we are already booted into a runlevel _without_ reading /var and / or using `runlevel` # PLEASE let me know. if ( echo "$VERSION" | egrep -iq "(snapshot|-rc)" ) then ENABLE_DEBUG="yes" debug_shell 4 >/dev/null 2>&1 & fi if test -f /proc/cmdline -a "`ps ax|wc -l|tr -d " "`" -gt 30 -a "$1" != "-force" then echo "altboot: Using real init [$REAL_INIT] [$*] [`ps ax|wc -l|tr -d " "`] *" >/dev/tty1 exec $REAL_INIT $* # exec $SH_SHELL /dev/tty0 2>&1 exit 0 else # Boot original init if altboot is turned off if test "$ENABLE_ALTBOOT" != "yes" then echo "altboot: Using real init [$REAL_INIT] **" >/dev/tty1 exec $REAL_INIT $INIT_RUNLEVEL exit 0 fi # Execute scripts in /etc/altboot.rc before doing anything else. # Required in special situations, like booting spitz RC_FILES=`ls /etc/altboot.rc | grep \.sh$` for file in $RC_FILES do . /etc/altboot.rc/$file >/dev/tty1 2>&1 || echo "/etc/altboot.rc/$file failed!" done # Make sure altboots master password is set set_password >/dev/tty0 test "$ASK_PW_ON_BOOT" = "yes" && verify_master_pw >/dev/tty0 # When started with -force, always print the menu echo "$*" | grep -q -- "-force" && TIMEOUT=0 # This timeout works by reading /proc/interrupts to see if the keyboard interrupt # increases while the timer is running. A TIMEOUT of 0 will always launch altboot. run_timer >/dev/tty1 echo "" >/dev/tty0 if test "$launch_altboot" != yes then # last_selection is the previously selected menu item by the user last_selection="`cat /etc/altboot.conf`" >/dev/null 2>&1 test -z "$last_selection" && last_selection="1" echo "Booting last selection: [$last_selection]" >/dev/tty0 # Set up the wanna-be array of available menu entries and their numbers show_menu /etc/altboot-menu >/dev/null junk="$last_selection" launch_selection /etc/altboot-menu >/dev/tty0 fi # Anything after this point will never be reached if $launch_altboot != yes # Show the altboot menu stty -echo /dev/tty0 2>&1 show_menu /etc/altboot-menu >/dev/tty0 # Load last selection for use as default if is pressed at the prompt last_selection="`cat /etc/altboot.conf`" >/dev/null 2>&1 test -z "$last_selection" && last_selection="1" # Ask the user which menu-item to use wait_for_input >/dev/tty0 # This should _never_ happen. if test -z "$junk" then echo "WARNING: Trying failsafe mode" >/dev/tty0 mount -o remount,rw / >/dev/tty0 2>&1 echo "Dumping environment to /altboot.env" echo "$CURRENT_ENV" > /altboot.env mount -o remount,ro / >/dev/tty0 2>&1 junk=1 fi launch_selection /etc/altboot-menu >/dev/tty0 # Uhoh, something went terribly wrong if we reach this point! die "WARNING: Failsafe fall-through activated. Spawning emergency shell" fi