#!/usr/bin/env bash
# rconsole -- attach to a server's remote console via IPMI SOL

. lib.bash || exit

usage() {
	echo "Usage: $progname <host> [-p] [-P <state>] [-v]"
	echo
	echo_opt "-p"		"show system power status"
	echo_opt "-P state"	"switch system power on/off/reset/cycle"
	echo_opt "-v"		"be verbose"
}

case $1 in
	--help)		usage; exit;;
esac
case ${0##*/} in
	*-console)	host=${0##*/}; host=${host%-console};;
	*)		host=${1?missing hostname}; shift;;
esac

verbose=0
opt_getpower=0
opt_setpower=""

while getopts ":P:pv" OPT; do
	case $OPT in
	P) opt_setpower=$OPTARG;;
	p) opt_getpower=1;;
	v) verbose=1;;
	*) lib:die_getopts;;
	esac
done; shift $((OPTIND-1))

# Accept 'hostname@ipaddress' for credential lookup while avoiding DNS
case $host in
	*@*)	bhost=${host##*@}; host=${host%@*};;
	*.*)	bhost=$host;;
	*)	bhost="bmc.$host.sym";;
esac

# Look up operator password in ~/.netrc
creds=$(getnetrc -qsdf %a:%u:%p "ipmi/$bhost" ||
        getnetrc -qsdf %a:%u:%p "ipmi/$host" ||
        getnetrc -qsdf %a:%u:%p "ipmi/*")

priv=${creds%%:*}; creds=${creds#*:}
user=${creds%%:*}; creds=${creds#*:}
pass=$creds

if [[ ! $user || ! $pass ]]; then
	vdie "no netrc entry for 'ipmi/$bhost' or 'ipmi/$host'"
fi

if [[ ! $priv ]]; then
	# Guess privilege level from username (e.g. Operator => priv OPERATOR)
	priv=${user^^}
fi

if (( verbose )); then
	echo "Connecting to '$bhost' as '$user', privilege level '$priv'" >&2
fi

# New ipmitool defaults to ciphersuite 17 (AES-CBC-128 with HMAC-SHA256), which
# iLO4 does not support. Fall back to the HMAC-SHA1 variant.
readonly AESCBC128_HMACSHA1=3
cipher=$AESCBC128_HMACSHA1

if have ipmipower && have ipmiconsole && [[ ! $USE_IPMITOOL ]]; then
	args=(-h "$bhost" -u "$user" -p "$pass" -l "$priv" -I "$cipher")
	bmc_console() { ipmiconsole "${args[@]}"; }
	bmc_get_power() { ipmipower -D LAN_2_0 "${args[@]}" --stat; }
	bmc_set_power() { ipmipower -D LAN_2_0 "${args[@]}" --${1?}; }
elif have ipmitool; then
	args=(-H "$bhost" -U "$user" -P "$pass" -L "$priv" -C "$cipher")
	# Use an escape character different from the SSH one
	args+=(-e '&')
	bmc_console() { ipmitool -I lanplus "${args[@]}" sol activate; }
	bmc_get_power() { ipmitool -I lanplus "${args[@]}" power status; }
	bmc_set_power() { ipmitool -I lanplus "${args[@]}" power ${1?}; }
else
	die "No IPMI client tool (freeipmi nor ipmitool) found"
fi

if (( opt_getpower )); then
	echo "[Connecting to '$bhost']"
	bmc_get_power
elif [[ $opt_setpower ]]; then
	if [[ $opt_setpower != @(on|off|cycle|reset) ]]; then
		die "Unknown power command '$opt_setpower'"
	fi
	confirm "Send 'power $opt_setpower' command to '$bhost'?" || exit
	echo "[Connecting to '$bhost']"
	bmc_set_power "$opt_setpower"
elif [[ ! $1 ]]; then
	echo "[Connecting to '$bhost' ... escape sequence is '&.']"
	bmc_console
else
	die "Unrecognized arguments: '$*'"
fi
