| #!/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-updates [-v] [-l host[:port]] | |
| ## -l host:port for "pm serve" to listen on | |
| ## --no-auto-config do not configure this host as a package server on the device | |
| ## -v verbose (do not suppress `pm serve` output) | |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/vars.sh || exit $? | |
| fx-config-read | |
| function usage { | |
| fx-command-help serve-updates | |
| } | |
| fx-standard-switches "$@" | |
| set -- "${FX_ARGV[@]}" | |
| port="8083" | |
| serve_flags=() | |
| verbose=false | |
| auto_config=true | |
| while (($#)); do | |
| case "$1" in | |
| -l) | |
| shift | |
| port="${1##*:}" | |
| serve_flags+=(-l "$1") | |
| ;; | |
| --no-auto-config) | |
| auto_config=false | |
| ;; | |
| -v|--verbose) | |
| verbose=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 [[ "${verbose}" != true ]]; then | |
| serve_flags+=("-q") | |
| fi | |
| pm_srv_pid= | |
| cleanup() { | |
| if [[ -n "${pm_srv_pid}" ]]; then | |
| if kill -0 "${pm_srv_pid}" 2> /dev/null; then | |
| kill -TERM "${pm_srv_pid}" 2> /dev/null | |
| wait "${pm_srv_pid}" 2> /dev/null | |
| fi | |
| fi | |
| } | |
| trap cleanup EXIT | |
| log() { | |
| # This format matches bootserver so that `fx serve` ui is easier to read. | |
| echo "$(date '+%Y-%m-%d %H:%M:%S') [serve-updates] $@" | |
| } | |
| # 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-device-ssh-port)" ]]; then | |
| serve_updates_target_addr="${serve_updates_target_addr}:$(get-device-ssh-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 | |
| ( | |
| export FUCHSIA_DEVICE_NAME="$(target-addr)" | |
| "$@" | |
| ) | |
| } | |
| if [[ "${auto_config}" == false ]]; then | |
| log "Flag '--no-auto-config' used, automatic device configuration disabled." | |
| log "Use 'fx -d DEVICE add-update-source --port ${port}' to reconfigure devices as needed, since it is not persistent accross reboots." | |
| log "Serving packages on port ${port}..." | |
| exec "${FUCHSIA_BUILD_DIR}/host_x64/pm" serve -repo "${FUCHSIA_BUILD_DIR}/amber-files" "${serve_flags[@]}" | |
| fi | |
| if [[ -z "${pm_srv_pid}" ]]; then | |
| # The use of host_x64/ is deliberate over tools/. | |
| "${FUCHSIA_BUILD_DIR}/host_x64/pm" serve -repo "${FUCHSIA_BUILD_DIR}/amber-files" "${serve_flags[@]}" & | |
| pm_srv_pid=$! | |
| fi | |
| # Allow a little slack for pm serve to startup, that way the first kill -0 will catch a dead server | |
| sleep 0.1 | |
| if ! kill -0 "${pm_srv_pid}" 2> /dev/null; then | |
| log "Server died, exiting" | |
| wait | |
| exit $? | |
| fi | |
| log "Discovery..." | |
| # State is used to prevent too much output | |
| state="discover" | |
| while true; do | |
| if ! kill -0 "${pm_srv_pid}" 2> /dev/null; then | |
| log "Server died, exiting" | |
| pm_srv_pid= | |
| exit 1 | |
| fi | |
| 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=$? | |
| else | |
| with-pinned-target fx-command-run shell -O check > /dev/null 2>&1 | |
| ping_result=$? | |
| fi | |
| if [[ "$state" == "discover" && "$ping_result" == 0 ]]; then | |
| log "Device up" | |
| state="config" | |
| fi | |
| if [[ "$state" == "config" ]]; then | |
| log "Registering devhost as update source" | |
| if with-pinned-target fx-command-run add-update-source --port "${port}"; then | |
| log "Ready to push packages!" | |
| # Log the uptime so that it is clear(er) from fx serve if the device rebooted. | |
| # The tr is present as the output sometimes contains spurious control characters. | |
| clock=$(with-pinned-target fx-command-run shell clock --monotonic) | |
| if [[ -n ${clock} ]]; then | |
| uptime=$(echo $clock | tr '[[:cntrl:]]' ' ' ) | |
| log "Target uptime: $(( $uptime / 1000000000 ))" | |
| state="ready" | |
| else | |
| log "Device lost while checking uptime" | |
| state="discover" | |
| fi | |
| else | |
| log "Device lost while configuring update source" | |
| state="discover" | |
| fi | |
| fi | |
| if [[ "$state" == "ready" ]]; then | |
| if [[ "$ping_result" != 0 ]]; then | |
| log "Device lost" | |
| state="discover" | |
| else | |
| sleep 1 | |
| fi | |
| fi | |
| done | |
| # See EXIT trap above for cleanup that occurs |