#! /bin/sh
# $Header: /global/src/usr.bin/RCS/cl-onserver.pl,v 1.48 2021/08/29 09:38:35 pb22 Exp $
# Command to run a command on a server.

# allow 'sudo cl-tgt [-v]' to be run by any user, to get TGT for 'rootallow' hosts.
case "$SUDO_UID++$SUDO_COMMAND" in
??*++*"cl-tgt"*) 
    case "$*" in "-v") : OK;; ?*) echo "# $0: no args expected when run under sodo" 1>&2; exit 1;;esac 
    # Requires /etc/sudoers.d/cl-tgt:Defaults        env_keep +="XDG_SESSION_ID"
    set x $* rootuserfetch USER=$SUDO_USER ID=XDG${XDG_SESSION_ID-$$} TGT_L=3m TGT_R=2h; shift
    case "$*" in "-v") echo "# '$0 $@' ... running under sudo:" $(env | grep ^SUDO);; esac
esac

# Set up some defaults
flagn="-n"		# ssh flags -- tweaked by gurus only !
flags="-a -x"		# ssh flags -- tweaked by gurus only !
ssh=ssh			# ssh command to use
iflag="-i"		# set if specifying a capability (i.e not guru)
pref="-oPreferredAuthentications=publickey"	# ditto
ver=-2			# which ssh version
ip=-4			# which IP version
user=root
at=@
ucmd=
cmd=
namei=
crons="cron-serv1 cron-serv0 cron-serv2" # two of the three likely to be available
SHOST=`uname -n | sed 's/\..*//'`
identities="/etc/ssh/identity-onserver-root"
test -r $identities || identities="/etc/ssh/identity-onserver"
servidentd="${CLOSservidentd-slogin-serv}"	# machine which runs identd so can pass info for cl-personal-www-log
servwol="${CLOSservwol-wol-serv}"		# wake on lan - mutli VLAN (DHCP server)
servmisc="${CLOSservmisc-on-serv}"		# new misc operations
servamttool="${CLOSamttool-on-serv}"		# cl-amttool server
servtgt="${CLOStgt-tgt-serv}"			# tgt server
case "$SHOST" in	# avoid contacting the "other" cron server
$servidentd*)		CLOSservwwwl="${CLOSservwwwl-on-serv}";;
esac
servwwwl="${CLOSservwwwl-$servidentd}" # identd machine
# exit if the exitrc from the remote command is 0
exitexitcode=0

mtas() {
	servexim="${CLOSservexim-$(host -t ptr mta | sed -n 's/.* domain name pointer //p' | sed 's/\.cl.cam.ac.uk\.//')}"
	case "$servexim" in '') servexim="mta0 mta3";;
	esac
}

# Try a DNS lookup on the PTR RR (happens to work with CNAME as well)
name() {
	other=`type host > /dev/null 2>&1 && host -t ptr $1|sed -n 's/\.$//;s/has no.*//;s/.*\s\(name pointer\|PTR\|is an alias for\)\s//p' | tail -1`;
	case "$other" in ?*) echo $other;; '') echo $1;; esac
}

# Guess the required operation based on the command name
op="`echo $0 | sed 's+.*/++'`"
args="$@"

case "$op" in
*ckadr)		op=ckadr; cmd=$op;;
*ckfilter)	op=ckfilter; cmd=$op;;
*tgt|*tgt-fetch) op=tgt; cmd=$op; ucmd="$ucmd $op";;
esac

# What to say if the args are wrong ...
usage() {
	cat << EOF 1>&2
$0: Usage: $0 [flags]
where flags include:
		any *ONE* of:
(user needs a key)
--eximlists	update the exim DLs
(no user key needed)
--amttool	run a cl-amtool command
--boot-mc	boot a machine
--ckadr		check an email address
--ckadr-v	check an email address with verbose info
--ckfilter	check an email filter file ~/.forward-new
--cl-personal-www-log	access log information
--copyin-rpm	copy in an RPM
--eximdist	do a 'make dist' for exim
--exim-adduser	add an exim user
--fixcvs	fixup cvs passwords
--fixpseudo	fixup pseudo users authorized_keys file
--fixsshcache	fixup the ssh authorized_keys cache
--fixsvn	fixup svn passwords
--keytab	re-install the hostkeytab
--ravengroups	update the raven group info (not for users)
--tgt		tgt manipulation. Subcommands: setup, boot, fetch
--wol		Wake On Lan
--xen-clients	give information about xen clients
--xe		run an xe command on a XenE server
		(including scripts such as cl-pdb-plug-all,
		 cl-vm-start-all, cl-vm-status, cl-ethtool-set)

		and any of:
--send-name	send the calling machine's name
EOF
	exit 1
}

