#!/bin/sh

run_and_log()
{
    RES=
    CMD=$1
    OUT=
    if [ "x$CMD" = "x" ]; then
        echo "Empty command to run" 2>&1 | tee -a $LOG
        return 0
    fi
    echo "Running: $CMD" 2>&1 | tee -a $LOG

    TMP_FILE_NAME=`mktemp /tmp/XXXXXX`
    rm -f $TMP_FILE_NAME || return 0
    mkfifo $TMP_FILE_NAME || return 0
    tee -a $LOG < ${TMP_FILE_NAME}&
    sh -c "$CMD" 2>&1 > ${TMP_FILE_NAME}
    RES=$?
    rm -f ${TMP_FILE_NAME}

    if [ $RES -ne 0 ]; then
        return 0
    fi

    return 1
}

log()
{
    STR=$1
    echo "$STR" 2>&1 | tee -a $LOG

    return 1
}

failed()
{
    EXIT=$1
    echo -e "\n    FAILED !\n" | tee -a $LOG
    if [ "x$EXIT" != "x" -a $EXIT -ne 0 ]; then
        if [ $EXIT -eq -1 ]; then
            echo -e " \n Halting guest system \n" | tee -a $LOG
            uptime | tee -a $LOG
            shutdown -p now
        fi
        exit $EXIT
    fi
    return 1
}

SRC_DIR="/onapp.src"
CONF="${SRC_DIR}/onapp-freebsd-prepare.conf"
LOG=${SRC_DIR}/onapp-freebsd-prepare.log
NETWORK_CONF="${SRC_DIR}/onapp-freebsd-network.conf"
PROVISION_COMPLETE="${SRC_DIR}/provision.complete"

IPV6ADDR=
IPV6GATEWAY=

# Set the USESYSINSTALL=1 if sysinstall should be used for disk's slices, partitions and file-system management under Xen
# Otherwise, gpart will be used
USESYSINSTALL=0
# Use bsdinstall to set up disks on most recent FreeBSDs >= 9.x
USEBSDINSTALL=1
BSDLABEL_DUMP='bsdlabel.dump'
SMART=0
DEVS="ad ada vtbd"

log ""
log "`date '+%F %H:%M'` Started FreeBSD based distributive deployment"
if [ ! -e $CONF ]; then
    log "File not found: $CONF !"
    failed "-1"
fi

. $CONF

if [ -e $SRC_DIR/boot-freebsd-onapp.version ]; then
    log "Using OnApp FreeBSD provisioning ISO version:"
    run_and_log "cat $SRC_DIR/boot-freebsd-onapp.version"
fi

log "'boot-freebsd-onapp' ISO used: `uname -a`"

if [ "x$RUN" = "x" -o $RUN -ne 1 ]; then
    log "Exiting ... (because of RUN=${RUN})"
    exit 0
fi

run_and_log "rm -f $PROVISION_COMPLETE"

ROOT_DEV=
for DEV in ${DEVS}; do
    if [ -e /dev/${DEV}0 ]; then
        ROOT_DEV=${DEV}
    fi
done

#if [ ! -e /dev/${ROOT_DEV}0 ]; then
#    log "${ROOT_DEV}0 not found !"
    #failed "-1"
#fi

if [ "x${ROOT_DEV}" = "x" ]; then
    log "None of supported block devices (${DEVS}) found !"
    failed "-1"
fi

log "The /dev/${ROOT_DEV}0 will be used to provision"

if [ $REBUILDFS -ne 0 ]; then
    log "Disks' partitions/file-system will be recreated"
    run_and_log "dd if=/dev/zero of=/dev/${ROOT_DEV}0 bs=1k count=1"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
fi

