#!/bin/sh
#
# Copyright 2012 Kris Moore (iXsystems)
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

# Define some defaults
DBDIR="/var/db/pc-metapkgmanager/pkgsets"
LOGFILE="/tmp/.pc-metapkgmanager.log"
FTP_PASSIVE_MODE="YES" ; export FTP_PASSIVE_MODE
PACKAGESUFFIX=".txz" ; export PACKAGESUFFIX
ARCH="`uname -m`"

# Start by sourcing /etc/profile
# This grabs any HTTP_ / FTP_ PROXY variables
. /etc/profile

# Fix some pkgs bugging us with license questions
PACKAGE_BUILDING=yes
export PACKAGE_BUILDING

# Trigger File for Tray Application
TRIGGERFILE="/tmp/.sysupdatetraytrigger"

display_usage() {
        cat <<EOF
usage: `basename $0` [options]
   
Options:
  add pkg1,pkg2 <loc>	 -- Add the specified list of meta-packages
			    <loc> should be a FTP / HTTP url where pkg_add
			    can fetch packages, or an absolute path to
			    location of pkg files on disk.
  checkup		 -- Check for updates to pkgs
  del pkg1,pkg2	  	 -- Delete the specified list of meta-packages
  list     	         -- List the available meta-packages
  status <pkg>    	 -- List the status of the specified meta-packages
  update <pkg,pkg2> <loc> -- Update system packages. Can use 'all' or <pkg1,pkg2> .
			    <loc> should be a FTP / HTTP url where pkg_add
			    can fetch packages, or an absolute path to
			    location of pkg files on disk.
  --pkgset <pkgset>	 -- Change default pkgset we are using
  --chroot <dir>	 -- Operate on the directory specified using chroot

EOF
        exit 1
}

