#!/usr/bin/expect # # Written by Dave Hansen # # This script watches a serial line for a GRUB menu, then finds and boots # the kernel that you request. # # Before you run this script, you should be quite sure that the machine # is rebooting and will present a GRUB prompt. It is not this script's # job to decide which kind of bootloader you have or if you're at a login # prompt, a fsck prompt, or anything else. GRUB only. # # This script correctly handles GRUB being at a the menu, or different # GRUB command prompts when the script is handed the serial line. # # exit codes: # 0 - GRUB successfully loaded a kernel # 1 - there was a problem with telnet # 2 - a GRUB prompt was never seen. The machine might still be powered # off or hung # 3 - the specified GRUB menu item was never seen. The name # passed in the 3rd argument is probably not in the GRUB menu # 4 - a GRUB error was encountered while trying to load the specified # kernel. The kernel probably doesn't exist on the disk # 5 - a Linux kernel header was never seen. The loaded kernel may # have crashed. log_user 1 stty echo set argc [llength $argv] set name [lindex $argv 0] set port [lindex $argv 1] set kernel [lindex $argv 2] # wait 20 minutes for any grub activity set start_timeout 1200 set elapsed_time 0 set timeout 1 spawn telnet $name $port # sit around on the serial port and send stuff to try and get a response # from GRUB. expect { "grub>" { } # someone left GRUB at a prompt with stuff there already # This should even recover from things at the GRUB line-edit # prompt, or even in the middle of a device string -re "Error \[0-9]+:" { send \033 send \033 } "Connection closed by foreign host." { puts "Telnet connection closed." puts "Does someone else have the serial line?" exit 1; } timeout { send "c\t"; incr elapsed_time "1"; puts "timeout: $elapsed_time of $start_timeout"; if { $elapsed_time >= $start_timeout } { puts "wait for grub timeout expired."; exit 2; } exp_continue; } } sleep 1 # send ESC, to have grub give us the menu send \033 send \033 set timeout 3; set movechar "v"; set restr "\\\[0m.\\\[.+;3H "; # [0m means unhighlight. There should only # be one of these per screen redraw or ^ or v # key press set hirestr "\\\[7m.\\\[.+;3H "; # [7m means highlight. append hirestr $kernel; # let it go back and forth in the menu a couple of times set change_dir_retries 6 expect { # the control sequence to highlight a GRUB entry on the serial # console always ends with "3H " and the grub title. -re $hirestr { } # when GRUB reaches the end of its menus, it doesn't send # anything as a response to key input. This is a heartbeat of # sorts to check for when we're *not* at the end of the menu -re $restr { send $movechar; set timeout 4; exp_continue; } # if we're pressing keys and not getting responses, we're at the # end of the menu timeout { if { $movechar == "v" } { set movechar "^"; } else { set movechar "v"; } incr change_dir_retries "-1"; if { $change_dir_retries < 0 } { puts "could not find requested GRUB menu entry"; exit 3; } send $movechar; exp_continue; } } send "\r" set timeout 60; expect { "^Linux version " { exit 0 } "Error " { puts "GRUB Error"; exit 4; } timeout { puts "Linux boot prompt never seen"; } } exit 5;