#!/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