#!/bin/sh
#  minc-farm : MINC file archives management module
#
# Copyright (C) 2015 Masami Hiramatsu <masami.hiramatsu@gmail.com>
# This program is released under the MIT License, see LICENSE.

set -e
[ "$MINC_DEBUG" ] && set -x

LIBEXEC=/usr/libexec/mincs
MINCCOAT=$LIBEXEC/minc-coat

# Set up parameters
MINCS_DIR=${MINCS_DIR:-/var/lib/mincs}
CONFIG=/etc/mincs.conf
if [ -f $CONFIG ]; then
  . $CONFIG
fi
MINCS_CONTAINERS=$MINCS_DIR/containers
MINCS_IMAGES=$MINCS_DIR/images
mkdir -p $MINCS_CONTAINERS $MINCS_IMAGES

error() {
  echo "Error: $*"
  exit 1
}

# UUID/storage  <- contents of this layer
# UUID/baseid   <- the uuid on which this layer depends
# UUID/root   <- mount point of fabricated rootfs
# UUID/umount   <- umount command

fabricate() { # image-id
  test "$1"
  test -d $MINCS_IMAGES/$1/root
  echo -n $MINCS_IMAGES/$1/root
  if [ -f $MINCS_IMAGES/$1/baseid ]; then
    BASEID=`cat $MINCS_IMAGES/$1/baseid`
    echo -n ":"`fabricate $BASEID`
  fi
  echo
}

# Import a rootdir to the image directory
import() { # rootdir
  test -d "$1"
  if [ "$MINC_IMPORT_UUID" ] ;then
    UUID=$MINC_IMPORT_UUID
  else
    UUID=`tar -c $1 2>/dev/null  | sha256sum | cut -f 1 -d " "`
  fi
  BASEDIR=$MINCS_IMAGES/$UUID
  if [ -d $BASEDIR ] ;then
    echo "$1 ($UUID) is already imported." 1>&2
    exit 0;
  fi
  mkdir -p $BASEDIR
  cp -a $1 $BASEDIR/root
  (cd $BASEDIR; ln -s root storage)
  test "$MINC_BASE_UUID" && echo $MINC_BASE_UUID > $BASEDIR/baseid
  test "$MINC_IMPORT_NAME" && echo $MINC_IMPORT_NAME > $BASEDIR/name
  echo $UUID
}

# Delivering new container directory
deliver() { # base-uuid
  test "$1"
  BASEID=$1
  UUID=`head -c 16K /dev/urandom | sha256sum | cut -f 1 -d " " `
  TMPDIR=$MINCS_CONTAINERS/$UUID
  mkdir -p $TMPDIR/root
  echo $BASEID > $TMPDIR/baseid
  echo $UUID
}

unbind() { # uuid
  test "$1"
  UUID=$1
  TMPDIR=$MINCS_CONTAINERS/$UUID
  $MINCCOAT unbind $TMPDIR
}

# Remove a container
remove() { # uuid
  test "$1"
  TMPDIR=$MINCS_CONTAINERS/$1
  test -d $TMPDIR
  if [ -f $TMPDIR/umount ]; then
    `cat $TMPDIR/umount`
  fi
  rm -rf $TMPDIR
}

