#!/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" ALTBOOT_LAST_FILE="/etc/altboot.last" # 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 # If this step fails the results are fatal. Seen on Collie / kernel 2.4 (where else...) OUT_TTY="`tty`" ; test -z "$OUT_TTY" && OUT_TTY="/dev/tty1" if ( echo "$OUT_TTY" | grep -q "not" ) then OUT_TTY="/dev/tty1" echo "WARNING: Assignment of OUT_TTY failed (2)!" > "$OUT_TTY" fi 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!" > "$OUT_TTY" if test -e "${ALTBOOT_CFG_FILE}.next-reboot" then . "${ALTBOOT_CFG_FILE}.next-reboot" rm "${ALTBOOT_CFG_FILE}.next-reboot" fi ENABLE_DEBUG="$ENABLE_DEBUGGING" 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}" >"$OUT_TTY" exec $SH_SHELL <"$OUT_TTY" >"$OUT_TTY" 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="" cd $1 cnt=0 ; reset_pref "menu_filelist" for file in `ls -1` do # debug_echo "show_menu(): file = [$file]" if ! test -d "$1/$file" then # NOTE: It is important to use "." here so that the script inherits # the shell environment / all set variables! M_TITLE="`. $1/$file title`" FLAGS="`$1/$file flags`" 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" test -n "$FLAGS" && set_pref "menu_fileflags" "$cnt" "$FLAGS" echo -e "\t\t[$cnt] $M_TITLE" fi M_TITLE="" fi done # debug_echo "FILES READ" # 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.... set_pref "menu_filelist" "$cnt" "$dir:DIR" # Set noRemember flag for directories set_pref "menu_fileflags" "$cnt" "noRemember" 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`" FLAGS="`$1/$dir/$file flags`" 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.... set_pref "menu_filelist" "$cnt" "$dir/$file" test -n "$FLAGS" && set_pref "menu_fileflags" "$cnt" "$FLAGS" fi M_TITLE="" fi done cd "$OLD_PWD" else debug_echo "show_menu(): \$M_TITLE is empty" fi M_TITLE="" fi done # debug_echo "DIRS READ" 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 -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 -e "\t\t[$d_entry_number] $d_entry_title" done echo "" } get_kbd_ints(){ if ( uname -r | grep -q ^2.6 ) then if test -z "$KBD_INT" then # find out how the keyboard is called for kbd in Spitzkbd corgikbd locomokbd tosakbd "Neo1973 AUX button" do if ( cat /proc/interrupts | grep -q "$kbd" ) then debug_echo "run_timer(): Using [$kbd] as keyboard interrupt" KBD_INT="$kbd" break fi done fi key_ints="`cat /proc/interrupts | grep "$KBD_INT"`" #debug_echo "run_timer(): key_ints = [$key_ints]" else KBD_INT="$kbd" debug_echo "\nrun_timer(): Using [keyboard] as keyboard interrupt in kernel-2.4 mode" key_ints="`cat /proc/interrupts | grep keyboard | awk '{print $2}'`" fi test -z "$KBD_INT" && debug_echo "Couldn't read keyboard ints!" } # Shows the timer and sets $launch_altboot to yes if a keypress was detected run_timer() { if test "$TIMEOUT" != 0 -a "$TIMEOUT" != "[none]" then mount -t proc proc /proc >/dev/null 2>&1 get_kbd_ints kbd_ints_old="$key_ints" #debug_echo "run_timer() old:`echo $key_ints | md5sum`" stty -echo <"$OUT_TTY" >"$OUT_TTY" 2>&1 case "$KBD_INT" in Neo1973*) echo -en "\n\nPlease press [AUX] to launch altboot." > "$OUT_TTY" ;; *) echo -en "\n\nPlease press any key to launch altboot." > "$OUT_TTY" ;; esac test -z "$TIMEOUT" && TIMEOUT="3" cnt=0 while test "$cnt" != "$TIMEOUT" do sleep 1 get_kbd_ints kbd_ints_new="$key_ints" if test "$kbd_ints_new" != "$kbd_ints_old" -o -z "$kbd_ints_new" then launch_altboot=yes stty echo <"$OUT_TTY" >"$OUT_TTY" 2>&1 break fi echo -n "." >"$OUT_TTY" 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 if test "$TIMEOUT" = "[none]" then echo "" echo -e "altboot v$VERSION ($MACHINE)\n" echo "NOTE: The menu is not available on this device" echo "Use \"set-bootdev\" to change the boot device" launch_altboot=no fi } # $1 = ID parse_module_flags() { if test -n "$1" then # Each module can set "flags" to influence handling of the module get_pref "menu_fileflags" "$1" flags test -n "$flags" && debug_echo "parse_module_flags(): [$1] has flags [$flags]" for flag in $flags do case "$flag" in noRemember) REMEMBER_LAST_SELECTION=no;; esac done else debug_echo "parse_module_flags(): Empty \$1" 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_" # debug_echo "[$file_]: [$type] : [$flags] / [$file] ($junk)" # The selected menu-item points to a directory if test "$type" = DIR then show_sub_menu /etc/altboot-menu/$file >"$OUT_TTY" wait_for_input >"$OUT_TTY" launch_selection /etc/altboot-menu >"$OUT_TTY" fi if test "$type" = MAIN then show_sub_menu /etc/altboot-menu >"$OUT_TTY" wait_for_input >"$OUT_TTY" launch_selection /etc/altboot-menu >"$OUT_TTY" fi MENU_POSITION="$junk" # AUTOSTART is "1" when the timer on boot times out ($USER didn't press # a key) if test "$AUTOSTART" = "true" then # debug_echo "launch_selection(): last_selection_data = [$last_selection_data]" >"$OUT_TTY" . $1/$file autorun "$file" "$last_selection_data" >"$OUT_TTY" else . $1/$file run "$file" >"$OUT_TTY" fi test "$OFFLINE_CONFIG" != "true" && die "WARNING: Using failsafe shell" >"$OUT_TTY" ;; esac } wait_for_input() { # Neo has only two buttons: AUX and PWR. # Only AUX is easily readable from userspace, so are touchscreen taps if test "$KBD_INT" = "Neo1973 AUX button" then ints_old="`cat /proc/interrupts | grep "$KBD_INT" | awk '{print $2}'`" ts_ints_old="`cat /proc/interrupts |grep action| tail -1 | awk '{print $2}'`" echo "Please press [AUX] to set a menu number" <"$OUT_TTY" > "$OUT_TTY" 2>&1 echo -e "Press the touchscreen to start\n" x="$last_selection_num" ; hold_events=0 echo -n "Your Choice: [$x / $cnt]" while true do ts_ints_now="`cat /proc/interrupts |grep action| tail -1 | awk '{print $2}'`" if test "$ts_ints_now" = "$ts_ints_old" then ints_now="`cat /proc/interrupts | grep "$KBD_INT" | awk '{print $2}'`" if test "$ints_now" -gt "$ints_old" then # Only react on every second interupt as both PRESS and RELEASE generate # one. if test -n "$tmp" then #debug_echo "wait_for_input(): PRESS/RELEASE EVENT [$ints_now <-> $ints_old]" MAX_ENTRIES="$cnt" test "$x" = "$cnt" && x=1 || let x=$x+1 #\r : go to beginning of the current line #\033[K : completely clear the current line echo -en "\r\033[KYour Choice: [$x / $cnt]" tmp="" else tmp=blahh fi fi ints_old="$ints_now" else # debug_echo "wait_for_input(): TOUCHSCREEN EVENT [$ts_ints_now <-> $ts_ints_old]" ts_ints_old="$ts_ints_now" junk="$x" break fi done else 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< "$OUT_TTY" 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_num]: " <"$OUT_TTY" > "$OUT_TTY" 2>&1 stty echo <"$OUT_TTY" >"$OUT_TTY" 2>&1 read junk< "$OUT_TTY" 2>&1 # This filters other chars the user may have used junk="`echo "$junk" | sed "s/[a-zA-Z]//g"`" if test "$junk" -lt "$cnt" -o "$junk" -eq "$cnt" then if test -z "$junk" then junk="$last_selection_num" parse_module_flags "$junk" break fi break fi done fi } # * * * * * * This is the main function * * * * * * if ( echo "$VERSION" | egrep -iq "(snapshot|-rc)" ) then if test "$ENABLE_DEBUGGING" = "auto" -o -z "$ENABLE_DEBUGGING" then ENABLE_DEBUG="yes" fi fi test "$ENABLE_DEBUG_SHELL" = "yes" && debug_shell 4 >/dev/null 2>&1 & if test "$ENABLE_DEBUGGING" = "yes" then ENABLE_DEBUG="yes" debug_shell 4 >/dev/null 2>&1 & fi # Note: this is positively ugly. If someone knows a better way to detect whether # we are already booted into a runlevel _without_ reading /var and / or using `runlevel` # PLEASE let me know. # # The NSLU2 is an exception as it uses the way-ugly busybox "ps" # mount | grep -q "^proc" || mount -t proc proc /proc >/dev/null 2>&1 MACHINE="`cat /proc/cpuinfo | sed -n "/^Hardware/s/.*\:\ \(.*\)/\1/p"`" # We were called by the user via /sbin/altboot if test "`basename "$0"`" = "altboot" then OFFLINE_CONFIG="true" . /etc/altboot.sbin exit 0 fi case "$MACHINE" in *NSLU2) if test -f /proc/cmdline -a "`ps |wc -l|tr -d " "`" -gt 23 -a "$1" != "-force" -a "$1" != "+force" then user_called=1 fi ;; *) if test -f /proc/cmdline -a "`ps ax|wc -l|tr -d " "`" -gt 30 -a "$1" != "-force" -a "$1"!= "+force" >/dev/null 2>&1 then user_called=1 fi ;; esac if test "$user_called" = "1" then echo "altboot: Using real init [$REAL_INIT] [$*] [`ps |wc -l|tr -d " "`] *" >"$OUT_TTY" 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] **" >"$OUT_TTY" 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 >"$OUT_TTY" 2>&1 || echo "/etc/altboot.rc/$file failed!" done # Make sure altboots master password is set set_password >"$OUT_TTY" test "$ASK_PW_ON_BOOT" = "yes" && verify_master_pw >"$OUT_TTY" # 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 >"$OUT_TTY" echo "" >"$OUT_TTY" # last_selection is the previously selected menu item by the user last_selection_num="`cat "$ALTBOOT_LAST_FILE" |awk '{print $1}'`" >/dev/null 2>&1 last_selection_data="`cat "$ALTBOOT_LAST_FILE" | sed -n "s/[0-9]\{1,\}\ \(.*\)$/\1/p"`" test -z "$last_selection_num" && last_selection_num="1" # launch_altboot is set to "yes" by run_timer when the user presses the button during the timeout if test "$launch_altboot" != yes then echo "Booting last selection: [$last_selection_num]" >"$OUT_TTY" # Set up the wanna-be array of available menu entries and their numbers show_menu /etc/altboot-menu >/dev/null 2>&1 junk="$last_selection_num" ; AUTOSTART="true" launch_selection /etc/altboot-menu >"$OUT_TTY" fi # Anything after this point will never be reached if $launch_altboot != yes # Show the altboot menu stty -echo <"$OUT_TTY" >"$OUT_TTY" 2>&1 show_menu /etc/altboot-menu >"$OUT_TTY" # Ask the user which menu-item to use wait_for_input >"$OUT_TTY" # This should _never_ happen. if test -z "$junk" then echo "WARNING: Trying failsafe mode" >"$OUT_TTY" mount -o remount,rw / >"$OUT_TTY" 2>&1 echo "Dumping environment to /altboot.env" echo "$CURRENT_ENV" > /altboot.env mount -o remount,ro / >"$OUT_TTY" 2>&1 junk=1 fi launch_selection /etc/altboot-menu >"$OUT_TTY" # Uhoh, something went terribly wrong if we reach this point! die "WARNING: Failsafe fall-through activated. Spawning emergency shell" fi