#!/bin/sh
# Copyright Precedence Technologies Ltd 2017

machine=`uname -m`
arch=`uname -p`
version=7

usage()
{
	cat << EOF
Syntax: osupgrade [-poh] [-d dir] [-r root]

	-p = pause after every stage
	-o = OS upgrade only, no packages or other software
	-h = this help
	-d = path to upgrade data (should be autodetermined)
	-r = path to mounted root filesystem to upgrade
EOF
}
dir=""
root=""
pause=""
osonly=""
while getopts pd:ohr: OPTION
do
	case $OPTION in
	p)	pause=y
		;;
	d)	dir="$OPTARG"
		;;
	r)	root="$OPTARG"
		;;
	o)	osonly=y
		;;
	h|?)	usage
		exit 1
		;;
	esac
done

shift `expr $OPTIND - 1`

if [ -z "$root" ]; then
	echo "Please specify location of installed system with -r flag"
	exit 1
fi

if [ ! -f $root/netbsd ]; then
	echo "No existing install found at $root"
	exit 1
fi

if [ -z "$dir" ]; then
	dir=`echo $0 | awk -F/ '
		BEGIN {
			if(substr("'$0'",1,1)!="/") printf "'$PWD'/"
		}
		{
			for (i=1;i<NF-1;i++) printf"%s/",$i
			print $(NF-1)
		}'`
fi
if [ ! -f $dir/amd64/binary/sets/base.tgz ]; then
	echo "Could not locate the NetManager CD"
	echo "Please specify a path using the -d option"
	exit 1
fi

dopause()
{
	local r
	[ -z "$pause" ] && return
	echo -n "Press RETURN to continue"
	read r
}

readbin()
{
	local file READELF elf
	file=$1
	READELF=""
	if [ -z "$file" ]; then
		echo "No file given to readbin" > /dev/stderr
		return 3
	fi
	if [ ! -f "$root/$file" ]; then
		echo "file $file does not exist" > /dev/stderr
		return 3
	fi
	
	if [ -x /usr/bin/readelf ]; then
		READELF=/usr/bin/readelf
	elif [ -x $root/usr/bin/readelf ]; then
		READELF="env LD_LIBRARY_PATH=$root/lib:$root/usr/lib $root/usr/bin/readelf"
	else
		echo "Could not locate readelf binary"
		return 2
	fi
	[ -z "$READELF" ] && return 2

	elf=`$READELF -hn $root/$file | awk '{
		if ($1 == "Machine:") {
			machine = $2
			for (n = 3; n <= NF; n++) machine = machine " " $n
		}
		if ($1 == "NetBSD" && $3 == "IDENT") {
			l = length($4)
			v = 0+substr($4, 1, l-8)
		}
		if ($1 == "NetBSD" && $3 == "NT_VERSION") {
			v = $2
		}
	}
	END{
		printf "%s/%s\n", v, machine
	}'`

	case "${elf%/*}" in
	5|7)
		bvers="${elf%/*}"
		;;
	0x00000004)
		bvers=5
		;;
	*)
		echo "Existing $file binary is for unknown OS version ${elf%/*}"
		return 1
		;;
	esac

	case "${elf#*/}" in
	*80386)
		barch=i386
		;;
	*x86_64|*X86-64)
		barch="x86_64"
		;;
	*)
		echo "Existing $file binary is for unknown architecture: ${elf#*/}"
		return 1
		;;
	esac
	echo "$barch/$bvers"
	return 0
}

sets=$dir/$machine/binary/sets

# Stage 1: check free space

free=`df -m $root | awk -v root=$root '{if ($6 == root) print $4}'`
if [ -z "$free" ]; then
	echo "$root is not a mountpoint"
	exit 1
fi
if [ "$free" -lt 500 ]; then
	echo "Not enough free space to upgrade ($free MB)"
	exit 1
fi

# Stage 2: check installed kernel
if [ ! -f $root/netbsd ]; then
	echo "Cannot find installed kernel"
	exit 1
fi

if [ -x /usr/bin/config ]; then
	CONFIG=/usr/bin/config
elif [ -x $root/usr/bin/config ]; then
	CONFIG=$root/usr/bin/config
else
	echo "Could not locate config binary"
	exit 1
fi
### START CONFIG FILE "/usr/src/5.0/sys/arch/i386/conf/NETMANXEN3"
kv=`$CONFIG -x $root/netbsd | awk 'BEGIN{
	q = sprintf("%c", 34)
}
{
	if ($1 == "###" && $2 == "START" && $3 == "CONFIG" && $4 == "FILE") {
		c = $5
		gsub(q, "", c)
		n = split(c, p, "/")
		printf "%s/%s\n", p[n], p[n-2]
	}
}'`
if [ -z "$kv" ]; then
	echo "Could not determine kernel type"
	exit 1