add_metapkgs() {
	if [ -z "$1" ] ; then exit_err "No meta-pkg specified!" ; fi
	local pkgAddMirror="$2"
	if [ -z "$pkgAddMirror" ] ; then pkgAddMirror=`use_default_mirror` ; fi
	if [ ! -e "${MPDIR}/${1}/pkg-list" ] ; then exit_err "No such meta-pkg: $1" ; fi
	_apkg=$1

        MIRRORURL=`parse_url "$pkgAddMirror"`

	# Figure out the type of location we are installing from
	echo $MIRRORURL | grep -e '^http://' -e '^ftp://' >/dev/null 2>/dev/null
	if [ "$?" = "0" ] ; then
		loc="NET"
	else
		if [ ! -d "${MIRRORURL}" ] ; then exit_err "The pkg location $MIRRORURL does not exist!" ; fi
		loc="PATH"
	fi

	echo "Package source: $loc"
        echo "Preparing to add: $_apkg... "
        echo "Checking for updates to old packages..."
        updatepkgs "all" "SILENT"

	echo "Installing Meta-Package: $_apkg"

	# Start by building the list of packages to download
	build_pkg_list "$_apkg" "YES"

	echo "Pending package changes: $PKGCOUNT"

	# If a pre-install script, run it now
	if [ -e "${MPDIR}/${_apkg}/pre-install.sh" ] ; then
	  if [ -z "$_chroot" ] ; then
	    sh ${MPDIR}/${_apkg}/pre-install.sh >/dev/null 2>/dev/null
	  else
	    cp ${MPDIR}/${_apkg}/pre-install.sh ${_chroot}/.pre-install.sh.$$ >/dev/null 2>/dev/null
	    chroot "$_chroot" sh /.pre-install.sh.$$ >/dev/null 2>/dev/null
	    rm ${_chroot}/.pre-install.sh.$$
          fi
	fi

        # Download / Copy each package now
        for pkg in $PKGLIST
        do
		# Start installing / downloading the packages now
		if [ "$loc" = "NET" ] ; then
		   echo "Downloading package: $pkg"
		   echo "Downloading package: $pkg" >>${LOGFILE}

	           # Create the TMPDIR
		   if [ ! -d "${_chroot}/$TMPDIR" ]; then mkdir -p "${_chroot}/$TMPDIR"; fi

		   # Download all the packages first
		   get_file "${MIRRORURL}/${pkg}.txz" "${_chroot}/${TMPDIR}/${pkg}.txz" 3
		   if [ $? -ne 0 ] ; then
		      exit_err "Failed downloading: ${MIRRORURL}/${pkg}.txz"
		   fi

		else
		   # Check if the package is already installed
		   if [ -d "${_chroot}/var/db/pkg/$pkg" ] ; then
		     echo "Skipping installed package: $pkg"
		     echo "Skipping installed package: $pkg" >>${LOGFILE}
		     continue
		   fi

		   echo "Installing package: $pkg"
		   echo "Installing package: $pkg" >>${LOGFILE}
		   if [ -z "$_chroot" ] ; then
		      cd "${MIRRORURL}"
		      pkg_add -f "${pkg}.txz" >>${LOGFILE} 2>>${LOGFILE}
		   else
		      # Do some nullfs mounting to get our dist dir into the chroot
		      mkdir ${_chroot}/.mnt.$$
		      mount_nullfs "$MIRRORURL" "${_chroot}/.mnt.$$"
		      ${_chrootcmd} pkg_add -f "/.mnt.$$/${pkg}.txz" >>${LOGFILE} 2>>${LOGFILE}
		   fi
		fi
        done

	# If this is a network install, then packages are now downloaded, lets install
	if [ "$loc" = "NET" ] ; then
           for pkg in $PKGLIST
           do
	      # Check if the package is already installed
	      if [ -d "${_chroot}/var/db/pkg/$pkg" ] ; then
	         echo "Skipping installed package: $pkg"
	         echo "Skipping installed package: $pkg" >>${LOGFILE}
	         continue
	      fi
	      echo "Installing package: $pkg"
	      echo "Installing package: $pkg" >>${LOGFILE}
	      if [ -z "${_chroot}" ] ; then
		cd ${TMPDIR}
	        pkg_add -f "${pkg}.txz" >>${LOGFILE} 2>>${LOGFILE}
              else
	        echo "#!/bin/sh
cd ${TMPDIR}
pkg_add -f ${pkg}.txz" > ${_chroot}/.insPkg.$$.sh
	        chmod 755 ${_chroot}/.insPkg.$$.sh
	        ${_chrootcmd} /.insPkg.$$.sh >>${LOGFILE} 2>>${LOGFILE}
	        rm "${_chroot}/.insPkg.$$.sh"
	      fi
	      rm "${_chroot}/${TMPDIR}/${pkg}.txz"
           done
	fi

	# Umount any nullfs stuff in chroot
        if [ -n "${_chroot}" -a "$loc" = "PATH" ] ; then
	   sleep 1
	   umount -f ${_chroot}/.mnt.$$
	   rmdir ${_chroot}/.mnt.$$
	fi

	# Make sure the program appears fully installed now, if not set error
	stat_metapkg ${_apkg}
	if [ "$?" != "0" ] ; then _pkgStatus=1 ; fi

	# Apply our PC-BSD specific xdg menu entry files
	if [ -z "$_chroot" ] ; then
	  /usr/local/bin/pc-xdgutil updatemenu >>${LOGFILE} 2>>${LOGFILE}
	elif [ -e "${_chroot}/usr/local/bin/pc-xdgutil" ] ; then
	  chroot ${_chroot} /usr/local/bin/pc-xdgutil updatemenu >>${LOGFILE} 2>>${LOGFILE}
	fi

	# Hack to ensure that we share cursors / icons between linux / FBSD apps
	if [ ! -h "${_chroot}/compat/linux/usr/share/icons" ] ; then
	   rm -rf ${_chroot}/compat/linux/usr/share/icons 2>/dev/null
	   ln -fs /usr/local/lib/X11/icons ${_chroot}/compat/linux/usr/share/icons 2>/dev/null
	fi
	if [ ! -h "${_chroot}/compat/linux/usr/share/fonts" ] ; then
	   rm -rf ${_chroot}/compat/linux/usr/share/fonts 2>/dev/null 
	   ln -fs /usr/local/lib/X11/fonts ${_chroot}/compat/linux/usr/share/fonts 2>/dev/null
	fi

	# If a post-install script, run it now
	if [ -e "${MPDIR}/${_apkg}/post-install.sh" ] ; then
	  if [ -z "$_chroot" ] ; then
	    sh ${MPDIR}/${_apkg}/post-install.sh >/dev/null 2>/dev/null
	  else
	    cp ${MPDIR}/${_apkg}/post-install.sh ${_chroot}/.post-install.sh.$$ >/dev/null 2>/dev/null
	    chroot "$_chroot" sh /.post-install.sh.$$ >/dev/null 2>/dev/null
	    rm ${_chroot}/.post-install.sh.$$
          fi
	fi

	echo "Finished Meta-Package: $_apkg"
	
}

