#!/bin/bash
# $Id: functions,v 1.11 2005/08/15 11:33:05 kir Exp $
#
# Copyright (C) 2004, 2005, SWsoft. Licensed under QPL.
# By Kir Kolyshkin.
#
# Various definitions and functions used by vzpkg tools.


# Set some parameters; can be overwritten by scripts.
test -z "$PROGNAME"	&& PROGNAME=`basename $0`
test -z "$VZP_LOGFILE"	&& VZP_LOGFILE=/var/log/vzpkg.log
test -z "$DEBUG_LEVEL"	&& DEBUG_LEVEL=3

# Some handy definitions
VZCTL=/usr/sbin/vzctl
VZLOCKDIR=/vz/lock
VECFGDIR=/etc/sysconfig/vz-scripts/
VZCFG=/etc/sysconfig/vz


# Handy functions


# Generic log; please use logN instead.
function log()
{
	local level=$1; shift
	test $level -gt $DEBUG_LEVEL && return 0
	local addstr
	case $level in
		1) addstr="ERROR: " ;;
		2) addstr="Warning: " ;;
		3) addstr="" ;;
		4) addstr="Debug: " ;;
		5) addstr="ExtDebug: " ;;
		*) addstr="-UNKNOWN_LEVEL- " ;;
	esac

	local msg="${addstr}$*"
	# Warnings and errors goes to stderr
	if test $level -lt 3; then
		echo -e $msg 1>&2
	else
		echo -e $msg
	fi
	# Log to file
	if ! test -z "$VZP_LOGFILE"; then
		local date=`LANG=C date "+%b %d %H:%M:%S"`
		local logmsg="$date [$PROGNAME] $msg"
		echo -e "$logmsg" >> $VZP_LOGFILE
	fi
}

function log1()
{
	log 1 $*
}

function log2()
{
	log 2 $*
}

function log3()
{
	log 3 $*
}

function log4()
{
	log 4 $*
}

function log5()
{
	log 5 $*
}

# Report error and exit
function abort()
{
	log 1 $*
	exit 1
}

# Calls template-specific script to be run from host system (VPS0)
# Parameters:
#  $1 - script name
function call_template_script()
{
	local script=$THISTEMPLATE/config/$1
	if ! test -x $script; then
		log4 "Script $script not found " \
			"or non-exec: skipped"
		return 0
	fi
	# Prepare script environment
	set -u
	export VE_ROOT
	export VE_PRIVATE
	export TEMPLATE
	export BASEDIR
	export VEID
	export VZCTL
	set +u
	log4 Calling script $script
	# Run script
	$script
}

## Borrowed from vzpkgtools

# Locks the VPS
# Parameters:
#  $1 - VPS ID
function lock_ve()
{
    local ntries=0 file=$VZLOCKDIR/$1.lck warned=0
    while [ $ntries -le 3 ]; do
        ntries=$[ntries+1]
	if lockfile -1 -r1 $file 2>/dev/null; then
	    echo -e "$$\nupdating" >$file
	    return
	else
	    [ -f $file ] || abort "Cannot create $file lockfile"
	    local pid=`cat $file 2>/dev/null`
	    pid=`echo $pid | awk '{print $1}'`
	    if [ "$pid" -a -e /proc/$pid/cmdline ]; then
		if [ $warned -eq 0 ]; then
		    log2 "VPS $1 locked by pid=$pid"
		    warned=1
		fi
		continue
	    else
		log2 "Removing stale lockfile $file, pid=$pid"
		rm -f $file
	    fi
	fi
    done
    abort "Too many retries waiting for lockfile $file"    
}

# Unlocks VPS
# Parameters:
#  $1 - VPS ID
function unlock_ve()
{
    local file=$VZLOCKDIR/$1.lck
#   log4 Unlocking VPS $1
    rm -f $file
}