fi
kname=${kv%/*}
kmachine=${kv#*/}
kerntype=`readbin netbsd`

if [ "$?" != 0 ]; then
	echo "Could not read kernel type"
	exit 1
fi
echo "Kernel:$kname  Machine:$kmachine Release:$kerntype"

dopause
if [ "$kerntype" != "$machine/$version" ]; then
	case "$kname" in
	XEN3PAE_DOMU|XEN3PAE_DOMUNMB)
		kname="XEN3_DOMU"
		;;
	GENERICNMB)
		kname="GENERIC"
		;;
        esac
	if [ -f $dir/build/version/$version/$machine/kernels/$kname.tgz ]; then
		kf=$dir/build/version/$version/$machine/kernels/$kname.tgz
	elif [ -f $dir/amd64/binary/sets/kern-$kname.tgz ]; then
		kf=$dir/amd64/binary/sets/kern-$kname.tgz
	else
		echo "Cannot find kernel for $version"
		exit 1
	fi
	echo "Copying old kernel"
	cp $root/netbsd $root/netbsd.old
	echo "Installing kernel for $machine version $version"
	export kf
	(cd $root ; tar -xpzf $kf || exit 1) || exit 1
else
	echo "Kernel already installed"
fi

# Stage 3: check /dev
dopause
lnod=`ls -l /dev/wd1a | awk '/^b/{ printf "%s%s\n", $5, $6}'`
tnod=`ls -l $root/dev/wd1a | awk '/^b/{ printf "%s%s\n", $5, $6}'`
if [ "$lnod" != "$tnod" ]; then
	echo "Creating /dev for $machine"
	(cd $root ; tar -xpzf $sets/etc.tgz ./dev/MAKEDEV || exit 1) || exit 1
	(cd $root/dev ; sh MAKEDEV all || exit 1) || exit 1
else
	echo "/dev already updated for $machine"
fi

continue_n()
{
	local resp
	echo -n "Continue anyway? (y/n) [n]:"
	read resp
	[ "$resp" != y ] && exit 1
}

# Stage 4: update userland
dopause
runup=0

lastfile=`tar -tvzf $sets/xserver.tgz | awk 'BEGIN {
	s = sprintf("%c.so%c.[0-9]+$", 92, 92)
}
{
	if ($1 ~ "^-r.[sx]..x..x$") lfile = $9
	else if ($9 ~ s) lfile = $9
		
}
END {
	if (substr(lfile, 1, 2) == "./") lfile = substr(lfile, 3)
	print lfile
}'`
if [ -z "$lastfile" ]; then
	echo "Cannot determine last file in archive"
	continue_n
	runup=1
	oldversion="Unknown"
else
	runup=0
	oldversion=`readbin $lastfile`
	
	ret=$?
	case "$ret" in
	1)
		continue_n
		;;
	2)
		continue_n
		runup=1
		;;
	esac
	if [ "$oldversion" = "$arch/$version" ]; then
		echo "Userland already upgraded"
		echo -n "Run userland upgrade again? (y/n) [n]:"
		read resp
		[ "$resp" = y ] && runup=1
	else
		runup=1
	fi
	[ -z "$oldversion" ] && oldversion="Unknown"
fi

if [ "$runup" = 1 ]; then
	echo "Upgrading userland from $oldversion to $arch/$version"
	cd $root
	if [ -d $root/usr/X11R7/lib/X11/xkb/symbols/pc ]; then
		rm -r $root/usr/X11R7/lib/X11/xkb/symbols/pc
	fi
	for i in base comp games man misc modules \
		tests text \
		xbase xcomp xfont xserver
	do
		echo "Extracting $i"
		tar -xpzf $sets/$i.tgz || continue_n
	done || exit 1
	tar -xpzf $sets/etc.tgz ./etc/rc.d ./etc/rc.subr ./etc/defaults \
	    ./etc/man.conf
	rm -f ./etc/ld.so.conf ./etc/rc.d/lkm  ./etc/rc.d/btattach \
	    ./etc/rc.d/btconfig ./etc/rc.d/btdevctl ./etc/rc.d/poffd
fi

echo "Copying bootloader"
cp $root/usr/mdec/boot $root/boot || exit 1

echo "Checking for postinstall prerequisites"
dopause
if [ -d $root/usr/lkm ]; then
	rm -r $root/usr/lkm
