#!/bin/sh -ef

po_domain="alterator-mkve"

alterator_api_version=1
. alterator-sh-functions

DEFAULT_BUNDLESRC_DIR=/srv/bundles
INFO_CACHE=/var/cache/alterator/mkve/bundles

# functions {{{
is_dev()
{
    local src="$1"
    [ -n "$src" ] || return

    hal-find-by-property --key block.device --string "$src"
}
# }}}
# mount routines {{{

# cdrom_volume_udi: return udi of the cd volume of requested device {{{
cdrom_volume_udi()
{
    local dev

    [ -n "$1" ] || return

    for dev in $(hal-find-by-property --key 'block.device' --string "$1"); do
        if [ "$(hal-get-property --udi "$dev" --key block.is_volume)" = "true" ]; then
            printf '%s' "$dev"
            return
        fi
    done
}
# }}}

is_mounted()
{
    local udi=$(cdrom_volume_udi "$1")
    [ -n "$udi" ] || return

    [ "$(hal-get-property --udi $udi --key volume.is_mounted)" = "true" ] &&
        echo "yes"
}

mountpoint()
{
    local udi=$(cdrom_volume_udi "$1")
    [ -n "$udi" ] || return

    hal-get-property --udi "$udi" --key volume.mount_point
}
# }}}

# bundle: returns current or default bundle {{{
bundle()
{
    [ -n "$in_bundle" ] &&              # if we have choosed it once,
        printf '%s' "$in_bundle" ||     # then use it;
        printf $(list_bundles)          # otherwise, use default
}
# }}}
# license: return text of license {{{
license()
{
    [ -f "$INFO_CACHE/$(bundle)/license.txt" ] &&
        cat "$INFO_CACHE/$(bundle)/license.txt"
}
# }}}
# get_info {{{
get_info()
{
    [ -n "$1" ] || return

     cat "$INFO_CACHE/$(bundle)/info" |
     egrep "^$1=" |
     cut -d= -f2
}
# }}}

# cache: search for bundles and cache information {{{

# cashe_bundles_info {{{
cashe_bundles_info()
{
    while read bun; do
        # printf >&2 'information: %s\n' "caching bundle $bun"
        echo "$bun" >> "$INFO_CACHE/.list"
        mkdir -p "$INFO_CACHE/$bun" &&
        pushd "$INFO_CACHE/$bun" >/dev/null &&
        tar -xf "$bun" --occurrence info >/dev/null &&
        printf '%s' "$src" > src &&
        popd >/dev/null || :
    done
}
# }}}
# find_available_sources {{{
find_available_sources()
{
    printf '%s' "$DEFAULT_BUNDLESRC_DIR"

    local drives="$(hal-find-by-capability --capability storage.cdrom 2>/dev/null)"
    [ -n "$drives" ] || return

    local udi
    for udi in $drives; do
        [ "$(hal-get-property --udi "$udi" --key volume.ignore 2>/dev/null)" != "true" ] ||
            continue

        local dev="$(hal-get-property --udi "$udi" --key block.device 2>/dev/null)" ||
            return

        local disk
        for disk in $(hal-find-by-property --key block.device --string "$dev" 2>/dev/null); do
            local is_volume="$(hal-get-property --udi "$disk" --key block.is_volume 2>/dev/null)" ||
                return
            [ "$is_volume" = "false" ] ||
                printf ' %s' "$dev"
        done
    done
}
# }}}
# cache_bundles: {{{
cache_bundles()
{
    local src="$1" mnt= need_to_umount=
    [ -n "$src" ] || return

    # in case $src is /dev/* we must mount it if it isn't mounted yet
    if [ -n "$(is_dev "$src")" ]; then
        if [ -z "$(is_mounted "$src")" ]; then
            mount "$src" >/dev/null && need_to_umount=yes || return
        fi

        mnt=$(mountpoint "$src")
        [ -n "$mnt" ] || return
    fi

    find "${mnt:-$src}" -name '*.bun' | cashe_bundles_info

    [ -z "$mnt" -o -z "$need_to_umount" ] ||
        umount "$mnt" >/dev/null
}
# }}}

cache()
{
    rm -rf "$INFO_CACHE" && mkdir -p "$INFO_CACHE" || return
    for src in $(find_available_sources); do
        cache_bundles "$src"
    done
    [ -n "$(ls "$INFO_CACHE")" ] ||
        write_error "`_ "no bundles found"`"
}
# }}}
# create {{{

# create_machine: {{{
create_machine()
{
    local err
    local need_to_umount=

    src="$(cat $INFO_CACHE/$in_bundle/src)"
    if [ -z "$src" ]; then
        echo "can't find src"
        return
    fi

    # in case $src is /dev/* we must mount it if it isn't mounted yet
    if [ -n "$(is_dev "$src")" ]; then
        if [ -z "$(is_mounted "$src")" ]; then
            if err=$(mount "$src" 2>&1); then
                need_to_umount=yes
            else
                echo "can't mount $src: $err"
                return
            fi
        fi
    fi

    err="$(
            mkve create --name "$in_machine_name" --bundle "$in_bundle" \
                --memory "$in_kvm_memory" \
                --kvm-disk-size "$in_kvm_disk_size" \
                --kvm-swap-size "$in_kvm_swap_size" \
            2>&1
          )" || echo "$err"

    [ -z "$need_to_umount" ] ||
        umount "$src" >/dev/null
}
# }}}

create()
{
    local err=

    if [ -z "$in_machine_name" ]; then
        err="Machine name is not specified"
    elif [ -z "$in_bundle" ]; then
        err="Bundle is not specified"
    else
        err=$(create_machine)
    fi

    [ -z "$err" ] || write_error "$err"
}
# }}}

# action list {{{
list_bundles()
{
    cat "$INFO_CACHE/.list"
}

action_list()
{
    case "${in__objects}" in
        bundles)
            for i in $(list_bundles); do write_enum_item "$i" "$(basename "$i")"; done
            ;;
    esac
}
# }}}
# action read {{{
action_read()
{
    case "$in__objects" in
        /)
            write_string_param 'bundle' "$(bundle)"
            write_string_param 'hypervisor' "$(get_info 'hypervisor')"
            write_string_param 'vendor' "$(get_info 'vendor')"
            write_string_param 'arch' "$(get_info 'arch')"
            ;;
        default_bundle)
            write_string_param 'bundle' "$(bundle)"
            ;;
        license)
            write_string_param 'license' "$(license)"
            ;;
    esac
}
# }}}
# action write {{{
action_write()
{
    [ -n "$in_create" ] && create
    [ -n "$in_cache" ] && cache
}
# }}}

on_message()
{
    set | egrep '^in_'
    echo
    case "$in_action" in
        constraints) write_nop          ;;
        list)        action_list        ;;
        write)       action_write       ;;
        read)        action_read        ;;
        type)        write_nop          ;;
        *)           write_bool 'false' ;;
    esac
}

message_loop

# vim:fdm=marker et sw=4 ts=4