log "Virtualization: ${VIRTUALIZATION}"
if [ "x${VIRTUALIZATION}" = "xxen" -a $USESYSINSTALL -ne 1 -a $USEBSDINSTALL -ne 1 ]; then
    if [ ! -e /dev/${ROOT_DEV}0s1 -o $REBUILDFS -ne 0 ]; then
        log "Installing MBR ..."
        run_and_log "gpart create -s MBR ${ROOT_DEV}0"
        if [ $? -ne 1 ]; then
            failed "-1"
        fi
        log "Creating bootable slice on the ${ROOT_DEV}0"
        run_and_log "fdisk -BI ${ROOT_DEV}0"
        if [ $? -ne 1 ]; then
            failed "-1"
        fi
        log "Installing boot loader into the slice"
        run_and_log "dd if=/dev/zero of=/dev/${ROOT_DEV}0s1 bs=512 count=32"
        if [ $? -ne 1 ]; then
            failed "-1"
        fi
    fi

    if ! gpart show ${ROOT_DEV}0s1 >/dev/null 2>&1 || [ $REBUILDFS -ne 0 ]; then
        log "Writing BSD lables"
        run_and_log "bsdlabel -w -B ${ROOT_DEV}0s1"
        if [ $? -ne 1 ]; then
            failed "-1"
        fi
    fi

    if ! gpart show ${ROOT_DEV}0s1 | grep freebsd-ufs >/dev/null 2>&1 || [ $REBUILDFS -ne 0 ]; then
        log "Modefy slice filesystem to be freebsd-ufs"
        run_and_log "gpart modify -i 1 -t freebsd-ufs ${ROOT_DEV}0s1"
        if [ $? -ne 1 ]; then
            failed "-1"
        fi
    fi

    if ! /sbin/dumpfs /dev/${ROOT_DEV}0s1a >/dev/null 2>&1 || [ $REBUILDFS -ne 0 ]; then
        log "Formatting ${ROOT_DEV}0s1a into UFS2"
        run_and_log "newfs -O2 ${ROOT_DEV}0s1a >/dev/null 2>&1"
        if [ $? -ne 1 ]; then
            failed "-1"
        fi
    fi
fi

if [ $USEBSDINSTALL -eq 1 -a $USESYSINSTALL -ne 1 ]; then
    log "Running bsdinstall to set up ${ROOT_DEV}0 non-interactively"
    /bin/sh -c "export TERM=xterm; bsdinstall scriptedpart ${ROOT_DEV}0 MBR { auto freebsd-ufs / }"
    if [ $? -ne 0 ]; then
        failed "-1"
    fi
fi

if [ $USEBSDINSTALL -ne 1 ] && [ "x${VIRTUALIZATION}" = "xkvm" -o "x${VIRTUALIZATION}" = "xkvm_virtio" -o $USESYSINSTALL -eq 1 ]; then

    log "Running sysinstall in batch mode: diskPartitionEditor"
    /bin/sh -c "export TERM=xterm; /usr/sbin/sysinstall debug=NO nonInteractive=YES noConfirm=YES noWarn=YES disk=${ROOT_DEV}0 partition=all bootManager=standard diskPartitionEditor diskPartitionWrite"
    if [ $? -ne 0 ]; then
        failed "-1"
    fi

    log "Running sysinstall in batch mode: diskLabelEditor"
    /bin/sh -c "export TERM=xterm; /usr/sbin/sysinstall debug=NO nonInteractive=YES noConfirm=YES noWarn=YES disk=${ROOT_DEV}0 '${ROOT_DEV}0s1-1=ufs 0 ${ROOT_MOUNT_POINT} 0' diskLabelEditor diskLabelCommit"
    if [ $? -ne 0 ]; then
        failed "-1"
    fi

    if mount | grep "/dev/${ROOT_DEV}0s1d" >/dev/null 2>&1; then
        log "Umounting /dev/${ROOT_DEV}0s1d"
        run_and_log "umount /dev/${ROOT_DEV}0s1d >/dev/null 2>&1"
    fi

    log "Read the BSD label on a disk partition ${ROOT_DEV}0s1 and store to the file ${BSDLABEL_DUMP}"
    run_and_log "bsdlabel ${ROOT_DEV}0s1 | sed 's/d:/a:/g' > ${BSDLABEL_DUMP}"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi

    if [ ! -e $BSDLABEL_DUMP ]; then
        log "File not found: ${BSDLABEL_DUMP}"
        failed "-1"
    fi

    log "Restore the BSD label on a disk partition ${ROOT_DEV}0s1 from the file ${BSDLABEL_DUMP}"
    run_and_log "bsdlabel -R ${ROOT_DEV}0s1 ${BSDLABEL_DUMP}"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi

