# $Id: routines.sh 1413 2009-12-22 19:08:27Z flip $
#
# Various funky shell script routines
#
# Copyright (c) 2006--2009 Philippe Kehl <flipflip at gmx dot net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# See the documentation for details and copying conditions.


# print an error message to stderr
function err()
{
    echo "Error: $@" 1>&2
    return
}

# routine to get turbostation firmware version and machine type
tsversion=
function get_tsversion()
{
    [ ! -z "$tsversion" ] && { echo "$tsversion"; return; }
    local m v b
    m=`awk '/^Model/ { print $NF; exit; }' /etc/config/uLinux.conf`
    v=`awk '/^Version/ { print $NF; exit; }' /etc/config/uLinux.conf`
    b=`awk '/^Build Number/ { print $NF; exit; }' /etc/config/uLinux.conf`
    tsversion="QNAP TurboStation $m / $v build $b"
    echo "$tsversion"
    return
}


# routine to get diskstation firmware version and machine type
dsversion=
function get_dsversion()
{
    [ ! -z "$dsversion" ] && { echo "$dsversion"; return; }
    local majorversion minorversion buildphase buildnumber builddate
    local unique product vender vendor
    . /etc.defaults/VERSION || majorversion="unknow"
    eval `grep -E '^(unique|product|vend.r)' /etc/synoinfo.conf` \
    || vender=unknow
    dsversion="$vender$vendor $product ${unique##*_} / $majorversion."
    dsversion="$dsversion$minorversion.$buildphase-$buildnumber ($builddate)"
    echo "$dsversion"
    return
}

# routine to get etrayz version and stuff
ezversion=
function get_ezversion()
{
    [ ! -z "$ezversion" ] && { echo "$ezversion"; return; }
    local fwversion
    fwversion=$(</etc/gluesys/version)
    echo "eTRAYz / $fwversion"
    return
}


# routine to report the slimserver/squeezecenter version
# tested with 6.2.2, 6.3.1, 6.5.4 and 7.0(2007-09-22)
function get_ssversion()
{
    [ ! -f ${SSBIN} ] && { echo "none"; return; }
    ssversion=`sed -n 's/^[^#]*\$VERSION[ \t]*=[ \t]*'"'"'\([0123456789\.]*\)'"'"'[ \t]*;/\1/p' ${SSBIN} 2>/dev/null`
    [ -z "$ssversion" ] && ssversion="unknow"
    echo "$ssversion"
    return
}

# get slimserver/squeezecenter revision
function get_ssrevision()
{
    [ -f ${SSDIR}/revision.txt ] && read ssrevision < ${SSDIR}/revision.txt
    [ -z "$ssrevision" ] && ssrevision="unknow"
    echo "$ssrevision"
    return
}

# assert that the current user is root
function assert_root()
{
    local u=`id -u`
    if [[ $u -ne 0 ]]; then
    err "Only root can do that."
    exit 1
    else
    return 0
    fi
}

# assert that the current user is $SSUSER
function assert_ssuser()
{
    local u=`id -un`
    if [ "$u" != "$SSUSER" ]; then
    err "Only $SSUSER can do that."
    exit 1
    else
    return 0
    fi
}

# assert that /proc is mounted
function assert_proc()
{
    if [ ! -L /proc/self ]; then
    err "/proc not mounted."
    exit 1
    else
    return 0
    fi
}

# assert that SS has been installed correctly
function assert_slimserver()
{
    if [ -f ${SSBIN} ]; then
    return 0
    else
    err "No SlimServer tar ball installed in ${SSDIR}."
    exit 1
    fi
}

