#!/bin/sh

# Copyright (c) 2001-2004 Todd T. Fries <todd@OpenBSD.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

afsp=/usr/local/libexec/openafs

# (borrowed from install.sub)
# Ask for user input.
#
#    $1    = the question to ask the user
#    $2    = the default answer
#
# Save the user input (or the default) in $resp.
#
# Allow the user to escape to shells ('!') or execute commands
# ('!foo') before entering the input.
ask() {
	local _question=$1 _default=$2

	set -o noglob
	while :; do
	        echo -n "$_question "
	        [[ -z $_default ]] || echo -n "[$_default] "
	        read resp
	        case $resp in
	        !)      echo "Type 'exit' to return to install."
	                sh
	                ;;
	        !*)     eval ${resp#?}
	                ;;
	        *)      : ${resp:=$_default}
	                break
	                ;;
	        esac
	done
	set +o noglob
}

# Ask for user input until a non-empty reply is entered.
#
#    $1    = the question to ask the user
#    $2    = the default answer
#
# Save the user input (or the default) in $resp.
ask_until() {
	resp=
	while [[ -z $resp ]] ; do
	        ask "$1" "$2"
	done
}

# Ask the user for a y or n, and insist on 'y', 'yes', 'n' or 'no'.
#
#    $1    = the question to ask the user
#    $2    = the default answer (assumed to be 'n' if empty).
#
# Return 'y' or 'n' in $resp.
ask_yn() {
	local _q=$1 _a=${2:-no} _resp
	typeset -l _resp

	while :; do
	        ask "$_q" "$_a"
	        _resp=$resp
	        case $_resp in
	        y|yes)  resp=y ; return ;;
	        n|no)   resp=n ; return ;;
	        esac
	done
}


# Logging routine
# 
#    $1    = -c or 1st arg
#    $2    = ...
#
# log all arguments
sc=0
log() {
	if [ "$1" = "-c" ]; then
		shift
	else
		let sc=sc+1
	fi
	printf "%2d " $sc
	echo "==> $@"
}

# Create a principal in kerberos.
#
#    $1    = principal
#    $2    = extra arg..
#
# Delete the principal first before re-adding it to make sure proper
# attributes exist.
kadd() {
	local principal=$1
	log creating principal: $principal
	shift
	kadmin del $principal > /dev/null 2>&1
	kadmin add \
		--{pw-,}expiration-time=never \
		--max-ticket-life="1 month" \
		--max-renewable-life="2 months" \
		--attributes="" \
		"$@" $principal
}

# Re-try a command until success.
#
#    $@    = full command to try
#
retry() {
	local try=1
	while ! $@
	do
		let try=try+1
		log -c "try $try: $@"
		sleep 2
	done
}

# Make an afs volume.
#
#    $1    = volume name
#    $2    = volume mount point
#
# Any user can read volumes created here.
mkvol() {
	local vol=$1 mnt=$2
	log "Creating afs volume $1 to be mounted at $mnt"
	retry vos create $h /vicepa $vol
	fs mkm $mnt $vol
	fs sa $mnt system:anyuser rl
}

cat <<__EOT

===========================================================================
Welcome to the OpenAFS server1 setup script!

This script will assist you in setting up your first afs server.

It will use OpenAFS for the AFS server, but arla's afsd that comes with
OpenBSD for the AFS client.

It will use heimdal KerberosV that comes with OpenBSD.

It presumes you have previously successfully setup a KerberosV realm,
you have the password to an administrative principal in the KerberosV realm,
you are running it as root, and you have created at least one partition
for OpenAFS to use for data storage.  Partitions should be mounted under
/vicepa, /vicepb, /vicepc, etc.

===========================================================================

__EOT

#
# Santiy checks
#

# Require root.
if [[ `/usr/bin/whoami` != "root" ]]
then
	echo "Please run this script as root.  Thanks."
	exit
