blob: 52e6d7ce7def851efa56f1c7e004bb1c47de3fcb [file] [log] [blame]
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Provides alert messages in boot stage, called by chromeos_startup.
# Two instances of this script should never be run in parallel: the alert
# animations will fight with each other, and there is a potential race in the
# emission of the boot-alert-request signal (see
# Since this script only provides messages, never abort.
set +e
# Prints usage help for commands supports
usage_help() {
echo <<EOF
Usage: $0 <message_id> [arg ...]
<message_id> is one of the messages ids (file name without the .txt suffix) in
/usr/share/chromeos-assets/text/boot_messages/\$locale "
# Prints out system locale by searching cached settings or VPD.
find_current_locale() {
# TODO(hungte) Find some better way other than hard coding file path here.
local state_file='/mnt/stateful_partition/home/chronos/Local State'
local locale=""
if [ -f "$state_file" ]; then
locale="$(grep -w '"app_locale":' "$state_file" |
sed 's/.*"\([^"]*\)",$/\1/')" || locale=""
if [ -z "${locale}" ]; then
locale="$(cros_region_data -s locales)"
echo "${locale}"
# Determine the right console. On Freon systems, the default VT (/run/frecon/vt0) may
# not exist until we've invoked Frecon (via display_boot_message) and would
# cause problems when we try to write then read (for example, warn_dev). To
# prevent that, fall back to /dev/null if not available.
setup_tty() {
[ -x /sbin/frecon ] && TTY=/run/frecon/vt0 || TTY=/dev/tty1
[ -e "${TTY}" ] || TTY=/dev/null
# Shows boot messages in assets folder on screen center if available.
# Arguments: message in /usr/share/chromeos-assets/text/boot_messages/$locale
show_assets_message() {
local message="$1"
local locale locale_list
# Build locale list
locale="$(find_current_locale)" || locale=""
# Starting from R34, the initial_locale from VPD may have multiple values,
# separated by ',' -- and we only want to try the primary one.
while [ "${locale%[-_]*}" != "$locale" ]; do
locale_list="$locale_list $locale"
locale_list="$locale_list en-US en"
if display_boot_message "${message}" "${locale_list}"; then
# Frecon may create the text terminal so we want to setup TTY again.
# Display the message code itself as fallback.
echo "${message}" >>"${TTY}"
# Prints the two byte hex code of the matched char or
# exists non-zero on timeout. It reads from the tty in arguments.
# Arguments: tty time_in_seconds two_byte_hex_match_1 two_byte_hex_match_2 ...
match_char_timeout() {
local tty="$1"
local delay_secs="$2"
local input=''
local match=''
local start_time=$(date +%s)
local stop_time=$((start_time + delay_secs))
local tty_config=$(stty -g -F "${tty}")
stty raw -echo cread -F "${tty}"
while [ $delay_secs -gt 0 ]; do
input=$(timeout -s KILL ${delay_secs}s head -c 1 "${tty}")
[ $? -eq 137 ] && break # Timed out.
input=$(printf "%02x" "'$input")
for char in "$@"; do
if [ "$input" = "$char" ]; then
[ -n "$match" ] && break
delay_secs=$((stop_time - $(date +%s) ))
# Restores the tty's settings.
stty $tty_config -F "${tty}"
[ -z "$match" ] && return 1
printf "$match"
return 0
# Returns if current device is using virtual developer switch.
has_virtual_dev_switch() {
local vdat_flags="$(crossystem vdat_flags || echo 0)"
[ "$((vdat_flags & VBSD_HONOR_VIRT_DEV_SWITCH))" != "0" ]
# Prints message when entering developer mode
# Argument: time to countdown (in seconds)
mode_enter_dev() {
local delay_secs="${1:-30}"
if has_virtual_dev_switch; then
show_assets_message "enter_dev1_virtual"
show_assets_message "enter_dev1"
local format='\r %-30s'
for dev_count_down in $(seq $delay_secs -1 1); do
# Trailing spaces must exist to clear previous message when the printed
# counter width changed (ex, 100->99).
# TODO(hungte) merge this with assets messages so it can be localized.
printf "$format" "Starting in $dev_count_down second(s)..." >>"${TTY}"
sleep 1
# Count-down
tput clear >>"${TTY}"
show_assets_message "enter_dev2"
# TODO(wad,wfrichar) Request a root password here.
# TODO(wad,reinauer) Inform the user of chromeos-firmwareupdate --mode=todev
# Prints message when leaving developer mode
mode_leave_dev() {
show_assets_message "leave_dev"
# Prints messages before starting firmware update
mode_update_firmware() {
show_assets_message "update_firmware"
# Prints messages before starting user-initiated wipe.
mode_power_wash() {
show_assets_message "power_wash"
# Prints message before starting to rebuild a corrupted stateful partition.
mode_self_repair() {
show_assets_message "self_repair"
# Prints a message telling the user that developer mode has been disabled for
# the device upon request by the device owner. Depending on hardware support,
# the system switches back to verified boot mode automatically or prompts the
# user to do so. The system reboots after the user confirms by pressing space or
# after timeout.
mode_block_devmode() {
local delay_secs=30
if has_virtual_dev_switch; then
show_assets_message "block_devmode_virtual"
# Leave the notification on the screen for 5 minutes to increase chances
# the user actually sees it before shutting down the device.
show_assets_message "block_devmode"
# Read a space bar or timeout.
local input=$(match_char_timeout "${TTY}" "${delay_secs}" 20)
tput clear >>"${TTY}"
if has_virtual_dev_switch; then
# Return to verified mode.
crossystem disable_dev_request=1
# Shut down instead of rebooting in a loop.
# To prevent the system from continuing to boot.
sleep infinity
# Updates the progress bar to reflect the provided percentage.
mode_update_progress() {
local progress="$1"
if ! display_boot_message update_progress "${progress}"; then
# Display the progress on the terminal as a fallback.
printf "${progress}%%\r" >>"${TTY}"
# Show a message indicating that we're doing a TPM firmware update.
mode_update_tpm_firmware() {
show_assets_message "update_tpm_firmware"
# Show a message indicating that we're doing a hammer firmware update.
mode_update_detachable_base_firmware() {
show_assets_message "update_detachable_base_firmware"
# Show a message indicating that we're doing a hammer firmware update.
mode_update_touchpad_firmware() {
show_assets_message "update_touchpad_firmware"
# Show a message explaining to the user that the battery isn't sufficiently
# charged to a apply a firmware update and ask for the charger to be plugged.
mode_update_tpm_firmware_low_battery() {
show_assets_message "update_tpm_firmware_low_battery"
# Show a message explaining to the user that the battery isn't sufficiently
# charged to a apply a firmware update and tell them that they need to wait
# while the battery is charging.
mode_update_tpm_firmware_low_battery_charging() {
show_assets_message "update_tpm_firmware_low_battery_charging"
# Show a message telling the user that the TPM firmware update failed and that
# they need to go through recovery to retry the update.
mode_update_tpm_firmware_failure() {
show_assets_message "update_tpm_firmware_failure"
# Read a space bar. Timeout after 10 years (i.e. practically no timeout).
local input=$(match_char_timeout "${TTY}" 315360000 20)
tput clear >>"${TTY}"
# Main initialization and dispatcher
main() {
# process args
if [ $# -lt 1 ]; then
exit 1
local mode="$(echo "$1" | tr -dc "[:alnum:]_")"
# For headless devices, we want to provide some messages so people can know
# what goes wrong.
local output
for output in /dev/kmsg /dev/console; do
(echo "chromeos-boot-alert: ${mode}" >>"${output}") 2>/dev/null || true
if [ -x /usr/sbin/board_boot_alert ]; then
# Allow overriding boot-alert behavior (usually for headless devices).
/usr/sbin/board_boot_alert "${mode}" "$@" && exit
# Wait until the boot-alert-ready abstract job has started, indicating that
# it's safe to display an alert onscreen.
if ! initctl status boot-alert-ready | grep -q running; then
initctl emit boot-alert-request
# Light up the screen if possible.
backlight_tool --set_brightness_percent=100 || true
# Hides cursor and prevents console from blanking after long inactivity.
setterm -cursor off -blank 0 -powersave off -powerdown 0 >>"${TTY}" || true
local handler="mode_${mode}"
if type "${handler}" 2>/dev/null | head -1 | grep -q "function"; then
"${handler}" "$@"
exit 1
# Main Entry
main "$@"