fi

log "Toggle Soft Updates on for /dev/${ROOT_DEV}0s1a"
run_and_log "tunefs -n enable /dev/${ROOT_DEV}0s1a"
if [ $? -ne 1 ]; then
    failed "-1"
fi

if ! mount | grep "/dev/${ROOT_DEV}0s1a" >/dev/null 2>&1; then
    log "Mounting /dev/${ROOT_DEV}0s1a on $ROOT_MOUNT_POINT"
    run_and_log "mount /dev/${ROOT_DEV}0s1a $ROOT_MOUNT_POINT"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
fi

if [ ! -d ${ROOT_MOUNT_POINT}/boot ]; then
    log "Unpacking ${ONAPP_FREEBSD_TEMPLATE} template into $ROOT_MOUNT_POINT"
    cd $ROOT_MOUNT_POINT
    if [ $? -ne 0 ]; then
        failed "-1"
    fi

    ## It is also possible to use /usr/local/bin/gtar (GNU tar) here
    run_and_log "tar -pzxf ${SRC_DIR}/${ONAPP_FREEBSD_TEMPLATE} >/dev/null"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
    cd - >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        failed "-1"
    fi
fi

cd $ROOT_MOUNT_POINT
if [ $? -ne 0 ]; then
    failed "-1"
fi
if [ -f boot/loader.conf.${VIRTUALIZATION}.ada ]; then
    run_and_log "cp boot/loader.conf.${VIRTUALIZATION}.ada boot/loader.conf"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
elif [ -f boot/loader.conf.${VIRTUALIZATION} ]; then
    run_and_log "cp boot/loader.conf.${VIRTUALIZATION} boot/loader.conf"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
else
    if [ "x${VIRTUALIZATION}" = "xkvm" -o "x${VIRTUALIZATION}" = "xkvm_virtio" ]; then
        log "boot/loader.conf.${VIRTUALIZATION} not found!"
        failed "-1"
    fi
fi
cd - >/dev/null 2>&1
if [ $? -ne 0 ]; then
    failed "-1"
fi

log "Setting kern.geom.debugflags=16"
run_and_log "sysctl kern.geom.debugflags=16"
if [ $? -ne 1 ]; then
    failed "-1"
fi

log "Creating the rc.d script to change root password"
run_and_log "mkdir -p ${ROOT_MOUNT_POINT}/usr/local/etc/rc.d"
CHPASS_ROOT="${ROOT_MOUNT_POINT}/usr/local/etc/rc.d/chpass_root.sh"
CHPASS_ROOT_DATA="#!/bin/sh
echo -n ' chpass_root.sh'
case \"\$1\" in
start)
        echo ' Changing password for user root'
        chpass -p '$ROOT_PASSWD' root
        rm -f /var/run/chpass_root.pid /usr/local/etc/rc.d/chpass_root.sh
        ;;
