#!/bin/sh
# polecat: Portable Containered Application build shell script
#
# Copyright (C) 2015 Masami Hiramatsu <masami.hiramatsu@gmail.com>
# This program is released under the MIT License, see LICENSE.

absdir() {
  (cd $1; pwd)
}

__LIBEXEC=`dirname $0`/libexec
LIBEXEC=/usr/libexec/mincs
MINCEXEC=$LIBEXEC/minc-exec
MINCCOAT=$LIBEXEC/minc-coat
TRAPPER=$LIBEXEC/minc-trapper
MINCFARM=$LIBEXEC/minc-farm
OUTFILE=polecat-out
FORMAT=sh

# Exit if any errors
set -e
set -u

usage() { # [error messages]
  test $# -ne 0 && echo "$*"
  echo "$0 - Build a self-executable containered application"
  echo "Usage: $0 [options] <rootdir> [command]"
  echo " options:"
  echo "    -h or --help           Show this help"
  echo "    -o or --output <FILE>  Output to FILE (default: $OUTFILE)"
  echo "    -s or --script <FILE>  Use FILE as build-script"
  echo "    -f or --format <FORMAT> Output format (tgz, squashfs(sfs), sh)"
  exit $#
}

export MINC_DEBUG_PREFIX=
export MINC_RWBIND=
export MINC_OPT_PTY=""
export MINC_CPUMASK=
export MINC_NETNS=
export MINC_DEBUG=
export MINC_BASEDIR=/
export MINC_USE_DEV=
export MINC_IGNORE_STATE=0
export MINC_PORT_MAP=
export MINC_ARCH=`uname -m`
export MINC_BACKGROUND=
export MINC_PIVOT=0
export MINC_SSHFS_HOST=
SCRIPT=
TMPDIR=

test $# -eq 0 && usage
while [ $# -ne 0 ]; do
case $1 in
-h|--help)
  usage
  ;;
-o|--output)
  OUTFILE=$2
  shift 2
  ;;
-s|--script)
  SCRIPT=$2
  shift 2
  ;;
-f|--format)
  FORMAT=$2
  case "$2" in sh|squashfs|sfs|tgz) ;;
  *) usage "$2 is not supported format " ;; esac
  shift 2
  ;;
-t|--tempdir)
  TMPDIR=$2
  shift 2
  ;;
--debug)
  set -x
  export MINC_DEBUG=1
  shift 1
  ;;
--ignore-state)
  export MINC_IGNORE_STATE=1
  shift 1
  ;;
*)
  break;
  ;;
esac
done

USE_FARM=no
#test -d "$1" || usage "$1 is not a directory"
if [ ! -d "$1" ]; then
  # ensure the given id image exists
  $MINCFARM pull $1 > /dev/null || \
    usage "Error: no such image: $MINC_BASEDIR"
  USE_FARM=image
fi
ROOTFS=$1
shift 1

:;: Preparing working dir ;:
if [ -d "$TMPDIR" ] ; then
  export MINC_TMPDIR=$TMPDIR
else
  export MINC_TMPDIR=`mktemp -d polecat-XXXXXXXX`
  MINC_TMPDIR=`(cd $MINC_TMPDIR; pwd)`
  trap "rm -rf $MINC_TMPDIR" EXIT
fi

# Working sub-directories
if [ $USE_FARM = "image" ]; then
  export MINC_BASEDIR=`$MINCFARM imagestack $ROOTFS`
else
  export MINC_BASEDIR=$ROOTFS
fi
$MINCCOAT bind $MINC_TMPDIR $MINC_BASEDIR
RD=$MINC_TMPDIR/root

# At first, install minc libs
mkdir -p $RD/opt/libexec
cp $LIBEXEC/minc* $RD/opt/libexec/

# Scripting mode
if [ -f "$SCRIPT" ]; then
  MKDIRS=
  RMDIRS=
  COMMAND=
  . $SCRIPT || usage "Importing error: $SCRIPT"
  cp $SCRIPT $RD/opt/libexec/
  # Build install script
  cat > $RD/install.sh << EOF