fi
awk 'BEGIN {
	notfound = 1
}
{
	if ($2 == "/var/shm" && $3 == "tmpfs") notfound = 0
}
END {
	exit(notfound)
}' /etc/fstab
if [ "$?" != 0 ]; then
	echo "Configuring SHM"
	echo "tmpfs	/var/shm	tmpfs	rw,-m1777,-sram%25" >> /etc/fstab
fi

cp $root/etc/master.passwd $root/etc/ptmp
cp $root/etc/master.passwd $root/etc/master.passwd.orig

for u in _rtadvd:30:/var/chroot/rtadvd \
	_gpio:29:/nonexistent \
	_tss:28:/var/tpm \
	_tcpdump:27:/var/chroot/tcpdump \
	_tests:26:/nonexistent \
	_mdnsd:17:/nonexistent \
	_httpd:24:/var/www \
	_timedc:22:/nonexistent
do
	user=${u%%:*}
	u=${u#*:}
	uid=${u%%:*}
	u=${u#*:}
	home=${u%%:*}
	cuser=`awk -F: -v user=$user -v gfile=$root/etc/group  \
	    -v uid=$uid -v min=75 -v max=99 'BEGIN {
		while((getline line < gfile) >0) {
			split(line, gr, ":")
			gid[gr[3]] = gr[1]
			gname[gr[1]] = gr[3]
		}
	}
	{
		if ($3 == uid) {
			cname=$1
			if (cgid == "") cgid=$4
		}
		if ($1 == user) {
			cuid=$3
			cgid=$4
		}
		if ($3 >= min && $3 <= max) id[$3] = $1
	}
	END {
		scangroup = 0
		if (cuid != "") {
			cname=user
		}
		else if (user in gname) {
			usegid = gname[user]
			cgid = usegid
		}
		else if (uid in gid) {
			if (gid[uid] != user) scangroup = 1
			else cgid = uid
		} else {
			cgid = uid
		}
		if ((cname != user && cname != "") || scangroup == 1) {
			for (n = min; n <= max; n++) if (n in id) {
			} else {
				if (scangroup == 1) {
					if (n in gid)
						if (gid[n] != user) continue
				}
				cuid = n
				cgid = n
				n = max
			}
		}
		usegid = cgid
		if (cgid in gid) {
			if (gid[cgid] == user) cgid = ""
		}
		printf "%s:%s:%s:%s\n", cname, cuid, usegid, cgid
	}' $root/etc/ptmp`
#	echo ${cuser}
	cname=${cuser%%:*}
	cuser=${cuser#*:}
	cuid=${cuser%%:*}
	cuser=${cuser#*:}
	usegid=${cuser%%:*}
	cuser=${cuser#*:}
	cgid=${cuser%%:*}
#	echo "$user:$uid $cname:$cuid $cgid"
	if [ -n "$cgid" ]; then
		echo "Create group $user with GID $cgid"
		echo "$user:*:$cgid:" >> $root/etc/group
	fi
	createuser=""
	if [ -z "$cname" ]; then
		createuser=1
		if [ -z "$cuid" ]; then
			echo "Create user $user with UID $uid and GID $usegid"
			cuid=$uid
		else
			echo "Create user $user with UID $cuid instead of $uid. GID $usegid"
		fi
	elif [ "$cname" != "$user" ]; then
		createuser=1
		echo "Create user $user with UID $cuid because of $cname having UID $uid. GID $usegid"
	fi
	if [ -n "$createuser" ]; then
		echo "$user:*:$cuid:$usegid::0:0:& pseudo-user:$home:/sbin/nologin" >> $root/etc/ptmp
	fi
done
echo "Rebuilding password database"
pwd_mkdb -p -d $root $root/etc/ptmp || exit 1
sync;sync

echo "Preparing for postinstall"
dopause
if [ -d $root/usr/X11R7/lib/X11/xkb/symbols/pc ]; then
	rm -r $root/usr/X11R7/lib/X11/xkb/symbols/pc
fi

for f in 10-autohint 10-no-sub-pixel 10-sub-pixel-bgr \
	10-sub-pixel-rgb 10-sub-pixel-vbgr \
	10-sub-pixel-vrgb 10-unhinted 25-unhint-nonlatin \
	65-khmer 70-no-bitmaps 70-yes-bitmaps
do
	[ -f $root/etc/fonts/conf.d/${f}.conf ] && rm $root/etc/fonts/conf.d/${f}.conf
done

if [ -d $root/etc/disklabels ]; then
	if [ -d $root/etc/netmanager ]; then
		mv $root/etc/disklabels $root/etc/netmanager/disklabels
	else
		mv $root/etc/disklabels $root/etc/disklabels.old
	fi
fi
mkdir -p $root/postup
cd $root/postup
tar -xpzf $sets/etc.tgz
tar -xpzf $sets/xetc.tgz
tasks=`$root/usr/sbin/postinstall -s $root/postup list | awk 'BEGIN{
	output=0
}
{
	if ($1 == "----") output++
	if ($1 == "uid" || $1 == "gid" || $1 == "Item") next
	if ($0~"^[[:space:]]+[a-zA-Z]" && output == 1) print $1
}'`

if [ $root != / ]; then
	echo "Running postinstall in chroot"
	chroot $root /usr/sbin/postinstall -s /postup fix uid gid
	chroot $root /usr/sbin/postinstall -s /postup fix $tasks
	piexit=$?
else
	echo "Running postinstall directly"
	/usr/sbin/postinstall -s /postup fix uid gid
	/usr/sbin/postinstall -s /postup fix $tasks
	piexit=$?
fi
cd /
rm -r $root/postup
if [ "$piexit" != 0 ]; then
	continue_n
fi

echo "Deleting i386-only files"
dopause
while read a
do
	if [ -d $root/$a ]; then
		echo "Directory $a"
		rm -r $root/$a
	elif [ -f $root/$a ]; then
		echo "File $a"
		rm $root/$a
	fi
done << EOF
lib/libm387.so
lib/libm387.so.0
lib/libm387.so.0.1
rescue/ldconfig
sbin/ldconfig
stand/i386
stand/i386-xen
stand/i386pae-xen
usr/X11R7/lib/modules/drivers/elographics_drv.so
usr/X11R7/lib/modules/drivers/elographics_drv.so.1
usr/X11R7/lib/modules/drivers/geode_drv.so
usr/X11R7/lib/modules/drivers/geode_drv.so.2
usr/X11R7/man/html4/elographics.html
usr/X11R7/man/man4/elographics.4
usr/bin/pmc
usr/include/pmc.h
usr/lib/libi386.a
usr/lib/libi386.so
usr/lib/libi386.so.1
usr/lib/libi386.so.1.0
usr/lib/libi386_p.a
usr/lib/libi386_pic.a
usr/lib/libm387.a
usr/lib/libm387.so
usr/lib/libm387.so.0
usr/lib/libm387.so.0.1
usr/lib/libm387_p.a
usr/lib/libm387_pic.a
usr/lib/libpmc.a
usr/lib/libpmc.so
usr/lib/libpmc.so.1
usr/lib/libpmc.so.1.0
usr/lib/libpmc_p.a
usr/lib/libpmc_pic.a
usr/libexec/ld.so
usr/sbin/apm
usr/sbin/apmd
usr/sbin/bad144
usr/sbin/ipwctl
usr/sbin/ndiscvt
usr/sbin/zzz
EOF

#echo "Fixing mail INBOX location"
#if [ -d $root/var/mail -a -d $root/usr/mail ]; then
#	rmdir $root/var/mail
#	if [ "$?" != 0 ]; then
#		echo "Could not delete /var/mail"
#		exit 1
#	fi
#	ln -s /usr/mail $root/var/mail
#fi

if [ "$osonly" = y ]; then
	echo "Completed. Warning: your old packages are still installed"
	exit
fi

echo "Removing all packages for wrong architecture or OS version"
dopause
for pkg in `pkg_info -K $root/var/db/pkg | awk '{print $1}'`
do
	delete=""
	parch=`pkg_info -K $root/var/db/pkg -Q MACHINE_ARCH $pkg 2>/dev/null`
	if [ -z "$parch" ]; then
		echo "$pkg has already been deleted"
	elif [ "$parch" != $arch ]; then
		echo "Deleting $pkg for $parch"
		delete=1
	else
		pvers=`pkg_info -K $root/var/db/pkg -Q OS_VERSION $pkg`
		pversmaj=`echo $pvers|awk '{print substr($1,1,1)}'`
		cvers=`echo $version|awk '{print substr($1,1,1)}'`
		if [ "$pversmaj" != "$cvers" ]; then
			echo "Deleting $pkg for $pvers"
			delete=1
		fi
	fi
	if [ -n "$delete" ]; then
		if [ "$root" = / ]; then
			pkg_delete -fr $pkg || exit 1
		else
			chroot $root /usr/sbin/pkg_delete -fr $pkg || exit 1
		fi
	fi
done