Add support for the OOM bias in the configuration.
[util-vserver.git] / scripts / vserver.functions
index 7a9b6a8..0eab27e 100644 (file)
@@ -20,7 +20,7 @@
 #  $VSERVER_NAME  ... name of vserver
 
 declare -a NICE_CMD=()
-declare -a CHBIND_OPTS=()
+declare -a CHBIND_CMD=()
 declare -a CAP_OPTS=()
 declare -a CHCONTEXT_INIT_OPTS=()
 declare -a CHCONTEXT_FLAG_OPTS=()
@@ -42,6 +42,10 @@ declare -a OPTS_VCONTEXT_MIGRATE=()
 declare -a OPTS_VCONTEXT_ENTER=()
 declare -a OPTS_VATTRIBUTE=( --flag fakeinit )
 declare -a OPTS_VSCHED=()
+declare -a OPTS_ENV=()
+declare -a OPTS_VTAG_CREATE=()
+declare -a OPTS_VTAG_ENTER=()
+declare -a OPTS_VMEMCTRL=()
 
 declare -a STOPCMD_PREPARE=()
 
@@ -72,38 +76,63 @@ else
     SILENT_OPT='--silent'
 fi
 
+function _readFileToArray
+{
+    local _rfta_f="$1"
+    local _rfta_a="$2"
+    local _rfta_p="$3"
+    local _rfta_v
+
+    test -e "$_rfta_f" || return 0
+    while read _rfta_v; do
+       case x"$_rfta_v" in
+           (x|x\#*)    ;;
+           (*)         eval "$_rfta_a=( \"\${$_rfta_a[@]}\" $_rfta_p \"$_rfta_v\" )";;
+       esac
+    done <"$_rfta_f"
+}
+
 function _generateChbindOptions
 {
     local vdir="$1"
     local i
     local bcast=
+    local lback=
     local nid=
 
     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$vdir"
 
-    local f=$vdir/interfaces/bcast
+    local f="$vdir"/interfaces/bcast
     getFileValue bcast "$f"
+    f="$vdir"/interfaces/lback
+    getFileValue lback "$f"
 
     getFileValue nid "$vdir/ncontext" "$vdir/context"
     
-    CHBIND_OPTS=( $SILENT_OPT ${nid:+--nid "$nid"} ${bcast:+--bcast "$bcast"} )
+    CHBIND_CMD=( $_CHBIND $SILENT_OPT --secure ${nid:+--nid "$nid"}
+                ${bcast:+--bcast "$bcast"} ${lback:+--lback "$lback"}
+               )
 
     for i in "${INTERFACES[@]}"; do
-       CHBIND_OPTS=( "${CHBIND_OPTS[@]}" --ip "$i" )
+       CHBIND_CMD=( "${CHBIND_CMD[@]}" --ip "$i" )
     done
 
+    _readFileToArray "$vdir"/nflags        CHBIND_CMD --flag
+    _readFileToArray "$vdir"/ncapabilities CHBIND_CMD --ncap
+
     _HAVE_CHBIND_OPTIONS=1
 }
 
 function _generateNiceCommand
 {
     local vdir=$1
-    local nice
+    local nice=0
+    local current_nice=`$_NICE`
 
-    test -r "$vdir/nice" || return 0;
-    read nice <"$vdir"/nice
+    test -r "$vdir/nice" && read nice <"$vdir"/nice
 
-    NICE_CMD=( $_NICE -$nice )
+    let nice=$nice-$current_nice || :
+    NICE_CMD=( $_NICE -n $nice )
 }
 
 
@@ -137,31 +166,15 @@ function _generatePersonalityOptions
 function _generateCCapabilityOptions
 {
     local vdir=$1
-    local cap
-    local f="$vdir"/ccapabilities
-    
-    test -e "$f" || return 0
-    while read cap; do
-       case x"$cap" in
-           (x|x\#*)    ;;
-           (*)         OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --ccap "$cap" );;
-       esac
-    done <"$f"
+
+    _readFileToArray "$vdir"/ccapabilities OPTS_VATTRIBUTE --ccap
 }
 
 function _generateBCapabilityOptions
 {
     local vdir=$1
-    local cap
-    local f="$vdir"/bcapabilities
-    
-    test -e "$f" || return 0
-    while read cap; do
-       case x"$cap" in
-           (x|x\#*)    ;;
-           (*)         OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --bcap "$cap" );;
-       esac
-    done <"$f"
+
+    _readFileToArray "$vdir"/bcapabilities OPTS_VATTRIBUTE --bcap
 }
 
 function _generateCapabilityOptions
@@ -245,18 +258,22 @@ function _generateInitOptions
     CHCONTEXT_INIT_OPTS=()
 
 
-    getFileValue INITSTYLE      "$cfgdir"/style
+    test x"$INITSTYLE" = xrescue || \
+      getFileValue INITSTYLE    "$cfgdir"/style
     getFileValue RUNLEVEL_START "$cfgdir"/runlevel
     getFileValue RUNLEVEL_START "$cfgdir"/runlevel.start
     getFileValue RUNLEVEL_STOP  "$cfgdir"/runlevel.stop
     getFileArray INITKILL_SEQ  "$cfgdir"/killseq || :
 
+    findFile _gio_env          "$cfgdir"/environment \
+       "$__CONFDIR"/.defaults/apps/init/environment \
+       "$__PKGLIBDEFAULTDIR"/environment
+    getFileArray OPTS_ENV      "$_gio_env" || :
+
     case x"$INITSTYLE" in
        (xrescue)
            INITCMD_START=( "${INITCMD_RESCUE[@]}" )
            INITCMD_STOP=( /sbin/killall5 )
-           _IS_FAKEINIT=1
-           _NEED_VSHELPER_SYNC=
            ;;
            
        (xsysv)
@@ -269,6 +286,11 @@ function _generateInitOptions
                INITCMD_STOP=(  "$i" "$RUNLEVEL_STOP"  )
            done
            INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
+           OPTS_ENV=( "${OPTS_ENV[@]}" PREVLEVEL=N RUNLEVEL="$RUNLEVEL_START" )
+           if test -n "$OPTION_DEBUG_SYSV"; then
+               INITCMD_START=( /bin/bash -x "${INITCMD_START[@]}" )
+               INITCMD_STOP=( /bin/bash -x "${INITCMD_STOP[@]}" )
+           fi
            ;;
            
        (xplain)
@@ -292,7 +314,12 @@ function _generateInitOptions
            ;;
 
        (xgentoo)
-           panic "init-style '$INITSTYLE' is no longer supported; please use plain instead; aborting";;
+           test -n "$RUNLEVEL_START" || RUNLEVEL_START="default"
+
+           INITCMD_START=( env TERM=$TERM /lib/rcscripts/sh/init-vserver.sh "$RUNLEVEL_START" )
+           INITCMD_STOP=( env -i TERM=$TERM RUNLEVEL=0 /sbin/rc shutdown )
+           INITCMD_PREPARE=( $_FAKE_RUNLEVEL 3 /var/run/utmp )
+           ;;
 
        (x) ;;
        (*) panic "Unknown init-style '$INITSTYLE'; aborting";;
@@ -383,6 +410,11 @@ function _generateChcontextOptions
 function _generateScheduleOptions
 {
     local vdir=$1
+    if test -d "$vdir"/sched; then
+      OPTS_VSCHED=( --dir "$vdir"/sched --missingok )
+      return 0
+    fi
+
     local f="$vdir"/schedule
     test -e "$f" || return 0
 
@@ -520,19 +552,26 @@ function _processSingleInterface
        ## LEGACY ALERT
        test ! -e "$iface"/only_ip || break
 
-       local vlan_info
-       if vlan_info=$(_getVLANInfo "$dev"); then
-           test -d /proc/net/vlan || {
-               echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
-               return 1
-           }
-           test -f /proc/net/vlan -o -e "$iface"/novlandev || {
+       test -e "$iface/vlandev" \
+            -o \( -e "$iface/../vlandev" -a ! -e "$iface/novlandev" \) \
+            -o \( -e "$__CONFDIR/.defaults/interfaces/vlandev" \
+                  -a ! -e "$iface/novlandev" \
+                  -a ! -e "$iface/../novlandev" \) && {
+           local vlan_info
+           if vlan_info=$(_getVLANInfo "$dev"); then
+               test -d /proc/net/vlan || {
+                   echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
+                   return 1
+               }
                _addInterfaceCmd VCONFIG $vlan_info
-           }
-       fi
+           fi
+       }
 
        if ! test -e "$iface"/indirect; then
-           _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" broadcast ${bcast:-+} ${name:+label "$dev:$name"} dev "$dev"
+           # XXX: IPv6 hack
+           use_bcast="broadcast ${bcast:-+}"
+           echo "$ip" | $_GREP -q : && use_bcast=
+           _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" $use_bcast ${name:+label "$dev:$name"} dev "$dev"
            #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
            _addInterfaceCmd IP_LINK  "$dev" $up
        elif ! test -n "$ctx"; then
@@ -638,6 +677,29 @@ function disableInterfaces
     unlock 1
 }
 
+function _generateTagOptions
+{
+    local vdir="$1"
+    local tag
+
+    getFileValue tag "$vdir/tag" "$vdir/context"
+    test -n "$tag" || return 0
+
+    OPTS_VTAG_CREATE=( --tag "$tag" )
+    OPTS_VTAG_ENTER=( --tag "$tag" )
+}
+
+function _generateMemctrlOptions
+{
+    local vdir="$"
+    local badness
+
+    getFileValue badness "$vdir/badness"
+    test -n "$badness" || return 0
+
+    OPTS_VMEMCTRL=( --badness "$badness" )
+}
+
 ## Usage: prepareInit <vserver-directory>
 function prepareInit
 {
@@ -664,7 +726,7 @@ function prepareStop
     pushd "$1/vdir" >/dev/null
     case "$INITSTYLE" in
        (sysv)
-           export PREVLEVEL=$RUNLEVEL_START # required by Debian's initscripts
+           export PREVLEVEL=$RUNLEVEL_START RUNLEVEL=$RUNLEVEL_STOP # required by Debian's initscripts
            ;;
     esac
     "${STOPCMD_PREPARE[@]}"
@@ -681,6 +743,8 @@ function generateOptions
     _generateChcontextOptions   "$1"
     _generateScheduleOptions    "$1"
     _generatePersonalityOptions "$1"
+    _generateTagOptions         "$1"
+    _generateMemctrlOptions     "$1"
 
     if test -n "$_IS_FAKEINIT"; then
        CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
@@ -688,6 +752,54 @@ function generateOptions
     fi
 }
 
+function addtoCPUSET
+{
+    local vdir=$1
+    local cpuset
+    local f="$vdir"/cpuset
+    local i
+    local configured=0
+
+    test -d "$f" || return 0
+    test -e "$f"/name || return 0
+
+    read cpuset < "$f"/name
+    test -e "$f"/nocreate || {
+       test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
+       for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
+           if test -e "$f"/"$i"; then
+               cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" || {
+                   configured=1
+                   break
+               }
+           fi
+       done
+    }
+
+    echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
+    if [ "$configured" -ne 0 ]; then
+       warning $"\
+WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
+       rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
+       return 0
+    fi
+}
+
+function removeCPUSET
+{
+    local vdir=$1
+    local cpuset
+    local f="$vdir"/cpuset
+
+    test -d "$f" || return 0
+    test -e "$f"/name || return 0
+
+    read cpuset < "$f"/name
+    test -e "$f"/nocreate || {
+       rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
+    }
+}
+
 function _mountVserverInternal
 {
     local fstab="$1"
@@ -741,12 +853,12 @@ function mountVserver
 
     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
 
-    test -z "$NAMESPACE_CLEANUP" || isAvoidNamespace "$cfgdir" || \
-       $_VNAMESPACE --cleanup
-
     _mountVserverInternal "$cfgdir"/fstab
     _mountVserverInternal "$cfgdir"/fstab.local
-    _mountVserverInternal "$cfgdir"/fstab.remote $_CHBIND "${CHBIND_OPTS[@]}"
+    _mountVserverInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}"
+
+    isNamespaceCleanup "$cfgdir" && \
+        _namespaceCleanup "$cfgdir"
 
     isAvoidNamespace "$cfgdir" || \
        $_SECURE_MOUNT --rbind -n "$vdir" "/"