# Decode the args ..,.
while	:
do	case "$1" in
	# Users are expected to use these ...
	--cl-personal-www-log) op=cl-personal-www-log-setgid; ucmd="$ucmd $op";;
	--amttool)	op=amttool;	ucmd="$ucmd $op";;
	--boot-mc)	op=boot-mc;	ucmd="$ucmd $op";;
	--eximlists)	op=eximlists;	ucmd="$ucmd $op";;
	--eximdist)	op=eximdist;	ucmd="$ucmd $op";;
	--exim-adduser) op=exim-adduser;ucmd="$ucmd $op";;
	--fixpseudo)	op=fixpseudo;	ucmd="$ucmd $op";;
	--fixcvs)	op=fixcvs;	ucmd="$ucmd $op";;
	--fixsvn)	op=fixsvn;	ucmd="$ucmd $op";;
	--fixsshcache)	op=fixsshcache;	ucmd="$ucmd $op";;
	--ravengroups)	op=ravengroups;	ucmd="$ucmd $op";;
	--keytab)	op=keytab;	ucmd="$ucmd $op";;
	--tgt)		op=tgt;		ucmd="$ucmd $op";;
	--ckfilter)	op=ckfilter;	ucmd="$ucmd $op";;
	--ckadr)	op=ckadr;	ucmd="$ucmd $op";;
	--ckadr-v)	op=ckadr-v;	ucmd="$ucmd $op";;
	--xen-clients)	op=xen-clients;	ucmd="$ucmd $op";;
	--wol)		op=wol;		ucmd="$ucmd $op";;
	--isidle)	op=isidle;	ucmd="$ucmd $op";;
	--scp)		op=scp;		ucmd="$ucmd $op";;
	--upmo)		op=scp;		ucmd="$ucmd $op";
		set upmo /var/log/cl-make-ownfiles /usr/groups/linux/ownfiles/IN/`uname -n | sed 's/\.cl.cam.*//'`;;
	--xe)		op=xe;		ucmd="$ucmd $op"
			case "$@" in *"snapshot-list"*|*"cl-vm-status"*) doall=1;; esac;;
	--copyin-rpm)	op=copyin-rpm;	ucmd="$ucmd $op";;
	# Users *may* use these (if so instructed)
	--identity)	shift; identity="$1";;
	--servers|--server)	shift; servers="$1";;
	# These are really for gurus only ...
	--user)		shift; user="$1";;
	--cmd)		shift; cmd="$1";;
	--flags)	shift; flags="$1";;
	--flagn)	shift; flagn="$1";;
	--doall)	doall=1;;
	--noidentity)	identity=none;;
	--send-name)	sendname=y;;
	-1|-2)		uver=$1;;
	-4|-6)		uip=$1;;
	-n)		echo=echo;;
	-v)		rdistv="-v";;
	# End of flags to parse
	--)		shift; break;;
	# Trap errors
	--*)		usage;;
	-*)		usage;;
	# flags all processed, rest are arguments.
	*)		break;;
	esac
	shift
done

case "$ucmd" in
?*" "*)	usage;;
?*)	cmd="$ucmd";;
esac

