#!/bin/sh -e

##########################################################################
#   Script description:
#       Generate a basic pbulk setup for a given pkgsrc prefix.
#       
#   Arguments:
#       None.
#
#   Returns:
#       
#   History:
#   Date        Name        Modification
#   2017-04-29  Jason Bacon Begin
##########################################################################

usage()
{
    printf "Usage: $0\n"
    exit 1
}


##########################################################################
#   Function description:
#       
#   Arguments:
#       
#   Returns:
#       
#   History:
#   Date        Name        Modification
#   2017-05-16  root        Begin
##########################################################################

auto_ask()
{
    if [ $# != 3 ]; then
	cat << EOM > /dev/stderr
Usage: $0 tag "question" default-answer

    'tag' is a name used to uniquely identify this question in the
    recorded responses.  It must be a single word without spaces or
    special shell characters, and must be unique across all calls
    to $0.
    
    'question' must be enclosed in quotes if it contains spaces or
    special characters.
    
    'default-answer' must also be encloded in quotes if it contains
    spaces or special characters.

EOM
	exit 1
    fi

    tag=$1
    question="$2"
    
    response_file=auto-ask-responses.txt
    touch $response_file
    
    default_response="`awk -F'|' '$1 == "'$tag'" { print $2 }' $response_file`"
    if [ 0"$default_response" = 0 ]; then
	default_response="$3"
    fi
    
    valid=0
    while [ $valid = 0 ]; do
	printf "$question [$default_response] " > /dev/stderr
	read resp
	# resp cannot contain '|'
	if echo "$resp" | fgrep '|'; then
	    printf "Sorry, responses cannot contain '|'.\n" > /dev/stderr
	else
	    valid=1
	fi
    done
    
    if [ 0"$resp" = 0 ]; then
	resp="$default_response"
    fi
    
    awk -F'|' '$1 != "'$tag'" { print $0 }' $response_file > $response_file.temp
    mv -f $response_file.temp $response_file
    printf "${tag}|${resp}\n" >> $response_file
    
    printf "$resp\n"
    return 0
}


##########################################################################
#   Main
##########################################################################

if [ $# != 0 ]; then
    usage
fi

if [ `whoami` != root ]; then
    printf "$0 must be run by root.\n"
    exit 1
fi

if ! which cvs; then
    printf "No cvs command in PATH.  Please install cvs and try again.\n"
    exit 1
fi

if [ `uname` != NetBSD ] && [ `uname` != SunOS ] && \
    [ `uname` != FreeBSD ] && [ `uname` != DragonFly ] && ! which java; then
    cat << EOM

Warning: No Java installation detected and the openjdk packages only build
on certain BSD and SunOS systems.  On Linux, Mac OS X, and other platforms,
you should install a Java JDK before bootstrapping pkgsrc.

EOM
    printf "Continue without Java? y/[n] "
    read continue
    if [ 0$continue != 0y ]; then
	exit
    fi
fi

cat << EOM

$0 should only be run in a chroot, VM, or on a system that is
dedicated exclusively to pbulk builds.  It will remove all installed
packages and perform extensive reconfiguration of your system.

EOM

printf "Continue? yes/[no] "
read continue
if [ 0$continue != 0yes ]; then
    exit
fi

cat << EOM

Enter the location of the pkgsrc tree from which you want to build packages.
The source directory will be mounted read/write within the chroot, but
work directories will be written there.

The install prefix will be recreated within the chroot environment.  This
will be the prefix to which packages generated by pbulk will install.  It
need not match anything outside the chroot.

The var and pkgdb directories will also be located under this prefix.

EOM

pkgsrc=`auto_ask source-prefix 'Pkgsrc source directory?' /usr/pkgsrc`
parent=`dirname $pkgsrc`
suffix=${pkgsrc##/*pkgsrc}
if [ ! -e $pkgsrc ]; then
    printf "Download and unpack pkgsrc? y/[n] "
    read download
    if [ 0$download = 0y ]; then
	snapshot=`auto_ask snapshot 'Snapshot? [e.g. 2017Q3 or current]' current`
	if [ 0$snapshot = 0current ]; then
	    url=ftp://ftp.netbsd.org/pub/pkgsrc/$snapshot/pkgsrc.tar.gz
	else
	    url=ftp://ftp.netbsd.org/pub/pkgsrc/pkgsrc-$snapshot/pkgsrc.tar.gz
	fi
	if which curl; then
	    curl -O $url
	elif which fetch; then
	    fetch $url
	elif which wget; then
	    wget $url
	else
	    printf "No curl, fetch, or wget found.  :-(\n"
	    exit 1
	fi
	start_dir=`pwd`
	umask 022           # Make sure source tree is world readable
	mkdir -p $parent
	cd $parent
	tar zxvf $start_dir/pkgsrc.tar.gz
	if [ 0$snapshot != 0current ]; then
	    mv pkgsrc `basename $pkgsrc`
	fi
	cd $start_dir
    else
	printf "Aborting...\n"
	exit 1
    fi
fi

# Get latest patches
printf "Update pkgsrc tree? [y]/n "
read update
if [ 0$update != 0n ]; then
    save_cwd=`pwd`
    cd $pkgsrc
    cvs -q up -dP || true
    # Make sure pbulk user can read everything
    chmod -R a+rX $pkgsrc
    cd $save_cwd
fi

prefix=`auto_ask bin-prefix 'Pkgsrc install prefix?' $parent/pkg$suffix`

pbulk_dir=`auto_ask pbulk-prefix 'Pbulk install prefix?' $parent/pbulk$suffix`

cat << EOM

Some packages that are thought to be make job safe will fail to build when
make jobs are set too high, so a max value of 4 or maybe 8 is recommended
to minimize build failures.

EOM

make_jobs=`auto_ask make-jobs 'Max # processes for make to use?' 4`

rsync_user=`auto_ask rsync-user 'rsync username (leave blank if you do not want to upload packages)?' ''`
if [ 0$rsync_user != 0 ]; then
    # Don't rely on auto-admin.  This script is often run in a minimal
    # chroot environment.  Duplicating auto-os-release here instead.
    if [ -e /etc/redhat-release ]; then
	release=`cat /etc/redhat-release`
	release=${release%%.*}
	release=${release##*release }
	os=RHEL$release
    elif [ `uname` = Darwin ]; then
	os=$(uname)$(uname -r | cut -d . -f 1)
    else
	os=$(uname)-$(uname -r)
    fi

    rsync_host=`auto_ask rsync-host 'rsync hostname?' ''`
    rsync_pkg_dir=`auto_ask rsync-pkg-dir 'rsync pkg directory?' "www/pkgsrc/packages$prefix/$os"`
    rsync_report_dir=`auto_ask rsync-report-dir 'rsync report directory?' "www/pkgsrc/reports$prefix/$os"`
fi

pbulk_user=`auto_ask pbulk-user 'User to run pbulk builds?' pbulk`
pbulk_group=`auto_ask pbulk-group 'Group to run pbulk builds?' pbulk`

# Set up env for this tree
PATH=/usr/bin:/usr/sbin:/bin:/sbin:/opt/pkg/bin:/opt/pkg/sbin:/usr/pkg/bin:/usr/pkg/sbin
export PATH
unset LD_LIBRARY_PATH

case `uname` in
FreeBSD)
    fetch_cmd=fetch
    ;;

NetBSD)
    fetch_cmd=ftp
    ;;

*)
    fetch_cmd=curl
    ;;
esac

# Make sure $pbulk_user user can access everything
umask 022

# When unpacking a tarball for a minimal chroot, tar may not honor some
# permissions for security reasons.
chmod 1777 /tmp /var/tmp

chmod -R a+rX $pkgsrc

# Remove existing prefix.  It may not have been created by our bootstrap
# script and therefore may not have the correct permissions and options.
rm -rf $prefix

# Generate mk.conf fragments
# Redirect workdir or it will use the pkgsrc tree outside the chroot

cat << EOM > /mk-pbulk.conf
# Written by $0

SKIP_LICENSE_CHECK=         yes
ALLOW_VULNERABLE_PACKAGES=  yes
PKG_DEVELOPER=              yes
X11_TYPE=                   modular
FETCH_USING=                $fetch_cmd
MAKE_JOBS=                  $make_jobs
WRKOBJDIR=                  /tmp/pkgbuild
EOM

cat << EOM > /mk-pkg.conf
# Written by $0

SKIP_LICENSE_CHECK=         yes
ALLOW_VULNERABLE_PACKAGES=  yes
PKG_DEVELOPER=              yes
X11_TYPE=                   modular
FETCH_USING=                $fetch_cmd
MAKE_JOBS=                  $make_jobs
PKGSRC_FORTRAN=             gfortran
WRKOBJDIR=                  /tmp/pkgbuild
PKG_OPTIONS.libfetch=       inet6 openssl
EOM

# Problems with gcc8 generating code with infinite loops on CentOS
if [ $(uname) = Linux ]; then
    export PKGSRC_USE_FORTIFY=no
    cat << EOM >> /mk-pbulk.conf
PKGSRC_USE_FORTIFY=         no
EOM
    cat << EOM >> /mk-pkg.conf
PKGSRC_USE_FORTIFY=         no
EOM
fi


if [ `uname` != NetBSD ]; then
    cat << EOM >> /mk-pkg.conf
PREFER_NATIVE=              no
PREFER_PKGSRC=              yes
EOM
fi

# FIXME: Verify that this is the minimal list of $base_gcc deps
# Pkgsrc 2017Q2 assumes a c++11 compiler.  Force CentOS 6 to build everything
# using a pkgsrc GCC instead of its native GCC 4.4.7, which has only partial
# c++11 support and does not support the -std=c++11 flag.
# Hopefully pkgsrc will handle cases like this automatically in the near
# future and this hack will no longer be needed.
if [ `uname` = Linux ]; then

    cat << EOM
    
Select a minimum GCC version.  Many packages will not build with older
GCC suites such as the GCC 4.4 provided by RHEL/CentOS 6.  When you select
a newer compiler here, a pkgsrc gcc will be used to build all packages
except itself.

1.. 4.8
2.. 4.9
3.. 5.0
4.. 6.0
5.. 7.0
6.. 8.0

EOM
    # Keep this in sync with auto-pkgsrc-setup
    default_minimum=5
    printf "Selection? [$default_minimum] "
    read selection
    if [ 0$selection = 0 ]; then
	selection=$default_minimum
    fi
    case $selection in
    1)
	base_gcc=gcc48
	gcc_version=4.8
	;;
    2)
	base_gcc=gcc49
	gcc_version=4.9
	;;
    3)
	base_gcc=gcc5
	gcc_version=5.0
	;;
    4)
	base_gcc=gcc6
	gcc_version=6.0
	;;
    5)
	base_gcc=gcc7
	gcc_version=7.0
	;;
    6)
	base_gcc=gcc8
	gcc_version=8.0
	;;
    esac

    # Keep this in sync with auto-pkgsrc-setup for installations that use both
    # binary packages and build from source.
    cat << EOM >> /mk-pkg.conf

