blob: 4712ac8d8de0819822e685754f1c74f44d26734f [file] [log] [blame]
#!/bin/sh
# 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 http://crosbug.com/33838).
# Since this script only provides messages, never abort.
set +e
# Prints usage help for commands supports
usage_help() {
echo "Usage: $0 mode terminal [arg ...]
warn_dev: Message for warning about developer mode on normal firmware.
Arg #1: (optional, default=30) Countdown in seconds before continue
enter_dev: Message for entering developer mode from non-dev.
Arg #1: (optional, default=30) Countdown in seconds before continue
leave_dev: Message when leaving developer mode.
Arg: none
update_firmware: Message before starting firmware update.
Arg: none
wipe: Message when starting to erase stateful partition.
Arg #1: (optional) Image file to show.
power_wash: Message when users wants to wipe the stateful partition.
Arg: none
self_repair: Message when starting to rebuild stateful partition.
dev_fwcheck: Message when staring with developer firmware, to check if there
is available updates.
block_devmode: Message shown to indicate that dev mode is blocked by request
of the device owner.
Arg: none
"
}
# 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'
# VPD cache file is generated by src/platform/util/dump_vpd_log.
local vpd_file='/mnt/stateful_partition/unencrypted/cache/vpd/filtered.txt'
local locale=""
if [ -f "$state_file" ]; then
locale="$(grep -w '"app_locale":' "$state_file" |
sed 's/.*"\([^"]*\)",$/\1/')" || locale=""
fi
if [ -n "$locale" ]; then
echo "$locale"
elif [ -f "$vpd_file" ]; then
sed -nr '/^"initial_locale"=".*"/s/.*="(.*)"/\1/p' "$vpd_file"
else
vpd -i RO_VPD -g initial_locale 2>/dev/null || true
fi
}
# 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_image() {
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.
locale="${locale%%,*}"
locale_list="$locale"
while [ "${locale%[-_]*}" != "$locale" ]; do
locale="${locale%[-_]*}"
locale_list="$locale_list $locale"
done
locale_list="$locale_list en-US en"
display_boot_message "$message" "$locale_list" || return "$?"
}
# Prints the two byte hex code of the matched char or
# exists non-zero on timeout. It reads from the current tty.
# To override, call it as follows,
# match_char_timeout args < "$STDOUT"
#
# Arguments: time_in_seconds two_byte_hex_match_1 two_byte_hex_match_2 ...
match_char_timeout() {
local delay_secs="$1"
shift
local input=''
local match=''
local start_time=$(date +%s)
local stop_time=$((start_time + delay_secs))
local tty_config=$(stty -g)
stty raw -echo
while [ $delay_secs -gt 0 ]; do
input=$(timeout -s KILL ${delay_secs}s head -c 1)
[ $? -eq 137 ] && break # Timed out.
input=$(printf "%02x" "'$input")
for char in "$@"; do
if [ "$input" = "$char" ]; then
match="$input"
break
fi
done
[ -n "$match" ] && break
delay_secs=$((stop_time - $(date +%s) ))
done
# Restores the tty's settings.
stty $tty_config
[ -z "$match" ] && return 1
printf "$match"
return 0
}
# Returns if current device is using virtual developer switch.
has_virtual_dev_switch() {
local VBSD_HONOR_VIRT_DEV_SWITCH="0x400"
local vdat_flags="$(crossystem vdat_flags || echo 0)"
[ "$((vdat_flags & VBSD_HONOR_VIRT_DEV_SWITCH))" != "0" ]
}
# Prints message when in developer mode
# Argument: time to countdown (in seconds)
mode_warn_dev() {
local delay_secs="${1:-30}"
# Don't show a broken/partial background image.
tput clear > "$STDOUT"
echo '
__________________________
|.------------------------.|
|| ||
|| (_) (_) ||
|| ||
|| __________ ||
|| / \ ||
||________________________||
/ __/___/_____________\__\__ \
/ _ \
/ /_______\ \_____\ \
/__________________\ \_____________\
\___________________\ \____________/
\ \_-_-_-.
/| |
| | |
\ |
\_______/
Chrome OS verification is turned off.
Press space to begin recovery.
' >"$STDOUT"
# TODO(wad) Use the developer mode firmware image
# Read a space bar or Ctrl+D or timeout.
local input=$(match_char_timeout "$delay_secs" 04 20 < "$STDOUT")
local exit_code=$?
tput clear > "$STDOUT"
# If we timed out, we're done.
[ $exit_code -ne 0 ] && return 0
case "$input" in
"04") # Ctrl+D
# Done.
;;
"20") # Spacebar
crossystem recovery_request=1
reboot
# To prevent the system from continuing to boot.
sleep infinity
;;
esac
return 0
}
# Prints message when entering developer mode
# Argument: time to countdown (in seconds)
mode_enter_dev() {
local delay_secs="${1:-30}"
# The text below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
local enter_dev1_text="
Local data will be cleared.
Modifications you make to the system are not supported by Google, may cause
hardware issues and may void warranty.
To cancel, turn your computer off now and toggle the Developer Switch back
to Verified Mode.
"
local enter_dev1_virtual_text="
Your system is transitioning to Developer Mode.
Local data has been cleared.
Modifications you make to the system are not supported by Google, may cause
hardware issues and may void warranty.
To cancel, turn your computer off now.
"
if has_virtual_dev_switch; then
show_assets_message_image "enter_dev1_virtual" ||
echo "$enter_dev1_virtual_text" >"$STDOUT"
else
show_assets_message_image "enter_dev1" ||
echo "$enter_dev1_text" >"$STDOUT"
fi
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)..." >"$STDOUT"
sleep 1
done
# Count-down
tput clear >"$STDOUT" &&
show_assets_message_image "enter_dev2" ||
# The text by echo below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
echo "
Preparing system for Developer Mode (may take 5 - 10 minutes).
Do not turn your computer off until it has restarted.
On reboot, press Ctrl+Alt+[->] (F2) for information about Developer Mode.
" >"$STDOUT"
# 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_image "leave_dev" ||
# The text by echo below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
echo "
Your system last booted in Developer Mode.
Returning to Normal (Verified) Mode...
Upon completion, the system will reboot.
" >"$STDOUT"
}
# Prints messages before starting firmware update
mode_update_firmware() {
show_assets_message_image "update_firmware" ||
# The text by echo below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
echo "
Your system is applying a critical update.
Please do not turn it off.
" >"$STDOUT"
}
# Prints message before starting to erase stateful partition (wipe).
# Argument: (optional) file for splash image to show
mode_wipe() {
local splash_file="$1"
if [ -s "$splash_file" ] && type ply-image >/dev/null 2>&1; then
# Do not use background execution because this file is going to be wiped.
ply-image "$splash_file"
else
echo " Erasing stateful partition..." >"$STDOUT"
fi
}
# Prints messages before starting user-initiated wipe.
mode_power_wash() {
show_assets_message_image "power_wash" ||
# The text by echo below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
echo "
Powerwash in progress. Do not switch off your device.
Powerwashes happen when critical errors are detected, or when you choose
to reset your device. This will reset Chrome OS to be just like new, and
you'll be back in operation in just a minute.
" >"$STDOUT"
}
# Prints message before starting to rebuild a corrupted stateful partition.
mode_self_repair() {
show_assets_message_image "self_repair" ||
# The text by echo below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
echo "
Your system is repairing itself. Please wait.
" >"$STDOUT"
}
# Prints message when starting with developer firmware, to check if there's
# available updates for firmware.
mode_dev_fwcheck() {
# TODO(hungte) Find some better way to store firmware value, like using
# /etc/lsb-release. Currently the firmware updater may contain fields like
# TARGET_* in the beginning of updater script.
local TARGET_FWID=""
local TARGET_ECID=""
# The magic value 40 is verified on current updaters (using line 23-24)
eval "$(head -40 /usr/sbin/chromeos-firmwareupdate |
grep '^ *TARGET_..ID=')"
if [ -z "$TARGET_FWID" -a -z "$TARGET_ECID" ]; then
return
fi
local fwid="$(crossystem fwid)"
local ec_info
ec_info="$(mosys -k ec info 2>/dev/null)" || ec_info=""
local ecid="$(fw_version="Unknown"; eval "$ec_info"; echo "$fw_version")"
# Ignore known firmware images carried by updaters with multiple images
# TODO(hungte) Replace this by a "compatible list" in updater if such request
# becomes a common feature.
case "$(mosys platform name 2>/dev/null):$ecid" in
"ZGB:0.14" | "Alex:00VFA616" )
TARGET_ECID="$ecid"
;;
esac
local notify_update=0
if [ "$TARGET_FWID" != "$fwid" ] &&
[ "$TARGET_FWID" != "IGNORE" ]; then
notify_update=1
echo "
System firmware update available: [$TARGET_FWID]
Currently installed system firmware: [$fwid]
" >"$STDOUT"
fi
if [ "$TARGET_ECID" != "$ecid" ] &&
[ "$TARGET_ECID" != "IGNORE" ]; then
notify_update=1
echo "
EC firmware update available: [$TARGET_ECID]
Currently installed EC firmware: [$ecid]
" >"$STDOUT"
fi
if [ $notify_update -ne 0 ]; then
echo "
Firmware auto updating is disabled for developer mode. If you want to
manually update your firmware, please run the following command from a
root shell:
sudo chromeos-firmwareupdate --force --mode=recovery
" >"$STDOUT"
fi
}
# 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_image "block_devmode_virtual" ||
# The text by echo below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
echo "
The device owner has disabled Developer Mode for this device. Please ask
the device owner to remove the restriction if you want to use Developer
Mode.
The system will reboot in Verified Mode.
" >"$STDOUT"
else
# Leave the notification on the screen for 5 minutes to increase chances
# the user actually sees it before shutting down the device.
delay_secs=300
show_assets_message_image "block_devmode" ||
# The text by echo below is only fallback of localized assets message.
# Please sync with text there if you want to change the message.
echo "
The device owner has disabled Developer Mode for this device. Please ask
the device owner to remove the restriction if you want to use Developer
Mode.
Please toggle the Developer Switch and reboot the device to return to
Verified Mode.
" >"$STDOUT"
fi
# Read a space bar or timeout.
local input=$(match_char_timeout "$delay_secs" 20 < "$STDOUT")
tput clear > "$STDOUT"
if has_virtual_dev_switch; then
# Return to verified mode.
crossystem disable_dev_request=1
reboot
else
# Shut down instead of rebooting in a loop.
halt
fi
# To prevent the system from continuing to boot.
sleep infinity
}
# Main initialization and dispatcher
main() {
# process args
if [ $# -lt 2 ]; then
usage_help
exit 1
fi
local mode="$1"
# global
STDOUT="$2"
shift
shift
# 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
fi
# 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 >"$STDOUT" || true
case "$mode" in
"warn_dev" | "enter_dev" | "leave_dev" | "dev_fwcheck" | \
"update_firmware" | "wipe" | "power_wash" | "self_repair" | \
"block_devmode" )
mode_"$mode" "$@"
;;
* )
usage_help
exit 1
;;
esac
}
# Main Entry
main "$@"