#!/bin/bash

. shell-config
. shell-var

po_domain="alterator-livecd"
alterator_api_version=1
source_dir="${source_dir:-/.ro}"
target_dir="${target_dir:-/mnt/destination}"
preinstall_hooks_dir=/usr/lib/alterator/hooks/livecd-preinstall.d
du_exclude_list='/mnt /home /media /run /var/run /srv'
install_pid=

slideshow_conf="/etc/alterator/slideshow.conf"
# Default slideshow timeout, sec
default_slideshow_step=30
default_slideshow_path="/usr/share/install2/slideshow"

export target_dir

. alterator-sh-functions
. livecd-functions
. install2-remount-functions

do_notify()
{
    alterator-mailbox-send "$1 \"$(write_string "$2")\""
    echo "message:$1 $2" >&2
    sleep 0.1
}

do_notify_error()
{
    do_notify error "$1"
}

do_notify_status()
{
    do_notify status "$1"
}

do_notify_stage()
{
    do_notify stage "$1"
}

##
# Return free space in bytes on mounted file system
#
free_space() {
        local target="$1" && shift
	local free_blk= blk_size=

        [ -e "$target" ] || return 1
	out=`stat --file-system --format='%f %S' "$target"`
	set -- $out
	free_blk="$1"
	blk_size="$2"
	echo $((free_blk*blk_size))
}

used_space() {
	local dir="$1"
	local out= free_blk= total_blk= used_blk= blk_size=
	out=`stat --file-system --format='%f %b %S' "$dir"`
	set -- $out
	free_blk="$1"
	total_blk="$2"
	blk_size="$3"
	used_blk=$((total_blk-free_blk))
	echo $((used_blk*blk_size))
}

# find the backing file of a mounted squashfs filesystem
squashfs_image_file() {
	local mntpt="$1"
	local loopdev
	local img
	local rc
	loopdev="`findmnt -n -o SOURCE $mntpt`"
	rc=$?
	if [ $rc -ne 0 ]; then
		echo "$mntpt is not a mount point" >&2
		return 2
	fi

	case "$loopdev" in
		/dev/loop*)
			img="$(losetup --noheadings -OBACK-FILE -l $loopdev)"
			;;
	esac
	if [ ! -f "$img" ]; then
		echo "squashfs image for "$mntpt" not found" >&2
		return 1
	fi
	echo "$img"
}

parse_unsquashfs_progress() {
	local str="$*"
	local percentage
	# unsquashfs progress line:
	# [==============/                                    ] 61882/154938  39%
	str="${str## }"
	case "$str" in
		[*)
		;; # make vim happy: ]
		*)
		return 0
		;;
	esac

	str="${str##*] }"
	set -- $str
	percentage="$2"
	percentage="${percentage%%%}"
	echo "$percentage"
}

##
# copy squashfs image
#

do_install()
{
    local src="$1";shift
    local dst="$1";shift
    local total_size= free= pct= src_img= rc=
    local save_nfiles= save_nfiles_soft=

    if [ ! -d "$src" ]; then
        do_notify_error "Can't find image mountpoint ($src)"
        return 1
    fi

    if ! mkdir -p -- "$dst";then
        do_notify_error "Can't write to $dst directory"
        return 1
    fi

    do_notify_stage preinstall

    shopt -sq dotglob

    total_size="$(used_space "$src")"
    free="$(free_space "$dst")"

    if [ $total_size -gt $free ]; then
        do_notify_error "No free space to copy image"
        return 1
    fi
    src_img="$(squashfs_image_file "$src")"
    rc=$?
    if [ $rc -ne 0 ]; then
	    notify_error "Squashfs image of "$src" not found"
	    return 1
    fi

    do_notify_stage install

    if [ -n "$(find "$dst" -mount -type f -print -quit)" ]; then
        do_notify_error "$dst is not empty!"
    fi

    # unsquashfs processes many files at once
    save_nfiles=`ulimit -H -n`
    save_nfiles_soft=`ulimit -S -n`
    ulimit -H -n $((1024*1024))
    ulimit -S -n $((1024*1024))
    unsquashfs -f -d "$dst" "$src_img" > >(
    while read -r -d '
' progress_str; do
	    pct=`parse_unsquashfs_progress "$progress_str"`
	    if [ -n "$pct" ]; then
		    do_notify_status "$pct"
	    fi
    done;
    )
    rc=$?
    ulimit -H -n $save_nfiles
    ulimit -S -n $save_nfiles_soft

    if [ $rc -ne 0 ]; then
	    do_notify_error "Error unpacking image ${src_img} into ${dst}: $rc"
	    return 1
    fi
    # The image has been successfully unpacked, set progress to 100
    do_notify_status "100"

    # Set right perms on root directory of installed system
    chmod 0755 "$dst"

    do_notify_stage postinstall

    for i in proc dev sys run; do
        mkdir -p -- "$dst/$i"
        mountpoint -q "$dst/$i" || mount --bind /$i "$dst/$i"
    done

    # Remount volumes (involves shutting down EVMS)
    if ! remount_chroot >&2; then
        do_notify_error "destination filesystem remount error, see /tmp/remount.log"
    fi

    # Mount cgroups in the $dst:
    # can be needed in case of systemd
    if mountpoint -q /sys/fs/cgroup; then
        mkdir -p -- "$dst"
        mountpoint -q "$dst/sys/fs/cgroup" || mount --rbind /sys/fs/cgroup "$dst/sys/fs/cgroup"
    fi

    # Run preinstall scripts
    run-parts "$preinstall_hooks_dir"

    alteratord_socket_dir="/var/run/alteratord"
    # replace itself with alteratord from chroot
    [ -n "${dst:-}" ] || return
    chroot "$dst" /etc/init.d/alteratord start
    mount -o bind "$dst/$alteratord_socket_dir" "$alteratord_socket_dir"

    # Mount root to target dir for access from chroot at finish stage
    mkdir -p "$dst/livecd-root"
    mount --rbind / "$dst/livecd-root" 2>/dev/null
    mount --make-rslave "$dst/livecd-root" 2>/dev/null

    # wait until new alteratord is ready to use
    alterator-wait

    # notify interface about finish
    do_notify_stage done
    sync

    return 0
}

### slideshow configuration
read_slideshow_conf()
{
    local step="$(shell_config_get "$slideshow_conf" step)"
    if [ -z "$step" ] || echo "$step" | egrep -vqs '^[[:digit:]]+$'; then
        step="$default_slideshow_step"
    fi
	write_string_param step "$step"

    local once="$(shell_config_get "$slideshow_conf" once)"
    if [ -n "$once" ] && shell_var_is_yes "$once"; then
        once='true'
    else
        once='false'
    fi
    write_bool_param once "$once"

    local url="$(shell_config_get "$slideshow_conf" url)"
    [ -n "$url" ] || url="$default_slideshow_path"
    write_string_param url "$url"
}

on_message()
{
        case "$in_action" in
                read)
                    case "$in__objects" in
                        slideshow-config)
                            read_slideshow_conf
                            ;;
                        *)
                        ;;
                    esac
                    ;;
                write)
                    if [ -n "$install_pid" ] && kill -0 "$install_pid" ;then
                        write_error "Process already running"
                        return
                    fi
                    do_install "$source_dir" "$target_dir"&
                    install_pid=$!
                    ;;
        esac
}

message_loop