.if \\
    empty(PKGPATH:Marchivers/bsdtar) && \\
    empty(PKGPATH:Marchivers/bzip2) && \\
    empty(PKGPATH:Marchivers/pax) && \\
    empty(PKGPATH:Marchivers/xz) && \\
    empty(PKGPATH:Mconverters/help2man) && \\
    empty(PKGPATH:Mconverters/libiconv) && \\
    empty(PKGPATH:Mconverters/p5-Unicode-EastAsianWidth) && \\
    empty(PKGPATH:Mdatabases/db4) && \\
    empty(PKGPATH:Mdevel/autoconf) && \\
    empty(PKGPATH:Mdevel/binutils) && \\
    empty(PKGPATH:Mdevel/gettext-lib) && \\
    empty(PKGPATH:Mdevel/gettext-tools) && \\
    empty(PKGPATH:Mdevel/gmake) && \\
    empty(PKGPATH:Mdevel/gmp) && \\
    empty(PKGPATH:Mdevel/gtexinfo) && \\
    empty(PKGPATH:Mdevel/libffi) && \\
    empty(PKGPATH:Mdevel/libtool-base) && \\
    empty(PKGPATH:Mdevel/libuuid) && \\
    empty(PKGPATH:Mdevel/makedepend) && \\
    empty(PKGPATH:Mdevel/pkgconf) && \\
    empty(PKGPATH:Mdevel/m4) && \\
    empty(PKGPATH:Mdevel/ncurses) && \\
    empty(PKGPATH:Mdevel/nbpatch) && \\
    empty(PKGPATH:Mdevel/p5-CPAN-Meta) && \\
    empty(PKGPATH:Mdevel/p5-Module-Build) && \\
    empty(PKGPATH:Mdevel/p5-Perl4-CoreLibs) && \\
    empty(PKGPATH:Mdevel/p5-Scalar-List-Utils) && \\
    empty(PKGPATH:Mdevel/p5-gettext) && \\
    empty(PKGPATH:Mdevel/p5-inc-latest) && \\
    empty(PKGPATH:Mdevel/readline) && \\
    empty(PKGPATH:Mdevel/zlib) && \\
    empty(PKGPATH:Mlang/gcc*) && \\
    empty(PKGPATH:Mlang/perl5) && \\
    empty(PKGPATH:Mlang/python*) && \\
    empty(PKGPATH:Mmath/cloog) && \\
    empty(PKGPATH:Mmath/isl) && \\
    empty(PKGPATH:Mmath/mpcomplex) && \\
    empty(PKGPATH:Mmath/mpfr) && \\
    empty(PKGPATH:Mmisc/p5-Locale-libintl) && \\
    empty(PKGPATH:Mnet/libfetch) && \\
    empty(PKGPATH:Mpkgtools/cwrappers) && \\
    empty(PKGPATH:Mpkgtools/digest) && \\
    empty(PKGPATH:Mpkgtools/pkg_install) && \\
    empty(PKGPATH:Mpkgtools/pkg_install-info) && \\
    empty(PKGPATH:Mpkgtools/pkgin) && \\
    empty(PKGPATH:Msecurity/mozilla-rootcerts*) && \\
    empty(PKGPATH:Msecurity/openssl) && \\
    empty(PKGPATH:Msysutils/checkperms) && \\
    empty(PKGPATH:Mtextproc/gsed) && \\
    empty(PKGPATH:Mtextproc/p5-Text-Unidecode) && \\
    empty(PKGPATH:Mx11/xorgproto)

