#!/bin/ksh93 -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2010, Richard Lowe
# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
# Copyright 2024 Bill Sommerfeld <sommerfeld@hamachi.org>

PATH=/usr/bin:/usr/sbin
export PATH

DEFAULTONPUB="on-nightly"

usage()
{
	echo "usage: $0 [opts] [-s beName] -t beName"
	echo "usage: $0 [opts] -r"
	echo
	echo "\t-c consolidation : consolidation being upgraded"
	echo "\t-d repodir : directory for repositories"
	echo "\t-r : configure publisher only"
	echo "\t-s : source BE to clone"
	echo "\t-t : new BE name"
	echo "\t-u uri : origin URI for redist repository"
	echo "\t-U prefix:  prefix for redist repository"
	echo "\t-v : verbose"
	echo "\t-Z : skip updating zones"
	echo
	echo "Update to an ON build:"
	echo "\tonu -t newbe -d /path/to/my/ws/packages/\`uname -p\`/nightly"
	echo
	echo "Re-enable the publishers in the current BE:"
	echo "\tonu -r -d /path/to/my/ws/packages/\`uname -p\`/nightly"
	exit 1
}

srcusage()
{
	cat 1>&2 <<EOF
No package source specified; use -d or -u or set ONURI in environment

EOF
	usage
}

exit_error()
{
	echo $*
	exit 2
}

do_cmd()
{
	[ $verbose -gt 0 ] && echo $*
	$*
	exit_code=$?
	[ $exit_code -eq 0 ] && return
	# pkg(1) returns 4 if "nothing to do", which is safe to ignore
	[ $1 = "pkg" -a $exit_code -eq 4 ] && return
	exit_error "$*" failed: exit code $exit_code
}

configure_publishers()
{
	root=$1

	#
	# Get the publisher name from the 'list -v' output.  It may seem we
	# could do this more tidily using 'info', but that is
	# internationalized.
	#
	typeset on_publisher=$(pkg -R $root list -Hv \
	    "${consolidation}-incorporation" | cut -d/ -f3)

	if [[ "$on_publisher" != "$redistpub" ]]; then
		do_cmd pkg -R $root set-publisher -r --no-refresh \
		    --set-property signature-policy=verify \
		    --non-sticky $on_publisher
	fi

	do_cmd pkg -R $root set-publisher -r -e --no-refresh -P \
	    --set-property signature-policy=verify -O $uri $redistpub

	do_cmd pkg -R $root refresh --full
}

prepare_image()
{
	print "**\n** Preparing for ONU from $distribution\n**"

	case $distribution in
	    omnios|helios)
		# This removes files from the image that cause conflicts with
		# stock illumos-gate, and removes omnios-only kernel drivers
		# etc.
		do_cmd pkg -R $root change-facet -r onu.ooceonly=false
		;;
	esac
}

update()
{
	root=$1

	pkg -R $root list -q entire && do_cmd pkg -R $root uninstall entire

	configure_publishers $root

	prepare_image

	do_cmd pkg -R $root image-update $update_args
}

update_zone()
{
	OIFS="$IFS"
	IFS=":"
	set -- $1
	zone=$2; state=$3; path=$4; brand=$6
	IFS="$OIFS"

	[[ "$zone" == "global" ]] && return
	[[ "$state" == "incomplete" ]] && return
	[[ "$state" == "configured" ]] && return
	[[ "$brand" == "$nlbrand" ]] || return

	if [ "$zone_warned" = 0 ]; then
		echo "WARNING: Use of onu(1) will prevent use of zone attach in the new BE" >&2
		echo "See onu(1)" >&2
		zone_warned=1
	fi

	print "**\n** Updating zone $zone (brand $brand)\n**"
	update "$path/root"
}

typeset -l distribution
if [[ -r /etc/os-release ]]; then
	distribution=$(awk -F= '$1 == "ID" { print $2 }' /etc/os-release)
elif [[ -r /etc/release ]]; then
	distribution=$(awk 'NR == 1 { print $1 }' /etc/release)
else
	distribution=unknown
fi

case $distribution in
    openindiana)
	nlbrand=nlipkg
	;;
    omnios|helios)
	nlbrand=ipkg
	;;
    *)
	nlbrand=unknown
	;;
esac

sourcebe=""
targetbe=""
uri=""
repodir=""
consolidation="osnet"
verbose=0
no_zones=0
zone_warned=0
reposonly=0
update_args=""

while getopts :c:d:Ors:t:U:u:vZ i ; do
	case $i in
	c)
		consolidation=$OPTARG
		;;
	d)
		repodir=$OPTARG
		;;
	O)			# no-op, compatibility with recommended use
		;;
	r)
		reposonly=1
		;;
	s)
		sourcebe=$OPTARG
		;;
	t)
		targetbe=$OPTARG
		;;
	U)
		redistpub=$OPTARG
		;;
	u)
		uri=$OPTARG
		;;
	v)
		verbose=1
		;;
	Z)
		no_zones=1
		;;
	*)
		usage
	esac
done
shift `expr $OPTIND - 1`

# Pass remaining arguments to pkg update.
if [ -n "$1" ]; then
	update_args="$*"
fi

if [ "$reposonly" -eq 1 ]; then
	[ -n "$sourcebe" ] && usage
	[ -n "$targetbe" ] && usage
	[ "$no_zones" -eq 1 ] && usage
else
	[ -z "$targetbe" ] && usage
fi
[ -z "$uri" ] && uri=$ONURI
[ -z "$redistpub" ] && redistpub=$ONPUB
[ -z "$redistpub" ] && redistpub=$DEFAULTONPUB

if [ -n "$repodir" ]; then
	redistdir=$repodir/repo.redist
	[ -d $redistdir ] || exit_error "$redistdir not found"
	typeset cfgfile=$redistdir/cfg_cache
	[[ ! -e $cfgfile ]] && cfgfile=$redistdir/pkg5.repository
	# need an absolute path
	[[ $redistdir == /* ]] || redistdir=$PWD/$redistdir
	redistpub=$(python@PYTHON_VERSION@ <<# EOF
		try:	# python3
			import configparser
			p = configparser.ConfigParser()
		except:	# python2
			import ConfigParser
			p = ConfigParser.SafeConfigParser()
		p.read("$cfgfile")
		pp = p.get("publisher", "prefix")
		print("{}".format(pp))
		EOF) || exit_error "Cannot determine publisher prefix"
	[[ -n "$redistpub" ]] || exit_error "Repository has no publisher prefix"
	uri="file://$redistdir"
fi

[ -z "$uri" ] && srcusage

if [ "$reposonly" -eq 1 ]; then
	configure_publishers /
	exit 0
fi

createargs=""
[ -n "$sourcebe" ] && createargs="-e $sourcebe"

# ksh seems to have its own mktemp with slightly different semantics
tmpdir=`/usr/bin/mktemp -d /tmp/onu.XXXXXX`
[ -z "$tmpdir" ] && exit_error "mktemp failed"

do_cmd beadm create $createargs $targetbe
do_cmd beadm mount $targetbe $tmpdir
update $tmpdir
do_cmd beadm activate $targetbe

if [ "$no_zones" != 1 ]; then
	for zone in `do_cmd zoneadm -R $tmpdir list -cip`; do
		update_zone $zone
	done
fi

exit 0