function lock_ve_silent()
{
    local ntries=0 file=$VZLOCKDIR/$1.lck
    while [ $ntries -le 3 ]; do
        ntries=$[ntries+1]
	if lockfile -1 -r1 $file 2>/dev/null; then
	    echo -e "$$\nupdating" >$file
	    return 0
	else
	    [ -f $file ] || return 1
	    local pid=`cat $file 2>/dev/null`
	    pid=`echo $pid | awk '{print $1}'`
	    if [ "$pid" -a -e /proc/$pid/cmdline ]; then
	    	continue
	    else
		log2 "Removing stale lockfile $file, pid=$pid"
		rm -f $file
	    fi
	fi
    done
    return 1
}


# Find and lock nearest veid
#
function find_lock_nearest_veid()
{
    [ $# -eq 1 ] ||
	abort "$FUNCNAME: usage $FUNCNAME OLD_VEID"
    local old_veid=$1

    local veid=$old_veid
    while ((++veid != old_veid)) ; do
	[ -f $VECFGDIR/$veid.conf ] && continue
    	lock_ve $veid
	rc=$?
   	[ $rc -eq 0 ] && {
	    [ -f $VECFGDIR/$veid.conf ] && {
		unlock_ve $veid
		continue
	    }
	    local status
	    status=`$VZCTL status $veid`
	    # vzctl status should always return 0; if not, something is
	    # really wrong (e.g. /dev/vzctl is missing).
	    if test $? -ne 0; then
		unlock_ve $veid
		abort "$FUNCNAME: 'vzctl status' command returned" \
			"non-zero exit code, which should not happen." \
			"See /var/log/vzctl.log for details"
	    fi
	    echo "$status" | grep -q 'deleted unmounted down' || {
		unlock_ve $veid
		continue
	    }
            # use get_vz_var since VPS config file not exist
	    local private=`VEID=$veid get_vz_var VE_PRIVATE`
	    [ -e "$private" ] && {
		unlock_ve $veid
		continue
	    }
	    > $VECFGDIR/$veid.conf
	    echo $veid
	    return 0
        }
    done
    return 1
}

# Returns requested variable from global VZ config file 
function get_vz_var()
{
	local gotdef=1
	local value=`eval "source $VZCFG && echo \\\$$1"`
	# We have defaults for two most requested things
	if test -z "$value"; then
		case $1 in
			PACKAGES)
				value='/vz/packages'
				;;
			TEMPLATE)
				value='/vz/template'
				;;
			*)
				gotdef=0
		esac
		if test $gotdef -ne 0; then
			log2 "Variable $1 not found in $VZCFG;" \
				"using default ($value)."
		else
			log2 "Variable $1 not found in $VZCFG;" \
				"using empty string."
		fi
	fi
	echo $value
}

# Returns some value from VPS configuration file. Parameters:
#   $1 - needed variable name
#   $VEID - VPS ID
function get_ve_var()
{
	local vecfg=$VECFGDIR/$VEID.conf
	local value=`eval "source $vecfg && echo \\\$$1"`
	echo $value
}

