#!/bin/ksh
##
## Bootstrap a basic RPM environment for OSSTech software products
## Copyright (c) OSS Technology, Inc. <http://www.osstech.co.jp/>
## Copyright (c) SATOH Fumiyasu @ OSS Technology, Inc.
##
## License: GNU General Public License version 2 or later
## Date: 2010-04-29, since 2008-06-10
##

set -u
umask 0022

LC_ALL="C"
export LC_ALL

uname=$(uname)

PATH="/opt/osstech/bin:/bin:/usr/bin:/sbin:/usr/sbin"
case "$uname" in
SunOS)
  PATH="/usr/sfw/bin:/usr/xpg6/bin:/usr/xpg4/bin:/usr/ccs/bin:$PATH"
  arch=$(uname -p)
  ;;
AIX)
  PATH="/usr/sysv/bin:/usr/ccs/bin:$PATH"
  arch=$(uname -p)
  ;;
Linux)
  if [ -f /etc/debian_version ]; then
    arch=$(uname -m)
  fi
  ;;
esac
export PATH

pinfo()
{
  { tput bold; tput smul; } 2>/dev/null || :
  echo "${0##*/}: INFO: $*"
  tput sgr0 2>/dev/null || :
}

pwarn()
{
  echo "${0##*/}: WARNING: $*" 1>&2
}

perr()
{
  echo "${0##*/}: ERROR: $*" 1>&2
}

pdie()
{
  perr "$*"
  exit 1
}

chroot()
{
  if [ X"$1" = X"/" ]; then
    shift
  else
    set -- command chroot ${1+"$@"}
  fi

  "$@"
  return $?
}

uid_of_user()
{
  /usr/bin/id ${1+"$1"} |/bin/sed -n 's/^[^=]*=\([0-9]*\).*$/\1/p'
}

quote_meta()
{
  sed 's/\([^A-Za-z_0-9]\)/\\\1/g'
}

## This function is ported from rpm2cpio.sh in RPM 4.4.2.3
rpm2cpio()
{
  typeset pkg="$1"
  if [ "$pkg" = "" -o ! -e "$pkg" ]; then
    pdie "No package supplied"
  fi

  typeset leadsize o il dl sigsize hdrsize

  leadsize=96
  o=`expr $leadsize + 8`
  set `od -j $o -N 8 -t u1 "$pkg"`
  il=`expr 256 \* \( 256 \* \( 256 \* $2 + $3 \) + $4 \) + $5`
  dl=`expr 256 \* \( 256 \* \( 256 \* $6 + $7 \) + $8 \) + $9`
  # echo "sig il: $il dl: $dl"

  sigsize=`expr 8 + 16 \* $il + $dl`
  o=`expr $o + $sigsize + \( 8 - \( $sigsize \% 8 \) \) \% 8 + 8`
  set `od -j $o -N 8 -t u1 "$pkg"`
  il=`expr 256 \* \( 256 \* \( 256 \* $2 + $3 \) + $4 \) + $5`
  dl=`expr 256 \* \( 256 \* \( 256 \* $6 + $7 \) + $8 \) + $9`
  # echo "hdr il: $il dl: $dl"

  hdrsize=`expr 8 + 16 \* $il + $dl`
  o=`expr $o + $hdrsize`

  dd if="$pkg" ibs="$o" skip=1 2>/dev/null |gunzip
}

## DEBUG: If the -x option is set, enable trace flag on all functions
[ X"${-#*x}" != X"$-" ] && typeset -ft $(typeset +f)

## Options
## ======================================================================

vendor_nickname="OSSTech"
vendor_id="osstech"
vendor_name_prefix="$vendor_id-"

rpm_basenames_noarch="base"
rpm_basenames_arch="rpm rpm-build"
case "$uname" in
SunOS)
  rpm_basenames_arch="$rpm_basenames_arch rpm-libs"
  ;;
AIX)
  rpm_basenames_arch="$rpm_basenames_arch rpm-libs popt zlib zsh"
  ;;
Linux)
  rpm_basenames_arch="$rpm_basenames_arch rpm-libs"
  ;;
esac

root="/"
ignore_if_bootstrapped=""