@@ -786,14 +898,30 @@ function umountVserver
     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
     
     pushd "$vdir/" >/dev/null || return 1
-       _umountVserverInternal  "$cfgdir"/fstab.remote $_CHBIND "${CHBIND_OPTS[@]}" || is_ok=
-       _umountVserverInternal  "$cfgdir"/fstab.local                               || is_ok=
-       _umountVserverInternal  "$cfgdir"/fstab                                     || is_ok=
+       _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
+       _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
+       _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
     popd >/dev/null           || return 1
 
     test -n "$is_ok"
 }
 
+function fsckAllFS
+{
+    local cfgdir=$1
+    local fstab="$cfgdir"/fstab
+    local FSTAB_FILE
+    local fsck_exitcode
+
+    test -e "$fstab" || return 0
+
+    export FSTAB_FILE="$fstab"
+    $_FSCK -s -n -A -T
+    fsck_exitcode=$?
+    test "$fsck_exitcode" -eq 0 -o \
+         "$fsck_exitcode" -eq 1 || return $fsck_exitcode
+}
+
 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
 function initSync
 {
@@ -986,7 +1114,16 @@ WARNING: There is no cachedirectory configured for this vserver;
        panic $"\
 /proc/uptime can not be accessed. Usually, this is caused by
 procfs-security. Please read the FAQ for more details
-http://www.linux-vserver.org/index.php?page=Linux-Vserver+FAQ"
+http://linux-vserver.org/Proc-Security"
+
+    test -e "$cfgdir"/context || {
+       TYPE=$( $_VSERVER_INFO 49152 XIDTYPE )
+       test "$TYPE" != "static" || panic $"\
+The kernel does not have dynamic contexts enabled. Please configure
+a static one by executing
+
+  echo [number between 2 and 49151] > $cfgdir/context"
+    }
 }
 
 
