#!/bin/bash
#******************************************************************************
# @(#) START_UML: start the UML instance for a particular user
# @(#) Copyright (C) 2006 by KUDOS BVBA <info@kudos.be>.  All rights reserved.
# @(#) $Id: start_uml,v 1.1 2006/07/22 20:54:38 patrick Exp $
# @(#) vim: set et ts=4 sw=4 ffs=unix:
#******************************************************************************
# This program is a free software; you can redistribute it and/or modify
# it under the same terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
#
#******************************************************************************
# Description: this script will start the UML instance for a particular system
# user by fetching all required UML startup parameters from the <config file>.
#
# Important Notes:
# 1. only one UML instance per UML user is supported
# 2. the UML instance uses a read-only root_fs and swap_fs in conjunction with
#    a set of read-write COW files in the system user's home directory
# 3. the read-only root_fs & swap_fs may be shared between multiple instances
#    and must be owned by the 'uml:uml' user/group and must also have group
#    readable permissions
# 4. the system user for which the UML instance is to be started must be 
#    member of the 'uml' group (/etc/group)
# 5. the COW files must be owned by the UML instance owner and must be writable
#    to the same user
# 6. Virtual networking is provided by TUN/TAP that are automatically
#    configured using the 'uml_helper' utility (part of UML utilities)
# 7. the UML instance is launched using the 'screen' utility
#
#******************************************************************************

#******************************************************************************
# DATA structures
#******************************************************************************

# -------------------- configuration starts here ------------------------------
# path setting
PATH="/usr/bin:/usr/sbin:/usr/local/bin:/sbin:/bin"
# --------------------- configuration ends here -------------------------------
SCRIPT_NAME="$(basename $0)"


#******************************************************************************
# FUNCTION routines
#******************************************************************************

# display help text
function display_usage ()
{
    cat << EOT
**** ${SCRIPT_NAME} **** 
(c) 2006 - KUDOS BVBA (Patrick Van der Veken) ****

Start UML instance for a particular system user

Syntax: ./${SCRIPT_NAME} [-h] [<path to config file>]

-h            : show this help text
<config file> : absolute path to UML configuration file for UML instance
                if not specified then the ~.uml_config file will be used
EOT
    exit 0
}

# check that we run as root
function check_user ()
{
    PROC_OWNER=$(IFS='()'; set -- $(id); echo $2)
    [[ "${PROC_OWNER}" != "root" ]] && { 
        echo "ERROR: script must be run as root"
        exit 1
    } 
    return 0
}