# Delete specified meta-pkg, and packages which it installed that are not used elsewhere
del_metapkgs() {
	if [ -z "$1" ] ; then exit_err "No meta-pkg specified!" ; fi
	if [ ! -e "${MPDIR}/${1}/pkg-list" ] ; then exit_err "No such meta-pkg: $1" ; fi
	_dpkg=$1
	kPkgs=""

	echo "Removing Meta-Package: $_dpkg"

	# Build a list of installed meta-pkgs
	for i in `find ${MPDIR}/* -type d`
	do
		if [ ! -e "${i}/pkg-list" ] ; then continue ; fi
		if [ "${i}" = "${MPDIR}/$_dpkg" ] ; then continue ; fi
		found=0
		_iList=""
		while read pkg
		do
			${_chrootcmd} pkg_info $pkg >/dev/null 2>/dev/null
			if [ "$?" != "0" ] ; then continue; fi
			_iList="${_iList}${pkg}:"
		done < ${i}/pkg-list
		if [ "$found" = "0" ] ; then kPkgs="${kPkgs}${_iList}" ; fi
	done

	# kPkgs is our list of packages which are required by other installed meta-pkgs
	export kPkgs

	# If a pre-remove script, run it now
	if [ -e "${MPDIR}/${_dpkg}/pre-remove.sh" ] ; then
	  if [ -z "$_chroot" ] ; then
	    sh ${MPDIR}/${_dpkg}/pre-remove.sh >/dev/null 2>/dev/null
	  else
	    cp ${MPDIR}/${_dpkg}/pre-remove.sh ${_chroot}/.pr.sh.$$ >/dev/null 2>/dev/null
	    chroot "$_chroot" sh /.pr.sh.$$ >/dev/null 2>/dev/null
	    rm ${_chroot}/.pr.sh.$$
          fi
	fi

	build_pkg_list "$_dpkg" "NO"
	echo "Pending package changes: $PKGCOUNT"

	# Lets remove the pkgs from this meta-pkg
	for rmPkg in $PKGLIST
	do
		# Make sure this pkg is installed
		${_chrootcmd} pkg_info $rmPkg >/dev/null 2>/dev/null
		if [ "$?" != "0" ] ; then 
			echo "Already uninstalled: ${rmPkg}" 
			echo "Already uninstalled: ${rmPkg}" >>${LOGFILE}
			continue
		fi

		# Check that this isnt an apart of another meta-pkg installed list
		echo "$kPkgs" | grep -q "${rmPkg}:"
		if [ "$?" = "0" ] ; then 
			echo "Skipping Meta-Required: ${rmPkg}" 
			echo "Skipping Meta-Required: ${rmPkg}" >>${LOGFILE}
			continue
		fi

		# confirm this package isn't required by any others
		${_chrootcmd} pkg_info -R ${rmPkg} 2>/dev/null | grep -q "Required by:"
		if [ "$?" = "0" ] ; then 
			# Still in use, we will re-scan it at the end
			skippedPkgs="${rmPkg} ${skippedPkgs}"
			continue
		fi

		echo "Removing: ${rmPkg}"
		echo "Removing: ${rmPkg}" >>${LOGFILE}
		${_chrootcmd} pkg_delete -f ${rmPkg} >>${LOGFILE} 2>>${LOGFILE}

	done

	# Check if we have any packages skipped that need removing
	recheck_skipped "$skippedPkgs"

	# If the program is still fully installed, set status to error
	stat_metapkg ${_dpkg}
	if [ "$?" = "0" ] ; then _pkgStatus=1 ; fi

	# If a post-remove script, run it now
	if [ -e "${MPDIR}/${_dpkg}/post-remove.sh" ] ; then
	  if [ -z "$_chroot" ] ; then
	    sh ${MPDIR}/${_dpkg}/post-remove.sh >/dev/null 2>/dev/null
	  else
	    cp ${MPDIR}/${_dpkg}/post-remove.sh ${_chroot}/.pr.sh.$$ >/dev/null 2>/dev/null
	    chroot "$_chroot" sh /.pr.sh.$$ >/dev/null 2>/dev/null
	    rm ${_chroot}/.pr.sh.$$
          fi
	fi

}

