diff options
-rw-r--r-- | doc/manual.cli | 2 | ||||
-rwxr-xr-x | login-machine | 127 |
2 files changed, 129 insertions, 0 deletions
diff --git a/doc/manual.cli b/doc/manual.cli index 91dc68b..a2484aa 100644 --- a/doc/manual.cli +++ b/doc/manual.cli @@ -592,6 +592,8 @@ command: echo cont | ssh build@build socat - UNIX-CONNECT:/tmp/monitor-<toolchain>-<instance> \ +\N|The \c{login-machine} helper script implements this sequence of steps.| + Other useful QEMU monitor commands are \c{system_powerdown} and \c{system_reset}. diff --git a/login-machine b/login-machine new file mode 100755 index 0000000..18e1f34 --- /dev/null +++ b/login-machine @@ -0,0 +1,127 @@ +#! /usr/bin/env bash + +# Login into a machine. +# +# -c <monitor> +# Execute QEMU 'cont' command in the specified machine monitor UNIX +# socket prior to logging in. +# +# <host> - build host the machine is running on +# <port> - machine's VNC port on the build host +# +usage="usage: $0 [-c <monitor>] <host> <port>" + +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) + shift + cmd=cont + mon="$1" + shift + ;; + -*) + error "unknown option: $1" + ;; + *) + break + ;; + esac +done + +host="$1" +fport="$2" + +if [ -z "$host" -o -z "$fport" ]; 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 +} + +lport=$(find_unused_port) + +# OpenSSH local port forwarding. +# +# ssh -L <localport>:<fowardhost>:<forwardport> <host> +# +# Forward connections on localhost:<localport> to <fowardhost>:<forwardport> +# via <host>. +# +# 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)" + +ssh -f -N -M -S "$csock" -L "$lport:localhost:$fport" \ + -o ExitOnForwardFailure=yes "build@$host" + +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" +fi + +vinagre "localhost:$lport" |