1#!/bin/bash
2
3# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7# Abort on error.
8set -e
9
10LSB_FILE=/etc/lsb-release
11
12# Load common constants and variables.
13. "$(dirname "$0")/common.sh"
14
15usage() {
16  echo "Usage $PROG image [config]"
17}
18
19# Usage: lsbval path-to-lsb-file key
20# Returns the value for the given lsb-release file variable.
21lsbval() {
22  local lsbfile="$1"
23  local key="$2"
24  grep ^$key= "$lsbfile" | sed s/^$key=//
25}
26
27# Usage: lsbequals path-to-lsb-file key expected-value
28# Returns 0 if they match, 1 otherwise.
29# Also outputs a warning message if they don't match.
30lsbequals() {
31  local lsbfile="$1"
32  local key="$2"
33  local expectval="$3"
34  local realval=$(lsbval "$lsbfile" $key)
35  if [ "$realval" != "$expectval" ]; then
36    echo "$key mismatch. Expected '$expectval', image contains '$realval'"
37    return 1
38  fi
39  return 0
40}
41
42# Usage: check_keyval_in_list lsbfile lsbkey [list of values]
43# Extracts the lsb-release value for the specified key, and confirms it
44# matches one of the whitelisted values specified in value_array.
45# Implementation note:
46# You can't really pass bash arrays to functions. Best you can do is either
47# serialize to string/pass/deserialize (e.g. using whitspace/IFS hacks), or,
48# let the array contents be received as multiple arguments to the target
49# function. We take the latter approach here, hence the shift's to get the
50# first 2 arguments out, before we process the rest of the varargs.
51check_keyval_in_list() {
52  local lsbfile="$1"
53  shift
54  local lsbkey="$1"
55  shift
56  local lsbval=$(lsbval "$lsbfile" "$lsbkey")
57  while [ $# -gt 0 ]; do
58    if [ "$lsbval" == "$1" ]; then
59      return 0
60    fi
61    shift
62  done
63  # If we get here, it wasn't found
64  echo "$lsbkey: Value '$lsbval' was not recognized"
65  return 1
66}
67
68# Usage: lsb_syntaxcheck path-to-lsb-file
69# Enforces a number of basic sanity checks on the overall format and contents
70# of the lsb-release file:
71# - Every line is "key=value".
72# - No space after key, no space before value.
73# - key is all A-Z or _, but not starting with _.
74# - value is made up of printable characters, or is empty.
75# - Each line is a reasonable size (<255 bytes).
76# - The whole file is a reasonable size (4kb).
77lsb_syntaxcheck() {
78  local lsbfile="$1"
79  syntaxbad=0
80  # Checks for key being A-Z_, 1 or more characters, not starting with _.
81  # Also checks for = with no spaces on either side.
82  # Checks that the value contains printables (and not starting with space).
83  # Alternatively, the value is permitted to be empty (0 chars) too.
84  badlines=$(grep -Ev '^[A-Z][A-Z_]*=([[:graph:]][[:print:]]*)?$' "$lsbfile")
85  if [ -n "$badlines" ]; then
86    syntaxbad=1
87    echo "$lsbfile: Some lines seem non-well-formed:"
88    echo "$badlines"
89  fi
90
91  # Checks for a lines exceeding a reasonable overall length.
92  badlines=$(grep -E '^.{255}' "$lsbfile")
93  if [ -n "$badlines" ]; then
94    syntaxbad=1
95    echo "$lsbfile: Some lsb-release lines seem unreasonably long:"
96    echo "$badlines"
97  fi
98  # Overall file size check:
99  size=$(ls -sk "$lsbfile" | cut -d ' ' -f 1)
100  if [ $size -gt 4 ]; then
101    syntaxbad=1
102    echo "$lsbfile: This file exceeds 4kb"
103  fi
104  return $syntaxbad
105}
106
107main() {
108  # We want to catch all the discrepancies, not just the first one.
109  # So, any time we find one, we set testfail=1 and continue.
110  # When finished we will use testfail to determine our exit value.
111  local testfail=0
112
113  if [ $# -ne 1 ] && [ $# -ne 2 ]; then
114    usage
115    exit 1
116  fi
117
118  local image="$1"
119
120  # Default config location: same directory as this script.
121  local configfile="$(dirname "$0")/default_lsb_release.config"
122  # Or, maybe a config was provided on the command line.
123  if [ $# -eq 2 ]; then
124    configfile="$2"
125  fi
126  # Either way, load test-expectations data from config.
127  echo -n "Loading config from $configfile... "
128  . "$configfile" || return 1
129  echo "Done."
130
131  local rootfs=$(make_temp_dir)
132  mount_image_partition_ro "$image" 3 "$rootfs"
133  local lsb="$rootfs/$LSB_FILE"
134
135  # Basic syntax check first.
136  lsb_syntaxcheck "$lsb" || testfail=1
137
138  lsbequals $lsb CHROMEOS_AUSERVER "$expected_auserver" || testfail=1
139  lsbequals $lsb CHROMEOS_RELEASE_NAME "$expected_release_name" || testfail=1
140  check_keyval_in_list $lsb CHROMEOS_RELEASE_TRACK \
141    "${expected_release_tracks[@]}" || testfail=1
142
143  if check_keyval_in_list $lsb CHROMEOS_RELEASE_BOARD \
144    "${expected_boards[@]}"; then
145    # Pick the right set of test-expectation data to use. The cuts
146    # turn e.g. x86-foo-pvtkeys into x86-foo.
147    local board=$(lsbval $lsb CHROMEOS_RELEASE_BOARD |
148                  cut -d = -f 2 |
149                  cut -d - -f 1,2)
150    # a copy of the board string with '-' squished to variable-name-safe '_'.
151    local boardvar=${board//-/_}
152    channel=$(lsbval $lsb CHROMEOS_RELEASE_TRACK)
153    # For a canary or dogfood channel, appid maybe a different default value.
154    if [ $channel = 'canary-channel' ] || [ $channel = 'dogfood-channel' ]; then
155      eval "expected_appid=\"\$expected_appid_${channel%\-channel}\""
156    else
157      eval "expected_appid=\"\$expected_appid_$boardvar\""
158    fi
159    lsbequals $lsb CHROMEOS_RELEASE_APPID "$expected_appid" || testfail=1
160  else # unrecognized board
161    testfail=1
162  fi
163
164  exit $testfail
165}
166
167main "$@"
168