#!/bin/sh -ef

po_domain="alterator-mkve"

alterator_api_version=1
. alterator-sh-functions

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"
}

read_param()
{
    local config=/etc/alterator/mkve/config
    local param=$( [ -f "$config" ] && . "$config"; eval echo "\$$1" )

    printf "%s" "${param:-$2}"
}
# }}}
# 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
}

is_in_fstab()
{
    egrep -q "^$1[[:space:]]" /etc/fstab
}
# }}}

# 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()
{
    # return default searching directory
    printf '%s' "$(read_param bundles_dir /srv/share/bundles)"

    local udi
    local usb_drives=
    for udi in $(hal-find-by-capability --capability storage 2>/dev/null); do
        [ "$(hal-get-property --udi "$udi" --key storage.bus)" = "usb" ] &&
        usb_drives="$usb_drives $udi"
    done

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

    [ -n "$drives" -o -n "$usb_drives" ] || return
    drives="$drives $usb_drives"

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

        local disk
        for p in {"",1,2,3,4,5,6,7,8,9}; do
            for disk in $(hal-find-by-property --key block.device --string "$dev$p" 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$p"
            done
        done
    done
}
# }}}
# cache_bundles: {{{
cache_bundles()
{
    local src="$1" mnt= need_to_umount= udi=
    [ -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
            if is_in_fstab "$src"; then
                mount "$src" >/dev/null && need_to_umount=yes || return
            else
                udi=$(cdrom_volume_udi "$src")
                if [ -z "$udi" ]; then
                    echo "can't find UDI for $src"
                    return
                fi

                dbus-send --system --print-reply --dest=org.freedesktop.Hal "$udi" \
                    org.freedesktop.Hal.Device.Volume.Mount string:"" string:"" array:string:"" >/dev/null &&
                    need_to_umount=yes || return
            fi
        fi

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

    find "${mnt:-$src}" -maxdepth $(read_param bundles_search_maxdepth 2) \
                        -name '*.bun' | cashe_bundles_info

    if [ -n "$mnt" -a -n "$need_to_umount" ]; then
        if is_in_fstab "$src"; then
            umount "$mnt" >/dev/null
        else
            dbus-send --system --print-reply --dest=org.freedesktop.Hal "$udi" \
                org.freedesktop.Hal.Device.Volume.Unmount array:string:"" >/dev/null
        fi
    fi
}
# }}}

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 {{{

# hypervisor_is_supported {{{
hypervisor_is_supported()
{
    case "$1" in
        kvm)
            virsh -c qemu:///system list >/dev/null 2>&1 &&
            return 0
            ;;
        openvz)
            virsh -c openvz:///system list >/dev/null 2>&1 &&
            return 0
            ;;
    esac

    return 1 # unsupported
}
# }}}

# arch_is_supported(arch, hypervisor) {{{
arch_is_supported()
{
    local arch="$1"
    local hype="$2"
    local uarch=$(uname -m)

    case "$hype" in
        openvz)
            case "$uarch" in
                i586|i686)
                    [ "$arch" = "i586" -o "$arch" = "i686" ]
                    return $?
                ;;
                x86_64)
                    return 0
                ;;
            esac
        ;;
        kvm)
            return 0
        ;;
    esac

    return 1 # unsupported
}
# }}}

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

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

    # in kvm case we need a running libvirtd instance
    if [ "$(service libvirtd status)" != "libvirtd is running" ]; then
        echo "`_ "you must start libvirtd daemon"`"
        return
    fi

    # check that system supports requested type of virtualization
    local hypervisor="$(get_info 'hypervisor')"
    if ! hypervisor_is_supported "$hypervisor"; then
        printf '%s: %s' "$hypervisor" "`_ "unsupported hypervisor"`"
        return
    fi

    # check that system supports requested architecture
    local arch="$(get_info 'arch')"
    if ! arch_is_supported "$arch" "$hypervisor"; then
        printf '%s: %s' "$arch" "`_ "unsupported architecture"`"
        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 is_in_fstab "$src"; then
                if err=$(mount "$src" 2>&1); then
                    need_to_umount=yes
                else
                    printf '%s %s: %s' "`_ "can't mount"`" "$src" "$err"
                    return
                fi
            else
                udi=$(cdrom_volume_udi "$src")
                if [ -z "$udi" ]; then
                    printf '%s %s' "`_ "can't find UDI for"`" "$src"
                    return
                fi

                if err=$(
                    dbus-send --system --print-reply --dest=org.freedesktop.Hal $udi \
                        org.freedesktop.Hal.Device.Volume.Mount string:"" string:"" array:string:"" \
                            2>&1 >/dev/null); then
                    need_to_umount=yes
                else
                    printf '%s %s: %s' "`_ "can't mount"`" "$src" "$err"
                    return
                fi
            fi
        fi
    fi

    err="$(
            mkve create --name "$in_machine_name" --bundle "$in_bundle" \
                --memory "$in_kvm_memory" \
                --kvm-bridge "$in_kvm_bridge" \
                --ovz-id "$in_ovz_id" \
            2>&1
          )" || echo "$err"

    if [ -n "$src" -a -n "$need_to_umount" ]; then
        if is_in_fstab "$src"; then
            umount "$src" >/dev/null
        else
            dbus-send --system --print-reply --dest=org.freedesktop.Hal "$udi" \
                org.freedesktop.Hal.Device.Volume.Unmount array:string:"" >/dev/null
        fi
    fi
}
# }}}

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_bridges()
{
    local iface

    for iface in $(find /sys/class/net -maxdepth 1 -mindepth 1 | sort); do
        if [ -d "$iface/bridge" ]; then
            printf '%s\n' "$(basename "$iface")"
            [ "$1" != "default" ] || return
        fi
    done
}

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
            ;;
        bridges)
            for i in $(list_bridges); do write_enum_item "$i"; done
            ;;
    esac
}
# }}}
# action read {{{

kvm_bridge()
{
    local bridge="$in_kvm_bridge"

    [ -n "$bridge" ] ||
        bridge="$(list_bridges default)"
    printf '%s' "$bridge"
}

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')"
            write_string_param 'kvm_bridge' "$(kvm_bridge)"
            write_string_param 'kvm_memory' "$(get_info 'memory')"
            ;;
        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
}
# }}}
# action type {{{
action_type()
{
    write_type_item machine_name mkve-machine-name
}
# }}}

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

message_loop

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