# Loop through skipped pkgs, until we are sure there are no more ones to remove
recheck_skipped()
{
   local found=0
   local newSkipped=""
   for skPkg in $1
   do
      ${_chrootcmd} pkg_info -R ${skPkg} 2>/dev/null | grep -q "Required by:"
      if [ "$?" = "0" ] ; then 
        newSkipped="$skPkg $newSkipped"	
        continue
      fi
      found=1
      echo "Removing: ${skPkg}"
      echo "Removing: ${skPkg}" >>${LOGFILE}
      ${_chrootcmd} pkg_delete -f ${skPkg} >>${LOGFILE} 2>>${LOGFILE}
   done

   if [ $found -eq 1 ] ; then
      recheck_skipped "$newSkipped"
   else
     # List the packages which still have things depending upon them
     for skPkg in $newSkipped
     do
	echo "Skipping Required: ${skPkg}" 
	echo "Skipping Required: ${skPkg}" >>${LOGFILE}
     done
   fi
}

# Check if the specified pkg doesn't have anything which requires it, and remove it if so
check_remove_pkg() {
        ${_chrootcmd} pkg_info -R "${1}" | grep "Required" >/dev/null 2>/dev/null
        if [ "$?" != "0" ] ; then
		echo "Removing non-used pkg: $1" >>${LOGFILE} 2>>${LOGFILE} 
                ${_chrootcmd} pkg_delete -f ${1} >>${LOGFILE} 2>>${LOGFILE}
                return 0
        fi
        return 1
}

# Function to list available metapkgs
list_metapkgs() {
	if [ ! -d "${MPDIR}" ] ; then exit_err "No available meta-pkg dir" ; fi

	# Start listing each meta-pkg
	for i in `find ${MPDIR}/* -type d`
	do
		# Is this package ignored on this arch?
		if [ -e "${i}/ignore-arch" ] ; then
			if [ `cat ${i}/ignore-arch` = "$ARCH" ] ; then continue; fi
		fi

		if [ -e "${i}/pkg-desc" ] ; then

			echo "Meta Package: `basename ${i}`"
			echo "-------------------------------------"
			echo "Description: `cat ${i}/pkg-desc`"
			if [ -e "${i}/pkg-icon.png" ] ; then
				echo "Icon: ${i}/pkg-icon.png"
			fi
			if [ -e "${i}/pkg-parent" ] ; then
				echo "Parent: `cat ${i}/pkg-parent`"
			fi
			if [ -e "${i}/desktop" ] ; then
				echo "Desktop: YES"
			else
				echo "Desktop: NO"
			fi
			echo " "
			if [ -e "${i}/pkg-list" ] ; then
				echo "Required Packages:"
				cat ${i}/pkg-list
			else
				echo "Category Entry"
			fi
			echo " "
		fi
	done

}

exit_err() {
	echo "ERROR: $1"
	echo "ERROR: $1" >>${LOGFILE}
	exit 1
}

# Check on a meta-pkg, see if its installed, partially installed, or not. 
stat_metapkg() {
	if [ -z "$1" ] ; then exit_err "No meta-pkg specified!" ; fi
	if [ ! -e "${MPDIR}/${1}/pkg-list" ] ; then exit_err "No such meta-pkg: $1" ; fi
	_mpkg=$1

	found=1
	nfound=0

	# Now query pkg_info to confirm each pkg is installed
	while read pkg
	do
		# See if the pkg is installed, without worrying about
		# version numbers. This means a meta-pkg shows as
		# installed even if the user manually updated
    		pName=`echo $pkg | rev | cut -d "-" -f 2-25 | rev`
		ls -d ${_chroot}/var/db/pkg/${pName}-* >/dev/null 2>/dev/null
		if [ $? -eq 0 ] ; then
			found=0
		else
			nfound=1
		fi
		
	done < ${MPDIR}/${_mpkg}/pkg-list

	if [ "$found" = "0" -a "$nfound" = "0" ] ; then
		echo "The meta-pkg $_mpkg is installed"
		return 0
	fi
	if [ "$found" = "0" -a "$nfound" = "1" ] ; then
		echo "The meta-pkg $_mpkg is partially installed"
		return 1
	fi
	if [ "$found" = "1" ] ; then
		echo "The meta-pkg $_mpkg is not installed"
		return 255
	fi

}

# Read through comma delimited list of meta-pkgs
parse_metapkgs() {

	if [ "$2" != "add" -a "$2" != "del" ] ; then
		exit_err "Internal error, must use add/del for parse_metapkgs"
	fi

	create_logfile

	get_num_pkgs "$1"
	echo "Pending Meta-Package changes: $VAL"
	echo "Pending Meta-Package changes: $VAL" >>${LOGFILE}

	# Set the package add exit status to default of 0
	_pkgStatus=0

 	local list
	list=`echo "$1" | sed 's|,| |g'`	
	for z in $list
	do
		if [ "$2" = "add" ] ; then add_metapkgs "$z" "$3" ; fi
		if [ "$2" = "del" ] ; then del_metapkgs "$z" ; fi
	done

	# Update the mime data
	xdg-update-defaults >/dev/null 2>/dev/null

	echo "Meta-Package changes finished!"
	exit "$_pkgStatus"

}


