#!/bin/sh

# Detect use under userns (unsupported)
for arg in "$@"; do
    [ "$arg" = "--" ] && break
    if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then
        echo "This template can't be used for unprivileged containers." 1>&2
        echo "You may want to try the \"download\" template instead." 1>&2
        exit 1
    fi
done

# Make sure the usual locations are in PATH
PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin
export PATH

key_sha256sums="9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4  alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub
2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab  alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub
ebf31683b56410ecc4c00acd9f6e2839e237a3b62b5ae7ef686705c7ba0396a9  alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub
1bb2a846c0ea4ca9d0e7862f970863857fc33c32f5506098c636a62a726a847b  alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub
12f899e55a7691225603d6fb3324940fc51cd7f133e7ead788663c2b7eecb00c  alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub"


get_static_apk () {
    wget="wget -q -O -"
    pkglist=alpine-keys:apk-tools-static
    auto_repo_dir=

    if [ -z "$repository" ]; then
        url=http://wiki.alpinelinux.org/cgi-bin/dl.cgi
	yaml_path="latest-stable/releases/$apk_arch/latest-releases.yaml"
        if [ -z "$release" ]; then
            echo -n "Determining the latest release... "
            release=$($wget $url/$yaml_path | \
	        awk '$1 == "branch:" {print $2; exit 0}')
            if [ -z "$release" ]; then
                release=$($wget $url/.latest.$apk_arch.txt | \
                    cut -d " " -f 3 | cut -d / -f 1 | uniq)
            fi
            if [ -z "$release" ]; then
                echo failed
                return 1
            fi
            echo $release
        fi
        auto_repo_dir=$release/main
        repository=$url/$auto_repo_dir
        pkglist=$pkglist:alpine-mirrors
    fi

    rootfs="$1"
    echo "Using static apk from $repository/$apk_arch"
    wget="$wget $repository/$apk_arch"

    # parse APKINDEX to find the current versions
    static_pkgs=$($wget/APKINDEX.tar.gz | \
        tar -Oxz APKINDEX | \
        awk -F: -v pkglist=$pkglist '
            BEGIN { split(pkglist,pkg) }
            $0 != "" { f[$1] = $2 }
            $0 == "" { for (i in pkg)
                           if (pkg[i] == f["P"])
                               print(f["P"] "-" f["V"] ".apk") }')
    [ "$static_pkgs" ] || return 1

    mkdir -p "$rootfs" || return 1
    for pkg in $static_pkgs; do
        echo "Downloading $pkg"
        $wget/$pkg | tar -xz -C "$rootfs"
    done

    # clean up .apk meta files
    rm -f "$rootfs"/.[A-Z]*

    # verify checksum of the key
    keyname=$(echo $rootfs/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//')
    checksum=$(echo "$key_sha256sums" |  grep -w "$keyname")
    if [ -z "$checksum" ]; then
        echo "ERROR: checksum is missing for $keyname"
        return 1
    fi
    (cd $rootfs/etc/apk/keys && echo "$checksum" | sha256sum -c -) || return 1

    # verify the static apk binary signature
    APK=$rootfs/sbin/apk.static
    openssl dgst -sha1 -verify $rootfs/etc/apk/keys/$keyname \
        -signature "$APK.SIGN.RSA.$keyname" "$APK" || return 1

    if [ "$auto_repo_dir" ]; then
        mirror_list=$rootfs/usr/share/alpine-mirrors/MIRRORS.txt
        mirror_count=$(wc -l $mirror_list | cut -d " " -f 1)
        random=$(hexdump -n 2 -e '/2 "%u"' /dev/urandom)
        repository=$(sed $(expr $random % $mirror_count + 1)\!d \
            $mirror_list)$auto_repo_dir
        echo "Selecting mirror $repository"
    fi
}

install_alpine() {
    rootfs="$1"
    shift
    mkdir -p "$rootfs"/etc/apk || return 1
    : ${keys_dir:=/etc/apk/keys}
    if ! [ -d "$rootfs"/etc/apk/keys ] && [ -d "$keys_dir" ]; then
        cp -r "$keys_dir" "$rootfs"/etc/apk/keys
    fi
    if [ -n "$repository" ]; then
        echo "$repository" > "$rootfs"/etc/apk/repositories
    else
        cp /etc/apk/repositories "$rootfs"/etc/apk/repositories || return 1
        if [ -n "$release" ]; then
            sed -E -i "s:/[^/]+/([^/]+)$:/$release/\\1:" \
                "$rootfs"/etc/apk/repositories
        fi
    fi
    opt_arch=
    if [ -n "$apk_arch" ]; then
        opt_arch="--arch $apk_arch"
    fi
    $APK add -U --initdb --root $rootfs $opt_arch "$@" alpine-base
}

configure_alpine() {
    rootfs="$1"
    echo "Setting up /etc/inittab"
    cat >"$rootfs"/etc/inittab<<EOF
::sysinit:/sbin/rc sysinit
::wait:/sbin/rc default
console:12345:respawn:/sbin/getty 38400 console
tty1:12345:respawn:/sbin/getty 38400 tty1
tty2:12345:respawn:/sbin/getty 38400 tty2
tty3:12345:respawn:/sbin/getty 38400 tty3
tty4:12345:respawn:/sbin/getty 38400 tty4
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/rc shutdown
EOF
    # set up timezone
    if [ -f /etc/TZ ]; then
        cp /etc/TZ "$rootfs/etc/TZ"
    fi

    # set up nameserver
    grep nameserver /etc/resolv.conf > "$rootfs/etc/resolv.conf"

    # configure the network using the dhcp
    cat <<EOF > $rootfs/etc/network/interfaces
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
EOF

    # set the hostname
    echo $hostname > $rootfs/etc/hostname

    # missing device nodes
    echo "Setting up device nodes"
    mkdir -p -m 755 "$rootfs/dev/pts"
    mkdir -p -m 1777 "$rootfs/dev/shm"
    mknod -m 666 "$rootfs/dev/zero" c 1 5
    mknod -m 666 "$rootfs/dev/full" c 1 7
    mknod -m 666 "$rootfs/dev/random" c 1 8
    mknod -m 666 "$rootfs/dev/urandom" c 1 9
    mknod -m 666 "$rootfs/dev/tty0" c 4 0
    mknod -m 666 "$rootfs/dev/tty1" c 4 1
    mknod -m 666 "$rootfs/dev/tty2" c 4 2
    mknod -m 666 "$rootfs/dev/tty3" c 4 3
    mknod -m 666 "$rootfs/dev/tty4" c 4 4
#    mknod -m 600 "$rootfs/dev/initctl" p
    mknod -m 666 "$rootfs/dev/tty" c 5 0
    mknod -m 666 "$rootfs/dev/console" c 5 1
    mknod -m 666 "$rootfs/dev/ptmx" c 5 2

    # start services
    ln -s /etc/init.d/bootmisc "$rootfs"/etc/runlevels/boot/bootmisc
    ln -s /etc/init.d/syslog "$rootfs"/etc/runlevels/boot/syslog

    return 0
}

copy_configuration() {
    path=$1
    rootfs=$2
    hostname=$3

    grep -q "^lxc.rootfs" $path/config 2>/dev/null \
        || echo "lxc.rootfs = $rootfs" >> $path/config
    if [ -n "$lxc_arch" ]; then
        echo "lxc.arch = $lxc_arch" >> $path/config
    fi

    lxc_network_link_line="# lxc.network.link = br0"
    for br in lxcbr0 virbr0 br0; do
        if [ -d /sys/class/net/$br/bridge ]; then
            lxc_network_link_line="lxc.network.link = $br"
            break
        fi
    done

    if ! grep -q "^lxc.network.type" $path/config 2>/dev/null; then
        cat <<EOF >> $path/config
lxc.network.type = veth
$lxc_network_link_line
lxc.network.flags = up
EOF
    fi

    # if there is exactly one veth or macvlan network entry, make sure
    # it has an associated mac address.
    nics=$(awk -F '[ \t]*=[ \t]*' \
        '$1=="lxc.network.type" && ($2=="veth" || $2=="macvlan") {print $2}' \
        $path/config | wc -l)
    if [ "$nics" -eq 1 ] && ! grep -q "^lxc.network.hwaddr" $path/config; then
        # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303
        hwaddr="fe:$(dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \
                      head -n 1 |awk '{print $2}' | cut -c1-10 |\
                      sed 's/\(..\)/\1:/g; s/.$//')"
        echo "lxc.network.hwaddr = $hwaddr" >> $path/config
    fi

    cat <<EOF >> $path/config

lxc.tty = 4
lxc.pts = 1024
lxc.utsname = $hostname
lxc.cap.drop = sys_module mac_admin mac_override sys_time sys_admin

# When using LXC with apparmor, uncomment the next line to run unconfined:
#lxc.aa_profile = unconfined

# devices
lxc.cgroup.devices.deny = a
# /dev/null, zero and full
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
lxc.cgroup.devices.allow = c 1:7 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 4:0 rwm
lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rm

# mounts point
lxc.mount.auto=cgroup:mixed proc:mixed sys:mixed
lxc.mount.entry=run run tmpfs nodev,noexec,nosuid,relatime,size=1m,mode=0755 0 0
lxc.mount.entry=shm dev/shm tmpfs nodev,nosuid,noexec,mode=1777,create=dir 0 0

EOF

    return 0
}

die() {
    echo "$@" >&2
    exit 1
}

usage() {
    cat >&2 <<EOF
Usage: $(basename $0) [-h|--help] [-r|--repository <url>]
                   [-R|--release <release>] [-a|--arch <arch>]
                   [--rootfs <rootfs>] -p|--path <path> -n|--name <name>
                   [PKG...]
EOF
}

usage_err() {
    usage
    exit 1
}

default_path=/var/lib/lxc
release=
arch=$(uname -m)

# template mknods, requires root
if [ $(id -u) -ne 0 ]; then
   echo "$(basename $0): must be run as root" >&2
   exit 1
fi

options=$(getopt -o hn:p:r:R:a: -l help,name:,rootfs:,path:,repository:,release:,arch: -- "$@")
[ $? -eq 0 ] || usage_err
eval set -- "$options"

while [ $# -gt 0 ]; do
    case "$1" in
    -h|--help)
        usage
        exit 0
        ;;
    -n|--name)
        name=$2
        ;;
    --rootfs)
        rootfs=$2
        ;;
    -p|--path)
        path=$2
        ;;
    -r|--repository)
        repository=$2
	;;
    -R|--release)
        release=$2
        ;;
    -a|--arch)
        arch=$2
        ;;
    --)
	shift
        break;;
    esac
    shift 2
done


[ -z "$name" ] && usage_err

if [ -z "${path}" ]; then
    path="${default_path}/${name}"
fi

if [ -z "$rootfs" ]; then
    rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null`
    if [ -z "$rootfs" ]; then
        rootfs="${path}/rootfs"
    fi
fi

lxc_arch=$arch
apk_arch=$arch

case "$arch" in
    i[3-6]86)
        apk_arch=x86
        lxc_arch=x86
        ;;
    x86)
        lxc_arch=i686
        ;;
    x86_64|"")
        ;;
    arm*)
        apk_arch=armhf
        ;;
    *)
        die "unsupported architecture: $arch"
        ;;
esac

: ${APK:=apk}
if ! which $APK >/dev/null; then
    get_static_apk "$rootfs" || die "Failed to download a valid static apk"
fi

install_alpine "$rootfs" "$@" || die "Failed to install rootfs for $name"
configure_alpine "$rootfs" "$name" || die "Failed to configure $name"
copy_configuration "$path" "$rootfs" "$name"
