#!/bin/sh

LIBEXEC="$(cd "$(dirname "$0")" && pwd)"
. "${LIBEXEC}/base.sh"

# shellcheck disable=SC2034
usage=$(cat <<EOF
Inject files from the host into an image directory.

Usage:

  $ ch-fromhost [OPTION ...] (-c CMD | -f FILE | --nvidia ...) IMGDIR

Which files (one or more required; can be repeated):

  -c, --cmd CMD    listed in the stdout of CMD
  -f, --file FILE  listed in file FILE
      --nvidia     recommended by nVidia (via "nvidia-container-cli list")

Options:

  -d, --dest DST   files whose destination can't be inferred go in IMGDIR/DST
  -h, --help       print this help and exit
      --no-infer   don't infer destination for shared libraries and executables
  -v, --verbose    list the injected files
      --version    print version and exit
EOF
)

DEST_DEFAULT=
IMAGE=
INFER=yes
NEWLINE='
'
FOUND_FILES=
FOUND_LIBS_P=
LIB_DEST=

debug () {
    if [ "$VERBOSE" ]; then
        printf '%s\n' "$1" 1>&2
    fi
}

ensure_nonempty () {
    [ "$2" ] || fatal "$1 must not be empty"
}

fatal () {
    printf 'ch-fromhost: %s\n' "$1" 1>&2
    exit 1
}

info () {
    printf 'ch-fromhost: %s\n' "$1" 1>&2
}

parse_basic_args "$@"

while [ $# -gt 0 ]; do
    OPT="$1"; shift
    OUT=
    case $OPT in
        -c|--cmd)
            ensure_nonempty --cmd "$1"
            OUT=$($1) || fatal "command failed: $1"
            shift
            ;;
        -d|--dest)
            ensure_nonempty --dest "$1"
            DEST_DEFAULT="$1"
            shift
            ;;
        -f|--file)
            ensure_nonempty --file "$1"
            OUT=$(cat "$1") || fatal "cannot read file: $1"
            shift
            ;;
        --no-infer)
            INFER=
            ;;
        --nvidia)
               OUT=$(nvidia-container-cli list --binaries --libraries) \
            || fatal "nvidia-container-cli failed; does this host have GPUs?"
            ;;
        -v|--verbose)
            VERBOSE=yes
            ;;
        -*)
            info "invalid option: $OPT"
            usage
            ;;
        *)
            ensure_nonempty "image path" "$OPT"
            [ -z "$IMAGE" ] || fatal "duplicate image path: $OPT"
            [ -d "$OPT" ] || fatal "image not a directory: $OPT"
            IMAGE="$OPT"
            ;;
    esac
    # This adds a delimiter newline only for the second and subsequent files.
    # See: https://chris-lamb.co.uk/posts/joining-strings-in-posix-shell
    FOUND_FILES="${FOUND_FILES:+$FOUND_FILES$NEWLINE}$OUT"
done

debug "injecting into image: $IMAGE"

# First pass tests if we have any shared libraries in the list. If so, we need
# to figure out where to put them.
if [ $INFER ]; then
    debug "checking for shared libraries"
    for FILE in $FOUND_FILES; do
        case $FILE in
            */lib*)
                FOUND_LIBS_P=yes
                break
                ;;
        esac
    done
    if [ $FOUND_LIBS_P ]; then
        # We want to put the libraries in the first directory that ldconfig
        # searches, so that we can override (or overwrite) any of the same
        # library that may already be in the image.
        debug "asking ldconfig for shared library path"
        "$CH_BIN"/ch-run -w "$IMAGE" -- /sbin/ldconfig  # cache maybe absent
        LIB_DEST=$(  "$CH_BIN"/ch-run "$IMAGE" -- \
                                      /sbin/ldconfig -v 2> /dev/null \
                   | grep -E '^/' | cut -d: -f1 | head -1)
        [ -z "${LIB_DEST%%/*}" ] || fatal "bad path from ldconfig: $LIB_DEST"
        debug "shared library destination: $LIB_DEST"
    else
        debug "no shared libraries found"
    fi
fi

debug "injecting"
OLD_IFS="$IFS"
IFS="$NEWLINE"
FOUND_FILE_P=
for FILE in $FOUND_FILES; do
    FOUND_FILE_P=yes
    TYPE_=unk
    DEST="$DEST_DEFAULT"
    if [ $INFER ]; then
        case $FILE in
            */bin*)
                TYPE_=bin
                DEST=/usr/bin
                ;;
            */lib*)
                TYPE_=lib
                DEST="$LIB_DEST"
                ;;
        esac
    fi
    debug "  $TYPE_: $FILE -> $DEST"
    [ "$DEST" ] || fatal "no destination for: $FILE"
    [ -z "${DEST%%/*}" ] || fatal "not an absolute path: $DEST"
    [ -d "$IMAGE$DEST" ] || fatal "not a directory: $IMAGE$DEST"
       cp --dereference --preserve=all "$FILE" "$IMAGE/$DEST" \
    || fatal "cannot inject: $FILE"
done
IFS="$OLD_IFS"

[ -z $FOUND_FILE_P ] && fatal "empty file list"

if [ $FOUND_LIBS_P ] && [ $INFER ]; then
    debug "found shared library, running ldconfig"
    "$CH_BIN"/ch-run -w "$IMAGE" -- /sbin/ldconfig
else
    debug "no shared libraries found"
fi