fi
if [[ `mount|grep " /vicepa "` = "" ]]
then
	echo "Could not find any filesystem mounted at /vicepa"
	echo "Without this OpenAFS will not function."
	echo "Please mount a partition under /vicepa"
	echo "A /vicepa directory will not work as"
	echo "OpenAFS"
	exit
fi

#
# Setup site specific variables
#

# Ask user for variables if not passed on the command line.
if ! [ "$6" ]
then
	cat <<__EOT

The hostname for this afs server should resolve in dns but
definately reside in /etc/hosts. e.g. afs0.example.com.
__EOT
	while :; do
		ask_until "System hostname?" "$(hostname)"
		h=${resp}
		if [[ `grep $h /etc/hosts` = "" ]]
		then
			echo "Could not find /etc/hosts entry for $h."
			continue
		fi
		break
	done
	cat <<__EOT

The IPv4 IP address for this afs server should resolve in dns but
definately reside in /etc/hosts. e.g. 192.168.1.200.
__EOT
	while :; do
		ask_until "System IP?" "$(host $h | \
			awk '/has address/{print $4}')"
		ip=${resp}
		if [[ `grep $ip /etc/hosts` = "" ]]
		then
			echo "Could not find /etc/hosts entry for $ip."
			continue
		fi
		break
	done
	cat <<__EOT

The cell name is typically a dns name.  e.g. example.com.
__EOT
	echo "\n$cell_blurb"
	ask_until "AFS Cell Name?" "${h#*.}"
	c=${resp}
	cat <<__EOT

The realm name is the KerberosV REALM, typically the capitalized dns name.
e.g. REALM.COM. Use something different at the expense of your sanity.
Really.
__EOT
	echo "\n$realm_blurb"
	ask_until "KerberosV REALM?" "$(echo "$c"|tr "[a-z]" "[A-Z]")"
	R=${resp}
	cat <<__EOT

This is an existing KerberosV principal with the ability to
create and delete other kerberos principals.  e.g. todd/admin.
__EOT
	user=$(id -un)
	ask_until "KerberosV principal for kerberos administration?" \
		"username/admin"
	p=${resp}
	cat <<__EOT