@@ -1082,6 +1219,8 @@ function _saveSingleDiskLimit
 
     $_VDLIMIT --xid $ctx "$directory" | \
        $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
+
+    $_VDLIMIT --xid $ctx --remove "$directory"
 }
 
 
@@ -1099,3 +1238,72 @@ function saveDiskLimits
        _saveSingleDiskLimit "$vdir" "$dlimit"
     done
 }
+
+function _namespaceCleanup
+{
+    local vdir="$1"
+    local root=$($_VSERVER_INFO "$1" VDIR 1)
+    local -a list
+    local -a skip
+    local i
+    local j
+
+    getFileArray skip "$vdir"/namespace-cleanup-skip \
+       "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
+
+    # these are things that have to be accessible post-cleanup
+    for i in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
+       "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
+       local real=`readlink -f "$i"`
+       test "$i" != "$real" || real=
+       for j in "$i" "$real"; do
+           while test -n "$j"; do
+               list=( "${list[@]}" "$j" )
+               j="${j%/*}"
+           done
+       done
+    done
+
+    local -a list_umount
+    while read dev path opts; do
+       test -n "$path" || continue
+       for i in "$root" /dev /proc; do
+           test "${path#$i}" != "$path" && continue 2
+       done
+       for i in "${list[@]}" /; do
+           test "$path" = "$i" && continue 2
+       done
+       # unmount them in reverse order so mounts further down the tree get unmounted first
+       list_umount=( "$path" "${list_umount[@]}" )
+    done < /proc/mounts
+    # separate loop to avoid races while reading /proc/mounts
+    for i in "${list_umount[@]}"; do
+       $_UMOUNT -l -n "$i"
+    done
+}
+
+function loadDeviceMap
+{
+    local xid="$1"
+    local dir="$2"
+    local flags device target
+
+    test -d "$dir" || return 0
+
+    for i in "$dir"/*; do
+       test -d "$i" || continue
+
+       local -a vdevmap_opts=()
+       test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
+       test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
+       test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
+
+       getFileValue flags "$i/flags" || :
+       getFileValue device "$i/device" || :
+       getFileValue target "$i/target" || :
+       vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
+                       ${device:+--device "$device"} ${target:+--target "$target"} )
+
+       $_VDEVMAP --xid "$xid" --set "${vdevmap_opts[@]}" || return $?
+    done
+}