| #!/bin/sh |
| # Copyright 2015 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. |
| |
| # This script provides easy access to data contained in cros-regions.json (the |
| # region database for Chromium OS). |
| |
| # The CROS_VPD_CACHE is defined in dump_vpd_log "filtered_file". |
| : ${CROS_VPD_CACHE:=/mnt/stateful_partition/unencrypted/cache/vpd/filtered.txt} |
| : ${CROS_VPD_SYS_RO:=/sys/firmware/vpd/ro} |
| : ${CROS_REGIONS_DATABASE=/usr/share/misc/cros-regions.json} |
| |
| load_vpd_cache() { |
| # There are multiple ways to read from VPD. The best approach is probably |
| # to call dump_vpd_log directly, but this script may be executed in |
| # boot-time (before stateful partition was mounted) or inside initramfs |
| # so we cannot use that. Instead we have to try the cache and then read via |
| # command 'vpd'. |
| |
| if [ -d "${CROS_VPD_SYS_RO}" ]; then |
| # Use sysfs (supported by ARM since kernel 3.18 and x86 since kernel 4.4). |
| CROS_VPD_CACHE="" |
| elif [ ! -f "${CROS_VPD_CACHE}" ]; then |
| CROS_VPD_CACHE="$(mktemp)" |
| trap "rm ${CROS_VPD_CACHE}" EXIT |
| vpd -i RO_VPD -l >"${CROS_VPD_CACHE}" |
| fi |
| } |
| |
| get_data_from_vpd() { |
| local name="$1" |
| |
| if [ -z "${CROS_VPD_CACHE}" ]; then |
| # Use sysfs. All keys used by cros_region_data should live in RO partition. |
| local value_file="${CROS_VPD_SYS_RO}/${name}" |
| if [ -f "${value_file}" ]; then |
| cat "${value_file}" |
| fi |
| else |
| sed -nr 's/^"'"${name}"'"="(.*)"$/\1/p' "${CROS_VPD_CACHE}" |
| fi |
| } |
| |
| get_legacy_value_from_vpd() { |
| local data_name="$1" |
| local select_first="$2" |
| local name value entry |
| |
| # There are some legacy VPD values that should override region data. |
| local legacy_map=" |
| locales=initial_locale |
| time_zones=initial_timezone |
| keyboards=keyboard_layout" |
| |
| for entry in ${legacy_map}; do |
| name="${entry%=*}" |
| if [ "${name}" != "${data_name}" ]; then |
| continue |
| fi |
| |
| name="${entry#*=}" |
| value="$(get_data_from_vpd "${name}")" |
| |
| if [ -n "${value}" ]; then |
| # Legacy values were defined in CSV so we have to convert output. |
| if ${select_first}; then |
| echo "${value%%,*}" |
| else |
| echo "${value}" | tr ',' '\n' |
| fi |
| return 0 |
| fi |
| done |
| return 1 |
| } |
| |
| lookup_data() { |
| local name="$1" |
| local region="$2" |
| local select_first="$3" |
| local output="" |
| local pattern='.[$region][$name]' |
| |
| # Check value type and modify pattern for array. |
| output="$(jq -c -r --arg region "${region}" --arg name "${name}" \ |
| "${pattern}|arrays" "${CROS_REGIONS_DATABASE}")" |
| if [ -n "${output}" ]; then |
| if ${select_first}; then |
| pattern="${pattern}[0]" |
| else |
| pattern="${pattern}[]" |
| fi |
| fi |
| |
| output="$(jq -c -r --arg region "${region}" --arg name "${name}" \ |
| "${pattern}" "${CROS_REGIONS_DATABASE}")" |
| if [ "${output}" != "null" ]; then |
| echo "${output}" |
| fi |
| } |
| |
| # Use legacy VPD data to look up region code. |
| reverse_lookup_region_by_timezone() { |
| local value="$1" |
| local region="$(jq -c -r --arg "timezone" "${value}" \ |
| 'map(select(.time_zones == [$timezone]).region_code)[0]' \ |
| "${CROS_REGIONS_DATABASE}")" |
| if [ "${region}" != "null" ]; then |
| echo "${region}" |
| fi |
| } |
| |
| report_available_fields() { |
| if [ ! -f "${CROS_REGIONS_DATABASE}" ]; then |
| return |
| fi |
| echo "Available fields in current system:" |
| # Region 'us' should be always available. |
| jq -c -r ".us|keys[]" "${CROS_REGIONS_DATABASE}" | sed 's/^/\t/' |
| } |
| |
| usage() { |
| echo "Usage: $1 [-s] data_name [region] |
| |
| -s: Selects only the first element (if value is list). |
| data_name: Name of field inside ${CROS_REGIONS_DATABASE}. |
| region: Region code to query. Read from VPD if omitted. |
| " >&2 |
| report_available_fields >&2 |
| } |
| |
| main() { |
| local region="" data_name="" |
| local value="" |
| local select_first=false |
| local myname="$0" |
| |
| if [ "$1" = "-s" ]; then |
| select_first=true |
| shift |
| fi |
| |
| case "$#" in |
| 1) |
| data_name="$1" |
| ;; |
| 2) |
| data_name="$1" |
| region="$2" |
| ;; |
| *) |
| usage "${myname}" |
| exit 1 |
| ;; |
| esac |
| |
| if [ -z "${region}" ]; then |
| load_vpd_cache |
| region="$(get_data_from_vpd region)" |
| if get_legacy_value_from_vpd "${data_name}" ${select_first} ; then |
| # Already printed. |
| exit 0 |
| fi |
| if [ -z "${region}" ]; then |
| region="$(reverse_lookup_region_by_timezone \ |
| "$(get_data_from_vpd initial_timezone)")" |
| fi |
| fi |
| |
| lookup_data "${data_name}" "${region}" ${select_first} |
| } |
| |
| set -e |
| main "$@" |