#!/bin/sh
# minc-trapper : Mini-container dependency trapper 
#
# Copyright (C) 2015 Masami Hiramatsu <masami.hiramatsu@gmail.com>
# This program is released under the MIT License, see LICENSE.
#
# This requires strace to trace open files

LIBEXEC=/usr/libexec/mincs
MINCEXEC=$LIBEXEC/minc-exec
MINCCOAT=$LIBEXEC/minc-coat
MINCFARM=$LIBEXEC/minc-farm

set -e
test "$MINC_DEBUG" && set -x
set -u

usage() { # [error messages]
  test $# -ne 0 && echo "Error: $@"
  echo "minc-trapper: Mini container dependency trapper"
  echo "Usage: $0 [-r rootdir] <minc-tempdir> <command>"
  exit $#
}

test $# -lt 2 && usage "$0 requires at least 2 arguments."

MINC_BASEDIR=/
while [ "$1" ]; do
case $1 in
-h|--help)
  usage
  ;;
-r|--rootdir)
  MINC_BASEDIR=$2
  if [ ! -d $MINC_BASEDIR ]; then
    export MINC_BASEDIR=`$MINCFARM imagestack $MINC_BASEDIR` 
    test -z "$MINC_BASEDIR" && usage "$MINC_BASEDIR is not a directory"
  fi
  shift 2
  ;;
*)
  break;
  ;;
esac
done

MINC_TMPDIR=$1
TARGET=$MINC_TMPDIR/storage
shift 1
test -d "$MINC_TMPDIR" || usage "$MINC_TMPDIR is not a directory"
test -d "$TARGET" || usage "$MINC_TMPDIR is not a minc tempdir"

# Run once to trace open files
TRACEOUT=`mktemp /tmp/traceout-XXXXXXXX`
TRAPCMD="rm -f $TRACEOUT";trap "$TRAPCMD" EXIT
trap '' INT

export MINC_BASEDIR
export MINC_TMPDIR

export MINC_DEBUG_PREFIX="strace -f -q -e trace=file -o $TRACEOUT"
test "$MINC_IGNORE_STATE" -ne 0 && set +e
$MINCEXEC $@
test "$MINC_IGNORE_STATE" -ne 0 && set -e
trap - INT

getreqlibs(){ # binary
  objdump -p $i 2>/dev/null | awk '/NEEDED/{print $2}' | xargs
}

libpath() { # libname
  chroot $MINC_BASEDIR ldconfig -p | grep -m 1 $1 | cut -f 2 -d \>
}

fileexist() { # path
  test -f $1 || test -L $1
}

isabspath() { # file
  case "$1" in /*) return 0;; *) return 1;; esac
}

mincinst() { # libpath
  fileexist $TARGET/$1 && return 0
  dir=`dirname $1`
  mkdir -p $TARGET/$dir
  fileexist $MINC_TMPDIR/root/$1 || return 0
  echo "Install $1"
  cp -fP $MINC_TMPDIR/root/$1 $TARGET/$dir
  # symlink must be followed
  orig=`readlink $MINC_TMPDIR/root/$1` || return 0
  isabspath $orig || orig=$dir/$orig
  test "$orig" && mincinst $orig
  return 0
}

$MINCCOAT bind $MINC_TMPDIR $MINC_BASEDIR
TRAPCMD="$MINCCOAT unbind $MINC_TMPDIR;$TRAPCMD";trap "$TRAPCMD" EXIT

# Install all accessed files
for i in `grep \" $TRACEOUT | cut -f 2 -d \" | xargs`; do
  mincinst $i
done

# Make a dependency list in existing binaries.
LIBS=
for i in `find $TARGET -type f`; do
  LIBS="$LIBS "`getreqlibs $i`
done

# Loop until all dependency is solved.
while test "$LIBS"; do
_LIBS=
for i in $LIBS; do
  # Copy required libraries.
  p=`libpath $i`
  test "$p" || continue
  mincinst $p || continue
  _LIBS="$_LIBS "`getreqlibs $p`
done
LIBS=$_LIBS 
done

# Copy ELF loader
mincinst /lib64/ld-linux-x86-64.so.2

# Prepare system dirs
echo "Add primitive directories"
cd $TARGET/
mkdir -p tmp var/run