cmd_usage="Usage: $0 [OPTIONS] RPMS_DIR

Options and Arguments:
 --root ROOTDIR
    Target root filesystem (default: $root)
 --ignore-if-bootstrapped
    Warn and exit with status 0 instead of 1
    if the environment was already bootstrapped.
 RPMS_DIR
    Directory that contains $vendor_nickname RPM packages for
    bootstrapping. The following RPM packages are required:"

rpm_basenames_noarch=$(echo " $rpm_basenames_noarch" |sed 's/ / '"$vendor_name_prefix"'/g')
for rpm_basename in $rpm_basenames_noarch; do
  cmd_usage="$cmd_usage
        $rpm_basename-X.X.X-X.noarch.rpm"
done
rpm_basenames_arch=$(echo " $rpm_basenames_arch" |sed 's/ / '"$vendor_name_prefix"'/g')
for rpm_basename in $rpm_basenames_arch; do
  cmd_usage="$cmd_usage
        $rpm_basename-X.X.X-X.$arch.rpm"
done
rpm_basenames="$rpm_basenames_noarch $rpm_basenames_arch"

## ======================================================================

getopts_want_arg()
{
  if [ $# -lt 2 ]; then
    pdie "Option requires an argument: $1"
  fi
  if [ $# -ge 3 ]; then
    if expr X"$2" : X"$3\$" >/dev/null; then
      : OK
    else
      pdie "Invalid value for option : $1 $2"
    fi
  fi
}

while test "$#" -gt 0; do
  OPT="$1"; shift
  case "$OPT" in
  -r|--root)
    getopts_want_arg "$OPT" ${1+"$1"} ${1+"/.*"}
    root="$1"; shift
    [ -d "$root" ] || pdie "Root directory not found: $root"
    ;;
  --ignore-if-bootstrapped)
    ignore_if_bootstrapped="yes"
    ;;
  --)
    break
    ;;
  -*)
    pdie "Invalid option: $OPT"
    exit 1
    ;;
  *)
    set -- "$OPT" ${1+"$@"}
    break
    ;;
  esac
done