# routine to send a KILL signal to a process n times, waits m seconds
# to check if successful
function sig_kill()
{
    local c m r
    if [ $# -ne 3 ]; then
    echo "Usage: sig_kill <pid> <n> <m>"
    return 3
    fi
    pid=$1
    n=$2
    m=$3
    if [ ! -f /proc/${pid}/cmdline ]; then
       err "Error: there is no such process (${pid})."
    return 2
    fi
    ntries=0
    while [ -f /proc/${pid}/cmdline ]; do
    echo "Casting the KILL spell on PID ${pid}.."
    kill -SIGKILL ${pid}
    c=0
    echo -n "Waiting "
    while [ $c -lt $m ]; do
        ((r=m-c))
        echo -n "."
        ((c=c+1))
        sleep 1
    done
    echo
    if [ ! -d /proc/${pid} ]; then
        echo "The process takes damage and dies."
        echo "You gain 1 experience point."
        return 0
    else
        echo "Missed!"
        ntries="${ntries}x"
        if [ $ntries -ge $n ]; then
        echo "Giving up. A stronger weapon, such as the Magic Power Button, might help."
        return 1
        fi
    fi
    done
}

# echo PID of a running slimserver, remove pidfile if not running
function get_sspid()
{
    local p
    if [ ! -f ${SSPIDFILE} ]; then
        return 1
    else
        read -t 0.25 p < $SSPIDFILE
        if [ -f /proc/$p/cmdline ]; then
            echo $p
            return 0
        else
            echo $p
            rm -f ${SSPIDFILE}
            return 2
        fi
    fi
}

# echo PID of a running slimserver mysqld, remove pidfile if not running
function get_mysqldpid()
{
    local p
    if [ ! -f ${SSMYSQLDPIDFILE} ]; then
        return 1
    else
        read -t 0.25 p < ${SSMYSQLDPIDFILE}
        if [ -f /proc/$p/cmdline ]; then
            echo $p
            return 0
        else
            echo $p
            rm -f ${SSMYSQLDPIDFILE}
            return 2
        fi
    fi
}

# routine to get values from the slimserver.pref file
# returns lines like "ssprefs_<key>=<value>".
# Example usage:
# eval `get_ssprefs_vars language`; echo $sspref_language
function get_ssprefs_vars()
{
    if [ $# -lt 1 ]; then
        echo "Usage: get_sspref_vars <key> [...]"
        return 1
    fi

    local p v
    v=`get_ssversion`
    while [ $# -gt 0 ]; do
        case "$v" in
            7.*)
                echo `sed -n "/^${1}:/s/\\(.*\\): /ssprefs_\\1=/p" ${SSPREFSDIR}/{,plugin/}*.prefs 2>/dev/null`
                ;;
            *)
                echo `sed -n "/^${1}:/s/\\(.*\\): /ssprefs_\\1=/p" ${SSCFG} 2>/dev/null`
                ;;
        esac
        shift
    done

    return
}

# get load averages
function get_loadavg()
{
    local a b c d
    read a b c d < /proc/loadavg
    echo "${a} ${b} ${c}"
    return
}

# get uptime and %idle
function get_uptime()
{
    read a b < /proc/uptime
    gawk -v a=${a} -v b=${b} '
       BEGIN {
               d = int(a/86400);                   # days
               h = int((a-(d*86400))/3600);        # hours
               m = int((a-(d*86400)-(h*3600))/60); # minutes
               s = a-(d*86400)-(h*3600)-(m*60);    # seconds
               i = b*100/a;                        # idle percentage
               printf "%id %02i.%02i:%02i (%.0f%% idle)\n", d, h, m, s, i;
             }'
    return
}

# get memory usage
function get_memusage()
{
  free -k | gawk '
    /^Mem/  { printf "%.0f/%.0f MB (shr: %.0f, buf: %.0f, cache: %.0f, rss: %.0f)",
                $3/1024, $2/1024, $5/1024, $6/1024, $7/1024, ($3-$5-$6-$7)/1024; }
    /^Swap/ { printf " -- %.0f/%.0f MB\n",
                  $3/1024, $2/1024; }'

}

# get process state (needs PID as argument)
function get_procstate()
{
    sed -n 's/^State:.\(.*\)$/\1/p' /proc/${1}/status
    return
}

# get process' memory footprint
function get_procmemusage()
{
    gawk '
      /^VmSize/ { t = $2; }
      /^VmRSS/  { r = $2; }
      END       { printf "%i MB total, " \
                    "%i MB (%.0f%%) RSS\n", t/1024, r/1024, r*100/t; }' \
      < /proc/${1}/status
    return
}

# convert seconds to [[d-]hh:]mm:ss
function sec2hms()
{
    gawk -v c=$1 '
      BEGIN { d=int(c/86400);
              h = int((c-(d*86400))/3600);
              m = int((c-(d*86400)-(h*3600))/60);
              s = c-(d*86400)-(h*3600)-(m*60);
              if (d > 0) sd = sprintf("%id-", d);
              if (h > 0) sh = sprintf("%02i:", h);
              printf "%s%s%02i:%02i", sd, sh, m, s; }'
    return
}

# convert [[d-]hh:]mm:ss to seconds and report sum in seconds
function hms2sec()
{
    gawk -F: 'NF == 3 && index($1, "-") > 0 {
                 i = index($1, "-");
                 s += substr($1, 1, i-1)*86400;
                 $1 = substr($1, i+1); }
              NF == 3 { s += $1*3600 + $2*60 + $3 } # hh:mm:ss
              NF == 2 { s +=           $1*60 + $2 } # mm:ss
              END { print s }'
    return
}

