mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 18:25:10 +00:00
Initial creation of the end-user recovery tool.
This is work in progress. I'm committing what I've got as a starting point for futher discussion, since we're just collaborating via email at the moment, which is painful. Change-Id: Iff21c008b3916d9612c021e5ee5c67258357d516 BUG=chromium-os:781 TEST=manual Download user_tools/linux/recovery.sh to a linux machine and run it. It should walk you through the process of creating a USB recovery key. Review URL: http://codereview.chromium.org/5562003
This commit is contained in:
2
user_tools/README.txt
Normal file
2
user_tools/README.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
The tools under this directory are for the end users of Chromium OS devices.
|
||||||
58
user_tools/README_recovery.txt
Normal file
58
user_tools/README_recovery.txt
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
The recovery tool assists the user in creating a bootable USB drive that can
|
||||||
|
recover a non-functional Chromium OS device. It generally operates in three
|
||||||
|
steps.
|
||||||
|
|
||||||
|
1. Download a config file from a known URL. This file describes the
|
||||||
|
available images and where they can be found.
|
||||||
|
|
||||||
|
2. Ask the user to select the appropriate image, download it, and verify
|
||||||
|
that it matches what the config file describes.
|
||||||
|
|
||||||
|
3. Ask the user to select a USB drive, and write the recovery image to it.
|
||||||
|
|
||||||
|
|
||||||
|
Here's the format of the config file:
|
||||||
|
|
||||||
|
The config file is a text file containing at least two paragraphs or
|
||||||
|
stanzas, which are separated by at least one blank line. Lines beginning
|
||||||
|
with '#' are completely ignored and do not count as blank lines. Non-blank
|
||||||
|
lines must consist of a non-blank key and non-blank value separated by a '='
|
||||||
|
with no spaces on either side. The key may not contain whitespace. The value
|
||||||
|
may contain spaces, but all trailing whitespace is discarded.
|
||||||
|
|
||||||
|
The first stanza must contain a key named "recovery_tool_version'. Its value
|
||||||
|
must match the version of the recovery tool. If the value does not match,
|
||||||
|
then the key 'recovery_tool_update', if it exists in the first stanza,
|
||||||
|
should contain a string to display to the user. Regardless, if the version
|
||||||
|
doesn't match, the recovery tool should exit.
|
||||||
|
|
||||||
|
The second and remaining stanzas describe recovery images to put on the USB
|
||||||
|
drive.
|
||||||
|
|
||||||
|
For recovery_tool_version=1.0, each image stanza must contain:
|
||||||
|
|
||||||
|
* One and only one of these keys:
|
||||||
|
|
||||||
|
display_name - string to show to the user
|
||||||
|
file - the name of the file to extract from the tarball
|
||||||
|
size - size in bytes of the tarball
|
||||||
|
|
||||||
|
* One or more of these keys:
|
||||||
|
|
||||||
|
url - where to find the tarball to download
|
||||||
|
|
||||||
|
* One or both of these keys:
|
||||||
|
|
||||||
|
md5 - md5sum of the tarball
|
||||||
|
sha1 - sha1sum of the tarball
|
||||||
|
|
||||||
|
* Any other keys are informational only and are not used by the recovery tool.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NOTE: This is still in flux. Possible additional keys are
|
||||||
|
|
||||||
|
hwid
|
||||||
|
name
|
||||||
|
channel
|
||||||
689
user_tools/linux/recovery.sh
Executable file
689
user_tools/linux/recovery.sh
Executable file
@@ -0,0 +1,689 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010 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 attempts to guide linux users through the process of putting a recovery
|
||||||
|
# image onto a removeable USB drive.
|
||||||
|
#
|
||||||
|
# We may not need root privileges if we have the right permissions.
|
||||||
|
#
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Configuration goes here
|
||||||
|
|
||||||
|
# Where should we do our work? Use 'WORKDIR=' to make a temporary directory,
|
||||||
|
# but using a persistent location may let us resume interrupted downloads or
|
||||||
|
# run again without needing to download a second time.
|
||||||
|
WORKDIR=/tmp/tmp.crosrec
|
||||||
|
|
||||||
|
# Where do we look for the config file?
|
||||||
|
CONFIGURL='http://www.chromium.org/some/random/place.cfg'
|
||||||
|
|
||||||
|
# What version is this script? It must match the 'recovery_tool_version=' value
|
||||||
|
# in the config file that we'll download.
|
||||||
|
MYVERSION='1.0'
|
||||||
|
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Some temporary filenames
|
||||||
|
debug='debug.log'
|
||||||
|
tmpfile='tmp.txt'
|
||||||
|
config='config.txt'
|
||||||
|
version='verson.txt'
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Various warning messages
|
||||||
|
|
||||||
|
DEBUG() {
|
||||||
|
echo "DEBUG: $@" >>"$debug"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo "$@" 1>&2
|
||||||
|
}
|
||||||
|
|
||||||
|
quit() {
|
||||||
|
warn "quitting..."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fatal() {
|
||||||
|
warn "ERROR: $@"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ufatal() {
|
||||||
|
warn "
|
||||||
|
ERROR: $@
|
||||||
|
|
||||||
|
You may need to run this program as a different user. If that doesn't help, try
|
||||||
|
using a different computer, or ask a knowledgeable friend for help.
|
||||||
|
|
||||||
|
"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
gfatal() {
|
||||||
|
warn "
|
||||||
|
ERROR: $@
|
||||||
|
|
||||||
|
You may need to run this program as a different user. If that doesn't help, it
|
||||||
|
may be a networking problem or a problem with the images provided by Google.
|
||||||
|
You might want to check to see if there is a newer version of this tool
|
||||||
|
available, or if someone else has already reported a problem.
|
||||||
|
|
||||||
|
If all else fails, you could try using a different computer, or ask a
|
||||||
|
knowledgeable friend for help.
|
||||||
|
|
||||||
|
"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Identify the external utilities that we MUST have available.
|
||||||
|
#
|
||||||
|
# I'd like to keep the set of external *NIX commands to an absolute minimum,
|
||||||
|
# but I have to balance that against producing mysterious errors because the
|
||||||
|
# shell can't always do everything. Let's make sure that these utilities are
|
||||||
|
# all in our $PATH, or die with an error.
|
||||||
|
#
|
||||||
|
# This also sets the following global variables to select alternative utilities
|
||||||
|
# when there is more than one equivalent tool available:
|
||||||
|
#
|
||||||
|
# FETCH = name of utility used to download files from the web
|
||||||
|
# FETCHNEW = command to invoke to download fresh each time
|
||||||
|
# FETCHCONT = command to invoke to download with resume if possible
|
||||||
|
# CHECK = command to invoke to generate checksums on a file
|
||||||
|
#
|
||||||
|
require_utils() {
|
||||||
|
local external
|
||||||
|
local errors
|
||||||
|
local tool
|
||||||
|
local tmp
|
||||||
|
|
||||||
|
external='cat cut dd grep ls mkdir mount readlink sed sync umount unzip wc'
|
||||||
|
if [ -z "$WORKDIR" ]; then
|
||||||
|
external="$external mktemp"
|
||||||
|
fi
|
||||||
|
errors=
|
||||||
|
|
||||||
|
for tool in $external ; do
|
||||||
|
if ! type "$tool" >/dev/null 2>&1 ; then
|
||||||
|
warn "ERROR: can't find \"$tool\""
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# We also need to a way to fetch files from teh internets. Note that the args
|
||||||
|
# are different depending on which utility we find. We'll use two variants,
|
||||||
|
# one to fetch fresh every time and one to try again from where we left off.
|
||||||
|
FETCH=
|
||||||
|
if [ -z "$FETCH" ] && tmp=$(type curl 2>/dev/null) ; then
|
||||||
|
FETCH=curl
|
||||||
|
FETCHNEW="curl -f -s -S -o"
|
||||||
|
FETCHCONT="curl -f -C - -o"
|
||||||
|
fi
|
||||||
|
if [ -z "$FETCH" ] && tmp=$(type wget 2>/dev/null) ; then
|
||||||
|
FETCH=wget
|
||||||
|
FETCHNEW="wget -nv -O"
|
||||||
|
FETCHCONT="wget -c -O"
|
||||||
|
fi
|
||||||
|
if [ -z "$FETCH" ]; then
|
||||||
|
warn "ERROR: can't find \"curl\" or \"wget\""
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Once we've fetched a file we need to compute its checksum. There are a
|
||||||
|
# couple of possiblities here too.
|
||||||
|
CHECK=
|
||||||
|
if [ -z "$CHECK" ] && tmp=$(type md5sum 2>/dev/null) ; then
|
||||||
|
CHECK="md5sum"
|
||||||
|
fi
|
||||||
|
if [ -z "$CHECK" ] && tmp=$(type sha1sum 2>/dev/null) ; then
|
||||||
|
CHECK="sha1sum"
|
||||||
|
fi
|
||||||
|
if [ -z "$CHECK" ]; then
|
||||||
|
warn "ERROR: can't find \"md5sum\" or \"sha1sum\""
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$errors" ]; then
|
||||||
|
ufatal "Some required linux utilities are missing."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Helper functions to handle the config file and image tarball.
|
||||||
|
|
||||||
|
# Each paragraph in the config file should describe a new image. Let's make
|
||||||
|
# sure it follows all the rules. This scans the config file and returns success
|
||||||
|
# if it looks valid. As a side-effect, it lists the line numbers of the start
|
||||||
|
# and end of each stanza in the global variables 'start_lines' and 'end_lines'
|
||||||
|
# and saves the total number of images in the global variable 'num_images'.
|
||||||
|
good_config() {
|
||||||
|
local line
|
||||||
|
local key
|
||||||
|
local val
|
||||||
|
local display_name
|
||||||
|
local file
|
||||||
|
local size
|
||||||
|
local url
|
||||||
|
local md5
|
||||||
|
local sha1
|
||||||
|
local skipping
|
||||||
|
local errors
|
||||||
|
local count
|
||||||
|
local line_num
|
||||||
|
|
||||||
|
display_name=
|
||||||
|
file=
|
||||||
|
size=
|
||||||
|
url=
|
||||||
|
md5=
|
||||||
|
sha1=
|
||||||
|
skipping=yes
|
||||||
|
errors=
|
||||||
|
count=0
|
||||||
|
line_num=0
|
||||||
|
|
||||||
|
# global
|
||||||
|
start_lines=
|
||||||
|
end_lines=
|
||||||
|
|
||||||
|
while read line; do
|
||||||
|
line_num=$(( line_num + 1 ))
|
||||||
|
|
||||||
|
# We might have some empty lines before the first stanza. Skip them.
|
||||||
|
if [ -n "$skipping" ] && [ -z "$line" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Got something...
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
key=${line%=*}
|
||||||
|
val=${line#*=}
|
||||||
|
if [ -z "$key" ] || [ -z "$val" ] || [ "$key=$val" != "$line" ]; then
|
||||||
|
DEBUG "ignoring $line"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# right, looks good
|
||||||
|
if [ -n "$skipping" ]; then
|
||||||
|
skipping=
|
||||||
|
start_lines="$start_lines $line_num"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $key in
|
||||||
|
display_name)
|
||||||
|
if [ -n "$display_name" ]; then
|
||||||
|
DEBUG "duplicate $key"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
display_name="$val"
|
||||||
|
;;
|
||||||
|
file)
|
||||||
|
if [ -n "$file" ]; then
|
||||||
|
DEBUG "duplicate $key"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
file="$val"
|
||||||
|
;;
|
||||||
|
size)
|
||||||
|
if [ -n "$size" ]; then
|
||||||
|
DEBUG "duplicate $key"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
size="$val"
|
||||||
|
;;
|
||||||
|
url)
|
||||||
|
url="$val"
|
||||||
|
;;
|
||||||
|
md5)
|
||||||
|
md5="$val"
|
||||||
|
;;
|
||||||
|
sha1)
|
||||||
|
sha1="$val"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
# Between paragraphs. Time to check what we've found so far.
|
||||||
|
end_lines="$end_lines $line_num"
|
||||||
|
count=$(( count + 1))
|
||||||
|
|
||||||
|
if [ -z "$display_name" ]; then
|
||||||
|
DEBUG "image $count is missing display_name"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
if [ -z "$file" ]; then
|
||||||
|
DEBUG "image $count is missing file"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
if [ -z "$size" ]; then
|
||||||
|
DEBUG "image $count is missing size"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
if [ -z "$url" ]; then
|
||||||
|
DEBUG "image $count is missing url"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
if [ "$CHECK" = "md5sum" ] && [ -z "$md5" ]; then
|
||||||
|
DEBUG "image $count is missing required md5"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
if [ "$CHECK" = "sha1sum" ] && [ -z "$sha1" ]; then
|
||||||
|
DEBUG "image $count is missing required sha1"
|
||||||
|
errors=yes
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prepare for next stanza
|
||||||
|
display_name=
|
||||||
|
file=
|
||||||
|
size=
|
||||||
|
url=
|
||||||
|
md5=
|
||||||
|
sha1=
|
||||||
|
skipping=yes
|
||||||
|
fi
|
||||||
|
done < "$config"
|
||||||
|
|
||||||
|
DEBUG "$count images found"
|
||||||
|
num_images="$count"
|
||||||
|
|
||||||
|
DEBUG "start_lines=($start_lines)"
|
||||||
|
DEBUG "end_lines=($end_lines)"
|
||||||
|
|
||||||
|
# return error status
|
||||||
|
[ "$count" != "0" ] && [ -z "$errors" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Make the user pick an image to download. On success, it sets the global
|
||||||
|
# variable 'user_choice' to the selected image number.
|
||||||
|
choose_image() {
|
||||||
|
local show
|
||||||
|
local count
|
||||||
|
local line
|
||||||
|
local num
|
||||||
|
|
||||||
|
show=yes
|
||||||
|
while true; do
|
||||||
|
if [ -n "$show" ]; then
|
||||||
|
echo
|
||||||
|
echo "There are $num_images recovery images to choose from:"
|
||||||
|
echo
|
||||||
|
count=0
|
||||||
|
echo "0 - <quit>"
|
||||||
|
grep '^display_name=' "$config" | while read line; do
|
||||||
|
count=$(( count + 1 ))
|
||||||
|
echo "$line" | sed "s/display_name=/$count - /"
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
show=
|
||||||
|
fi
|
||||||
|
echo -n "Please select a recovery image to download: "
|
||||||
|
read num
|
||||||
|
if [ -z "$num" ] || [ "$num" = "?" ]; then
|
||||||
|
show=yes
|
||||||
|
elif echo "$num" | grep -q '[^0-9]'; then
|
||||||
|
echo "Sorry, I didn't understand that."
|
||||||
|
else
|
||||||
|
if [ "$num" -lt "0" ] || [ "$num" -gt "$num_images" ]; then
|
||||||
|
echo "That's not one of the choices."
|
||||||
|
elif [ "$num" -eq 0 ]; then
|
||||||
|
quit
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
|
||||||
|
# global
|
||||||
|
user_choice="$num"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fetch and verify the user's chosen image. On success, it sets the global
|
||||||
|
# variable 'image_file' to indicate the local name of the unpacked binary that
|
||||||
|
# should be written to the USB drive.
|
||||||
|
fetch_image() {
|
||||||
|
local start
|
||||||
|
local end
|
||||||
|
local line
|
||||||
|
local key
|
||||||
|
local val
|
||||||
|
local file
|
||||||
|
local size
|
||||||
|
local url
|
||||||
|
local md5
|
||||||
|
local sha1
|
||||||
|
local line_num
|
||||||
|
local tarball
|
||||||
|
local err
|
||||||
|
local sum
|
||||||
|
|
||||||
|
file=
|
||||||
|
size=
|
||||||
|
url=
|
||||||
|
md5=
|
||||||
|
sha1=
|
||||||
|
line_num="0"
|
||||||
|
|
||||||
|
# Convert image number to line numbers within config file.
|
||||||
|
start=$(echo $start_lines | cut -d' ' -f$1)
|
||||||
|
end=$(echo $end_lines | cut -d' ' -f$1)
|
||||||
|
|
||||||
|
while read line; do
|
||||||
|
# Skip to the start of the desired stanza
|
||||||
|
line_num=$(( line_num + 1 ))
|
||||||
|
if [ "$line_num" -lt "$start" ] || [ "$line_num" -ge "$end" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Process the stanza.
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
key=${line%=*}
|
||||||
|
val=${line#*=}
|
||||||
|
if [ -z "$key" ] || [ -z "$val" ] || [ "$key=$val" != "$line" ]; then
|
||||||
|
DEBUG "ignoring $line"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $key in
|
||||||
|
# The descriptive stuff we'll just save for later.
|
||||||
|
file)
|
||||||
|
file="$val"
|
||||||
|
;;
|
||||||
|
size)
|
||||||
|
size="$val"
|
||||||
|
;;
|
||||||
|
md5)
|
||||||
|
md5="$val"
|
||||||
|
;;
|
||||||
|
sha1)
|
||||||
|
sha1="$val"
|
||||||
|
;;
|
||||||
|
url)
|
||||||
|
# Try to download each url until one works.
|
||||||
|
if [ -n "$url" ]; then
|
||||||
|
# We've already got one (it's very nice).
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
warn "Downloading image tarball from $val"
|
||||||
|
warn
|
||||||
|
tarball=${val##*/}
|
||||||
|
if $FETCHCONT "$tarball" "$val"; then
|
||||||
|
# Got it.
|
||||||
|
url="$val"
|
||||||
|
else
|
||||||
|
# If you give curl the '-C -' option but the file you want is
|
||||||
|
# already complete and the server doesn't report the total size
|
||||||
|
# correctly, it will report an error instead of just doing nothing.
|
||||||
|
# We'll try to work around that.
|
||||||
|
err=$?
|
||||||
|
if [ "$FETCH" = "curl" ] && [ "$err" = "18" ]; then
|
||||||
|
warn "Ignoring spurious complaint"
|
||||||
|
url="$val"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done < "$config"
|
||||||
|
|
||||||
|
if [ -z "$url" ]; then
|
||||||
|
DEBUG "couldn't fetch tarball"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify the tarball
|
||||||
|
if ! ls -l "$tarball" | grep -q "$size"; then
|
||||||
|
DEBUG "size is wrong"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sum=$($CHECK "$tarball" | cut -d' ' -f1)
|
||||||
|
DEBUG "$CHECK is $sum"
|
||||||
|
if [ "$CHECK" = "md5sum" ] && [ "$sum" != "$md5" ]; then
|
||||||
|
DEBUG "wrong $CHECK"
|
||||||
|
return 1
|
||||||
|
elif [ "$CHECK" = "sha1sum" ] && [ "$sum" != "$sha1" ]; then
|
||||||
|
DEBUG "wrong $CHECK"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Unpack the file
|
||||||
|
warn "Unpacking the tarball"
|
||||||
|
rm -f "$file"
|
||||||
|
if ! unzip "$tarball" "$file"; then
|
||||||
|
DEBUG "Can't unpack the tarball"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# global
|
||||||
|
image_file="$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Helper functions to manage USB drives.
|
||||||
|
|
||||||
|
# Return a list of base device names ("sda sdb ...") for all USB drives
|
||||||
|
get_devlist() {
|
||||||
|
local dev
|
||||||
|
local t
|
||||||
|
local r
|
||||||
|
|
||||||
|
for dev in $(cat /proc/partitions); do
|
||||||
|
[ -r "/sys/block/$dev/device/type" ] &&
|
||||||
|
t=$(cat "/sys/block/$dev/device/type") &&
|
||||||
|
[ "$t" = "0" ] &&
|
||||||
|
r=$(cat "/sys/block/$dev/removable") &&
|
||||||
|
[ "$r" = "1" ] &&
|
||||||
|
readlink -f "/sys/block/$dev" | grep -q -i usb &&
|
||||||
|
echo "$dev" || true
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return descriptions for each provided base device name ("sda sdb ...")
|
||||||
|
get_devinfo() {
|
||||||
|
local dev
|
||||||
|
local v
|
||||||
|
local m
|
||||||
|
local s
|
||||||
|
local ss
|
||||||
|
|
||||||
|
for dev in $1; do
|
||||||
|
v=$(cat "/sys/block/$dev/device/vendor") &&
|
||||||
|
m=$(cat "/sys/block/$dev/device/model") &&
|
||||||
|
s=$(cat "/sys/block/$dev/size") && ss=$(( $s * 512 / 1000000 )) &&
|
||||||
|
echo "/dev/$dev ${ss}MB $v $m"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enumerate and descript the specified base device names ("sda sdb ...")
|
||||||
|
get_choices() {
|
||||||
|
local dev
|
||||||
|
local desc
|
||||||
|
local count
|
||||||
|
|
||||||
|
count=1
|
||||||
|
echo "0 - <quit>"
|
||||||
|
for dev in $1; do
|
||||||
|
desc=$(get_devinfo "$dev")
|
||||||
|
echo "$count - Use $desc"
|
||||||
|
count=$(( count + 1 ))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make the user pick a USB drive to write to. On success, it sets the global
|
||||||
|
# variable 'user_choice' to the selected device name ("sda", "sdb", etc.)
|
||||||
|
choose_drive() {
|
||||||
|
local show
|
||||||
|
local devlist
|
||||||
|
local choices
|
||||||
|
local num_drives
|
||||||
|
local msg
|
||||||
|
local num
|
||||||
|
|
||||||
|
show=yes
|
||||||
|
while true; do
|
||||||
|
if [ -n "$show" ]; then
|
||||||
|
devlist=$(get_devlist)
|
||||||
|
choices=$(get_choices "$devlist")
|
||||||
|
if [ -z "$devlist" ]; then
|
||||||
|
num_drives="0"
|
||||||
|
msg="I can't seem to find a valid USB drive."
|
||||||
|
else
|
||||||
|
num_drives=$(echo "$devlist" | wc -l)
|
||||||
|
if [ "$num_drives" != "1" ]; then
|
||||||
|
msg="I found $num_drives USB drives"
|
||||||
|
else
|
||||||
|
msg="I found $num_drives USB drive"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo -n "
|
||||||
|
|
||||||
|
$msg
|
||||||
|
|
||||||
|
$choices
|
||||||
|
|
||||||
|
"
|
||||||
|
show=
|
||||||
|
fi
|
||||||
|
echo -n "Tell me what to do (or just press Enter to scan again): "
|
||||||
|
read num
|
||||||
|
if [ -z "$num" ] || [ "$num" = "?" ]; then
|
||||||
|
show=yes
|
||||||
|
elif echo "$num" | grep -q '[^0-9]'; then
|
||||||
|
echo "Sorry, I didn't understand that."
|
||||||
|
else
|
||||||
|
if [ "$num" -lt "0" ] || [ "$num" -gt "$num_drives" ]; then
|
||||||
|
echo "That's not one of the choices."
|
||||||
|
elif [ "$num" -eq 0 ]; then
|
||||||
|
quit
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# global
|
||||||
|
user_choice=$(echo $devlist | cut -d' ' -f$num)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Okay, do something...
|
||||||
|
|
||||||
|
# Make sure we have the tools we need
|
||||||
|
require_utils
|
||||||
|
|
||||||
|
# Need a place to work. We prefer a fixed location so we can try to resume any
|
||||||
|
# interrupted downloads.
|
||||||
|
if [ -n "$WORKDIR" ]; then
|
||||||
|
if [ ! -d "$WORKDIR" ] && ! mkdir "$WORKDIR" ; then
|
||||||
|
warn "Using temporary directory"
|
||||||
|
WORKDIR=
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -z "$WORKDIR" ]; then
|
||||||
|
WORKDIR=$(mktemp -d)
|
||||||
|
# Clean up temporary directory afterwards
|
||||||
|
trap "cd; rm -rf ${WORKDIR}" EXIT
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$WORKDIR"
|
||||||
|
warn "Working in $WORKDIR/"
|
||||||
|
rm -f "$debug"
|
||||||
|
|
||||||
|
# Download the config file to see what choices we have.
|
||||||
|
warn "Downloading config file from $CONFIGURL"
|
||||||
|
$FETCHNEW "$tmpfile" "$CONFIGURL" || \
|
||||||
|
gfatal "Unable to download the config file"
|
||||||
|
|
||||||
|
# Un-DOS-ify the config file and separate the version info from the images
|
||||||
|
sed 's/\r//g' "$tmpfile" | grep '^recovery_tool' > "$version"
|
||||||
|
sed 's/\r//g' "$tmpfile" | grep -v '^#' | grep -v '^recovery_tool' > "$config"
|
||||||
|
# Add one empty line to the config file to terminate the last stanza
|
||||||
|
echo >> "$config"
|
||||||
|
|
||||||
|
# Make sure that the config file version matches this script version
|
||||||
|
tmp=$(grep '^recovery_tool_version=' "$version") || \
|
||||||
|
gfatal "The config file doesn't contain a version string."
|
||||||
|
filevers=${tmp#*=}
|
||||||
|
if [ "$filevers" != "$MYVERSION" ]; then
|
||||||
|
tmp=$(grep '^recovery_tool_update=' "$version");
|
||||||
|
msg=${tmp#*=}
|
||||||
|
warn "This tool is version $MYVERSION." \
|
||||||
|
"The config file is for version $filevers."
|
||||||
|
fatal ${msg:-Please download a matching version of the tool and try again.}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check the config file to be sure it's valid. As a side-effect, this sets the
|
||||||
|
# global variable 'num_images' with the number of image stanzas read, but
|
||||||
|
# that's independent of whether the config is valid.
|
||||||
|
good_config || gfatal "The config file isn't valid."
|
||||||
|
|
||||||
|
# Make the user pick an image to download, or exit.
|
||||||
|
choose_image
|
||||||
|
|
||||||
|
# Download the user's choice
|
||||||
|
fetch_image "$user_choice" || \
|
||||||
|
gfatal "Unable to download a valid recovery image."
|
||||||
|
|
||||||
|
# Make the user pick a USB drive, or exit.
|
||||||
|
choose_drive
|
||||||
|
|
||||||
|
# Be sure
|
||||||
|
dev_desc=$(get_devinfo "$user_choice")
|
||||||
|
echo "
|
||||||
|
Is this the device you want to put the recovery image on?
|
||||||
|
|
||||||
|
$dev_desc
|
||||||
|
"
|
||||||
|
echo -n "You must enter 'YES' to continue: "
|
||||||
|
read tmp
|
||||||
|
if [ "$tmp" != "YES" ]; then
|
||||||
|
quit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Be very sure
|
||||||
|
echo "
|
||||||
|
|
||||||
|
I'm really going to erase this device. This will permanently ERASE
|
||||||
|
whatever you may have on that drive. You won't be able to undo it.
|
||||||
|
|
||||||
|
$dev_desc
|
||||||
|
"
|
||||||
|
|
||||||
|
echo -n "If you're sure that's the device to use, enter 'DoIt' now: "
|
||||||
|
read tmp
|
||||||
|
if [ "$tmp" != "DoIt" ]; then
|
||||||
|
quit
|
||||||
|
fi
|
||||||
|
echo "
|
||||||
|
|
||||||
|
Installing the recovery image
|
||||||
|
|
||||||
|
"
|
||||||
|
|
||||||
|
# Unmount anything on that device.
|
||||||
|
echo "unmounting..."
|
||||||
|
for tmp in $(mount | grep ^"/dev/${user_choice}" | sed 's/[ \t].*//'); do
|
||||||
|
umount $tmp || ufatal "Unable to unmount $tmp."
|
||||||
|
done
|
||||||
|
|
||||||
|
# Write it.
|
||||||
|
echo "copying... (this may take several minutes)"
|
||||||
|
dd of=/dev/${user_choice} if="$image_file" ||
|
||||||
|
ufatal "Unable to write the image."
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "
|
||||||
|
|
||||||
|
Done. Remove the USB drive and insert it in your Chrome OS netbook.
|
||||||
|
|
||||||
|
"
|
||||||
|
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user