lock(),unlock(): added
[util-vserver.git] / util-vserver / scripts / functions
index 6735f2e..4b50d0a 100644 (file)
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+_VS_LOGFILE=
+_VS_ERRFILE=
+
+_VS_NEWLINE='
+'
+declare -r _VS_NEWLINE=${_VS_NEWLINE:0-1}
+
 function findObject
 {
     local mod=$1
@@ -46,11 +53,99 @@ function findDir
     findObject -d "$@"
 }
 
+function findAndCopy
+{
+    local dst=$1
+    test ! -s "$dst"         || return 0
+    
+    local tmp
+    shift
+    findFile tmp "$@"
+
+    test "$tmp" -a -s "$tmp" || return 0
+    cp -af "$tmp" "$dst"
+}
+
 function getPhysicalDir
 {
     ( set -P && cd "$1" && pwd )
 }
 
+function logging
+{
+    if test "$_VS_LOGFILE"; then
+       echo "$@" >>"$_VS_LOGFILE"
+    else
+       echo "$@"
+    fi
+}
+
+function warning
+{
+    if test "$_VS_ERRFILE"; then
+       echo "$@" >>"$_VS_ERRFILE"
+    else
+       echo "$@" >&2
+    fi
+}
+
+function panic
+{
+    if test "$_VS_ERRFILE"; then
+       echo "$@" >>"$_VS_ERRFILE"
+    else
+       echo "$@" >&2
+    fi
+
+    exit 1
+}
+
+function execute
+{
+    test -z "${DEBUG_EXEC:-}"       || echo "$@"
+    test "${DEBUG_EXEC:-}" = noexec || exec "$@"
+    exit 0
+}
+
+## Usage: isNumber <arg>
+function isNumber
+{
+    local tmp
+    let tmp=$1+0 2>/dev/null || test -z "${1//0/}" -a "$1" || return 1
+    return 0
+}
+
+_UV_LOCKS=''
+## Usage: lock <lockfile> [<timeout>]
+function lock
+{
+    local tmp=$(mktemp /tmp/vserver-lock.XXXXXX)
+    rm -f $tmp
+    mkfifo -m600 $tmp
+
+    sleep 10
+    $_LOCKFILE "$1" $tmp $2 &
+    cat <$tmp >/dev/null
+    
+    _UV_LOCKS="$! $_UV_LOCKS"
+}
+
+## Usage: unlock [<num>]
+function unlock
+{
+    local num=$1
+    local i
+
+    set -- $_UV_LOCKS
+    while test "$#" -gt 0; do
+       kill -HUP "$1" >/dev/null || :
+       shift
+       test "$num" != 1 || break
+       test -z "$num"   || let --num
+    done
+    _UV_LOCKS="$@"
+}
+
 function _pkgMountBindDir()
 {
     test "$1" != "$2" || return 0
@@ -61,26 +156,38 @@ function _pkgMountBindDir()
 function _pkgSetVarsBase
 {
     case "$vserver" in
-       /*)
-           echo "not supported yet"
-           exit 1
+       ./*|/*)
+           if test -d "$vserver/vdir"; then
+               BASEDIR=$vserver
+               VDIR=$(getPhysicalDir "$vserver/vdir")
+               
+               PKGDIR=$BASEDIR/apps/pkgmgmt
+               test -d "$PKGDIR" || {
+                   echo "Can not find configuration-directory for package-managment tools"
+                   exit 1
+               } >&2
+               findDir EXECDIR      $PKGDIR/execdir     /
+           else
+               VDIR=$(getPhysicalDir "$vserver")
+               PKGDIR=
+           fi
            ;;
         *)
            BASEDIR=$CONFDIR/$vserver
            test -d "$BASEDIR" || {
                echo "Can not find configuration-directory"
                exit 1
-           }
+           } >&2
            
            VDIR=$BASEDIR/vdir
-           test -d "$VDIR"   || VDIR=/vservers/$vserver
+           test -d "$VDIR"   || VDIR=$DEFAULT_VSERVERDIR/$vserver
            VDIR=$(getPhysicalDir "$VDIR")
            
            PKGDIR=$BASEDIR/apps/pkgmgmt
            test -d "$PKGDIR" || {
                echo "Can not find configuration-directory for package-managment tools"
                exit 1
-           }
+           } >&2
 
            findDir EXECDIR      $PKGDIR/execdir     /
 
@@ -96,39 +203,38 @@ function _pkgSetVarsBase
 
 function _pkgSetVarsRPM
 {
-    case "$vserver" in
-       /*)
-           echo "not supported yet"
-           exit 1
-           ;;
-           
-        *)
-           findDir RPMETCDIR    $PKGDIR/rpmetc      $PKGDIR/base/rpm/etc       /etc/rpm
-           findDir RPMSTATEDIR  $PKGDIR/rpmstate    $PKGDIR/base/rpm/state
+    if test "$PKGDIR"; then
+       findDir RPMETCDIR    $PKGDIR/rpmetc      $PKGDIR/base/rpm/etc       /etc/rpm
+       findDir RPMSTATEDIR  $PKGDIR/rpmstate    $PKGDIR/base/rpm/state
 
-           findDir RPMLIBDIR    $PKGDIR/rpmlib  /
+       findDir RPMLIBDIR    $PKGDIR/rpmlib      /
 
-           RPMSTATEDIR=$(getPhysicalDir "$RPMSTATEDIR")
-           RPMETCDIR=$(getPhysicalDir "$RPMETCDIR")
-           ;;
-    esac
+    else
+       findDir RPMETCDIR    "$VDIR"/etc/rpm     /etc/rpm
+       findDir RPMSTATEDIR  "$VDIR"/var/lib/rpm
+       RPMLIBDIR=/
+    fi
+    
+    RPMSTATEDIR=$(getPhysicalDir "$RPMSTATEDIR")
+    RPMETCDIR=$(getPhysicalDir "$RPMETCDIR")
 }
 
 function _pkgSetVarsApt
 {
-    case "$vserver" in
-       /*)
-           echo "not supported yet"
-           exit 1
-           ;;
-           
-        *)
-           findDir APTETCDIR    $PKGDIR/aptetc      $PKGDIR/base/apt/etc       /etc/apt
-           findDir APTSTATEDIR  $PKGDIR/aptstate    $PKGDIR/base/apt/state
-           findDir APTCACHEDIR  $PKGDIR/aptcache    $PKGDIR/base/apt/cache
-           findDir APTARCHIVDIR $PKGDIR/aptarchives $PKGDIR/base/apt/archives  /var/cache/apt/archives
-           ;;
-    esac
+    if test "$PKGDIR"; then
+       findDir APTETCDIR    $PKGDIR/aptetc      $PKGDIR/base/apt/etc       /etc/apt
+       findDir APTSTATEDIR  $PKGDIR/aptstate    $PKGDIR/base/apt/state
+       findDir APTCACHEDIR  $PKGDIR/aptcache    $PKGDIR/base/apt/cache
+       findDir APTARCHIVDIR $PKGDIR/aptarchives $PKGDIR/base/apt/archives  /var/cache/apt/archives
+    else
+       findDir APTETCDIR    "$VDIR"/etc/apt            /etc/apt
+       findDir APTSTATEDIR  "$VDIR"/var/state/apt
+       findDir APTCACHEDIR  "$VDIR"/var/cache/apt
+       findDir APTARCHIVDIR "$VDIR"/var/cache/apt/archives /var/cache/apt/archives
+    fi
+
+    findFile APT_CONFIG "$APTETCDIR"/apt.conf ""
+    test -z "$APT_CONFIG" || export APT_CONFIG
 }
 
 function _pkgMountBase
@@ -146,8 +252,11 @@ function _pkgMountRPM
     _pkgMountBindDir "$RPMETCDIR" /etc/rpm
     test "$RPMLIBDIR" = "/" || _pkgMountBindDir "$RPMLIBDIR" /usr/lib/rpm
 
-    "$_SECURE_MOUNT" "$VDIR" "$RPMSTATEDIR" "$_rpmdb_mntpoint"
+    "$_SECURE_MOUNT" --chroot "$VDIR" -n --secure --bind "$RPMSTATEDIR" "$_rpmdb_mntpoint"
     test -z "$WORKAROUND_106057" || mount -n --bind "$RPMSTATEDIR" "$_rpmdb_mntpoint"
+
+    test -e "$VDIR"/proc/self/status || \
+       "$_SECURE_MOUNT" --chroot "$VDIR" -n --secure -t proc none /proc
 }
 
 function _pkgSetEnvBase
@@ -185,7 +294,7 @@ function pkgInit
        case "$i" in
            rpm)        _pkgSetVarsRPM;;
            apt)        _pkgSetVarsApt;;
-           *)          echo "Unknown packaging flavor"; exit 1;;
+           *)          echo "Unknown packaging flavor" >&2; exit 1;;
        esac
     done
 
@@ -209,35 +318,404 @@ function pkgInit
     _PKG_VSERVER=$vserver
 }
 
+function isAvoidNamespace
+{
+    local cfgdir
+
+    $_VSERVER_INFO - FEATURE namespace   || return 0
+    cfgdir=$($_VSERVER_INFO "$1" CFGDIR) || return 0
+    test ! -e "$cfgdir"/namespace        || return 1
+    test -e "$CONFDIR"/.defaults/nonamespace -o \
+         -e "$cfgdir"/nonamespace
+}
+
 function getAllVservers
 {
     local i
-    local var=$1
+    declare -a _tmp=()
 
     for i in $CONFDIR/*; do
        test   -d "$i"          || continue
        test ! -e "$i"/disabled || continue
        test   -d "$i"/vdir     || continue
+       case "$i" in
+           *.~*~) continue;;
+       esac
 
-       eval "$var=\"$var ${i##$CONFDIR/}\""
+       _tmp=( "${_tmp[@]}" "${i##$CONFDIR/}")
     done
+
+    eval $1='( "${_tmp[@]}" )'
 }
 
+## Usage: getVserverCtx <vdir> <result-varname> [<procnumber-varname> [<do-cleanup>]]
+## Returns: 0 iff vserver is running
+function getVserverStatus
+{
+    test -r "$1"/run || return 1
+
+    local _ctx
+    read _ctx <"$1"/run
+    eval "$2"=\$_ctx
+
+    test "$3"        || return 0
+    local _tmp=$($_VPS ax | $_AWK '{print $2}' | $_GREP -x "$_ctx" | $_WC -l )
+    let _tmp=_tmp+0
+    eval "$3"=\$_tmp
+
+    if test "$_tmp" = 0; then
+       local runfile=$($_READLINK "$1/run")
+       test -z "$4" || rm -f "$runfile"
+       return 1
+    fi
+
+    return 0
+}
+
+## Usage: isCtxRunning <ctx>
+function isCtxRunning
+{
+    local _tmp=$($_VPS ax | $_AWK '{print $2}' | $_GREP -x "$1" | $_WC -l )
+    test $_tmp -gt 0
+}
+
+## Usage: isVserverRunning <vdir> [<ctx-varname>]
 function isVserverRunning
 {
-    local vdir=$1
-    test -r "$vdir/run"   || return 1
+    local ctx procnum
 
-    local ctx
-    read ctx <"$vdir"/run || { echo "Failed to read ctx information" >&2; return 0; }
+    getVserverStatus "$1" ctx procnum 1 || return 1
+    test $procnum != 0                  || return 1
+    test -z "$2" || eval "$2"=\$ctx
+    return 0
+}
+
+## Called as 'getFileValue <varname> <filename>'
+function getFileValue
+{
+    test -r "$2" || return 0
+    eval read "$1" <"$2"
+}
+
+## Called as 'getFileArray <varname> <filename>'
+function getFileArray
+{
+    test -r "$2" || return 1
     
-    local cnt=$($_VPS ax | $_AWK '{print $2}' | $_GREP -x "$ctx" | $_WC -l )
+    local IFS=$_VS_NEWLINE
+    eval "$1"='( $(< "$2") )'
+}
+
+function checkComponents
+{
+    local      i
+    local      msg=$1
+    local      x_failed=
+
+    shift
+    
+    for i; do
+       local failed=
+       case "$i" in
+           core)       test -x "$_CHBIND"           || failed=1;;
+           build)      test -x "$_VSERVER_BUILD"    || failed=1;;
+           sysv)       test -x "$INITRDDIR/vserver" || failed=1;;
+           devel)      test -d "$INCLUDEDIR/vserver.h" || failed=1;;
+           *)          echo "Unknown component '$i'" >&2
+                       return false
+                       ;;
+       esac
+
+       test -z "$failed" || {
+           echo "$msg: $i"
+           x_failed=1
+       } >&2
+    done
 
-    if test $cnt = 0; then
-       ## Remove old lock-file
-       rm -f $(readlink "$vdir/run")
+    test -z "$x_failed"
+}
+
+## Usage: isKernelAPI <ver> [<cmp-modifier>]
+function isKernelAPI
+{
+    local api=$($_VSERVER_INFO - APIVER) || api=0
+    test $[ $api ] -${2:-ge} $[ $1 ]
+}
+
+## Usage: callInNamespace <vserver> <command> <args>*
+function callInNamespace
+{
+    local ctx=
+    
+    isAvoidNamespace "$1" || \
+    ctx=$( $_VSERVER_INFO "$1" CONTEXT t ) || ctx=
+
+    shift
+    if test "$ctx"; then
+       $_VNAMESPACE --enter "$ctx" -- "$@"
+    else
+       "$@"
+    fi
+}
+
+## Usage: pkgmgmt.guessStyle <vserver> <resultvar>
+function pkgmgmt.guessStyle()
+{
+    local vdir=$($_VSERVER_INFO "$1" VDIR) || {
+       echo $"Can not determine vserver-root" >&2
        return 1
+    }
+    local cfgdir=$($_VSERVER_INFO "$1" APPDIR pkgmgmt) || :
+
+    if test "$cfgdir" -a -e "$cfgdir"/style; then
+       read style <"$cfgdir"/style
+    elif test -e "$vdir"/etc/redhat-release -o -e "$vdir"/etc/fedora-release; then
+       style=redhat
+    elif test -e "$vdir"/etc/mandrake-release; then
+       style=mandrake
+    elif test -e "$vdir"/etc/debian_version; then
+       style=debian
+    elif test -e "$vdir"/etc/SuSE-release; then
+       style=suse
     else
-       return 0
+       echo $"Can not determine packagemanagement style" >&2
+       return 1
+    fi
+
+    eval $2=\$style
+    return 0
+}
+
+## Usage: pkgmgmt.isInternal <vserver>
+## returns true iff <vserver> is configured for internal packagemanagement
+## A typical application is
+## | is_external=
+## | pkgmgmt.isInternal "$vserver" || is_external=1
+function pkgmgmt.isInternal
+{
+    local cfgdir=$($_VSERVER_INFO "$1" APPDIR pkgmgmt) || :
+
+    test -z "$cfgdir" -o ! -d "$cfgdir" -o -e "$cfgdir"/internal
+}
+
+## Usage: pkgmgmt.isAptAvailable <cfgdir> <vdir> [<is-internal>]
+function pkgmgmt.isAptAvailable
+{
+    local cfgdir="$1"
+    local vdir="$2"
+    local is_internal="$3"
+    
+    local have_apt i
+    if test "$is_internal"; then
+       have_apt=1
+       test -d "$cfgdir"/base/apt -o -d "$cfgdir"/aptetc || have_apt=
+    else
+       have_apt=
+       for i in /bin /usr/bin /usr/local/bin; do
+           test ! -x "$vdir$i"/apt-get || { have_apt=1; break; }
+       done
+    fi
+
+    test "$have_apt" && return 0 || return 1
+}
+
+function vshelper.doSanityCheck
+{
+    local vshelper this_xid i
+    declare -a warnings=()
+    local solution_disable=
+    local solution_sysctl=
+
+    vshelper.isEnabled && vshelper.isEnabled warning || return 0
+    
+    this_xid=$($_VSERVER_INFO - XID) ||
+       panic $"Failed to determine current context; aborting..."
+
+    ## Do nothing in other xid's; the helper will be executed in xid 0 only
+    test "$this_xid" = 0 || return 0
+
+    local proc_file=/proc/sys/kernel/vshelper
+
+    if ! test -r "$proc_file"; then
+       vshelper=
+       warnings=( "${warnings[@]}"
+                  $"File '$proc_file' does not exists but is required for vshelper setup" )
+        solution_disable=1
+    else
+       vshelper=$(cat "$proc_file")
+
+       cmp -s "$vshelper" "$_VSHELPER" || {
+           warnings=( "${warnings[@]}"
+                      $"The configured vshelper '$vshelper' does not match the 'vshelper'
+  script of the util-vserver package"
+                    )
+           solution_disable=1
+           solution_sysctl=1
+       }
+    fi
+
+    test -d "$VSHELPERSTATEDIR" || {
+       warnings=( "${warnings[@]}"
+                  $"\
+The vshelper state-directory '$VSHELPERSTATEDIR' does not exist;
+  since it is created by 'make install', this indicates a serious
+  problem with your util-vserver installation" )
+       solution_disable=1
+    }
+
+    test "${#warnings[@]}" -eq 0 || {
+       warning $"\
+The following problem(s) were encountered while verifying vshelper
+functionality:"
+
+       for i in "${warnings[@]}"; do
+           warning "* $i"
+       done
+
+       warning $"\
+       
+To fix this, you can:"
+
+       test -z "$solution_disable" || warning $"\
+* disable vshelper entirely by executing
+  | touch \"$CONFDIR/.defaults/apps/vshelper/disabled\"
+* disable only this message by executing
+  | touch \"$CONFDIR/.defaults/apps/vshelper/warning-disabled\""
+
+       test -x "$solution_sysctl" || warning $"\
+* configure the util-vserver vshelper script, e.g. by adding
+  | kernel.vshelper = \"$_VSHELPER\"
+  to /etc/sysctl.conf and rebooting the machine, or by executing
+  | echo \"$_VSHELPER\" >$proc_file"
+
+       warning ""
+
+       return 1
+    }
+
+    return 0
+}
+
+## Usage: vshelper.isEnabled [<style>] [<vserver>]
+function vshelper.isEnabled
+{
+    local f=${1:+$1-}disabled
+    test ! -e "$CONFDIR"/.defaults/apps/vshelper/"$f"       || return 1
+    $_VSERVER_INFO - FEATURE vshelper                       || return 1
+    if test "$2"; then
+       local appdir=$($_VSERVER_INFO "$2" APPDIR vshelper) || return 0
+       test -z "$2" -o ! -e "$appdir/$f"                   || return 1
     fi
+
+    return 0
+}
+
+function vshelper._getHandlerInternal
+{
+    local var=$1
+    local tmp
+    shift
+    shift      ## HACK: see below the note about the 'set -u' mode
+    
+    while test "$#" -ge 2; do
+       local mod=$1
+       local obj=$2
+       shift 2
+
+       test "$mod" "$obj" || continue
+       case "$mod" in
+           (-x)
+               eval $var=\$obj
+               ;;
+           (-e)
+               read tmp <"$obj"
+               eval $var=:\$tmp
+               ;;
+           (*) panic $"Internal error, unexpected modifier '$mod'"
+       esac
+       return 0
+    done
+    
+    return 1
+}
+
+## Usage: vshelper.getHandler <result-var> <vserver> <action>
+function vshelper.getHandler
+{
+    local appdir=$($_VSERVER_INFO "$2" APPDIR vshelper) || appdir=
+    local script
+    declare -a search_list=( X )
+    ## HACK: when we are in 'set -u' mode, empty lists are causing errors
+
+    test -z "$appdir" || search_list=( "${search_list[@]}" -x "$appdir/$3" )
+    test -z "$appdir" || search_list=( "${search_list[@]}" -e "$appdir/action" )
+    search_list=( "${search_list[@]}" -x "$CONFDIR"/.defaults/apps/vshelper/"$3" )
+    search_list=( "${search_list[@]}" -e "$CONFDIR"/.defaults/apps/vshelper/action )
+
+    ! vshelper._getHandlerInternal "$1" "${search_list[@]}" || return 0
+    eval $1=':restart'
+}
+
+## Usage: vshelper.init <vserver> [<method> <args>*]
+function vshelper.doInit
+{
+    vshelper.isEnabled || return 0
+    
+    local xid=$($_VSERVER_INFO "$1" CONTEXT false) || {
+       warning $"vshelper.init: can not determine xid of vserver '$vserver'"
+       return 1
+    }
+
+    local f="$VSHELPERSTATEDIR/$xid"
+    
+    set -C
+    rm -f "$f"
+    echo "$1" >"$f"
+    set +C
+    
+    if test "$2"; then
+       shift 1
+       local i
+       for i; do
+           echo "$i" 
+       done
+    else
+       echo "default"
+    fi >>"$f"
+
+    return 0
+}
+
+## Usage: vshelper.doDestroy <vserver> <xid>
+function vshelper.doDestroy
+{
+    vshelper.isEnabled || return 0
+
+    rm -f "$VSHELPERSTATEDIR/$2"
+}
+
+## Usage: vshelper.initSync <vserver> <pipe-varname>
+function vshelper.initSync
+{
+    local tmpdir=$(mktemp -d /tmp/vserver-stop.XXXXXX) || {
+       warning $"Failed to generate temporary directory for vshelper sync"
+       return 1
+    }
+
+    local fifo="$tmpdir"/pipe
+    mkfifo -m700 "$fifo"
+    vshelper.doInit "$VSERVER_DIR" sync "$fifo"
+    eval $2=\$fifo
+}
+
+## Usage: vshelper.getSyncTimeout <vserver> <varname>
+function vshelper.getSyncTimeout
+{
+    local appdir=$($_VSERVER_INFO "$1" APPDIR vshelper) || appdir=
+    local file tmp
+
+    findFile file ${appdir:+"$appdir"/sync-timeout} "$CONFDIR"/.defaults/apps/vshelper/sync-timeout ''
+    test "$file" || return 1
+    read tmp <"$file"
+    eval $2=\$tmp
 }