#!/bin/bash

set -e
umask 022

error() {
	echo $* >&2
	exit 1
}

log() {
	echo $* >/dev/null
}

command_build_base_dev() {
	dir="$root"/base/dev
	rm -rf "$dir" 
	log "Building dev base in $dir"
	mkdir -p "$dir"
	cp -a /dev/{fd,full,null,ptmx,stderr,stdin,stdout,tty,urandom,zero} "$dir"
	ln -s /dev/urandom "$dir"/random
}

command_build_base_type() {
	[ "$1" ] || error "Missing discriptive name"
	name="$1"
	[ "$2" ] || error "Missing arch"
	arch="$2"
	[ "$3" ] || error "Missing type"
	type="$3"
	dir="$root"/base/"$type"/"$arch"/"$name"/root
	rm -rf "$dir" 
	mkdir -p "$dir"/etc/apt/
	log "Creating empty initial sources.list. Please do edit before using chroot."
	touch "$dir"/etc/apt/sources.list
}

command_build_base_type_buildd() {
	dir="$root"/base/buildd/root
	rm -rf "$dir" 
	log "Building buildd base in $dir"
	mkdir -p "$dir"
}

command_build_base_type_user() {
	dir="$root"/base/user/root
	rm -rf "$dir" 
	log "Building user base in $dir"
	mkdir -p "$dir"/{build,home}
	cp -a /etc/{group,passwd,sudoers} "$dir"/etc
	sed -i -e 's,^\([^:]*\):x:,\1:*:,' "$dir"/etc/{group,passwd}
	chmod 0 "$dir"/home
}

command_build_base_dist() {
	[ "$1" ] || error "Missing discriptive name"
	name="$1"
	[ "$2" ] || error "Missing arch"
	arch="$2"
	[ "$3" ] || error "Missing dist"
	dist="$3"
	[ "$4" ] || error "Missing mirror"
	mirror="$4"
	shift 4
	dir="$root"/base/"$arch"/"$name"/root
	cdbflags=""
	log "Bootstrapping $arch chroot base for dist $dist in $dir from $mirror"
	rm -rf "$dir" 
	mkdir -p "$dir"
	[ "$arch" = "amd64" -a "$dist" = "sarge" ]&&cdbflags="--allow-unauthenticated"
	cdebootstrap -q --arch "$arch" --flavour build $cdbflags "$dist" "$dir" "$mirror" "$@"
	rm -rf "$dir"/{boot,etc/opt,home,initrd,media,mnt,opt,srv,sys,var/opt}
	chmod 0 "$dir"/proc
}

command_update_base_dist() {
	[ "$1" ] || error "Missing discriptive name"
	name="$1"
	[ "$2" ] || error "Missing arch"
	arch="$2"
	dir="$root"/base/"$arch"/"$name"/root
	log "Updating $arch chroot base in $dir"
	apt "$dir" update
	apt "$dir" upgrade -y
	rm -rf "$dir"/{boot,etc/opt,home,initrd,media,mnt,opt,srv,sys,var/opt}
	chmod 0 "$dir"/proc
}

command_build_active() {
	[ "$1" ] || error "Missing uuid"
	uuid="$1"
	[ "$2" ] || error "Missing discriptive name"
	name="$2"
	[ "$3" ] || error "Missing arch"
	arch="$3"
	[ "$4" ] || error "Missing type"
	type="$4"
	shift 4
	[ "$SUDO_USER" ] || error "Not called from sudo"
	user="$SUDO_USER"
	dir="$root"/active/"$uuid"
	[ -e "$dir" ] && error "Root already exists ($dir)!"
	log "Bulding active $arch chroot in $dir"
	build_active "$dir" "$name" "$arch" "$type" "$user" "$*"
}

command_execute_active() {
	[ "$1" ] || error "Missing uuid"
	uuid="$1"
	shift 1
	dir="$root"/active/"$uuid"
	log "Executing command in active chroot in $dir"
	exec chroot "$dir" "$@"
}