# check files/dirs configuration parameters
function check_config ()
{
    # check for memory size
    [[ -z "${UML_MEM}" ]] && {
        echo "ERROR: UML memory size not defined"
        exit 1
    }
    # check for tap IP address
    [[ -z "${TAP_IP}" ]] && {
        echo "ERROR: TAP device IP address not defined"
        exit 1
    }
    # check UML owner
    [[ -z "${UML_OWNER}" ]] && {
        echo "ERROR: UML owner not defined"
        exit 1
    }
    # check if kernel exist and is executable
    [[ ! -x ${UML_KERNEL} ]] && {
        echo "ERROR: missing or non-executable UML kernel: ${UML_KERNEL}"
        exit 1
    }
    # check if root_fs exists
    [[ ! -f ${ROOT_FS} ]] && {
        echo "ERROR: missing or non-readable root_fs: ${ROOT_FS}"
        exit 1
    }
    # check if swap_fs exists
    [[ ! -f ${SWAP_FS} ]] && {
        echo "ERROR: missing or non-readable swap_fs: ${SWAP_FS}"
        exit 1
    }
    # check if COW files exist
    [[ ! -f ${COW_ROOT_FS} ]] && {
        echo "ERROR: missing COW file for root_fs: ${COW_ROOT_FS}"
        exit 1
    }
    [[ ! -f ${COW_SWAP_FS} ]] && {
        echo "ERROR: missing COW file for swap_fs: ${COW_SWAP_FS}"
        exit 1
    }
    # fetch ownerships/permissions
    LS_BIT1=$(ls -l ${ROOT_FS} | awk '{ printf ("%s:%s:%s", $1, $3, $4) }')
    LS_BIT2=$(ls -l ${SWAP_FS} | awk '{ printf ("%s:%s:%s", $1, $3, $4) }')
    LS_BIT3=$(ls -l ${COW_ROOT_FS} | awk '{ printf ("%s:%s:%s", $1, $3, $4) }')
    LS_BIT4=$(ls -l ${COW_SWAP_FS} | awk '{ printf ("%s:%s:%s", $1, $3, $4) }')
    # check ownership/permissions of root_fs
    [[ "${LS_BIT1}" != "-r--r-----:uml:uml" ]] && {
        echo "ERROR: wrong ownership/permissions on root_fs: ${ROOT_FS}; should be: -r--r-----:uml:uml" ]]
        exit 1
    }
    [[ "${LS_BIT2}" != "-r--r-----:uml:uml" ]] && {
        echo "ERROR: wrong ownership/permissions on swap_fs: ${SWAP_FS}; should be: -r--r-----:uml:uml" ]]
        exit 1
    }
    [[ "${LS_BIT3}" != "-rw-r-----:${UML_OWNER}:uml" ]] && {
        echo "ERROR: wrong ownership/permissions on COW root_fs: ${COW_ROOT_FS}; should be: -rw-r-----:${UML_OWNER}:uml" ]]
        exit 1
    }
    [[ "${LS_BIT4}" != "-rw-r-----:${UML_OWNER}:uml" ]] && {
        echo "ERROR: wrong ownership/permissions on COW swap_fs: ${COW_SWAP_FS}; should be: -rw-r-----:${UML_OWNER}:uml" ]]
        exit 1
    }
    return 0
}

# check for helpers applications
function check_helpers
{
    UML_NET=$(which uml_net)
    [[ ! -x ${UML_NET} ]] && {
        echo "ERROR: cannot find UML helper 'uml_net' in PATH: ${PATH}"  
        exit 1
    }
    SCREEN=$(which screen)
    [[ ! -x ${SCREEN} ]] && {
        echo "ERROR: cannot find 'screen' utility in PATH: ${PATH}"  
        exit 1
    }
    return 0
}

# check for running UML instance
function check_uml 
{
    CHECK_UML=$(screen -ls | grep -c "${UML_OWNER}")
    [[ ${CHECK_UML} -ne 0 ]] && {
        echo "ERROR: UML instance already running for user: ${UML_OWNER}"
        exit 1
    }
    return 0
}


#******************************************************************************
# MAIN routine
#******************************************************************************

# check for cmd line option
if [[ $# -eq 1 ]] 
then
    if [[ "$1" = "-h" ]]
    then
        display_usage
    else
        CONFIG_FILE=$1
    fi
else
    CONFIG_FILE="./.uml_config"
fi

# read configuration
if [[ ! -f ${CONFIG_FILE} ]]
then
    echo "ERROR: could not find config file: ${CONFIG_FILE}"
    exit 1
else
    . ${CONFIG_FILE}
fi

# do pre-launch checks
echo "Checking configuration ..."
check_config
echo "Checking UML user ..."
check_user
echo "Checking UML helper applications ..."
check_helpers
echo "Checking for existing UML instance ..."
check_uml

# launch UML
UML_CMD="${UML_KERNEL} mem=${UML_MEM} ubd0=${COW_ROOT_FS},${ROOT_FS} ubd1=${COW_SWAP_FS},${SWAP_FS} eth0=tuntap,,,${TAP_IP} con0=fd:0,fd:1 con=null root=/dev/ubda u
mid=${UML_OWNER}"
if [[ "${UML_DETACH}" = "yes" ]]
then
    echo "Starting UML in detached mode ..."
    screen -d -m -S ${UML_OWNER} su - ${UML_OWNER} -c "${UML_CMD}"
else
    echo "Starting UML in non-detached mode ..."
    screen -S ${UML_OWNER} su - ${UML_OWNER} -c "${UML_CMD}"
fi

exit 0

#******************************************************************************
# END of script 
#******************************************************************************