# Get the number of packages we are working on
get_num_pkgs() {
        VAL=`echo $1 | sed 's|,| |g' | wc -w | tr -d ' '`
}

# Create the logfile
create_logfile() {
	if [ -e "${LOGFILE}" ]; then
		# Rotate the log file if over 5 MB
 		if [ `du -m ${LOGFILE} | awk '{print $1}'` -gt 5 ]; then
			rm ${LOGFILE}
		fi
	fi
	touch ${LOGFILE}
	chmod 666 ${LOGFILE}
}

checkup_pkgs() {

  _nodsp="$1"

  if [ ! -e "${MPDIR}/master-pkg-index" ] ; then
    return
  fi

  # Checkout the installed pkgs and compare to master list
  local fPkgUp=0
  ls ${_chroot}/var/db/pkg > /tmp/.pkgList.$$
  cat ${MPDIR}/master-pkg-index | cut -d ":" -f 1-2 | sed 's|:|-|g' > /tmp/.pkgList2.$$
  diff /tmp/.pkgList.$$ /tmp/.pkgList2.$$ | grep '^<' | sed 's|< ||g' > /tmp/.dList.$$
  rm /tmp/.pkgList.$$
  rm /tmp/.pkgList2.$$
  while read pLine
  do
    pName=`echo $pLine | rev | cut -d "-" -f 2-25 | rev`
    pVer=`echo $pLine | rev | cut -d "-" -f 1 | rev`
		
    # Make sure this version isn't in our index
    grep "^${pName}:${pVer}" ${MPDIR}/master-pkg-index >/dev/null 2>/dev/null
    if [ $? -eq 0 ] ; then continue ; fi

    # Make sure the old pkg is something we've updated, not the user manually
    grep "^${pName}:${pVer}" ${MPDIR}/master-pkg-index-old >/dev/null 2>/dev/null
    if [ $? -ne 0 ] ; then continue ; fi

    # Get the info from master-pkg-index
    iLine=`grep "^${pName}:" ${MPDIR}/master-pkg-index`
    if [ -z "$iLine" ] ; then continue ; fi
    iVer="`echo $iLine | cut -d ':' -f 2`"
    iSiz="`echo $iLine | cut -d ':' -f 3`"
    iFile="`echo $iLine | cut -d ':' -f 4`"

    # Is this version different?
    if [ "$pVer" = "$iVer" ] ; then continue ; fi

    if [ $fPkgUp -eq 0 -a "$_nodsp" != "YES" ] ; then 
       echo "The following package updates are available:"
       echo ""
       fPkgUp=1
    fi

    if [ "$_nodsp" != "YES" ] ; then
      # Display the pkg update data
      echo "PKGUPDATE: ${pName} ${pVer} -> ${iVer}"
      echo "PKGUPDATEKB: ${iSiz}"
      echo "PKGUPDATEFILE: ${iFile}"
      echo "To update this pkg run \"${0} ${_chroottag} update ${pName} <mirror url>\""
      echo ""
    fi

    # Save the variable of available pkg updates
    PKGUPDATES="${PKGUPDATES}${pName}:"
  done < /tmp/.dList.$$
  rm /tmp/.dList.$$

  # Display command to use for pkg-updating
  if [ $fPkgUp -eq 1 -a "$_nodsp" != "YES" ] ; then 
     echo " "
     echo "To update all run \"${0} ${_chroottag} update all <mirror url>\""
     echo " "
  elif [ "$_nodsp" != "YES" ] ; then
     echo "All packages are up to date!"
  fi
  return 0
}

parse_url()
{
   # Make sure URL ends with '/'
   if [ "`echo $1 | rev | cut -c 1`" != "/" ] ; then
      echo "${1}/"
   else
      echo "$1"
   fi
}

