| #!/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 [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: Text message 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' |
| local locale="" |
| if [ -f "$state_file" ]; then |
| locale="$(grep -w '"app_locale":' "$state_file" | |
| sed 's/.*"\([^"]*\)",$/\1/')" || locale="" |
| fi |
| if [ -z "${locale}" ]; then |
| locale="$(cros_region_data -s locales)" |
| fi |
| echo "${locale}" |
| } |
| |
| # Determine the right console. On Freon systems, the default TTY (/dev/tty1) 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() { |
| 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. |
| locale="${locale%%,*}" |
| locale_list="$locale" |
| while [ "${locale%[-_]*}" != "$locale" ]; do |
| locale="${locale%[-_]*}" |
| locale_list="$locale_list $locale" |
| done |
| 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. |
| setup_tty |
| else |
| # Display the message code itself as fallback. |
| echo "${message}" >>"${TTY}" |
| fi |
| } |
| |
| # 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" |
| shift |
| shift |
| |
| 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 |
| match="$input" |
| break |
| fi |
| done |
| [ -n "$match" ] && break |
| delay_secs=$((stop_time - $(date +%s) )) |
| done |
| # 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 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}" |
| # In bash and dash, the "NAME=VAL program" will set a temporary environment |
| # variable. However if the "program" is a shell function and then calls a |
| # standalone shell script, only bash can pass the variable properly. |
| # To work around that, here we have to explicitly set the variable with |
| # "export" and use a sub shell. |
| (export IMAGE_FONT_NAME="monospace"; show_assets_message "warn_dev") |
| |
| # Read a space bar or Ctrl+D or timeout. |
| local input=$(match_char_timeout "${TTY}" "$delay_secs" 04 20) |
| local exit_code=$? |
| tput clear >>"${TTY}" |
| # 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}" |
| |
| if has_virtual_dev_switch; then |
| show_assets_message "enter_dev1_virtual" |
| else |
| show_assets_message "enter_dev1" |
| 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)..." >>"${TTY}" |
| sleep 1 |
| done |
| |
| # 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 message before starting to erase stateful partition (wipe). |
| # Argument: Text file for message to show. |
| mode_wipe() { |
| local message_file="$1" |
| |
| if display_boot_message "show_spinner" "${message_file}"; then |
| # Frecon may create the text terminal so we want to setup TTY again. |
| setup_tty |
| else |
| echo "Wiping" >>"${TTY}" |
| fi |
| } |
| |
| # 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 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] |
| " >>"${TTY}" |
| fi |
| if [ "$TARGET_ECID" != "$ecid" ] && |
| [ "$TARGET_ECID" != "IGNORE" ]; then |
| notify_update=1 |
| echo " |
| EC firmware update available: [$TARGET_ECID] |
| Currently installed EC firmware: [$ecid] |
| " >>"${TTY}" |
| 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 |
| " >>"${TTY}" |
| 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 "block_devmode_virtual" |
| 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 "block_devmode" |
| fi |
| |
| # 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 |
| 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 1 ]; then |
| usage_help |
| exit 1 |
| fi |
| local mode="$1" |
| shift |
| |
| # 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 |
| done |
| |
| setup_tty |
| |
| if [ -x /usr/sbin/board_boot_alert ]; then |
| # Allow overriding boot-alert behavior (usually for headless devices). |
| /usr/sbin/board_boot_alert "${mode}" "$@" && exit |
| fi |
| |
| # 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 >>"${TTY}" || 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 "$@" |