find_depends() { # dir uuid
  for i in $1/* ; do
    if [ ! -f $i/baseid ] ; then continue; fi
    if [ "$2" = `cat $i/baseid` ] ; then basename $i; fi
  done
}

# Remove an image
remove_image() { # uuid
  test "$1"
  test -d "$MINCS_IMAGES/$1"
  DEP_IMGS=`find_depends $MINCS_IMAGES $1`
  DEP_CNTS=`find_depends $MINCS_CONTAINERS $1`
  # TBD: remove depends too if forced
  [ "$DEP_CNTS" ]  && error "following containers depends on $1\n $DEP_CNTS"
  [ "$DEP_IMGS" ]  && error "following images depends on $1\n $DEP_IMGS"
  # This must fail if someone use it
  $MINCCOAT unbind $MINCS_IMAGES/$1 || error "$1 is currently using"
  rm -rf $MINCS_IMAGES/$1
}

# Commit a container as an image
commit() { # uuid
  test "$1"
  test -d $MINCS_CONTAINERS/$1/storage
  test -z "$MINC_IMPORT_NAME" && test -f $MINCS_CONTAINERS/$1/utsname &&\
    MINC_IMPORT_NAME=`cat $MINCS_CONTAINERS/$1/utsname`
  MINC_BASE_UUID=`cat $MINCS_CONTAINERS/$1/baseid`
  import $MINCS_CONTAINERS/$1/storage
}

pull_image() { # UUID
  test "$1"
  BASEDIR=$MINCS_IMAGES/$1
  test -d $BASEDIR
}

LONG=""
WINDOW="1-12"
if [ "$MINC_LONG_ID" ]; then
  LONG="							"
  WINDOW="1-64"
fi

list_containers() {
  echo "ID	$LONG	PID	NAME"
  cd $MINCS_CONTAINERS
  for i in */ ; do
    test $i = '*/' && break
    ID=`echo $i | cut -b $WINDOW`
    NAME="<noname>"
    PID=""
    test -f $i/utsname && NAME=`cat $i/utsname`
    test -f $i/pid && PID=`cat $i/pid`
    echo "$ID	$PID	$NAME"
  done
}

list_images() {
  echo "ID	$LONG	SIZE	NAME"
  cd $MINCS_IMAGES
  for i in */; do
    test $i = '*/' && break
    ID=`echo $i | cut -b $WINDOW`
    NAME="(noname)"
    test -f $i/name && NAME=`cat $i/name`
    SIZE=`du -sh $i/root | (read i j; echo $i)`
    echo "$ID	$SIZE	$NAME"
  done
}

check_dir() { # dirname UUID
  test "$2"
  test -d $1/$2
  echo $1/$2
}

c2uuid() { # container-id
  test "$1"
  cd $MINCS_CONTAINERS/
  for i in *; do
    if test -f $i/utsname && test `cat $i/utsname` = "$1"; then
      echo $i; return 0
    fi
  done
  ls $MINCS_CONTAINERS/ | grep -m 1 ^"$1"
}

i2uuid() { # image-id
  test "$1"
  cd $MINCS_IMAGES/
  for i in *; do
    if test -f $i/name && test `cat $i/name` = "$1"; then
      echo $i; return 0
    fi
  done
  ls $MINCS_IMAGES/ | grep -m 1 ^"$1"
}

test $# -eq 0 && usage
cmd=$1
shift 1
case $cmd in
  lc|list)
  list_containers
  ;;
  li|images)
  list_images
  ;;
  import) # DIR
  import $1
  ;;
  commit) # COID
  UUID=`c2uuid $1`
  commit $UUID
  ;;
  fork) # IMID
  UUID=`i2uuid $1`
  deliver $UUID
  ;;
  bind) # COID
  UUID=`c2uuid $1`
  DIR=`check_dir $MINCS_CONTAINERS $UUID`
  BASEID=`cat $DIR/baseid`
  LOWERS=`fabricate $BASEID`
  $MINCCOAT bind $DIR/root $LOWERS
  echo $UUID
  ;;
  unbind) # COID
  UUID=`c2uuid $1`
  unbind $UUID
  ;;
  rm) # COID
  UUID=`c2uuid $1`
  remove $UUID
  ;;
  rmi) # IMID
  UUID=`i2uuid $1`
  remove_image $UUID
  ;;
  pull) # IMID
  UUID=`i2uuid $1`
  pull_image $UUID
  echo $UUID
  ;;
  dir) # COID
  check_dir $MINCS_CONTAINERS `c2uuid $1`
  ;;
  idir) # IMID
  check_dir $MINCS_IMAGES `i2uuid $1`
  ;;
  imagestack) # IMID
  fabricate `i2uuid $1`
  ;;
  baseid) # COID
  UUID=`c2uuid $1`
  DIR=`check_dir $MINCS_CONTAINERS $UUID`
  cat $DIR/baseid
  ;;
  *)
  exit 0
esac