GCC_REQD+=$gcc_version

.endif  # GCC_REQD

# Keep this in sync with auto-pkgsrc-setup
.if exists(/etc/redhat-release) && !empty(PKGPATH:Mlang/gcc*)

# RHEL systems may have an outdated "as" that cannot translate instructions
# from current GCC code generators, so force pkgsrc binutils.
CONFIGURE_ARGS+=        --with-gnu-as --with-as=\${PREFIX}/bin/gas
CONFIGURE_ARGS+=        --with-gnu-ld --with-ld=\${PREFIX}/bin/gld
BUILDLINK_DEPMETHOD.binutils=   full
.include "../../devel/binutils/buildlink3.mk"

# pkgsrc gcc packages don't install libgcc_s on some platforms, to
# avoid problems when mixing compiler versions.  This breaks our use
# of pkgsrc gcc on EL.
PKG_DEFAULT_OPTIONS+=   always-libgcc
.endif

EOM
fi

mkdir -p $pbulk_dir
cat << EOM > $pbulk_dir/bootstrap
#!/bin/sh -e

# Written by $0

# Redirect workdir or it will use the pkgsrc tree outside the chroot

# Install pbulk under a different prefix, since it wipes out the
# package building prefix before each build.

# Make sure $pbulk_user user can access everything
umask 022