#!/bin/sh
set -e
. /opt/libexec/`basename $SCRIPT`
install_command
EOF
  chmod a+x $RD/*.sh
  trap "" INT
  $MINCCOAT unbind $MINC_TMPDIR

  # Install phase
  echo "--- Installation stage ---"
  export MINC_TMPDIR
  $MINCEXEC /install.sh

  # Testing phase
  trap '' TERM
  echo "--- Testing stage (Press ^C after test run) ---"
  $TRAPPER -r $ROOTFS $MINC_TMPDIR $COMMAND

  # Note that this could cause directory traversal. Do not shoot your foot.
  echo "--- Preparing files ---"
  UD=$MINC_TMPDIR/storage
  [ "$MKDIRS" ] && for i in $MKDIRS; do mkdir -p $UD/$i; done
  [ "$RMDIRS" ] && for i in $RMDIRS; do rm -rf $UD/$i; done
  rm -f $UD/install.sh
  trap - INT

  # Now we can directly use upper directory as root directory.
  RD=$UD
else
  $MINCCOAT unbind $MINC_TMPDIR
  # Simple command execution
  COMMAND="$@"
  $TRAPPER -r $ROOTFS $MINC_TMPDIR $COMMAND
  RD=$MINC_TMPDIR/storage
fi

:;: "Adding some required files" ;:
touch $RD/etc/hosts
touch $RD/etc/resolv.conf

OUTDIR=`dirname $OUTFILE`
mkdir -p $OUTDIR
OUTDIR=`absdir $OUTDIR`
OUTFILE=`basename $OUTFILE`

:;: "Output files in specified format" ;:
case $FORMAT in
  tgz)
  cd $RD;
  tar czf ${OUTDIR}/${OUTFILE}.${FORMAT} .
  exit 0
  ;;
  squashfs|sfs)
  mksquashfs $RD ${OUTDIR}/${OUTFILE}.${FORMAT}
  exit 0
  ;;
esac

# Make a squashfs image
SFS=$MINC_TMPDIR/rootfs.sfs
mksquashfs $RD $SFS

SIZE=`stat --format=%s $SFS`
cat > $MINC_TMPDIR/polecat.sh << EOF
#!/bin/sh
set -e
SELF=\$0
SIZE=$SIZE
TOTAL=\`stat --format=%s \$SELF\`
OFFS=\$((TOTAL - SIZE))
PLCDIR=\`mktemp -d /tmp/polecat-run-XXXXXXXX\`
TRAPCMD="rm -rf \$PLCDIR"; trap "\$TRAPCMD" EXIT
export MINC_DEBUG_PREFIX=
export MINC_RWBIND=
export MINC_OPT_PTY=""
export MINC_CPUMASK=
export MINC_NETNS=
export MINC_PIVOT=0
[ "\$MINC_DEBUG" ] || export MINC_DEBUG=
export MINC_USE_DEV=
export MINC_BASEDIR=\$PLCDIR/rootfs
export MINC_TMPDIR=\$PLCDIR/tmp
mkdir \$MINC_BASEDIR \$MINC_TMPDIR
mount -t squashfs -o ro,loop,offset=\$OFFS \$SELF \$MINC_BASEDIR
TRAPCMD="umount \$MINC_BASEDIR;\$TRAPCMD"; trap "\$TRAPCMD" EXIT
sh \$MINC_BASEDIR/opt/libexec/minc-coat bind \$MINC_TMPDIR \$MINC_BASEDIR
TRAPCMD="sh \$MINC_BASEDIR/opt/libexec/minc-coat unbind \$MINC_TMPDIR;\$TRAPCMD"; trap "\$TRAPCMD" EXIT
trap '' INT
export MINC_DIRECT=1
sh \$MINC_BASEDIR/opt/libexec/minc-exec $COMMAND
exit \$?
EOF

cat $MINC_TMPDIR/polecat.sh $SFS > $OUTDIR/$OUTFILE
chmod a+x $OUTDIR/$OUTFILE

echo -n "done:" ; ls -sh $OUTDIR/$OUTFILE

exit 0