stop)
        kill -9 \`cat /var/run/chpass_root.pid\`
        ;;
*)
        echo \"Usage: \`basename \$0\` {start|stop}\" >&2
        exit 64
        ;;
esac
exit 0"

cat <<! >$CHPASS_ROOT
$CHPASS_ROOT_DATA
!

if [ ! -e $CHPASS_ROOT ]; then
    log "Failed to create the rc.d script to change root password !"
    failed "-1"
fi

run_and_log "chmod 755 $CHPASS_ROOT"
if [ $? -ne 1 ]; then
    failed "-1"
fi

ETC_LOCALTIME=/etc/localtime
ZONEINFO_DIR=/usr/share/zoneinfo
if [ -e ${ZONEINFO_DIR}/${TIME_ZONE} ]; then
    log "Set timezone into $TIME_ZONE"
    run_and_log "/bin/cp -a ${ROOT_MOUNT_POINT}${ZONEINFO_DIR}/${TIME_ZONE} ${ROOT_MOUNT_POINT}${ETC_LOCALTIME}"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
else
    log "Timezone $TIME_ZONE file doesn't exist"
fi

log "Configuring network"
RC_CONF=${ROOT_MOUNT_POINT}/etc/rc.conf
if [ ! -e ${RC_CONF}.bak ]; then
    run_and_log "cp -a $RC_CONF ${RC_CONF}.bak"
fi
run_and_log "cat ${ROOT_MOUNT_POINT}/etc/rc.conf | grep -v network_interfaces | grep -v ifconfig_ | grep -v defaultrouter | grep -v hostname | grep -v sshd_enable | grep -v ipv6_enable | grep -v ipv6_defaultrouter | grep -v ipv6_ifconfig | grep -v ipv6_gateway_enable | grep -v ntpd_enable | grep -v ntpdate_enable > ${RC_CONF}.new"

if [ -e ${NETWORK_CONF} ]; then
    cat ${NETWORK_CONF} >> ${RC_CONF}.new
else
    log "Network configuration file ${NETWORK_CONF} not found!"
fi

if [ ${SMART} -eq 1 ]; then
    log "Running SmartServer related tasks ..."

    rm -f ${SRC_DIR}/nic_macaddr.map
    # HACK we will probably need to grep more the 2 symbols for the NIC name
    dmesg | grep -E '^[[:alpha:]]{2,3}[[:digit:]]{1}:[[:space:]]+Ethernet address:[[:space:]]+[a-zA-Z0-9:]+$' | sed 's/^\([[:alpha:]]\{2,3\}[[:digit:]]\{1\}\):[[:space:]]*Ethernet[[:space:]]*address:[[:space:]]*\([a-f0-9:]\{17\}\)$/\1 \2/g' > ${SRC_DIR}/nic_macaddr.map

    if [ -e ${SRC_DIR}/nic_macaddr.map ]; then
        while read NETIF_NAME_DMESG MAC_DMESG; do
            # Managing NICs for IPv4
            if [ -e ${SRC_DIR}/macaddr_ipv4.map ]; then
                while read MAC_SMART IP_SMART; do
                    if [ "x${MAC_DMESG}" = "x${MAC_SMART}" ]; then
                        log "Reconfiguring NIC '${NETIF_NAME}*' into '${NETIF_NAME_DMESG}' for '${MAC_DMESG}' and ipv4 '${IP_SMART}'"
                        run_and_log "sed -i.bak 's/ifconfig_${NETIF_NAME}.=\"inet ${IP_SMART} /ifconfig_$NETIF_NAME_DMESG=\"inet ${IP_SMART} /g' ${RC_CONF}.new"
                        if [ $? -ne 1 ]; then
                            failed "-1"
                        fi
                        log "Reconfiguring NIC '${NETIF_NAME}*' aliases into '${NETIF_NAME_DMESG}' for '${MAC_DMESG}' and ipv4 '${IP_SMART}'"
                        run_and_log "sed -i.bak 's/ifconfig_${NETIF_NAME}.\(_alias.\)=\"inet ${IP_SMART} /ifconfig_$NETIF_NAME_DMESG\1=\"inet ${IP_SMART} /g' ${RC_CONF}.new"
                        if [ $? -ne 1 ]; then
                            failed "-1"
                        fi
                        run_and_log "rm -f ${RC_CONF}.new.bak"
                    fi
                done < ${SRC_DIR}/macaddr_ipv4.map
            fi
            unset MAC_SMART IP_SMART

            # Managing NICs for IPv6
            if [ -e ${SRC_DIR}/macaddr_ipv6.map ]; then
                while read MAC_SMART IP_SMART; do
                    if [ "x${MAC_DMESG}" = "x${MAC_SMART}" ]; then
                        log "Reconfiguring NIC '${NETIF_NAME}*' into '${NETIF_NAME_DMESG}' for '${MAC_DMESG}' and ipv6 '${IP_SMART}'"
                        run_and_log "sed -i.bak 's/ipv6_ifconfig_${NETIF_NAME}.=\"${IP_SMART}\//ipv6_ifconfig_$NETIF_NAME_DMESG=\"${IP_SMART}\//g' ${RC_CONF}.new"
                        if [ $? -ne 1 ]; then
                            failed "-1"
                        fi
                        log "Reconfiguring NIC '${NETIF_NAME}*' aliases into '${NETIF_NAME_DMESG}' for '${MAC_DMESG}' and ipv6 '${IP_SMART}'"
                        run_and_log "sed -i.bak 's/ipv6_ifconfig_${NETIF_NAME}.\(_alias.\)=\"${IP_SMART}\//ipv6_ifconfig_$NETIF_NAME_DMESG\1=\"${IP_SMART}\//g' ${RC_CONF}.new"
                        if [ $? -ne 1 ]; then
                            failed "-1"
                        fi
                        run_and_log "rm -f ${RC_CONF}.new.bak"
                    fi
                done < ${SRC_DIR}/macaddr_ipv6.map
            fi
         done < ${SRC_DIR}/nic_macaddr.map
    fi
fi

log "Enabling ntpd and ntpdate at boot time"
run_and_log "echo ntpdate_enable=\"YES\" >> ${RC_CONF}.new"
run_and_log "echo ntpd_enable=\"YES\" >> ${RC_CONF}.new"

run_and_log "mv -f ${RC_CONF}.new ${RC_CONF}"
if [ $? -ne 1 ]; then
    failed "-1"
fi

log "Configuring hosts"
HOSTS=${ROOT_MOUNT_POINT}/etc/hosts
for IP in $IPADDR; do
    run_and_log "cat $HOSTS | grep -v \"$IP\" >> ${HOSTS}.new"
    run_and_log "echo \"$IP ${HOSTNAME}.${DOMAIN} ${HOSTNAME}\" >> ${HOSTS}.new"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
    run_and_log "mv -f ${HOSTS}.new ${HOSTS}"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
    break
done

log "Configuring nameservers"
RESOLV_CONF="${ROOT_MOUNT_POINT}/etc/resolv.conf"
run_and_log "cat $RESOLV_CONF | grep -v domain | grep -v nameserver >> ${RESOLV_CONF}.new"
run_and_log "echo \"domain $DOMAIN\" >> ${RESOLV_CONF}.new"
if [ $? -ne 1 ]; then
    failed "-1"
fi
for NAMESERVER in $NAMESERVERS; do
    run_and_log "echo \"nameserver $NAMESERVER\" >> ${RESOLV_CONF}.new"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
done
run_and_log "mv -f ${RESOLV_CONF}.new ${RESOLV_CONF}"
if [ $? -ne 1 ]; then
    failed "-1"
fi

log "Generating SSH host keys"
run_and_log "yes | ssh-keygen -t rsa1 -f ${ROOT_MOUNT_POINT}/etc/ssh/ssh_host_key -N \"\""
run_and_log "yes | ssh-keygen -t dsa -f  ${ROOT_MOUNT_POINT}/etc/ssh/ssh_host_dsa_key -N \"\""
run_and_log "yes | ssh-keygen -t rsa -f  ${ROOT_MOUNT_POINT}/etc/ssh/ssh_host_rsa_key -N \"\""

if [ ! -e ${ROOT_MOUNT_POINT}/root/.ssh ]; then
    run_and_log "mkdir -p ${ROOT_MOUNT_POINT}/root/.ssh"
fi
if [ -e ${ROOT_MOUNT_POINT}/root/.ssh -a ! -e ${ROOT_MOUNT_POINT}/root/.ssh/authorized_keys -a "x$SSHPUBKEYS" != "x" ]; then
    log "Setting ssh public keys for remote connections"
    run_and_log "echo '$SSHPUBKEYS' > ${ROOT_MOUNT_POINT}/root/.ssh/authorized_keys"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
    run_and_log "chown root:wheel ${ROOT_MOUNT_POINT}/root/.ssh/authorized_keys"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
    run_and_log "chmod 0600 ${ROOT_MOUNT_POINT}/root/.ssh/authorized_keys"
    if [ $? -ne 1 ]; then
        failed "-1"
    fi
fi

log "Cleaning logs"
run_and_log "rm -f ${ROOT_MOUNT_POINT}/var/log/lastlog"
run_and_log "touch ${ROOT_MOUNT_POINT}/var/log/lastlog"
run_and_log "rm -f ${ROOT_MOUNT_POINT}/var/log/wtmp"
run_and_log "touch ${ROOT_MOUNT_POINT}/var/log/wtmp"
run_and_log "rm -f ${ROOT_MOUNT_POINT}/var/log/utmp"
run_and_log "touch ${ROOT_MOUNT_POINT}/var/log/utmp"
run_and_log "rm -f ${ROOT_MOUNT_POINT}/var/log/utx.log"
run_and_log "touch ${ROOT_MOUNT_POINT}/var/log/utx.log"
run_and_log "rm -f ${ROOT_MOUNT_POINT}/var/log/utx.lastlogin"
run_and_log "rm -f \"${ROOT_MOUNT_POINT}/root/.history\""

FSTAB="${ROOT_MOUNT_POINT}/etc/fstab"
FSTAB_DATA="# Device		Mountpoint	FStype	Options		Dump	Pass#
/dev/${ROOT_DEV}1		none		swap	sw		0	0
/dev/${ROOT_DEV}0s1a		/		ufs	rw		1	1"

if [ -f ${FSTAB}.${VIRTUALIZATION}.ada ]; then
    log "fstab from ${FSTAB}.${VIRTUALIZATION}.ada"
    run_and_log "rm -f $FSTAB"
    run_and_log "/bin/cp -fa ${FSTAB}.${VIRTUALIZATION}.ada ${FSTAB}"
elif [ -f ${FSTAB}.${VIRTUALIZATION} ]; then
    log "fstab from ${FSTAB}.${VIRTUALIZATION}"
    run_and_log "rm -f $FSTAB"
    run_and_log "/bin/cp -fa ${FSTAB}.${VIRTUALIZATION} ${FSTAB}"
else
    log "Creating $FSTAB"
    cat <<! >$FSTAB
$FSTAB_DATA
!
fi

if [ ! -e $FSTAB ]; then
    log "Failed to create fstab !"
    failed "-1"
fi

log "`date '+%F %H:%M'` Finished FreeBSD based distributive deployment"
uptime | tee -a $LOG

run_and_log "cp -av ${LOG} ${ROOT_MOUNT_POINT}/var/log/"
run_and_log "touch $PROVISION_COMPLETE"

if mount | grep "/dev/${ROOT_DEV}0s1a" >/dev/null 2>&1; then
    log "Umounting /dev/${ROOT_DEV}0s1a"
    run_and_log "umount /dev/${ROOT_DEV}0s1a >/dev/null 2>&1"
fi

log "Halting and power off guest system now..."
run_and_log "shutdown -p now"
log "Schedule guest system halt and power off in a minute (if background file-system check runs)."
run_and_log "shutdown -p +1"

exit 0
