blob: 275ddc55bce9c5b0a2dcba31230e735e5f257e54 [file] [log] [blame]
#!/bin/bash
# Copyright 2020 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# Helper functions, no environment specific functions should be included below
# this line.
# Force all pipes to return any non-zero error code instead of just the last
set -e -o pipefail
SCRIPT_SRC_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
DEFAULT_FUCHSIA_BUCKET="fuchsia"
SSH_BIN="$(command -v ssh)"
FUCHSIA_PROPERTY_NAMES=(
"bucket" # Used as the default for --bucket
"device-ip" # Used as the default for --device-ip
"device-name" # Used as the default for --device-name
"image" # Used as the default for image
)
function is-mac {
[[ "$(uname -s)" == "Darwin" ]] && return 0
return 1
}
# Add Mac specific support
if is-mac; then
# Fuchsia mac functions.
realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
fi
# Returns the fuchsia sdk root dir. Assuming this script is in ${FUCHSIA_SDK}/bin.
function get-fuchsia-sdk-dir {
dirname "${SCRIPT_SRC_DIR}"
}
# Returns the data directory for the fuchsia sdk.
# This directory is expected to be per-developer, not checked into source code revision systems,
# and is used to store device images and packages and related data items.
# The default is $HOME/.fuchsia. This can be overridden by setting the environment variable
# FUCHSIA_SDK_DATA_DIR.
function get-fuchsia-sdk-data-dir {
local data_dir="${FUCHSIA_SDK_DATA_DIR:-}"
if [[ -z "${data_dir}" ]]; then
data_dir="${HOME}/.fuchsia"
if [[ ! -d "${data_dir}" ]]; then
mkdir -p "${data_dir}"
fi
fi
echo "${data_dir}"
}
# fx-warn prints a line to stderr with a yellow WARNING: prefix.
function fx-warn {
if [[ -t 2 ]]; then
echo -e >&2 "\033[1;33mWARNING:\033[0m $*"
else
echo -e >&2 "WARNING: $*"
fi
}
# fx-error prints a line to stderr with a red ERROR: prefix.
function fx-error {
if [[ -t 2 ]]; then
echo -e >&2 "\033[1;31mERROR:\033[0m $*"
else
echo -e >&2 "ERROR: $*"
fi
}
function get-fuchsia-property-names {
echo "${FUCHSIA_PROPERTY_NAMES[@]}"
}
function is-valid-fuchsia-property {
[[ "${FUCHSIA_PROPERTY_NAMES[*]}" =~ $1 ]]
}
function set-fuchsia-property {
local prop_path
prop_path="$(get-fuchsia-sdk-data-dir)/.properties/$1.txt"
if ! mkdir -p "$(dirname "${prop_path}")"; then
fx-error "Cannot write property to $prop_path"
exit 1
fi
echo "$2" > "${prop_path}"
}
function get-fuchsia-property {
local prop_path
prop_path="$(get-fuchsia-sdk-data-dir)/.properties/$1.txt"
if [[ -e "${prop_path}" ]]; then
cat "${prop_path}"
else
echo ""
fi
}
function ssh-cmd {
check-fuchsia-ssh-config
"${SSH_BIN}" -F "$(get-fuchsia-sshconfig-file)" "$@"
}
function get-device-ip {
# $1 is the SDK_PATH (optional. defaults to get-fuchsia-sdk-dir)
# -ipv4 false: Disable IPv4. Fuchsia devices are IPv6-compatible, so
# forcing IPv6 allows for easier manipulation of the result.
local device_addr
device_addr="$(get-fuchsia-property device-addr)"
if [[ "${device_addr}" != "" ]]; then
echo "${device_addr}"
return 0
else
"${1-$(get-fuchsia-sdk-dir)}/tools/device-finder" list -netboot -device-limit 1 -ipv4=false
fi
}
function get-device-name {
# $1 is the SDK_PATH (optional. defaults to get-fuchsia-sdk-dir)
# Check for a device name being configured.
local device_name
if ! device_name="$(get-fuchsia-property device-name)"; then
return $?
fi
if [[ "${device_name}" != "" ]]; then
echo "${device_name}"
return 0
else
if device_name="$("${1-$(get-fuchsia-sdk-dir)}/tools/device-finder" list -netboot -device-limit 1 -full)"; then
echo "${device_name}" | cut -d' ' -f2
fi
fi
}
function get-device-ip-by-name {
# Writes the IP address of the device with the given name.
# If no such device is found, this function returns with a non-zero status
# code.
# $1 is the SDK_PATH, if specified else get-fuchsia-sdk-dir value is used.
# $2 is the hostname of the Fuchsia device. If $2 is empty, this function
# returns the IP address of an arbitrarily selected Fuchsia device.
if [[ "${#}" -ge 2 && -n "$2" ]]; then
# There should typically only be one device that matches the nodename
# but we add a device-limit to speed up resolution by exiting when the first
# candidate is found.
"${1-$(get-fuchsia-sdk-dir)}/tools/device-finder" resolve -device-limit 1 -ipv4=false -netboot "${2}"
else
if [[ "${#}" -ge 1 && -n "$1" ]]; then
get-device-ip "$1"
else
get-device-ip
fi
fi
}
function get-host-ip {
# $1 is the SDK_PATH, if specified else get-fuchsia-sdk-dir value is used.
# $2 is the hostname of the Fuchsia device. If $2 is empty, this function
# returns the IP address of an arbitrarily selected Fuchsia device.
local DEVICE_NAME
if [[ "${#}" -ge 2 && "${2}" != "" ]]; then
DEVICE_NAME="${2}"
else
DEVICE_NAME="$(get-device-name "${1-$(get-fuchsia-sdk-dir)}")"
fi
# -ipv4 false: Disable IPv4. Fuchsia devices are IPv6-compatible, so
# forcing IPv6 allows for easier manipulation of the result.
# cut: Remove the IPv6 scope, if present. For link-local addresses, the scope
# effectively describes which interface a device is connected on. Since
# this information is device-specific (i.e. the Fuchsia device refers to
# the development host with a different scope than vice versa), we can
# strip this from the IPv6 result. This is reliable as long as the Fuchsia
# device only needs link-local networking on one interface.
"${1-$(get-fuchsia-sdk-dir)}/tools/device-finder" resolve -local -ipv4=false "${DEVICE_NAME}" | head -1 | cut -d '%' -f1
}
function get-sdk-version {
# Get the Fuchsia SDK id
# $1 is the SDK_PATH, if specified else get-fuchsia-sdk-dir value is used.
local FUCHSIA_SDK_METADATA="${1-$(get-fuchsia-sdk-dir)}/meta/manifest.json"
if ! SDK_VERSION="$(grep \"id\": "${FUCHSIA_SDK_METADATA}" | cut -d\" -f4)"; then
return 1
fi
echo "${SDK_VERSION}"
}
function get-package-src-path {
# $1 is the SDK ID. See #get-sdk-version.
# $2 is the image name.
echo "gs://${FUCHSIA_BUCKET}/development/${1}/packages/${2}.tar.gz"
}
function get-image-src-path {
# $1 is the SDK ID. See #get-sdk-version.
# $2 is the image name.
echo "gs://${FUCHSIA_BUCKET}/development/${1}/images/${2}.tgz"
}
# Run gsutil from the directory of this script if it exists, otherwise
# use the path.
function run-gsutil {
GSUTIL_BIN="${SCRIPT_SRC_DIR}/gsutil"
if [[ ! -e "${GSUTIL_BIN}" ]]; then
GSUTIL_BIN="$(command -v gsutil)"
fi
if [[ ! -e "${GSUTIL_BIN}" ]]; then
GSUTIL_BIN="$(command -v gsutil.py)"
fi
if [[ "${GSUTIL_BIN}" == "" ]]; then
fx-error "Cannot find gsutil."
exit 2
fi
# Prevent gsutil prompting for updates by making stdin not a TTY
"${GSUTIL_BIN}" "$@" < /dev/null
}
# Run cipd from the directory of this script if it exists, otherwise
# use the path.
function run-cipd {
CIPD_BIN="${SCRIPT_SRC_DIR}/cipd"
if [[ ! -e "${CIPD_BIN}" ]]; then
CIPD_BIN="$(command -v cipd)"
fi
if [[ "${CIPD_BIN}" == "" ]]; then
fx-error "Cannot find cipd."
exit 2
fi
"${CIPD_BIN}" "$@"
}
# Runs md5sum or equivalent on mac.
function run-md5 {
if is-mac; then
MD5_CMD=("/sbin/md5" "-r")
else
MD5_CMD=("md5sum")
fi
MD5_CMD+=("$@")
"${MD5_CMD[@]}"
}
function get-available-images {
# $1 is the SDK ID.
# $2 is the bucket, or uses the default.
local IMAGES=()
local BUCKET=""
BUCKET="${2:-${DEFAULT_FUCHSIA_BUCKET}}"
GSURL="gs://${BUCKET}/development/${1}/images"
if ! RESULTS=$(run-gsutil "ls" "${GSURL}" | cut -d/ -f7 | tr '\n' ' '); then
return 1
fi
if [[ "${RESULTS}" == "" ]]; then
return 2
fi
for f in ${RESULTS}; do
IMAGES+=("${f%.*}")
done
if [[ "${BUCKET}" != "${DEFAULT_FUCHSIA_BUCKET}" ]]; then
echo -n "${IMAGES[*]} "
get-available-images "${1}" "${DEFAULT_FUCHSIA_BUCKET}"
else
echo "${IMAGES[*]}"
fi
}
function kill-running-pm {
local PM_PROCESS=()
IFS=" " read -r -a PM_PROCESS <<< "$(pgrep -ax pm)"
if [[ ${#PM_PROCESS[@]} -gt 0 && -n "${PM_PROCESS[*]}" ]]; then
# mac only provides the pid, not the name
if is-mac; then
fx-warn "Killing existing pm process"
kill "${PM_PROCESS[0]}"
return $?
elif [[ ${#PM_PROCESS[@]} -gt 1 && "${PM_PROCESS[1]}" == *"tools/pm" ]]; then
fx-warn "Killing existing pm process"
kill "${PM_PROCESS[0]}"
return $?
fi
else
fx-warn "existing pm process not found"
fi
return 0
}
function check-fuchsia-ssh-config {
# This function creates the ssh keys needed to
# work with devices running Fuchsia. There are two parts, the keys and the config.
#
# The keys are stored in the Fuchsia SDK data directory in a directory named .ssh.
# This is the same structure as used "in-tree" for Fuchsia development. You can copy the
# keys from the other directory to make the keys identical, allowing SSH access using both
# SDK commands and in-tree "fx" commands.
#
# The authorized key file used for paving is in .ssh/authorized_keys.
# The private key used when ssh'ing to the device is in .ssh/pkey.
#
#
# The second part of is the sshconfig file used by the SDK when using SSH.
# This is stored in the Fuchsia SDK data directory named sshconfig.
# This script checks for the private key file being referenced in the sshconfig and
# the matching version tag. If they are not present, the sshconfig file is regenerated.
# The ssh configuration should not be modified.
local SSHCONFIG_TAG="Fuchsia SDK config version 2 tag"
local ssh_dir
ssh_dir="$(get-fuchsia-sdk-data-dir)/.ssh"
local authfile="${ssh_dir}/authorized_keys"
local keyfile="${ssh_dir}/pkey"
local sshconfig_file
sshconfig_file="$(get-fuchsia-sdk-data-dir)/sshconfig"
if [[ -e "${authfile}" && -e "${keyfile}" ]]; then
if grep "${keyfile}" "${sshconfig_file}" > /dev/null 2>&1; then
if grep "${SSHCONFIG_TAG}" "${sshconfig_file}" > /dev/null 2>&1; then
return 0
fi
fi
fi
mkdir -p "${ssh_dir}"
if [[ ! -f "${authfile}" ]]; then
if [[ ! -f "${keyfile}" ]]; then
if ! result="$(ssh-keygen -P "" -t ed25519 -f "${keyfile}" -C "${USER}@$(hostname -f)" 2>&1)"; then
fx-error "${result}"
return 1
fi
fi
if ! result="$(ssh-keygen -y -f "${keyfile}" > "${authfile}" 2>&1)"; then
fx-error "${result}"
return 1
fi
fi
cat >"${sshconfig_file}" <<EOF
# ${SSHCONFIG_TAG}
# Configure port 8022 for connecting to a device with the local address.
# This makes it possible to forward 8022 to a device connected remotely.
Host 127.0.0.1
Port 8022
Host ::1
Port 8022
Host *
# Turn off refusing to connect to hosts whose key has changed
StrictHostKeyChecking no
CheckHostIP no
# Disable recording the known hosts
UserKnownHostsFile=/dev/null
# Do not forward auth agent connection to remote, no X11
ForwardAgent no
ForwardX11 no
# Connection timeout in seconds
ConnectTimeout=10
# Check for server alive in seconds, max count before disconnecting
ServerAliveInterval 1
ServerAliveCountMax 10
# Try to keep the master connection open to speed reconnecting.
ControlMaster auto
ControlPersist yes
ControlPath=/tmp/fuchsia--%r@%h:%p
# Connect with user, use the identity specified.
User fuchsia
IdentitiesOnly yes
IdentityFile "${keyfile}"
GSSAPIDelegateCredentials no
EOF
return 0
}
function get-fuchsia-auth-keys-file {
check-fuchsia-ssh-config
echo "$(get-fuchsia-sdk-data-dir)/.ssh/authorized_keys"
}
function get-fuchsia-sshconfig-file {
check-fuchsia-ssh-config
echo "$(get-fuchsia-sdk-data-dir)/sshconfig"
}