command_remove_active() {
	[ "$1" ] || error "Missing uuid"
	uuid="$1"
	shift 1
	dir="$root"/active/"$uuid"
	log "Removing active chroot in $dir"
	umount_active "$dir"
	rm -rf "$dir"
}

build_active() {
	dir="$1"
	dist="$2"
	arch="$3"
	type="$4"
	user="$5"
	shift 5

	base_dist="$root"/base/"$arch"/"$dist"/root
	base_dist_type="$root"/base/"$type"/"$arch"/"$dist"/root
	base_dev="$root"/base/dev
	base_type="$root"/base/"$type"/root
	[ -d "$base_dist" ] || error "$base_dist does not exist"
	[ -d "$base_dist_type" ] || error "$base_dist_typ does not exist"
	[ -d "$base_dev" ] && dodev=1

	mkdir -p "$dir"

	excludes="--exclude lost+found"

	install="$*"
	[ "$type" = user ] && install="$install devscripts sudo vim whiptail zsh"

	sync "$base_dist"/ "$dir" --delete $excludes
	if [ "$dodev" ]; then
		sync "$base_dev"/ "$dir"/dev --delete --exclude pts
	else
		rm -r "$dir"/dev/*
		chmod 0 "$dir"/dev
		touch "$dir"/.mount-dev
	fi
	[ -d "$base_type" ] && sync "$base_type"/ "$dir" $excludes
	sync "$base_dist_type"/ "$dir" $excludes

	mount_active "$dir"

	[ -f "$base_dist_type"/../key ] && gpg -q --no-options --no-default-keyring --secret-keyring "$dir"/etc/apt/secring.gpg --trustdb-name "$dir"/etc/apt/trustdb.gpg --keyring "$dir"/etc/apt/trusted.gpg --import < "$base_dist_type"/../key

	apt "$dir" update
	apt "$dir" install -y --force-yes $install
	apt "$dir" clean

	mkdir -p "$dir"/{build,var/debbuild/srcdep-lock}
	passwd_entry="$(getent passwd "$user")"
	uid="$(echo "$passwd_entry" | cut -d ':' -f 3)"
	gid="$(echo "$passwd_entry" | cut -d ':' -f 4)"
	echo "$passwd_entry" >> "$dir"/etc/passwd
	getent group "$gid" >> "$dir"/etc/group
	sed -i -e 's,^\([^:]*\):x:,\1:*:,' "$dir"/etc/{group,passwd}
	chown "$uid":"$gid" "$dir"/{build,var/debbuild/srcdep-lock}
	chmod 700 "$dir"/{build,var/debbuild/srcdep-lock}
}

mount_active() {
	dir="$1"
	if [ -f "$dir"/.mount-dev ]; then
		mount --bind -o ro /dev "$dir"/dev
	fi
	mount -t devpts none "$dir"/dev/pts
	mount -t proc none "$dir"/proc
}

umount_active() {
	dir="$1"
	for mount in $(grep "$dir" /proc/mounts | awk '{print $2}' | sort -r); do
		umount "$mount"
	done
}

sync () {
	command rsync -aHx "$@"
}

apt () {
	chroot="$1"
	shift
	command chroot "$chroot" apt-get "$@"
}

main() {
	[ "$1" ] || error "No command specified."
	command="$1"
	shift

	case "$command" in
		help)
		command_help
		return
		;;
	esac

	[ "$1" ] || error "No root specified."
	root="$1"
	shift
	[ -d "$root" ] || log "Root $root doesn't exist. Will be created."

	case "$command" in
		build-base-dev)
		command_build_base_dev "$@"
		;;
		build-base-type)
		command_build_base_type "$@"
		;;
		build-base-type-buildd)
		command_build_base_type_buildd "$@"
		;;
		build-base-type-user)
		command_build_base_type_user "$@"
		;;
		build-base-dist)
		command_build_base_dist "$@"
		;;
		update-base-dist)
		command_update_base_dist "$@"
		;;
		build-active)
		command_build_active "$@"
		;;
		execute-active)
		command_execute_active "$@"
		;;
		remove-active)
		command_remove_active "$@"
		;;
		*)
		error "Unknown command. Exiting."
	esac
}

main "$@"