use_default_mirror()
{
  # Get the default mirror URL
  PCBSD_ETCCONF="/usr/local/etc/pcbsd.conf"
  local mirrChk="`sed -n 's/PCBSD_MIRROR: //p' ${PCBSD_ETCCONF} 2>/dev/null`"
  local pcVer="`pbreg get /PC-BSD/Version 2>/dev/null`"
  local pcArch="`uname -m`"
  if [ -z "${mirrChk}" -o -z "${pcVer}" -o -z "$pcArch" ] ; then 
	exit_err "No mirror specified, and could not get default!"
  fi
  echo "${mirrChk}/${pcVer}/${pcArch}/netinstall/metapackages/${PKGSET}/packages/"
}

updatepkgs() {
  # Check for updates to the pkgs quietly
  checkup_pkgs "YES"
  if [ -z "$PKGUPDATES" -a "$2" = "SILENT" ] ; then return; fi
  if [ -z "$PKGUPDATES" ] ; then
     echo "No packages need updating!"
     exit 1
  fi

  if [ -z "$1" ] ; then
     echo "No packages specified, use 'all' or pkg1,pkg2,pkg3"
     exit 1
  fi

  local pkgAddMirror="$2"
  if [ -z "$pkgAddMirror" ] ; then pkgAddMirror=`use_default_mirror` ; fi

  # Make sure the URL is correctly formatted
  MIRRORURL=`parse_url "$pkgAddMirror"`

  if [ -n "$1" -a "$1" != "all" ] ; then
     for pkg in `echo $1 | sed 's|,| |g'`
     do
       echo "$PKGUPDATES" | grep -q "${pkg}:"
       if [ $? -ne 0 ] ; then
         echo "No update available for ${pkg}!"
         exit 1
       fi
     done
  fi

  # Figure out the type of location we are installing from
  echo $MIRRORURL | grep -e '^http://' -e '^ftp://' >/dev/null 2>/dev/null
  if [ "$?" = "0" ] ; then
     loc="NET"
     PACKAGESITE="$MIRRORURL" ; export PACKAGESITE
  else
     if [ ! -d "${MIRRORURL}" ] ; then exit_err "The pkg location $MIRRORURL does not exist!" ; fi
     loc="PATH"
     cd "${MIRRORURL}"
  fi

  if [ "$loc" = "PATH" -a -n "$_chroot" ] ; then
     # Do some nullfs mounting to get our dist dir into the chroot
     mkdir ${_chroot}/.mnt.$$
     mount_nullfs "$MIRRORURL" "${_chroot}/.mnt.$$"
  fi

  # Do the updates now
  if [ "$1" = "all" ] ; then
    # Updating all
    get_num_pkgs "`echo $PKGUPDATES | sed 's|:| |g'`"
    echo "Pending Meta-Package changes: $VAL"
    echo "Pending Meta-Package changes: $VAL" >>${LOGFILE}
    for pkg in `echo $PKGUPDATES | sed 's|:| |g'`
    do
      do_update "$pkg"
    done
  else
    # Updating a list of pkgs
    get_num_pkgs "$1"
    echo "Pending Meta-Package changes: $VAL"
    echo "Pending Meta-Package changes: $VAL" >>${LOGFILE}
    for pkg in `echo $1 | sed 's|,| |g'`
    do
      do_update "$pkg"
    done
  fi

  # Unmount any distdir
  if [ "$loc" = "PATH" -a -n "$_chroot" ] ; then
     umount -f ${_chroot}/.mnt.$$
     rmdir ${_chroot}/.mnt.$$
  fi

  echo "INSTALLFINISHED: MetaPkg Updates" >${TRIGGERFILE}
  echo "Meta-Package changes finished!"

}

do_update() {
  _pU="$1"
  # get the old pkg-name
  oPkg=$(get_pkg_from_name $_pU sys)
  nPkg=$(get_pkg_from_name $_pU ${MPDIR}/master-pkg-index)
  echo "Updating package: $oPkg -> $nPkg"
  echo "Updating package: $oPkg -> $nPkg" >> ${LOGFILE}

  # Make sure the new package is fetchable
  # Create the TMPDIR
  if [ ! -d "${_chroot}/$TMPDIR" ]; then mkdir -p "${_chroot}/$TMPDIR"; fi

  # Download all the packages first
  get_file "${MIRRORURL}/${nPkg}.txz" "${_chroot}/${TMPDIR}/${pkg}.txz" 3
  if [ $? -ne 0 ] ; then
      exit_err "Failed downloading: ${MIRRORURL}/${nPkg}.txz"
  fi

  # Remove the old pkg
  #echo "${_chrootcmd} pkg_delete -f $oPkg"
  ${_chrootcmd} pkg_delete -f "$oPkg" >>${LOGFILE} 2>>${LOGFILE}

  # Start installing the packages now
  if [ "$loc" = "NET" ] ; then
     ${_chrootcmd} pkg_add -f "${TMPDIR}/${nPkg}.txz" >>${LOGFILE} 2>>${LOGFILE}
  else
     if [ -z "$_chroot" ] ; then
        pkg_add -f "${nPkg}.txz" >>${LOGFILE} 2>>${LOGFILE}
     else
        ${_chrootcmd} pkg_add -f "/.mnt.$$/${nPkg}.txz" >>${LOGFILE} 2>>${LOGFILE}
     fi
  fi

}

