blob: 477f22df91b1ccc8fdae59665a0cbe8e7aee50aa [file] [log] [blame]
#!/bin/bash
# Copyright 2018 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.
#### CATEGORY=Software delivery
### start the update server and attach to a running fuchsia device
## usage: fx serve [-v] [-l host[:port]] [-c version] [--no-device] [--name NAME]
##
## -l port port that "fx serve" will listen on
## --no-device Do not register the repository with a specific device.
## --name NAME Name the generated update source config NAME.
## --[no-]persist enable or disable persistence of repository metadata. Disabled
## by default.
## -v verbose mode, shows info and debug messages from "ffx repository serve"
## -C|--clean clean the package repository first. This flag is only
## valid if the incremental package publishing is enabled.
## --foreground run the repository server in the foreground (the default).
## --background run the repository server in the background.
##
## This command supports:
## - incremental package publishing. If enabled, it will auto-publish packages as they
## are created or modified.
##
## To enable incremental package serving, run "fx --enable=incremental serve ..."
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $?
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/updates.sh || exit $?
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/fx-optional-features.sh || exit $?
fx-config-read
function usage {
fx-command-help serve
}
fx-standard-switches "$@"
set -- "${FX_ARGV[@]}"
port=""
verbose=false
clean_first=false
source_name=""
storage_type="ephemeral"
no_device=""
server_mode="--foreground"
while (($#)); do
case "$1" in
-l)
port="${2##*:}"
shift
;;
--name)
source_name="$2"
shift
;;
--no-persist)
storage_type="ephemeral"
;;
--persist)
storage_type="persistent"
;;
--no-device)
no_device="--no-device"
;;
--background)
server_mode="--background"
;;
--foreground)
server_mode="--foreground"
;;
-v|--verbose)
verbose=true
;;
-C|--clean)
clean_first=true
;;
*)
echo "Unrecognized option: $1"
usage
exit 1
;;
esac
shift
done
if fx-is-bringup; then
fx-error "$0 is not supported in the bringup build configuration, as there are no package features in bringup."
exit 1
fi
if [[ -z "${source_name}" ]]; then
source_name="$(ffx-default-repository-name)"
fi
log() {
# This format matches bootserver so that `fx serve` ui is easier to read.
echo "$(date '+%Y-%m-%d %H:%M:%S') [serve] $@"
}
# In any multi-homing scenario (two target interfaces, or two host interfaces),
# the resolve process can return different results at different times. As such
# we pin the address here, and let the ssh check loop below perform a clear
# whenever it is in the "discovery" state. This stabilizes the "connection"
# under multi-homed conditions.
serve_updates_target_addr=""
target-addr() {
if [[ -z "${serve_updates_target_addr}" ]]; then
serve_updates_target_addr="$(get-device-addr-resource)"
if [[ -n "${serve_updates_target_addr}" && -n "$(get-fuchsia-device-port)" ]]; then
serve_updates_target_addr="${serve_updates_target_addr}:$(get-fuchsia-device-port)"
fi
fi
if [[ -n "${serve_updates_target_addr}" ]]; then
echo "${serve_updates_target_addr}"
return 0
fi
return 1
}
clear-target-addr() {
serve_updates_target_addr=""
}
with-pinned-target() {
# Ensure we pin in the running shell before making a subshell. We also want
# to make sure we get an address at all.
if ! target-addr > /dev/null; then
return 1
fi
(
FUCHSIA_NODENAME="$(target-addr)"
export FUCHSIA_NODENAME
"$@"
)
}
# TODO(b/360930691): Set the default repo_dir in the configuration.
repo_dir="${FUCHSIA_BUILD_DIR}/amber-files"
if is_feature_enabled "incremental" || is_feature_enabled "incremental_new"; then
# macOS in particular has a low default for number of open file descriptors
# per process, which is prohibitive for higher job counts. Here we raise
# the number of allowed file descriptors per process if it appears to be
# low in order to avoid failures due to the limit. See `getrlimit(2)` for
# more information.
if [[ $(ulimit -n) -lt 1000 ]]; then
ulimit -n 32768
fi
if $clean_first; then
$verbose && echo -n >&2 "Cleaning the package repository..."
if [[ -d "${repo_dir}" ]]; then
rm -Rf "${repo_dir}"
fi
$verbose && echo >&2 "done"
fi
if [[
! -e "${repo_dir}/keys/snapshot.json" ||
! -e "${repo_dir}/keys/targets.json" ||
! -e "${repo_dir}/keys/timestamp.json" ||
! -e "${repo_dir}/repository/1.root.json" ||
! -e "${repo_dir}/repository/root.json"
]]; then
echo >&2 "Preparing the package repository..."
# TODO(b/361326913: Would `ffx repository create` work here?
if ! fx-command-run build build/images/updates:prepare_publish
then
exit $?
fi
echo >&2 "done"
fi
else
if $clean_first; then
fx-error "Flag '-C' or '--clean' can only be used if the incremental feature is enabled"
exit 1
fi
fi
# TODO(b/360931702): Allow setting the port independent of the IP address.
# Default the port to 8083 if it is unset.
if [[ -z "${port}" ]]; then
port="$(fx-command-run ffx config get repository.server.default_port 2>/dev/null | tr -d '"')"
fi
# If it is still not set, just use 8083
if [[ -z "${port}" ]]; then
port="8083"
fi
# Error out if we can't start a package server.
check-if-we-can-start-package-server "" "${port}"
err=$?
if [[ "${err}" -ne 0 ]]; then
exit 1
fi
if [[ "${no_device}" == "--no-device" ]]; then
fx-info "Please note that '--no-device' disables automatic device configuration."
fx-info "Use 'fx -d DEVICE add-update-source --port ${port}' to reconfigure devices"
fx-info "as needed, since it is not persisted accross flashes."
# move to config state directly
log "Skipping discovering device because ${no_device} was specified"
state="config"
else
# Make sure the ssh config is present and has the expected private key
check-ssh-config
state="discover"
log "Discovery..."
# Check if device is set via fx set-device before the discovery loop is started
device_name="$(get-device-name 2>/dev/null)"
if [[ -z "${device_name}" ]]; then
fx-warn ""
fx-warn "No default device set. If 'fx serve' gets stuck on"
fx-warn "\"Discovery...\" you can set a default device by running"
fx-warn "'ffx target list' to get a list of devices, and then running"
fx-warn "'ffx target default set'."
fx-warn "\n"
else
fx-info ""
fx-info "Default device set: ${device_name}"
fx-info "\n"
fi
fi
# Keep track if we reported any discovery failures
reported_discovery_failed=0
# exponential backoff in seconds.
backoff=1
while true; do
if [[ "${state}" == "discover" ]]; then
# While we're still trying to connect to the device, clear the target
# address state so we re-resolve.
clear-target-addr
with-pinned-target fx-command-run shell exit 2>/dev/null
ping_result=$?
if [[ "${ping_result}" == 0 ]]; then
log "Device up"
reported_discovery_failed=0
backoff=1
state="config"
elif [[ "${reported_discovery_failed}" == 0 ]]; then
reported_discovery_failed=1
# Log if we don't know the device's address yet, or if we don't have a device configured.
device_addr="$(get-fuchsia-device-addr 2>/dev/null)"
if [[ -z "${device_addr}" ]]; then
device_name="$(get-device-name 2>/dev/null)"
if [[ -n "${device_name}" ]]; then
log "Trying to resolve '${device_name}'..."
else
log "Either no device or multiple devices are discoverable. Run 'ffx target list' and if necessary run 'ffx target default set'."
fi
else
log "Trying to connect to ${device_addr}..."
fi
fi
if [[ "${ping_result}" != 0 ]]; then
sleep "${backoff}"
backoff=$(( backoff * 2 ))
if [[ "${backoff}" -gt 10 ]]; then
backoff=10
fi
fi
else
with-pinned-target fx-command-run shell -O check > /dev/null 2>&1
ping_result=$?
fi
if [[ "${state}" == "config" ]]; then
log "Starting repository server"
if [[ "${verbose}" == true ]]; then
ffx_flags+=( --verbose )
fi
# Start repository server in foreground, wait until it
# voluntarily exits
# TODO(b/360931702): Allow listening IP address to be set.
server_flags=( repository server start "${server_mode}" --address "[::]:${port}" )
server_flags+=( --repository "${source_name}" --repo-path "${repo_dir}" )
server_flags+=( --trusted-root "${repo_dir}/repository/9.root.json" )
server_flags+=( --alias "fuchsia.com" --alias "chromium.org" )
server_flags+=( --storage-type "${storage_type}" )
if [[ -n "$no_device" ]]; then
server_flags+=( --no-device )
fi
if is_feature_enabled "incremental" || is_feature_enabled "incremental_new"; then
server_flags+=( --auto-publish "${FUCHSIA_BUILD_DIR}/all_package_manifests.list" )
fi
fx-command-run ffx "${ffx_flags[@]}" "${server_flags[@]}"
exit 0
fi
done