if [ $# -ne 1 ]; then
  echo "$cmd_usage"
  exit 1
fi

rpms_dir="$1"; shift

prefix="${root%/}/opt/$vendor_id"
bindir="$prefix/bin"
datadir="$prefix/share"
localstatedir="$prefix/var"
localstatedir_new="${root%/}/var/opt/$vendor_id"

base_pkg_name="${vendor_name_prefix}base"
base_os_pkg_name="${vendor_name_prefix}base-os"
base_os_spec="$datadir/base/base-os.spec"

## Check environment
## ======================================================================

if [ X"`uid_of_user`" != X"0" ]; then
  pdie "You must be root to bootstrap"
fi

if chroot "$root" "$bindir/rpm" -qi $rpm_basenames >/dev/null 2>&1; then
  msg="This environment was already bootstrapped"
  if [ -n "$ignore_if_bootstrapped" ]; then
    pwarn "$msg"
    pinfo "Done."
    exit 0
  else
    pdie "$msg"
  fi
fi

shift $#
for rpm_basename in $rpm_basenames_noarch; do
  rpm=$(ls "$rpms_dir/$rpm_basename"-[0-9]*-[0-9]*."noarch.rpm" 2>/dev/null)
  [ -f "$rpm" ] || pdie "Required RPM package not found: $rpm_basename-*.noarch.rpm"
  set -- ${1+"$@"} "$rpm"
done
for rpm_basename in $rpm_basenames_arch; do
  rpm=$(ls "$rpms_dir/$rpm_basename"-[0-9]*-[0-9]*."$arch.rpm" 2>/dev/null)
  [ -f "$rpm" ] || pdie "Required RPM package not found: $rpm_basename-*.$arch.rpm"
  set -- ${1+"$@"} "$rpm"
done

## Migration: Local state directory
## ======================================================================

if [ -d "$localstatedir" ] && [ ! -h "$localstatedir" ]; then
  if [ X"$root" != X"/" ]; then
    ## FIXME
    pdie "Migration with --root ROOTDIR option is not supported"
  fi

  pinfo "Old local state directory found: $localstatedir"
  pinfo "Preparing to migrate ..."

  if [ -d "$localstatedir_new" ]; then
    pdie "New local state directory already exist: $localstatedir_new"
  fi

  localstatedir_re=$(echo "$localstatedir" |quote_meta)
  localstatedir_fs=$(df "$localstatedir" |sed 's/ .*//')
  (fuser -c "$localstatedir_fs"; echo) 2>/dev/null \
  |sed 's/^ *//;s/  */ /g' \
  |tr ' ' '\n' \
  |xargs pfiles 2>/dev/null \
  |nawk '
    $0 ~ /^[1-9][0-9]*:/ {
      pid=$1
      sub(/:/, "", pid)
    }
    $0 ~ /'"$localstatedir_re"'/ {
      print pid " " $0
    }
  ' \
  |egrep " +$localstatedir_re(/.*)*\$" \
  |sed 's/ .*//' \
  |sort -nu \
  |(
    file_opened=0
    while read pid; do
      if [ "$file_opened" -eq 0 ]; then
        perr "Old local state directory is used by the following process(es)"
      fi
      file_opened="1"
      echo -n "  PID $pid: "
      ps -p "$pid" -o args |tail -n +2
    done
    exit "$file_opened"
  )
  [ $? -ne 0 ] && exit 1

  pinfo "Migrating local state directory: $localstatedir_new ..."
  (
    find "$localstatedir" -type s |xargs rm -f
    mkdir -p "$localstatedir_new"
    mv "$localstatedir"/{.??*,*} "$localstatedir_new/"
    rmdir "$localstatedir" \
  ) || pdie "Migrating local state directory failed"
fi

## Installation
## ======================================================================

pinfo "Extracting RPM packages by rpm2cpio and cpio ..."

if [ X"$uname" = X"AIX" ]; then
  echo "NOTE: You can ignore warning message \"chown() failed ...\" from cpio."
fi

for rpm in "$@"; do
  echo "${rpm##*/}"
  rpm2cpio "$rpm" |(cd "$root" && cpio -idu)
  if [ $? -ne 0 ]; then
    pdie "Extracting $rpm failed"
  fi
done

## ----------------------------------------------------------------------

pinfo "Building $base_os_pkg_name package ..."

if ! rpmbuild_tempdir=$(mktemp -d /tmp/${0##*/}.tmp.XXXXXX); then
  pdie "Cannot create a temporary directory";
fi
trap 'rm -rf "$rpmbuild_tempdir"' 0 INT

echo "%_topdir $rpmbuild_tempdir" >"$rpmbuild_tempdir/.rpmmacros.$vendor_id"
mkdir "$rpmbuild_tempdir/BUILD" "$rpmbuild_tempdir/RPMS"

HOME="$rpmbuild_tempdir" \
  "$bindir/$vendor_id-rpmbuild" \
    -bb \
    "$base_os_spec" \
|| exit 1

base_os_rpm=$(echo "$rpmbuild_tempdir/RPMS"/*/"$base_os_pkg_name"-*.rpm)

#fixme
cp -p "$base_os_rpm" /tmp

## ----------------------------------------------------------------------

pinfo "Initializing RPM database ..."

chroot "$root" "$bindir/rpm" --initdb
if [ $? -ne 0 ]; then
  pdie "Initializing RPM database failed"
fi

## ----------------------------------------------------------------------

if [ X"$root" != X"/" ]; then
  rpms_tempdir=$(mktemp -d "$root/tmp/${0##*/}.tmp.XXXXXX") || exit 1
  cp -p "$rpms_dir"/*.rpm "$base_os_rpm" "$rpm_tmp/" || exit 1
  shift $#
  for rpm in "$rpms_tempdir"/*.rpm; do
    set -- ${1+"$@"} "${rpm#$root}"
  done
else
  set -- "$rpms_dir"/*.rpm "$base_os_rpm"
fi

pinfo "Installing RPM packages by rpm ..."
chroot "$root" "$bindir/rpm" -Uv "$@"
if [ $? -ne 0 ]; then
  pdie "Installing RPM packages failed"
fi

if [ X"$root" != X"/" ]; then
  rm "$rpms_tempdir"
fi

## ----------------------------------------------------------------------

pinfo "Done."