# calculate percent
function percent()
{
    #gawk -v total=$1 -v fraction=$2 'BEGIN { printf("%.1f", fraction*100/total); }'
    local t=$1 f=$2 p=0
    ((f=f*1000))
    # FIXME: handle division by zero
    ((p=f/t))
    [ "${p%?}" = "" ] && p="0$p"
    echo "${p%?}.${p: -1}"
    return
}

# get process' uptime and all processes average cpu usage
function get_procuptime()
{
    local upt cput p

    # uptime + cpu usage
    upt=`ps -o etime -p $1 | hms2sec`    # just first process
    cput=`ps -o cputime -p $@ | hms2sec` # all processes (threads)
    p=`percent $upt $cput`
    echo `sec2hms $upt`", "`sec2hms $cput`" ($p%) CPU time"
    return
}

# get disk usage for file/dir
function get_diskusage()
{
    du -hsc "$@" 2>/dev/null | \
    sed -n 's/k.*total/K/;s/m.*total/M/p;/total/s/\(.*\)[\t ].*/\1/p'
}

# routine that returns a unique temporary file name
function mktemp()
{
    tmp=${SSODSTMP}/`date +%s`.tmp
    until [ ! -e ${tmp} ]; do
        sleep 1
        tmp=${SSODSTMP}/`date +%s`.tmp
    done
    : > ${tmp}
    echo ${tmp}
    return
}

# routine to return a timestamp à la YYYY-DD-MM_hhmm
function timestamp()
{
    echo `date +"%Y-%m-%d_%H%M"`
    return 0
}



# routine to determine the system MySQL errmsg.sys and errmsg.txt
# which can be in various locations..
function get_mysql_errmsg_files()
{
    # path to the errmsg.sys and errmsg.txt files
    local sys txt mysqld prefix t

    # determine the mysql prefix
    mysqld=`readlink ${SSODSDIR}/bin/mysqld`
    prefix=${mysqld%/*}
    # find errmsg.txt
    for t in \
        ${prefix}/../share/mysql/errmsg.txt \
        ${prefix}/share/mysql/errmsg.txt \
        ${prefix}/../share/mysql/english/errmsg.txt \
        ${prefix}/share/mysql/english/errmsg.txt; do
        [ -f $t ] && txt=$t
    done
    # find errmsg.sys
    for t in \
        ${prefix}/../share/mysql/errmsg.sys \
        ${prefix}/share/mysql/errmsg.sys \
        ${prefix}/../share/mysql/english/errmsg.sys \
        ${prefix}/share/mysql/english/errmsg.sys; do
        [ -f $t ] && sys=$t
    done
    # return what we found (which may be nothing)
    echo "errmsgsys=$sys"
    echo "errmsgtxt=$txt"
    return 0
}

# routine to determine the host architecture
function get_host_arch()
{
    local uname=`uname -m`
    local arch=
    case "$uname" in
        i?86)            arch="i686"; ;;
        powerpc*|ppc*)   arch="ppc"; ;;
        arm*)            arch="arm"; ;;
        *)               arch="unknow"; ;;
    esac
    echo "$arch"
    return 0
}

# routine to determine where the SS binaries are
function get_ss_bindir()
{
    [ -z "$SSDIR" ] && return 2
    uname=`uname -m`
    bindir=
    case "$uname" in
        i?86)            bindir="i386-linux"; ;;
        powerpc*|ppc*)   bindir="powerpc-linux"; ;;
        arm*)            bindir="arm-linux"; ;;
        *)               bindir=""; ;;
    esac
    [ -z "$bindir" ] && return 1
    echo "$SSDIR/Bin/$bindir"
    return 0
}




# eof
