#! /usr/bin/env bash # Login into a machine. # # -c|-r|-p # Execute QEMU 'cont', 'system_reset', or 'system_powerdown' command on the # specified machine monitor UNIX socket prior to logging in. # # - build host the machine is running on # - machine's VNC port on the build host (no login if unspecified) # usage="usage: $0 [-c|-r|-p ] []" trap "{ exit 1; }" ERR set -o errtrace # Trap in functions. function info () { echo "$*" 1>&2; } function error () { info "$*"; exit 1; } mon= cmd= while [ "$#" -gt 0 ]; do case "$1" in -c|-r|-p) case "$1" in -c) cmd=cont ;; -r) cmd=system_reset ;; -p) cmd=system_powerdown ;; esac shift mon="$1" shift ;; -*) error "unknown option: $1" ;; *) break ;; esac done host="$1" fport="$2" if [ -z "$host" ]; then error "$usage" fi if [ -z "$fport" -a -z "$cmd" ]; then error "$usage" fi if [ -n "$cmd" -a -z "$mon" ]; then error "$usage" fi # Find an unused local port. Surprisingly, it's harder than one would expect. # function find_used_ports () { # Use netstat(1) to find the list of port in use. # # Proto Recv-Q Send-Q Local Address Foreign Address State # tcp 0 0 0.0.0.0:139 0.0.0.0:* LISTEN # local f="([^[:space:]]+)[[:space:]]+" netstat --listening --all --tcp --numeric | \ sed -n -re "s/^$f$f$f$f.*$/\4/p" | \ sed -n -re "s/^.+:([0-9]+)$/\1/p" | \ sort --unique --numeric-sort } function find_unused_port () { local ups ups=($(find_used_ports)) local lp up u for ((lp=49152; lp < 65535; lp++)); do u= for up in "${ups[@]}"; do if [ "$lp" -eq "$up" ]; then u=true break fi done if [ ! "$u" ]; then echo "$lp" return 0 fi done echo "all ports are in use" 1>&2 return 1 } # OpenSSH local port forwarding. # # ssh -L :: # # Forward connections on localhost: to : # via . # # We are putting OpenSSH to background (-f -N) and then using the "control # socket" mechanism (-M -S) to terminate it. This also makes sure we don't # share any existing control sockets that may be set in .ssh/config (e.g., # for connection caching/multiplexing). # csock="$(mktemp -u)" if [ -n "$fport" ]; then lport=$(find_unused_port) ssh -f -N -M -S "$csock" -o ExitOnForwardFailure=yes \ -L "$lport:localhost:$fport" "build@$host" else ssh -f -N -M -S "$csock" -o ExitOnForwardFailure=yes \ "build@$host" fi function exit_trap () { # OpenSSH prints "Exit request sent." regardless of -q. # if ! ssh -q -S "$csock" -O exit "build@$host" 2>/dev/null; then error "unable to terminate background ssh process via $csock" fi } trap exit_trap EXIT if [ -n "$cmd" ]; then echo "$cmd" | ssh -S "$csock" "build@$host" socat - "UNIX-CONNECT:$mon" echo # Add newline after QEMU prompt. fi if [ -n "$fport" ]; then vinagre "localhost:$lport" fi