This principal will be deleted if it exists, and in any event
created again with specific attributes.  It will be used to administer afs
in a similar way that \`root' can administrate OpenBSD.  Tread lightly when
using this principal.
__EOT
	ask_until "KerberosV princiapl for afs administration?" "username/afs"
	A=${resp}
else
	# for advanced users, this script can be started with the above
	# pre-populated via arguments
	h="$1" ip="$2" c="$3" R="$4" p="$5" A="$6"
fi

cat <<__EOT

Confirm these look correct:"
hostname   : $h
IP address : $ip
cellname   : $c
realm      : $R
krb admin  : $p
afs admin  : $A

The next step *DESTROYS* all existing OpenAFS configuration on this system!
__EOT

ask_yn "Are you really sure that you're ready to proceed?"
[[ $resp == n ]] && { echo "Ok, try again later.\n" ; exit ; }

log prepare dirs /etc/openafs, /usr/afs, /var/openafs, /etc/afs
kdestroy
pkill afsd
umount /afs > /dev/null 2>&1
[ -d /var/spool/afs ] && rm -rf /var/spool/afs/*
if [ "$(pgrep bosserver)" ]
then
	bos shutdown localhost -noauth > /dev/null 2>&1
fi
if [ "$(pgrep bosserver)" ]
then
	bos shutdown localhost -localauth > /dev/null 2>&1
fi
rm -rf /etc/openafs /usr/afs /var/openafs
rm -rf /vicep*/{V*,AFSIDat,Lock}
rm -f /etc/kerberosV/krb5.keytab
mkdir -p /etc/openafs/server /usr/afs
mkdir -m 700 /var/openafs
echo $c | tee /etc/openafs/server/ThisCell > /etc/afs/ThisCell
ln -s /var/openafs/db /usr/afs/db
ln -s /etc/openafs/server /usr/afs/etc
if [ "$(pgrep bosserver)" ]
then
	pkill bosserver
fi

log authenticating $p@$R
retry kinit $p@$R
kadd host/$h --random-key 
kadd $A
kadmin ext --keytab=/etc/kerberosV/krb5.keytab host/$h
chmod 0400 /etc/kerberosV/krb5.keytab
#kadmin list host/$h "afs*"
kadd afs/$c --random-key 
log creating /etc/openafs/server/KeyFile
kadmin ext -k /tmp/afsv5key afs/$c
ktutil copy /tmp/afsv5key AFSKEYFILE:/etc/openafs/server/KeyFile
chmod 600 /etc/openafs/server/KeyFile
rm /tmp/afsv5key
ls -l /etc/openafs/server/KeyFile
# XXX perhaps remove existing entries?
log updating /etc/afs/CellServDB /etc/openafs/server/CellServDB
echo ">$c	# $c" > /tmp/CellServDB
echo "$ip	#$h" >> /tmp/CellServDB
cat /tmp/CellServDB | \
  tee -a /etc/afs/CellServDB /etc/openafs/server/CellServDB
chmod 644 /usr/sbin/{bos,pts,vos,fs}

PATH=/usr/local/sbin:/usr/local/bin:$PATH

bosserver -log -syslog -noauth
retry bos setcellname $h $c -noauth

log creating buserver/ptserver/vlserver entries with bos
retry bos create $h buserver simple $afsp/buserver -cell $c -noauth
retry bos create $h ptserver simple $afsp/ptserver -cell $c -noauth
retry bos create $h vlserver simple $afsp/vlserver -cell $c -noauth
log setting up pts memberships, todd.afs as initial afs admin
retry pts createuser -name todd -id `id -u` -cell $c -noauth
retry pts createuser -name todd.afs -cell $c -noauth
retry pts adduser todd.afs system:administrators -cell $c -noauth
retry pts mem system:administrators -cell $c -noauth
retry pts listentries -cell $c -noauth
retry bos adduser $h todd.afs -cell $c -noauth
retry bos addhost $h $h -noauth
retry bos shutdown $h -cell $c -noauth -wait

log getting status of bos config
retry bos status $h -noauth -cell $c -long
pkill -HUP bosserver

log creating fs entry with bos
bosserver -log -syslog 
bos restart $h -all -cell $c -localauth
bos create $h fs fs $afsp/{fileserver,volserver,salvager} -cell $c -localauth
log getting partition list
vos listpart $h -noauth

log creating root.afs
retry vos create $h /vicepa root.afs -localauth -verbose

log starting afs client
mkdir -p /afs
[ "$(mount | egrep "^/afs")" ] || mount -t xfs /dev/xfs0 /afs
/usr/libexec/afsd -z --log=/var/log/afsd.log

sleep 5

log authenticating $A
kinit $A
pts listentries

# do this on reboot
grep "^afs=YES" /etc/rc.conf.local > /dev/null 2>&1 || \
echo afs=YES >> /etc/rc.conf.local

retry ls /afs

log setting permissions/creating volumes
retry fs sa /afs system:anyuser rl
vos create $h /vicepa root.cell
fs mkm /afs/$c root.cell -cell $c -fast
fs sa /afs/$c system:anyuser rl
fs mkm /afs/.$c root.cell -cell $c -rw
fs mkm /afs/.root.afs root.afs -cell $c -rw

mkvol user /afs/$c/u
mkvol ftp /afs/$c/ftp
fs mkm /afs/$c/.ftp ftp -cell $c -rw

log adding replication sites for root.afs, root.cell
vos addsite $h /vicepa root.afs
vos addsite $h /vicepa root.cell
vos addsite $h /vicepa ftp

log initial release of replicated volumes
vos release root.afs
vos release root.cell
vos release ftp

log enjoy OpenAFS!