# Look around for a suitable capability ...
case "$identity" in
'')	case "$op" in
	eximlists) identities="$HOME/.ssh/identity2-eximlists-update"
		case "$servers" in '') mtas; servers=$servexim;; esac
		doall=1;
		case "$*" in '') set "$USER";; esac;;
	ckadr|ckadr-v)
		case "$rdistv" in -v) rdistv= op=ckadr-v; cmd=$op;; esac
		identities="/etc/ssh/identity-ckadr"
		exitexitcode="1"
		case "$servers" in '') mtas; servers=$servexim;; esac;;
	ckfilter)
		file=~/.forward-new;
		if test ! -r $file; then echo No $file found 1>&2; exit 1; fi
		sum=`md5sum $file | sed 's/ .*//'`;
		# the server expects the user, and the file checksum
		# (to prove that the user can read the file)
		set X "$* -rcinc 0 $USER $sum"; shift
		exitexitcode="1"
		identities="/etc/ssh/identity-ckadr"
		case "$servers" in '') mtas; servers=$servexim;; esac;;
	xe)
		case "$servers" in '')
			servers="${CLOSservxe-$(host -t ptr xene-pools | sed 's/.* //;s/.cl.cam.ac.uk.//')}"
		esac;;
	wol)
		case "$servers" in '') servers=$servwol;; esac;;
	tgt)
		case "$servers" in '')
			servers="$(host -t ptr $servtgt | sed 's/.* //;s/.cl.cam.ac.uk.//')";;
		esac;
		doall=1;
		sub="$1";
		kextra="USER=$USER"
		case "$sub" in
		setup)	flagn=-t; shift;;
		boot)		  shift;;
		fetch)		  shift;;
		'') sub=None;; # echo "$0: no 'subcommand' included" 1>&2; exit 1;;
		*)		  shift;;
		esac
		cmd="$cmd SUB=$sub"
		namei="$namei $kextra"
		;;
	boot-mc|amttool)
		case "$servers" in '')
			servers="$(host -t ptr $servamttool | sed 's/.* //;s/.cl.cam.ac.uk.//')";;
		esac;
		sub="$1";
		kextra="USER=$USER"
		cmd="$cmd HOST=$sub"
		namei="$namei $kextra"
		;;
	isidle|scp|fixpseudo|fixsvn|fixsshcache|fixcvs|xen-clients|copyin-rpm|keytab|ravengroups|eximdist|exim-adduser)
		case "$servers" in '') servers=$servmisc;; esac;;
	cl-personal-www-log-setgid)
		case "$servers" in '') servers=$servwwwl;; esac
		case "$servers" in $servidentd) user= identities= identity=none at= pref= prehostarg=-K;; esac
		;;
	cl-onserver)
		echo "$0: no 'operation' requested: unused args '$*'" 1>&2;
		exit 1;;
	*)
		echo "# Please report to sys-admin: '$0 unknown op=$op'";;
	esac
	case "$op" in
	scp)	ssh=scp cmd= flagn= flags= prehostarg=$1 hostsuffix=":$2"; shift; shift;;
	esac

	# Knowing where to look, try to find a suitable capability
	for try in $identities
	do	if	test -r $try
		then	identity=$try; break
		fi
	done

	# if none found, complain and give up.
	true 1
	case "$identity" in
	'')	echo "$0: Failed to find a suitable capability" 1>&2;
		exit 1;
	esac
	true 2
	;;
*)	case "$servers" in
	'') echo "$0: --server must be specified if --identity is used" 1>&2;;
	esac
	;;
esac

case "$sendname" in
y*)	namei=HOST=`uname -n`;;
esac

# Avoid using user keys
SSH_AUTH_SOCK= HOME=/

case "$identity" in
none)	identity= iflag=;;
esac

true 3 $servers -- extras are $* -- exitexitcode is $exitexitcode
rc=99
erred=
# Whew !!!  do it ....

true $servers

for h in $servers
do	
	H=`name $h`
	$echo : ssh=$ssh u-ver=${uver-$ver} uip=${uip-$ip} flagn=$flagn flags=$flags pref=$pref iflag=$iflag identity=$identity prehostarg=$prehostarg user=$user h=$h H=$H hostsuffix=$hostsuffix rootcmd=$rootcmd cmd=$cmd rdistv=$rdistv namei=$namei rest=$*
	case "$testout" in
	?*)	out=$( $echo $ssh ${uver-$ver} ${uip-$ip} $flagn $flags $pref $iflag $identity $prehostarg $user$at$H$hostsuffix $rootcmd $cmd $rdistv $namei $* 2>&1 )
		rc=$?
		echo "$out"
		;;
	*)	$echo $ssh ${uver-$ver} ${uip-$ip} $flagn $flags $pref $iflag $identity $prehostarg $user$at$H$hostsuffix $rootcmd $cmd $rdistv $namei $*
		rc=$?
	esac
	# If the exit RC is one which is passed on, exit
	true "$doall+$rc+$out"
	case "$doall+$rc+$out" in 
	+0+*Error*)	rc=-1;; # error, so fail
	+0+*|$exitexitcode+*)
			case "$erred" in
			1) echo "# exit code $rc for $h / $H: $0 $args"
			   echo "# i.e. it worked OK after an earlier failure";;
			esac
			exit $rc;;
	*)		: do all, so go on to next;;
	esac
	case "$rc" in 0) : OK;; *) erred=1; echo "# exit code $rc for $h / $H: $0 $args";; esac
done

echo
case "$rc" in
99)	echo "# No exit code set";;
*)	echo "# Final exit code $rc"
esac

# Return the last exit code (or 99 if none)
exit $rc
