lxc-alpine.in revision c01c25fcdd1e0cacad8075bcfcef4c8e8d4b8cb6
2ronwalf#!/bin/sh
2ronwalf
2ronwalfkey_sha256sums="9c102bcc376af1498d549b77bdbfa815ae86faa1d2d82f040e616b18ef2df2d4 alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub
2ronwalf2adcf7ce224f476330b5360ca5edb92fd0bf91c92d83292ed028d7c4e26333ab alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub"
2ronwalf
2ronwalfget_static_apk () {
2ronwalf wget="wget -q -O -"
2ronwalf pkglist=alpine-keys:apk-tools-static
2ronwalf auto_repo_dir=
2ronwalf
22daenzerorama if [ -z "$repository" ]; then
2ronwalf url=http://wiki.alpinelinux.org/cgi-bin/dl.cgi
2ronwalf if [ -z "$release" ]; then
2ronwalf echo -n "Determining the latest release... "
2ronwalf release=$($wget $url/.latest.$apk_arch.txt | \
2ronwalf cut -d " " -f 3 | cut -d / -f 1 | uniq)
2ronwalf if [ -z "$release" ]; then
28daenzerorama echo failed
2ronwalf return 1
2ronwalf fi
2ronwalf echo $release
2ronwalf fi
2ronwalf auto_repo_dir=$release/main
2ronwalf repository=$url/$auto_repo_dir
2ronwalf pkglist=$pkglist:alpine-mirrors
2ronwalf fi
2ronwalf
2ronwalf rootfs="$1"
2ronwalf echo "Using static apk from $repository/$apk_arch"
2ronwalf wget="$wget $repository/$apk_arch"
2ronwalf
2ronwalf # parse APKINDEX to find the current versions
2ronwalf static_pkgs=$($wget/APKINDEX.tar.gz | \
2ronwalf tar -Oxz APKINDEX | \
2ronwalf awk -F: -v pkglist=$pkglist '
2ronwalf BEGIN { split(pkglist,pkg) }
2ronwalf $0 != "" { f[$1] = $2 }
2ronwalf $0 == "" { for (i in pkg)
2ronwalf if (pkg[i] == f["P"])
2ronwalf print(f["P"] "-" f["V"] ".apk") }')
2ronwalf [ "$static_pkgs" ] || return 1
2ronwalf
2ronwalf mkdir -p "$rootfs" || return 1
2ronwalf for pkg in $static_pkgs; do
2ronwalf echo "Downloading $pkg"
2ronwalf $wget/$pkg | tar -xz -C "$rootfs"
2ronwalf done
2ronwalf
2ronwalf # clean up .apk meta files
2ronwalf rm -f "$rootfs"/.[A-Z]*
2ronwalf
2ronwalf # verify checksum of the key
2ronwalf keyname=$(echo $rootfs/sbin/apk.static.*.pub | sed 's/.*\.SIGN\.RSA\.//')
2ronwalf checksum=$(echo "$key_sha256sums" | grep -w "$keyname")
2ronwalf if [ -z "$checksum" ]; then
2ronwalf echo "ERROR: checksum is missing for $keyname"
2ronwalf return 1
2ronwalf fi
2ronwalf (cd $rootfs/etc/apk/keys && echo "$checksum" | sha256sum -c -) || return 1
2ronwalf
2ronwalf # verify the static apk binary signature
2ronwalf APK=$rootfs/sbin/apk.static
2ronwalf openssl dgst -verify $rootfs/etc/apk/keys/$keyname \
2ronwalf -signature "$APK.SIGN.RSA.$keyname" "$APK" || return 1
2ronwalf
2ronwalf if [ "$auto_repo_dir" ]; then
2ronwalf mirror_list=$rootfs/usr/share/alpine-mirrors/MIRRORS.txt
2ronwalf mirror_count=$(wc -l $mirror_list | cut -d " " -f 1)
2ronwalf repository=$(sed $(expr $RANDOM % $mirror_count + 1)\!d \
2ronwalf $mirror_list)$auto_repo_dir
2ronwalf echo "Selecting mirror $repository"
2ronwalf fi
2ronwalf}
2ronwalf
2ronwalfinstall_alpine() {
2ronwalf rootfs="$1"
2ronwalf shift
2ronwalf mkdir -p "$rootfs"/etc/apk || return 1
2ronwalf : ${keys_dir:=/etc/apk/keys}
2ronwalf if ! [ -d "$rootfs"/etc/apk/keys ] && [ -d "$keys_dir" ]; then
2ronwalf cp -r "$keys_dir" "$rootfs"/etc/apk/keys
2ronwalf fi
2ronwalf if [ -n "$repository" ]; then
2ronwalf echo "$repository" > "$rootfs"/etc/apk/repositories
2ronwalf else
2ronwalf cp /etc/apk/repositories "$rootfs"/etc/apk/repositories || return 1
2ronwalf if [ -n "$release" ]; then
2ronwalf sed -i -e "s:/[^/]\+/\([^/]\+\)$:/$release/\1:" \
2ronwalf "$rootfs"/etc/apk/repositories
2ronwalf fi
2ronwalf fi
2ronwalf opt_arch=
2ronwalf if [ -n "$apk_arch" ]; then
2ronwalf opt_arch="--arch $apk_arch"
2ronwalf fi
2ronwalf $APK add -U --initdb --root $rootfs $opt_arch "$@" alpine-base
2ronwalf}
2ronwalf
2ronwalfconfigure_alpine() {
2ronwalf rootfs="$1"
2ronwalf echo "Setting up /etc/inittab"
2ronwalf cat >"$rootfs"/etc/inittab<<EOF
2ronwalf::sysinit:/sbin/rc sysinit
2ronwalf::wait:/sbin/rc default
2ronwalftty1:12345:respawn:/sbin/getty 38400 tty1
2ronwalftty2:12345:respawn:/sbin/getty 38400 tty2
2ronwalftty3:12345:respawn:/sbin/getty 38400 tty3
2ronwalftty4:12345:respawn:/sbin/getty 38400 tty4
2ronwalf::ctrlaltdel:/sbin/reboot
2ronwalf::shutdown:/sbin/rc shutdown
2ronwalfEOF
2ronwalf # set up nameserver
2ronwalf grep nameserver /etc/resolv.conf > "$rootfs/etc/resolv.conf"
2ronwalf
2ronwalf # configure the network using the dhcp
2ronwalf cat <<EOF > $rootfs/etc/network/interfaces
2ronwalfauto lo
2ronwalfiface lo inet loopback
2ronwalf
2ronwalfauto eth0
2ronwalfiface eth0 inet dhcp
2ronwalfEOF
2ronwalf
2ronwalf # set the hostname
2ronwalf echo $hostname > $rootfs/etc/hostname
2ronwalf
2ronwalf # missing device nodes
2ronwalf echo "Setting up device nodes"
2ronwalf mkdir -p -m 755 "$rootfs/dev/pts"
2ronwalf mkdir -p -m 1777 "$rootfs/dev/shm"
2ronwalf mknod -m 666 "$rootfs/dev/zero" c 1 5
2ronwalf mknod -m 666 "$rootfs/dev/full" c 1 7
2ronwalf mknod -m 666 "$rootfs/dev/random" c 1 8
2ronwalf mknod -m 666 "$rootfs/dev/urandom" c 1 9
2ronwalf mknod -m 666 "$rootfs/dev/tty0" c 4 0
2ronwalf mknod -m 666 "$rootfs/dev/tty1" c 4 1
2ronwalf mknod -m 666 "$rootfs/dev/tty2" c 4 2
2ronwalf mknod -m 666 "$rootfs/dev/tty3" c 4 3
2ronwalf mknod -m 666 "$rootfs/dev/tty4" c 4 4
2ronwalf# mknod -m 600 "$rootfs/dev/initctl" p
2ronwalf mknod -m 666 "$rootfs/dev/tty" c 5 0
2ronwalf mknod -m 666 "$rootfs/dev/console" c 5 1
2ronwalf mknod -m 666 "$rootfs/dev/ptmx" c 5 2
2ronwalf
2ronwalf # start services
2ronwalf ln -s /etc/init.d/bootmisc "$rootfs"/etc/runlevels/boot/bootmisc
2ronwalf ln -s /etc/init.d/syslog "$rootfs"/etc/runlevels/boot/syslog
2ronwalf
2ronwalf return 0
2ronwalf}
2ronwalf
2ronwalfcopy_configuration() {
2ronwalf path=$1
2ronwalf rootfs=$2
2ronwalf hostname=$3
2ronwalf
2ronwalf grep -q "^lxc.rootfs" $path/config 2>/dev/null \
2ronwalf || echo "lxc.rootfs = $rootfs" >> $path/config
2ronwalf if [ -n "$lxc_arch" ]; then
2ronwalf echo "lxc.arch = $lxc_arch" >> $path/config
2ronwalf fi
2ronwalf
2ronwalf lxc_network_link_line="# lxc.network.link = br0"
2ronwalf for br in lxcbr0 virbr0 br0; do
2ronwalf if [ -d /sys/class/net/$br/bridge ]; then
2ronwalf lxc_network_link_line="lxc.network.link = $br"
2ronwalf break
2ronwalf fi
2ronwalf done
2ronwalf
2ronwalf if ! grep -q "^lxc.network.type" $path/config 2>/dev/null; then
2ronwalf cat <<EOF >> $path/config
2ronwalflxc.network.type = veth
2ronwalf$lxc_network_link_line
2ronwalflxc.network.flags = up
2ronwalfEOF
2ronwalf fi
2ronwalf
2ronwalf # if there is exactly one veth or macvlan network entry, make sure
2ronwalf # it has an associated mac address.
2ronwalf nics=$(awk -F '[ \t]*=[ \t]*' \
2ronwalf '$1=="lxc.network.type" && ($2=="veth" || $2=="macvlan") {print $2}' \
2ronwalf $path/config | wc -l)
2ronwalf if [ "$nics" -eq 1 ] && ! grep -q "^lxc.network.hwaddr" $path/config; then
2ronwalf # see http://sourceforge.net/tracker/?func=detail&aid=3411497&group_id=163076&atid=826303
2ronwalf hwaddr="fe:$(dd if=/dev/urandom bs=8 count=1 2>/dev/null |od -t x8 | \
2ronwalf head -1 |awk '{print $2}' | cut -c1-10 |\
2ronwalf sed 's/\(..\)/\1:/g; s/.$//')"
2ronwalf echo "lxc.network.hwaddr = $hwaddr" >> $path/config
2ronwalf fi
2ronwalf
2ronwalf cat <<EOF >> $path/config
2ronwalf
2ronwalflxc.tty = 4
2ronwalflxc.pts = 1024
2ronwalflxc.utsname = $hostname
2ronwalflxc.cap.drop = sys_module mac_admin mac_override sys_time
2ronwalf
2ronwalf# When using LXC with apparmor, uncomment the next line to run unconfined:
2ronwalf#lxc.aa_profile = unconfined
2ronwalf
2ronwalf# devices
2ronwalflxc.cgroup.devices.deny = a
2ronwalf# /dev/null, zero and full
2ronwalflxc.cgroup.devices.allow = c 1:3 rwm
2ronwalflxc.cgroup.devices.allow = c 1:5 rwm
2ronwalflxc.cgroup.devices.allow = c 1:7 rwm
2ronwalf# consoles
2ronwalflxc.cgroup.devices.allow = c 5:1 rwm
2ronwalflxc.cgroup.devices.allow = c 5:0 rwm
2ronwalflxc.cgroup.devices.allow = c 4:0 rwm
2ronwalflxc.cgroup.devices.allow = c 4:1 rwm
2ronwalf# /dev/{,u}random
2ronwalflxc.cgroup.devices.allow = c 1:9 rwm
2ronwalflxc.cgroup.devices.allow = c 1:8 rwm
2ronwalflxc.cgroup.devices.allow = c 136:* rwm
2ronwalflxc.cgroup.devices.allow = c 5:2 rwm
2ronwalf# rtc
2ronwalflxc.cgroup.devices.allow = c 254:0 rm
2ronwalf
2ronwalf# mounts point
2ronwalflxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0
2ronwalflxc.mount.entry=run run tmpfs nodev,noexec,nosuid,relatime,size=1m,mode=0755 0 0
2ronwalflxc.mount.entry=none dev/pts devpts gid=5,mode=620 0 0
2ronwalf
2ronwalfEOF
2ronwalf
2ronwalf return 0
2ronwalf}
2ronwalf
2ronwalfdie() {
2ronwalf echo "$@" >&2
2ronwalf exit 1
2ronwalf}
2ronwalf
2ronwalfusage() {
2ronwalf cat >&2 <<EOF
2ronwalfUsage: $(basename $0) [-h|--help] [-r|--repository <url>]
2ronwalf [-R|--release <release>] [-a|--arch <arch>]
2ronwalf [--rootfs <rootfs>] -p|--path <path> -n|--name <name>
2ronwalf [PKG...]
2ronwalfEOF
2ronwalf}
2ronwalf
2ronwalfusage_err() {
2ronwalf usage
2ronwalf exit 1
2ronwalf}
2ronwalf
2ronwalfoptarg_check() {
2ronwalf if [ -z "$2" ]; then
2ronwalf usage_err "option '$1' requires an argument"
2ronwalf fi
2ronwalf}
2ronwalf
2ronwalfdefault_path=@LXCPATH@
2ronwalfrelease=
2ronwalfarch=$(uname -m)
2ronwalf
2ronwalf# template mknods, requires root
2ronwalfif [ $(id -u) -ne 0 ]; then
2ronwalf echo "$(basename $0): must be run as root" >&2
2ronwalf exit 1
2ronwalffi
2ronwalf
2ronwalfwhile [ $# -gt 0 ]; do
2ronwalf opt="$1"
2ronwalf shift
2ronwalf case "$opt" in
2ronwalf -h|--help)
2ronwalf usage
2ronwalf exit 0
2ronwalf ;;
2ronwalf -n|--name)
2ronwalf optarg_check $opt "$1"
2ronwalf name=$1
2ronwalf shift
2ronwalf ;;
2ronwalf --rootfs)
2ronwalf optarg_check $opt "$1"
2ronwalf rootfs=$1
2ronwalf shift
2ronwalf ;;
2ronwalf -p|--path)
2ronwalf optarg_check $opt "$1"
2ronwalf path=$1
2ronwalf shift
2ronwalf ;;
2ronwalf -r|--repository)
2ronwalf optarg_check $opt "$1"
2ronwalf repository=$1
2ronwalf shift
2ronwalf ;;
2ronwalf -R|--release)
2ronwalf optarg_check $opt "$1"
2ronwalf release=$1
2ronwalf shift
2ronwalf ;;
2ronwalf -a|--arch)
2ronwalf optarg_check $opt "$1"
2ronwalf arch=$1
2ronwalf shift
2ronwalf ;;
2ronwalf --)
2ronwalf break;;
2ronwalf --*=*)
2ronwalf # split --myopt=foo=bar into --myopt foo=bar
2ronwalf set -- ${opt%=*} ${opt#*=} "$@"
2ronwalf ;;
2ronwalf -?)
2ronwalf usage_err "unknown option '$opt'"
2ronwalf ;;
2ronwalf -*)
2ronwalf # split opts -abc into -a -b -c
2ronwalf set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
2ronwalf ;;
2ronwalf esac
2ronwalfdone
2ronwalf
2ronwalf
2ronwalf[ -z "$name" ] && usage_err
2ronwalf
2ronwalfif [ -z "${path}" ]; then
2ronwalf path="${default_path}/${name}"
2ronwalffi
2ronwalf
2ronwalfif [ -z "$rootfs" ]; then
2ronwalf rootfs=`awk -F= '$1 ~ /^lxc.rootfs/ { print $2 }' "$path/config" 2>/dev/null`
2ronwalf if [ -z "$rootfs" ]; then
2ronwalf rootfs="${path}/rootfs"
2ronwalf fi
2ronwalffi
2ronwalf
2ronwalflxc_arch=$arch
2ronwalfapk_arch=$arch
2ronwalf
2ronwalfcase "$arch" in
2ronwalf i[3-6]86)
2ronwalf apk_arch=x86
2ronwalf ;;
2ronwalf x86)
2ronwalf lxc_arch=i686
2ronwalf ;;
2ronwalf x86_64|"")
2ronwalf ;;
2ronwalf *)
2ronwalf die "unsupported architecture: $arch"
2ronwalf ;;
2ronwalfesac
2ronwalf
2ronwalf: ${APK:=apk}
2ronwalfif ! which $APK >/dev/null; then
2ronwalf get_static_apk "$rootfs" || die "Failed to download a valid static apk"
2ronwalffi
2ronwalf
2ronwalfinstall_alpine "$rootfs" "$@" || die "Failed to install rootfs for $name"
2ronwalfconfigure_alpine "$rootfs" "$name" || die "Failed to configure $name"
2ronwalfcopy_configuration "$path" "$rootfs" "$name"
2ronwalf