| #!/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. |
| |
| set -e |
| |
| # Global variables |
| SELF="$(readlink -f "$0")" |
| |
| # Set by make_temp function and removed by clean_temp |
| TMP_DIR= |
| # Decides if we need to print debug messages |
| IS_DEBUG= |
| |
| # Tag file to prohibit updater execution |
| TAG_FILE_DISABLED='/root/.leave_firmware_alone' |
| # Set to True to bypass checking TAG_FILE_DISABLED |
| IS_FORCED= |
| |
| # Set to true to prevent printing error alerts by error return value |
| IS_IGNORE_RC= |
| |
| # Prints a message and return an error code ($1). |
| die_as() { |
| local ret="$1" |
| shift |
| echo "ERROR: $@" >&2 |
| exit "$ret" |
| } |
| |
| # Prints a message and return error as 1. |
| die() { |
| die_as 1 "$@" |
| } |
| |
| # Prints messages if $IS_DEBUG is not empty. |
| debug() { |
| [ -z "$IS_DEBUG" ] || echo "$@" >&2 |
| } |
| |
| # Creates a temporary folder |
| make_temp() { |
| TMP_DIR="$(mktemp -d --tmpdir)" || |
| die "Failed to create temporary folder" |
| trap clean_temp EXIT |
| } |
| |
| # Creates a temporary folder with execution permission |
| make_exec_temp() { |
| make_temp |
| if [ "$(id -u)" = "0" ]; then |
| debug "bind and remount for allowing execution ..." |
| (mount --bind "$TMP_DIR" "$TMP_DIR" && mount -o remount,exec "$TMP_DIR") || |
| die "Failed to enable execution permission of folder $TMP_DIR" |
| else |
| debug "Not running as root: assuming we can execute in /tmp" |
| fi |
| } |
| |
| # Cleans temporary folders |
| clean_temp() { |
| debug "clean_temp: started." |
| if [ -d "$TMP_DIR" ]; then |
| umount -f "$TMP_DIR" >/dev/null 2>&1 || true |
| rm -rf "$TMP_DIR" >/dev/null 2>&1 || true |
| TMP_DIR="" |
| fi |
| } |
| |
| # Extracts bundle content to specified location |
| extract_bundle() { |
| local destination="$1" |
| sh "$SELF" --unpack "$destination" >/dev/null || |
| die "Cannot extract bundle content to: $destination" |
| } |
| |
| # Executes the updater bundle |
| exec_bundle() { |
| local rc=0 |
| make_exec_temp |
| extract_bundle "$TMP_DIR" |
| type futility >/dev/null 2>&1 || die "Missing program 'futility'." |
| |
| debug "Start running updater: futility update -a ${TMP_DIR} $@" |
| (cd "$TMP_DIR" && futility update -a . "$@") || rc="$?" |
| |
| if [ "$rc" -ne 0 -a -z "$IS_IGNORE_RC" ]; then |
| die_as "$rc" "Execution failed: (error code = $rc)" |
| fi |
| exit "$rc" |
| } |
| |
| # Prepares for extraction with shar |
| prepare_shar_extract() { |
| local destination="$1" |
| if [ -z "$destination" ]; then |
| make_temp |
| destination="$TMP_DIR" |
| # Don't remove the temporary files |
| TMP_DIR="" |
| fi |
| echo "Extracting to: $destination" |
| cd "$destination" || die "Invalid destination: $destination" |
| exec >/dev/null # Prevent shar messages in stdout |
| set -- "-c" # Force shar to overwrite files |
| } |
| |
| # Repacks current file ($SELF) by given source folder. |
| perform_shar_repack() { |
| local new_source="$1" |
| local cut_mark="$(sed -n '/^##CUTHERE##/=' "$SELF")" |
| local manifest="${new_source}/manifest.json" |
| |
| [ "$cut_mark" -gt 0 ] || die "File corrupted: $SELF" |
| sed -i "$((cut_mark + 1)),\$d" "$SELF" || |
| die "Failed to truncate existing data in $SELF" |
| |
| if type futility >/dev/null 2>&1; then |
| futility update -a "${new_source}" --manifest >"${manifest}" || |
| rm -f "${manifest}" |
| else |
| rm -f "${manifest}" |
| fi |
| |
| # Use a standard timestamp for the version files so that we get the same |
| # exact sharball each time. Otherwise the changing timestamps creates small |
| # differences. |
| touch -t 201701010000 "${new_source}/VERSION" "${manifest}" |
| |
| # Build shar content with files in sorted order for repeatability. |
| (cd "$new_source" && |
| find . -type f | sort | |
| shar -Q -q -x -m --no-character-count -D --no-i18n -z -g 1 | |
| sed -r 's/^lock_dir=_sh.*/lock_dir=_fwupdate/; |
| s"^begin ([0-9]+) _sh[^/]*"begin \1 _fwupdate" |
| /^# Made on .* by/d; |
| /^# Source directory was /d') >>"$SELF" || |
| die "Failed repacking from $new_source" |
| } |
| |
| # Prints the VAR from '--param VAR' and '--param=VAR' format. |
| get_parameter_variable() { |
| local param="$1" |
| local param_name="${param%%=*}" |
| local param_value="${param#*=}" |
| |
| if [ "$param" = "$param_name" ]; then |
| echo "$2" |
| else |
| echo "$param_value" |
| fi |
| } |
| |
| # Main entry |
| main() { |
| local original_params="$*" |
| |
| # The sb_* are kept for backward compatibility, especially for signing. |
| case "$1" in |
| --sb_extract | --sb_extract=* | --unpack | unpack=*) |
| local destination="$(get_parameter_variable "$@")" |
| prepare_shar_extract "$destination" |
| return # Let shar handle the remaining stuff |
| ;; |
| |
| --sb_repack | --repack ) |
| local new_source="$(get_parameter_variable "$@")" |
| [ -d "$new_source" ] || die "Invalid source folder: $new_source" |
| echo "Repacking from: $new_source" |
| perform_shar_repack "$new_source" |
| exit 0 |
| ;; |
| |
| -V) |
| # Read information |
| make_temp |
| extract_bundle "$TMP_DIR" |
| cat "$TMP_DIR/VERSION"* |
| echo "Package Content:" |
| (cd "${TMP_DIR}" && find . -type f -exec md5sum -b '{}' ';' ) |
| echo "This is only for debugging and the format may change any time." >&2 |
| echo "If you want to parse with programs, use --manifest instead." >&2 |
| exit 0 |
| ;; |
| |
| --manifest) |
| # Read information |
| make_temp |
| extract_bundle "$TMP_DIR" |
| if [ -s "${TMP_DIR}/manifest.json" ]; then |
| cat "$TMP_DIR/manifest.json" |
| else |
| echo "No manifest.json available" >&2 |
| fi |
| exit 0 |
| ;; |
| |
| -h | "-?" | --help) |
| echo " |
| USAGE: $SELF [bundle_option|--] [updater_options] |
| |
| bundle_option (only one option can be selected): |
| -h,--help: Show usage help |
| -V: show version and content of bundle |
| --manifest: print JSON manifest if available |
| --force: force execution and ignore $TAG_FILE_DISABLED |
| --unpack [PATH]: extract bundle content to a temporary folder |
| --repack PATH: update bundle content from given folder |
| |
| updater_options: |
| " |
| # Invoke script with -h for usage help |
| IS_IGNORE_RC=TRUE |
| exec_bundle "-h" |
| ;; |
| |
| --force) |
| # Pass this into updaters |
| IS_FORCED=TRUE |
| ;; |
| |
| --debug | --debug | -v) |
| # do not shift here because this needs to be passed into the script |
| IS_DEBUG=TRUE |
| ;; |
| |
| --) |
| shift |
| ;; |
| esac |
| |
| # Do nothing if the OS specifies that. |
| # TODO(hungte) move this flag to kernel command line, or updater bundle |
| # itself. |
| if [ -e "$TAG_FILE_DISABLED" ] && [ -z "$IS_FORCED" ]; then |
| echo "WARNING: $SELF is disabled by $TAG_FILE_DISABLED" |
| echo "To force execution, please prefix --force to your command:" |
| echo " sudo $SELF --force $original_params" |
| exit 0 |
| fi |
| |
| if type futility >/dev/null 2>&1; then |
| exec_bundle "$@" |
| else |
| die "Need 'futility' in PATH to execute updater." |
| fi |
| } |
| |
| main "$@" |
| |
| # Below are for shar execution. Don't put any code below main. |
| ##CUTHERE################################################################## |