Collect storage information for chrome://system (with workaround fix for stout)

This script will run at startup and dump result in /var/log
for SSD device display smartctl and hdparm result
for eMMC device display various eMMC attribute e.g. CID, CSD

This script also include a workaround fix for stout machine.
(Run "smartctl -a" instead of "smartctl -x" on stout)

BUG=chromium:315380,chromium:328587
TEST=Tested in link, pit, stout
Reboot, verified that /var/log/storage_info show meaningful
result and SSD does not crash on stout.

Change-Id: I85d9426131ad624930af9cfdd10311383d4b7dab
Reviewed-on: https://chromium-review.googlesource.com/180342
Reviewed-by: Shawn Nematbakhsh <shawnn@chromium.org>
Commit-Queue: Gwendal Grignou <gwendal@chromium.org>
Commit-Queue: Puthikorn Voravootivat <puthik@chromium.org>
Tested-by: Puthikorn Voravootivat <puthik@chromium.org>
Reviewed-by: Puthikorn Voravootivat <puthik@chromium.org>
diff --git a/init/storage-info.conf b/init/storage-info.conf
new file mode 100644
index 0000000..74258be
--- /dev/null
+++ b/init/storage-info.conf
@@ -0,0 +1,11 @@
+# Copyright (c) 2013 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.
+
+description   "Launch storage info dump script to populate chrome://system"
+author        "chromium-os-dev@chromium.org"
+
+start on started system-services
+script
+  /usr/share/userfeedback/scripts/storage_info
+end script
diff --git a/scripts/storage_info b/scripts/storage_info
new file mode 100755
index 0000000..88efe00
--- /dev/null
+++ b/scripts/storage_info
@@ -0,0 +1,127 @@
+#! /bin/sh
+
+# Copyright (c) 2013 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 various data about the internal disk to show on the
+# chrome://system page. It will run once on startup to dump output to the file
+# /var/log/storage_info.txt which will be read later by debugd when the user
+# opens the chrome://system page.
+
+STORAGE_INFO_FILE="/var/log/storage_info.txt"
+
+SSD_CMD_0="hdparm -I"
+SSD_CMD_1_NORMAL="smartctl -x"
+SSD_CMD_1_ALTERNATE="smartctl -a"
+SSD_CMD_MAX=1
+
+# This match SanDisk SSD U100 with any size with version 10.52.*
+SSD_BLACKLIST="Device_Model:_*SanDisk_SSD_U100.*Firmware_Version:_*10\.52\..*"
+
+MMC_NAME_0="cid"
+MMC_NAME_1="csd"
+MMC_NAME_2="date"
+MMC_NAME_3="enhanced_area_offset"
+MMC_NAME_4="enhanced_area_size"
+MMC_NAME_5="erase_size"
+MMC_NAME_6="fwrev"
+MMC_NAME_7="hwrev"
+MMC_NAME_8="manfid"
+MMC_NAME_9="name"
+MMC_NAME_10="oemid"
+MMC_NAME_11="preferred_erase_size"
+MMC_NAME_12="prv"
+MMC_NAME_13="raw_rpmb_size_mult"
+MMC_NAME_14="rel_sectors"
+MMC_NAME_15="serial"
+MMC_NAME_MAX=15
+
+is_blockdev() {
+  [ -b /dev/$1 ]
+}
+
+is_removable() {
+  [ $(cat /sys/block/$1/removable) -eq 1 ]
+}
+
+# We need to trim white space because vendor is padding with white space
+is_vendor_ata() {
+  [ "$(cat /sys/block/$1/device/vendor | tr -d ' ')" = "ATA" ]
+}
+
+is_internal_ssd() {
+  is_blockdev $1 && ! is_removable $1 && is_vendor_ata $1
+}
+
+# replace space and new line with "_"
+get_ssd_model_and_version() {
+  smartctl -i /dev/$1 \
+    | grep -E "Device Model|Firmware Version" \
+    | sed -r "N; s/\n| /_/g"
+}
+
+is_ssd_blacklist() {
+  echo "$(get_ssd_model_and_version $1)" | grep -Eq $SSD_BLACKLIST
+}
+
+is_type_mmc() {
+  [ "$(cat /sys/block/$1/device/type)" = "MMC" ]
+}
+
+is_internal_mmc() {
+  is_blockdev $1 && ! is_removable $1 && is_type_mmc $1
+}
+
+print_ssd_info() {
+  # BUG: On stout smartctl -x causes SSD error (crbug.com/328587)
+  # We need to check model and firmware version of the SSD to avoid this bug.
+  if is_ssd_blacklist $1; then
+    SSD_CMD_1=$SSD_CMD_1_ALTERNATE
+  else
+    SSD_CMD_1=$SSD_CMD_1_NORMAL
+  fi
+
+  for i in $(seq 0 $SSD_CMD_MAX); do
+    # use eval for variable indirection
+    eval SSD_CMD=\$SSD_CMD_$i
+    echo "$ $SSD_CMD /dev/$1"
+    $SSD_CMD /dev/$1
+    echo ""
+  done
+}
+
+print_mmc_info() {
+  for i in $(seq 0 $MMC_NAME_MAX); do
+    eval MMC_NAME=\$MMC_NAME_$i
+    MMC_PATH=/sys/block/$1/device/$MMC_NAME
+    MMC_RESULT=$(cat $MMC_PATH 2>/dev/null)
+    printf "%-20s | %s\n" "$MMC_NAME" "$MMC_RESULT"
+  done
+}
+
+rm -f $STORAGE_INFO_FILE
+touch $STORAGE_INFO_FILE
+chmod 644 $STORAGE_INFO_FILE
+exec > $STORAGE_INFO_FILE
+
+# We need to check both sda and sdb for internal disk because:
+# 1) If we use rootdev command to determine the disk to check, it will return
+#    the usb stick, not the internal drive, when we boot from usb.
+# 2) We also can not assume that sda is an internal disk because in some
+#    systems (e.g., parrot), the usb stick might be labeled as sda and the
+#    internal disk as sdb.
+for dev in sda sdb; do
+  if is_internal_ssd $dev; then
+    print_ssd_info $dev
+  fi
+done
+
+# For devices with eMMC, the internal disk will always be labeled as mmcblk0
+# so we can check only mmcblk0, but to be safe we will check both mmcblk0 and
+# mmcblk1.
+for dev in mmcblk0 mmcblk1; do
+  if is_internal_mmc $dev; then
+    print_mmc_info $dev
+  fi
+done