# Create pbulk user
case `uname` in
Linux|NetBSD)
    if ! grep -q "^${pbulk_group}:" /etc/group; then
	groupadd $pbulk_group
    fi
    if ! grep -q "^${pbulk_user}:" /etc/passwd; then
	useradd -c 'pbulk user' -m -g $pbulk_group -s /bin/sh $pbulk_user
    fi
    ;;
esac

PATH=$pbulk_dir/bin:$PATH
export PATH

if [ ! -e $pbulk_dir/bin/bmake ]; then
    cd $pkgsrc/bootstrap
    ./bootstrap \\
	--abi=64 \\
	--make-jobs=$make_jobs \\
	--prefix=$pbulk_dir \\
	--varbase=$pbulk_dir/var \\
	--prefer-pkgsrc=yes \\
	--mk-fragment=/mk-pbulk.conf \\
	--workdir=/tmp/pbulk-bootstrap
    ./cleanup
fi

cd $pkgsrc/pkgtools/pbulk
bmake install

# Create the package building prefix and create a bootstrap kit, just a
# tarball of prefix with nothing installed but bmake.  The bulk build will
# wipe prefix and recreate it from this tarball before each build.

if [ ! -e $prefix/bin/bmake ]; then
    cd $pkgsrc/bootstrap
    ./bootstrap \\
	--abi=64 \\
	--make-jobs=$make_jobs \\
	--prefix=$prefix \\
	--varbase=$prefix/var \\
	--prefer-pkgsrc=yes \\
	--mk-fragment=/mk-pkg.conf \\
	--workdir=/tmp/pkg-bootstrap
    ./cleanup