# 
# Add/update single var to /etc/sysconfig/vz-scripts/$VEID.conf
# $VEID passed as global variable
#
function save_ve_var()
{
    [ $# -eq 2 ] || abort "$FUNCNAME: Usage $FUNCNAME VAR VALUE"
    local veconfig s var=$1 value=$2 tmpfile
#    if [ $VECFGDIR ]; then
        veconfig="$VECFGDIR/$VEID.conf"
#    else
#	    veconfig="$_RH_VECONFDIR/$VEID.conf"
#    fi
    [ -r $veconfig ] || abort "Could not open $veconfig."

    tmpfile=`mktemp /tmp/temp.XXXXXX` ||
        abort "Could not create temporary file."
    awk -f - << 'EOF' > $tmpfile tvar="$var" tval="$value" $veconfig
BEGIN{
    found=0;
}
($0 ~ "^" tvar "=.*$" ){
    printf "%s=\"%s\"\n", tvar, tval;
    found=1;
    next;
}
{
    print $0;
}
END{
    if (!found){
        printf "%s=\"%s\"\n", tvar, tval;
    }
}
EOF
    [ $? -eq 0 ] || abort "Could not save $val=$value in $veconfig."
    mv -f $tmpfile $veconfig
}



# Checks if VPS ID is number
# Parameters:
#  $1 - VPS ID
function check_veid()
{
	echo $1 | egrep -q '^[[:digit:]]+$'
}


# Converts OS template name to it's basedir.
# Parameters:
#  $1 - OS template name (like "redhat-9")
# Returns:
#  OS template basedir (like "redhat/9")

function name2basedir()
{
	echo $1 | sed 's#-\([^-]*\)$#/\1#'
}

# Returns full name of template config file.
# Parameters:
#  $1 - OS template name (like "redhat-9")
#  $2 - optional app template name (like "mysql")
#  THISTEMPLATE - template base dir (should be set!)
function get_tmpl_config()
{
	if test "x$THISTEMPLATE" == "x"; then
		log1 "Internal error! THISTEMPLATE is undefined " \
			"in call to get_tmpl_config. Aborting!"
		exit 1
	fi
	local osname=$1
	local basedir=`name2basedir $osname`
	local cfgdir=$THISTEMPLATE/config
	if test $# -eq 1; then
		# OS template
		local file=$cfgdir/ostemplate.conf
		echo $file
	elif test $# -eq 2; then
		# App template
		appname=$2
		local file=$cfgdir/$appname/template.conf
		echo $file
	else
		log1 "Internal error! get_tmpl_config requires 1 or 2 args!"
		exit 1
	fi
}

# Returns list of packages to be installed into VPS.
# Parameters:
#  $1 - OS template name (like "fedora-3")
#  $2 - optional app template name (like "mysql")
#  THISTEMPLATE - template base dir (should be set!)
function get_packages()
{
	if test "x$THISTEMPLATE" == "x"; then
		log1 "Internal error! THISTEMPLATE is undefined " \
			"in call to get_packages. Aborting!"
		exit 1
	fi
	local osname=$1
	local basedir=`name2basedir $osname`
	local cfgdir=$THISTEMPLATE/config
	if test $# -eq 1; then
		# OS template
		local file=$cfgdir/ostemplate.list
		cat $file | egrep -v '^#|^[[:space:]]*$'
	elif test $# -eq 2; then
		# App template
		appname=$2
		local file=$cfgdir/$appname/package-list
		cat $file | egrep -v '^#|^[[:space:]]*$'
	else
		log1 "Internal error! get_packages requires 1 or 2 args!"
		exit 1
	fi
}

function get_all_basedirs()
{
	local packages=$PACKAGES
	if test -z "$packages"; then
		packages=`get_vz_var PACKAGES`
	fi
	local basedirs=`find $packages -maxdepth 2 -mindepth 2 -type d \
		-print | sed "s@^$packages[/]*@@"`
	echo $basedirs
}

function get_all_ostemplates()
{
	local tdir pdir ost ret
	local template=$TEMPLATE
	local packages=$PACKAGES
	if test -z "$template"; then
		template=`get_vz_var TEMPLATE`
	fi
	if test -z "$packages"; then
		packages=`get_vz_var PACKAGES`
	fi
	local pdirs=`find $packages -maxdepth 2 -mindepth 2 -type d -print`
	for pdir in $pdirs; do
		tdir=`echo $pdir | sed -e "s@^$packages@$template@"`
		if test -f $tdir/config/ostemplate.conf; then
			ost=`echo $tdir | sed -e "s@^$template[/]*@@" \
				-e 's@/\([^/]*\)$@-\1@'`
			ret="$ret $ost"
		fi
	done
	echo $ret
}


#
# Parameters:
# $1 - name of the OS template to look app-templates for
#
function get_app_templates()
{
    local osname=$1 aconf ret at
    local osdir=`name2basedir $osname`
    local template=$TEMPLATE
    local packages=$PACKAGES
    if test -z "$template"; then
	template=`get_vz_var TEMPLATE`
    fi
    if test -z "$packages"; then
	packages=`get_vz_var PACKAGES`
    fi
    local aconfigs=`find $template/$osdir/config -name template.conf -print`
    for aconf in $aconfigs; do
	at=`echo $aconf | sed -e "s@^$template/$osdir/config/@@" -e "s@/template.conf@@"`
	ret="$ret $at"
    done
    echo $ret
}

function get_ve_os_template()
{
	test -z "$VEID" && abort "get_ve_os_template(): VEID is not set!"

	OSTEMPLATE=`get_ve_var OSTEMPLATE`
	test -z "$OSTEMPLATE" && abort "OSTEMPLATE is not set for VPS $VEID!"
	if echo $OSTEMPLATE | fgrep -q '/'; then
		abort "VPS $VEID can not be used with vzpkg utilities."
	fi
#
#	It seems that THISTEMPLATE should be set in gets_and_checks later
#
#	local basedir=`name2basedir $OSTEMPLATE`
#	THISTEMPLATE=$TEMPLATE/$basedir
}

# Returns -c parameters needed for yum. OSTEMPLATE should be set.
function yum_conf()
{
	test -z "$OSTEMPLATE" && abort "yum_conf(): OSTEMPLATE is not set"
	test -z "$TEMPLATE" && abort "yum_conf(): TEMPLATE is not set"
	local basedir=`name2basedir $OSTEMPLATE`
	local conf=$TEMPLATE/$basedir/config/yum.conf
	test -f $conf || abort "yum repository config file " \
		"($conf) not found!"
	echo "-c $conf"
}


# Import gpgkeys from OS template's config/gpgkeys directory to the VPS.
# Environment: OSTEMPLATE, TEMPLATE, VE_ROOT should be set.
function import_gpgkeys()
{
	test -z "$OSTEMPLATE" && abort "import_gpgkeys(): OSTEMPLATE not set"
	test -z "$TEMPLATE" && abort "import_gpgkeys(): TEMPLATE not set"
	test -z "$VE_ROOT" && abort "import_gpgkeys(): VE_ROOT not set"
	local basedir=`name2basedir $OSTEMPLATE`
	local keydir=$TEMPLATE/$basedir/config/gpgkeys
	test -d $keydir || return
	local file
	local files=''
	for file in $keydir/*; do
		files="$files $file"
	done
	test -z "$files" || rpm --root $VE_ROOT --import $files
}


# full rpm name = name-version-release, get name
function get_rpm_name()
{
    [ $# -eq 1 ] || abort "$FUNCNAME: usage $FUNCNAME RPM"
    local pkg=$1
    shopt -s nullglob extglob
    pkg=${pkg%%-+([^-])}
    pkg=${pkg%%-+([^-])}
    shopt -u nullglob extglob
    echo $pkg
}

#
# Check pretend mode
function_check_pretend()
{
case $1 in
        -p)
	PRETEND=yes
	;;
esac
}

# Gets VPS ID from command line argument
function get_veid()
{
	VEID=$1
	if ! check_veid $VEID; then
		log1 "VPS ID is not a number: $VEID"
		usage 1
	fi
	VE_ROOT=`get_vz_var VE_ROOT`
	VE_PRIVATE=`get_vz_var VE_PRIVATE`
}

function get_cache_dir() {
	test -z "$TEMPLATE" && abort "get_cache_dir: TEMPLATE not set"

	echo $TEMPLATE/cache
}

function get_all_os_templates() {
	local tdir=`get_vz_var TEMPLATE`
	# Here we need it to be ended by slash
	echo $tdir | egrep -q '/$' || tdir="${tdir}/"

	local list=`find $tdir -maxdepth 4 -mindepth 4 -type f \
		-name ostemplate.list | \
		sed -e "s@^${tdir}@@"  | \
		fgrep '/config/ostemplate.list' | \
		sed -e "s@/config/ostemplate.list@@" | \
		sed -e "s@/@-@" `

	echo $list
}

# Check if given OS template exists.
# Parameters:
#   $1 - OS name (like "fedora-3")
function check_ost_exist() {
	local template=`get_vz_var TEMPLATE`
	local thistemplate="$template/`name2basedir $1`"
	local configdir=$thistemplate/config/
	local pkglist=${configdir}ostemplate.list
	test -f $pkglist || abort "OS template $1 does not exist!"
}