get_pkg_from_name() {
  if [ "$2" = "sys" ] ; then
    ${_chrootcmd} pkg_info | cut -d ' ' -f 1 >/tmp/.plist.$$ 2>/dev/null
  else
    cat $2 | cut -d ':' -f 1-2 | sed 's|:|-|g' > /tmp/.plist.$$ 2>/dev/null 
  fi

  while read line
  do
    pName=`echo $line | rev | cut -d "-" -f 2-25 | rev`
    if [ "$pName" != "$1" ] ; then continue; fi
    pVer=`echo $line | rev | cut -d "-" -f 1 | rev`
    echo "${pName}-${pVer}"
    break
  done < /tmp/.plist.$$
  rm /tmp/.plist.$$ 2>/dev/null
}

get_pkg_deps()
{
  local tPkg="$1"
  if [ ! -e "${DEPDIR}/${tPkg}.deps" ] ; then
     echo "Warning: No dependancy file for ${tPkg}!"
     return
  fi
  while read pkgDeps
  do
     echo "$pkgDeps" | grep -q "Dependency: "
     if [ $? -ne 0 ] ; then continue ; fi
     pkgDeps=`echo $pkgDeps | cut -d ' ' -f 2`

     echo "${DEPS}" | grep -q " $pkgDeps"
     if [ $? -eq 0 ] ; then continue ; fi

     # Save this as a dependency
     #echo "Added DEP: $pkgDeps"
     DEPS="${DEPS} $pkgDeps"

     # Drill down
     get_pkg_deps "$pkgDeps"
  
  done < ${DEPDIR}/${tPkg}.deps
  
}

build_pkg_list() 
{
  local _mpkg="$1"
  local _strip="$2"
  PKGLIST=""
  DEPS=""
  echo "Getting package list..."
  while read mPkg
  do
    if [ ! -e "${DEPDIR}/${mPkg}.deps" ] ; then
       echo "Warning: No dependancy file for ${mPkg}!"
       echo "Warning: No dependancy file for ${mPkg}!" >>${LOGFILE}
       continue
    fi
    get_pkg_deps "$mPkg"
    DEPS="$mPkg $DEPS"
  done < ${MPDIR}/${_mpkg}/pkg-list

  for pkg in $DEPS
  do
    echo "$pkg" >> /tmp/.pc-meta-sort.$$
  done
  sort /tmp/.pc-meta-sort.$$ | uniq > /tmp/.pc-meta-sort2.$$
  mv /tmp/.pc-meta-sort2.$$ /tmp/.pc-meta-sort.$$

  PKGCOUNT=0
  while read line
  do
     if [ -d "${_chroot}/var/db/pkg/$line" -a "$_strip" = "YES" ] ; then continue ; fi
     PKGLIST="${PKGLIST} ${line}"
     PKGCOUNT=`expr ${PKGCOUNT} + 1`
  done < /tmp/.pc-meta-sort.$$
  rm /tmp/.pc-meta-sort.$$
}