fi

cd /
mkdir -p bulklog
chown -Rh ${pbulk_user}:$pbulk_group $prefix $pkgsrc/packages /tmp/pkgbuild
tar zcvf $pbulk_dir/bootstrap.tar.gz .$prefix

# Generate a pbulk.list file.  Users can add whatever packages they want
# to this.

cat << EOM2 > $pbulk_dir/etc/pbulk.list
pkgtools/digest
EOM2

# Update pbulk.conf with some reasonable defaults for this build.

if ! fgrep -q "Overrides from $0" $pbulk_dir/etc/pbulk.conf; then
    cat << EOM2 >> $pbulk_dir/etc/pbulk.conf

################################################################
# Overrides from $0

# Scrub path
PATH=/usr/bin:/usr/sbin:/bin:/sbin:/opt/pkg/bin:/opt/pkg/sbin:/usr/pkg/bin:/usr/pkg/sbin
export PATH

# https://pkgsrc.joyent.com/docs/bulk/
# Limit processes to an hour of CPU time.  Anything which takes longer than
# this is most probably broken.
ulimit -t 3600

bootstrapkit=$pbulk_dir/bootstrap.tar.gz
# limited_list=$pbulk_dir/etc/pbulk.list
reuse_scan_results=yes
unprivileged_user=$pbulk_user
make=$prefix/bin/bmake
bulklog=/bulklog

# Must be writable by pbulk user
umask 022
packages=$pkgsrc/packages
prefix=$prefix
pkgdb=$prefix/pkgdb
varbase=$prefix/var
pkgsrc=$pkgsrc

# Change to real IP and add IPs of other bulk build machines if desired
master_mode=no

# Change these if mail is enabled
report_subject_prefix="pkgsrc"
report_recipients="pkgsrc-bulk@netbsd.org"
mail=:

EOM2
else
    printf "Overrides already exist in $pbulk_dir/etc/pbulk.conf.\n"
    printf "Leaving $pbulk_dir/etc/pbulk.conf alone.\n"
fi

EOM

if [ 0$rsync_user != 0 ]; then
    cat << EOM >> $pbulk_dir/bootstrap
cat << EOM2 >> $pbulk_dir/etc/pbulk.conf
pkg_rsync_target="$rsync_user@$rsync_host:$rsync_pkg_dir"
report_rsync_target="$rsync_user@$rsync_host:$rsync_report_dir"
rsync=$pbulk_dir/bin/rsync
EOM2
EOM
    echo "Authorizing ${rsync_user}@$(hostname) on $rsync_host."

    if [ ! -f $HOME/.ssh/id_rsa.pub ] ; then
       ssh-keygen -P "" -f "$HOME/.ssh/id_rsa"
    fi

    key=`cat $HOME/.ssh/id_rsa.pub`
    ssh ${rsync_user}@$rsync_host "umask 022; mkdir -p .ssh; touch .ssh/known_hosts .ssh/authorized_keys; chmod 600 .ssh/authorized_keys; ssh-keygen -R $(hostname) && echo $key >> .ssh/authorized_keys"
    ssh ${rsync_user}@$rsync_host mkdir -p $rsync_pkg_dir
    ssh ${rsync_user}@$rsync_host mkdir -p $rsync_report_dir
else
    cat << EOM >> $pbulk_dir/bootstrap
printf "rsync=:\n" >> $pbulk_dir/etc/pbulk.conf
EOM
fi

cat << EOM >> $pbulk_dir/bootstrap
printf "Bootstrap done.  You may now run $pbulk_dir/bin/bulkbuild.\n"
EOM

chmod 755 $pbulk_dir/bootstrap

if ! [ -e $HOME/.ssh/id_rsa ]; then
    ssh-keygen -b 2048 -t rsa
fi

# Inform the user what to do next.

cat << EOM

Now run

$pbulk_dir/bootstrap

EOM