# Function to download a file from remote using fetch
get_file() {
	_rf="${1}"
	_lf="${2}"
        _ftries=${3}
	if [ -z "$_ftries" ] ; then _ftries=3; fi

	if [ -e "${_lf}" ] ; then 
		echo "Resuming download of: ${_lf}"
	fi

	if [ "$GUI_FETCH_PARSING" != "YES" ] ; then
		fetch -r -o "${_lf}" "${_rf}"
		_err=$?
	else
		# Doing a front-end download, parse the output of fetch
		_eFile="/tmp/.fetch-exit.$$"
		fetch -s "${_rf}" > /tmp/.fetch-size.$$ 2>/dev/null
		_fSize=`cat /tmp/.fetch-size.$$ 2>/dev/null`
		_fSize="`expr ${_fSize} / 1024 2>/dev/null`"
		rm "/tmp/.fetch-size.$$" 2>/dev/null
		_time=1

		( fetch -r -o "${_lf}" "${_rf}" >/dev/null 2>/dev/null ; echo "$?" > ${_eFile} ) &
		FETCH_PID=`ps -auwwwx | grep -v grep | grep "fetch -r -o ${_lf}" | awk '{print $2}'`
		while : 
		do
			if [ -e "${_lf}" ] ; then
				_dSize=`du -k ${_lf} | tr -d '\t' | cut -d '/' -f 1`
				if [ $(is_num "$_dSize") ] ; then
					if [ ${_fSize} -lt ${_dSize} ] ; then _dSize="$_fSize" ; fi
					_kbs=`expr ${_dSize} \/ $_time`
					echo "SIZE: ${_fSize} DOWNLOADED: ${_dSize} SPEED: ${_kbs} KB/s"
				fi
			fi

			# Make sure download isn't finished
			ps -p $FETCH_PID >/dev/null 2>/dev/null
			if [ "$?" != "0" ] ; then break ; fi
			sleep 2
			_time=`expr $_time + 2`
		done

		_err="`cat ${_eFile}`"
		if [ "$_err" = "0" ]; then echo "FETCHDONE" ; fi
		unset FETCH_PID
	fi

	echo ""
	if [ $_err -ne 0 -a $_ftries -gt 0 ] ; then
		sleep 30
		_ftries=`expr $_ftries - 1`

		# Remove the local file if we failed
		if [ -e "${_lf}" ]; then rm "${_lf}"; fi

		get_file "${_rf}" "${_lf}" $_ftries	
		_err=$?
	fi
	return $_err
}

is_num()
{
        expr $1 + 1 2>/dev/null
        return $?
}

# Unset some vars
_chroot=""
_chrootcmd=""
_pkgflags=""
PKGUPDATES=""

# The default PKGSET
PKGSET="pcbsd"


# Check if we have a different dataset specified in pcbsd.conf
PCBSD_ETCCONF="/usr/local/etc/pcbsd.conf"
_pkgChk="`sed -n 's/PCBSD_METAPKGSET: //p' ${PCBSD_ETCCONF} 2>/dev/null`"
if [ -n "${_pkgChk}" ] ; then PKGSET="$_pkgChk" ; fi
MPDIR="${DBDIR}/${PKGSET}"
DEPDIR="${MPDIR}/pkg-deps"
TMPDIR="/usr/local/tmp"

if [ $# -eq 0 ]; then display_usage; fi

# Parse ye olde cli flags
while [ $# -gt 0 ]; do
  case "$1" in
      list) list_metapkgs ; exit 0 ;;
       add) parse_metapkgs "$2" "add" "$3" ; exit 0 ;;
       del) parse_metapkgs "$2" "del" ; exit 0 ;;
    status) stat_metapkg "$2" ; exit 0 ;;
   checkup) checkup_pkgs ; exit 0 ;;
    update) updatepkgs "${2}" "${3}" ; exit 0 ;;
  --chroot) if [ -z "$2" ] ; then display_usage ; fi  
	    _chroot="$2" 
            if [ ! -e "${_chroot}/usr/sbin/pkg_add" ] ; then
              echo "Invalid chroot dir: ${_chroot}"
              exit 1
	    fi
	    _pkgflags="-C ${_chroot}"
	    _chrootcmd="chroot ${_chroot}"
	    _chroottag="--chroot ${_chroot}"

	    # Check for an alternative default pkgset in this chroot
	    PCBSD_ETCCONF="${_chroot}/usr/local/etc/pcbsd.conf"
	    _pkgChk="`sed -n 's/PCBSD_METAPKGSET: //p' ${PCBSD_ETCCONF} 2>/dev/null`"
	    if [ -n "${_pkgChk}" ] ; then PKGSET="$_pkgChk" ; fi
            MPDIR="${DBDIR}/${PKGSET}"
	    DEPDIR="${MPDIR}/pkg-deps"
	    shift
            ;;
  --pkgset) if [ -z "$2" ] ; then display_usage ; fi  
            PKGSET="$2" 
            if [ ! -d "${DBDIR}/$PKGSET" ] ; then
              echo "No such package set $PKGSET"
              exit 1
            fi
            MPDIR="${DBDIR}/${PKGSET}"
	    DEPDIR="${MPDIR}/pkg-deps"
            shift ;;
         *) if [ -z "$1" ] ; then
	      shift
	    else
	      display_usage
	    fi 
	    ;;
  esac
  shift
done

exit 0

