Use source variable
[util-vserver.git] / scripts / vserver.functions
1 # $Id$  --*- sh -*--
2
3 # Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 #  
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License.
8 #  
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #  
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 ## Expected env:
19 #  $VSERVER_DIR   ... path to vserver-cfg dir
20 #  $VSERVER_NAME  ... name of vserver
21
22 declare -a NICE_CMD=()
23 declare -a IONICE_CMD=()
24 declare -a CHBIND_CMD=()
25 declare -a CAP_OPTS=()
26 declare -a CHCONTEXT_INIT_OPTS=()
27 declare -a CHCONTEXT_FLAG_OPTS=()
28 declare -a CHCONTEXT_OPTS=()
29 declare -a CAPCHROOT_OPTS=()
30 declare -a INTERFACES=()
31
32 declare -a INITCMD_RESCUE=( /bin/sleep 900 )
33 declare -a INITCMD_START=()
34 declare -a INITCMD_START_SYNC=()
35 declare -a INITCMD_STOP=()
36 declare -a INITCMD_STOP_SYNC=()
37 declare -a INITCMD_PREPARE=()
38 declare -a INITKILL_SEQ=()
39 declare -a ENTER_SHELL=()
40
41 declare -a OPTS_VCONTEXT_CREATE=()
42 declare -a OPTS_VCONTEXT_MIGRATE=()
43 declare -a OPTS_VCONTEXT_ENTER=()
44 OPT_VCONTEXT_CHROOT=--chroot
45 declare -a OPTS_VATTRIBUTE=( --flag fakeinit )
46 declare -a OPTS_VSCHED=()
47 declare -a OPTS_ENV=()
48 declare -a OPTS_VTAG_CREATE=()
49 declare -a OPTS_VTAG_ENTER=()
50 declare -a OPTS_VMEMCTRL=()
51 declare -a OPTS_VSPACE=( --default )
52 declare -a OPTS_VSPACE_SHARED=()
53 declare -a VSPACE_SHARED_CMD=()
54
55 declare -a STOPCMD_PREPARE=()
56
57 declare -a VSERVER_EXTRA_CMDS=()
58
59 INIT_RESCUE=
60 VSHELPER_SYNC_TIMEOUT=30
61 USE_VNAMESPACE=
62 INTERFACE_CMDS_IDX=0
63 RUNLEVEL_START=
64 RUNLEVEL_STOP=
65 _HAVE_INTERFACE_OPTIONS=
66 _HAVE_CHBIND_OPTIONS=
67 _NEED_VSHELPER_SYNC=
68 _IS_FAKEINIT=
69
70 INITSTYLE=sysv
71
72 S_CONTEXT=
73 N_CONTEXT=
74
75 SILENT_OPT=
76
77 CGROUP_MNT=/dev/cgroup
78 declare -a CGROUP_SUBSYS=()
79 declare -a CGROUP_INHERIT=( cpuset.cpus cpuset.mems )
80 CGROUP_BASE=""
81 CGROUP_MNT_PER_SS=""
82
83 : ${VSERVER_NAME:=$(basename "$VSERVER_DIR")}
84
85 if test -e "$VSERVER_DIR"/noisy -o -n "$OPTION_VERBOSE"; then
86     SILENT_OPT=
87 else
88     SILENT_OPT='--silent'
89 fi
90
91 function _readFileToArray
92 {
93     local _rfta_f="$1"
94     local _rfta_a="$2"
95     local _rfta_p="$3"
96     local _rfta_v
97
98     test -e "$_rfta_f" || return 0
99     while read _rfta_v; do
100         case x"$_rfta_v" in
101             (x|x\#*)    ;;
102             (*)         eval "$_rfta_a=( \"\${$_rfta_a[@]}\" $_rfta_p \"$_rfta_v\" )";;
103         esac
104     done <"$_rfta_f"
105 }
106
107 function _generateChbindOptions
108 {
109     local vdir="$1"
110     local i
111     local bcast=
112     local lback=
113     local nid=
114
115     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$vdir"
116
117     if test -e "$vdir"/noncontext -o \
118             \( -e "$vdir"/spaces/net -a ! -e "$vdir"/ncontext \); then
119         _HAVE_CHBIND_OPTIONS=1
120         return 0
121     fi
122
123     local f="$vdir"/interfaces/bcast
124     getFileValue bcast "$f"
125     f="$vdir"/interfaces/lback
126     getFileValue lback "$f"
127
128     CHBIND_CMD=( $_CHBIND $SILENT_OPT --secure ${N_CONTEXT:+--nid "$N_CONTEXT"}
129                  ${bcast:+--bcast "$bcast"} ${lback:+--lback "$lback"}
130                 )
131
132     for i in "${INTERFACES[@]}"; do
133         CHBIND_CMD=( "${CHBIND_CMD[@]}" --ip "$i" )
134     done
135
136     _readFileToArray "$vdir"/nflags        CHBIND_CMD --flag
137     _readFileToArray "$vdir"/ncapabilities CHBIND_CMD --ncap
138
139     _HAVE_CHBIND_OPTIONS=1
140 }
141
142 function _generateNiceCommand
143 {
144     local vdir=$1
145     local nice=0
146     local current_nice=`$_NICE`
147
148     test -r "$vdir/nice" && read nice <"$vdir"/nice
149
150     let nice=$nice-$current_nice || :
151     NICE_CMD=( $_NICE -n $nice )
152 }
153
154 function _generateIONiceCommand
155 {
156     local vdir=$1
157     local ionice_class=
158     local ionice_priority=
159
160     test -n "$_IONICE" || return 0
161
162     test -r "$vdir/ionice/class" && read ionice_class <"$vdir"/ionice/class
163     test -r "$vdir/ionice/priority" && read ionice_priority <"$vdir"/ionice/priority
164
165     test -n "$ionice_class$ionice_priority" || return 0
166
167     if test -z "$ionice_class" -a -n "$ionice_priority"; then
168         IONICE_CMD=( $_IONICE -c2 -n"$ionice_priority" )
169     else
170         IONICE_CMD=( $_IONICE ${ionice_class:+-c$ionice_class} ${ionice_priority:+-n$ionice_priority} )
171     fi
172 }
173
174 function _generatePersonalityOptions
175 {
176     local vdir="$1"
177     local f="$vdir"/personality
178     local type flags
179
180     test -s "$f" || return 0
181
182     {
183         local delim tmp
184
185         read type
186         while read tmp; do
187             case x$tmp in
188                 (x\#*|x)        ;;
189                 (*)             flags=$flags$delim$tmp
190                                 delim=,
191                                 ;;
192             esac
193         done
194     } <"$f"
195
196     OPTS_VCONTEXT_ENTER=( "${OPTS_VCONTEXT_ENTER[@]}"
197                           --personality-type "$type"
198                           ${flags:+--personality-flags "$flags"} )
199 }
200
201 function _generateCCapabilityOptions
202 {
203     local vdir=$1
204
205     _readFileToArray "$vdir"/ccapabilities OPTS_VATTRIBUTE --ccap
206 }
207
208 function _generateBCapabilityOptions
209 {
210     local vdir=$1
211
212     _readFileToArray "$vdir"/bcapabilities OPTS_VATTRIBUTE --bcap
213 }
214
215 function _generateUMaskOptions
216 {
217     local vdir=$1
218
219     _readFileToArray "$vdir"/umask OPTS_VATTRIBUTE --umask
220 }
221
222 function _generateCapabilityOptions
223 {
224     local vdir=$1
225     local cap
226
227     _generateBCapabilityOptions "$vdir"
228     _generateCCapabilityOptions "$vdir"
229     
230     test -e "$vdir"/capabilities || return 0
231
232     CAP_OPTS=()
233     CAPCHROOT_OPTS=()
234
235     while read cap; do
236         case x"$cap" in
237             (x|x\#*)    ;;
238             (!CAP_SYSCHROOT)
239                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
240                 CAPCHROOT_OPTS=( "${CAPCHROOT_OPTS[@]}" --nochroot )
241                 ;;
242             (*)
243                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
244                 ;;
245         esac
246     done <"$vdir"/capabilities
247 }
248
249 function getEnterShell
250 {
251     local vdir=$1
252     local shell_file
253
254     ENTER_SHELL=()
255
256     getFileValue ENTER_SHELL "$vdir"/shell "$__CONFDIR"/.defaults/shell
257     
258     test -n "$ENTER_SHELL" || {
259         local i
260         for i in "/bin/bash -login" "/bin/sh -l" /bin/csh; do
261             set -- $i
262             test -x "$vdir/vdir/$1" || continue
263             ENTER_SHELL=( "$@" )
264             break
265         done
266     }
267 }
268
269 ## Usage: sendKillSequence <ctx> <signal> [<wait> <signal>]*
270 function sendKillSequence
271 {
272     local ctx=$1
273     local wait=
274     shift
275
276     while isCtxRunning "$ctx"; do
277         test -z "$wait" || sleep "$wait"
278
279         killContext "$ctx" "$1"
280         test -n "$2" || break
281         wait="$2"
282         shift 2
283     done
284 }
285
286 function _generateInitOptions
287 {
288     local vdir=$1
289     local cfgdir=$vdir/apps/init
290     local i f
291
292     INITCMD_START=()
293     INITCMD_STOP=()
294     INITCMD_START_SYNC=()
295     INITCMD_STOP_SYNC=()
296     INITCMD_PREPARE=()
297     STOPCMD_PREPARE=()
298
299     INITKILL_SEQ=( 15 5 9 )
300     CHCONTEXT_INIT_OPTS=()
301
302
303     test x"$INITSTYLE" = xrescue || \
304       getFileValue INITSTYLE    "$cfgdir"/style
305     getFileValue RUNLEVEL_START "$cfgdir"/runlevel
306     getFileValue RUNLEVEL_START "$cfgdir"/runlevel.start
307     getFileValue RUNLEVEL_STOP  "$cfgdir"/runlevel.stop
308     getFileArray INITKILL_SEQ   "$cfgdir"/killseq || :
309
310     findFile _gio_env           "$cfgdir"/environment \
311         "$__CONFDIR"/.defaults/apps/init/environment \
312         "$__PKGLIBDEFAULTDIR"/environment
313     getFileArray OPTS_ENV       "$_gio_env" || :
314
315     case x"$INITSTYLE" in
316         (xrescue)
317             INITCMD_START=( "${INITCMD_RESCUE[@]}" )
318             INITCMD_STOP=( /sbin/killall5 )
319             ;;
320             
321         (xsysv)
322             test -n "$RUNLEVEL_START" || RUNLEVEL_START=3
323             test -n "$RUNLEVEL_STOP"  || RUNLEVEL_STOP=6
324
325             for i in /etc/init.d/rc /etc/rc.d/rc; do
326                 test -x "$vdir/vdir/$i" || continue
327                 INITCMD_START=( "$i" "$RUNLEVEL_START" )
328                 INITCMD_STOP=(  "$i" "$RUNLEVEL_STOP"  )
329             done
330             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
331             OPTS_ENV=( "${OPTS_ENV[@]}" PREVLEVEL=N RUNLEVEL="$RUNLEVEL_START" )
332             if test -n "$OPTION_DEBUG_SYSV"; then
333                 INITCMD_START=( /bin/bash -x "${INITCMD_START[@]}" )
334                 INITCMD_STOP=( /bin/bash -x "${INITCMD_STOP[@]}" )
335             fi
336             ;;
337             
338         (xplain)
339             INITCMD_START=( /sbin/init )
340             INITCMD_STOP=(  /sbin/init )
341             _IS_FAKEINIT=1
342             _NEED_VSHELPER_SYNC=1
343             test -z "$RUNLEVEL_START" || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
344             test -z "$RUNLEVEL_STOP"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
345             ;;
346             
347         (xminit)
348             INITCMD_START=( /sbin/minit-start )
349             INITCMD_STOP=(  /sbin/minit-stop  )
350             _IS_FAKEINIT=1
351             INITCMD_START_SYNC=( "$_INITSYNC_MINIT_START" "$vdir" )
352             _NEED_VSHELPER_SYNC=1
353             test -z "$RUNLEVEL_START"         || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
354             test -z "$RUNLEVEL_STOP"          || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
355             ! isNumber "${RUNLEVEL_START:-3}" || INITCMD_PREPARE=( $_FAKE_RUNLEVEL "${RUNLEVEL_START:-3}" /var/run/utmp )
356             ;;
357
358         (xgentoo)
359             test -n "$RUNLEVEL_START" || RUNLEVEL_START="default"
360             RC_PATH=/usr/sbin:/usr/bin:/sbin:/bin
361
362             if test -x "$vdir/vdir/lib/rcscripts/sh/init-vserver.sh"; then
363                 RC_WRAP=/lib/rcscripts/sh/init-vserver.sh
364             elif test -x "$vdir/vdir/lib/rc/sh/init-vserver.sh"; then
365                 RC_WRAP=/lib/rc/sh/init-vserver.sh
366             else
367                 panic "init-vserver.sh not found; aborting"
368             fi
369
370             OPTS_ENV=( "${OPTS_ENV[@]}" TERM=$TERM )
371             INITCMD_START=( $RC_WRAP "$RUNLEVEL_START" )
372             INITCMD_STOP=( env -i PATH=$RC_PATH TERM=$TERM RUNLEVEL=0 /sbin/rc shutdown )
373             INITCMD_PREPARE=( $_FAKE_RUNLEVEL 3 /var/run/utmp )
374             ;;
375
376         (xarch)
377             test -n "$RUNLEVEL_START" || RUNLEVEL_START=3
378             INITCMD_START=( /etc/rc.multi )
379             INITCMD_STOP=( /etc/rc.shutdown )
380             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
381             ;;
382
383         (x) ;;
384         (*) panic "Unknown init-style '$INITSTYLE'; aborting";;
385     esac
386
387     if test x"$INITSTYLE" != xrescue; then
388         getFileArray INITCMD_START      "$cfgdir"/cmd.start      || :
389         getFileArray INITCMD_STOP       "$cfgdir"/cmd.stop       || :
390         getFileArray INITCMD_START_SYNC "$cfgdir"/cmd.start-sync || :
391         getFileArray INITCMD_STOP_SYNC  "$cfgdir"/cmd.stop-sync  || :
392         getFileArray INITCMD_PREPARE    "$cfgdir"/cmd.prepare    || :
393     fi
394     
395     test -n "$OPTION_FORCE_SYNC" -o -e "$cfgdir"/sync || {
396         INITCMD_START_SYNC=()
397         INITCMD_STOP_SYNC=()
398         _NEED_VSHELPER_SYNC=
399     }
400
401     if vshelper.isEnabled; then
402         vshelper.getSyncTimeout "$vdir" VSHELPER_SYNC_TIMEOUT || :
403     else
404         _NEED_VSHELPER_SYNC=
405     fi
406 }
407
408 function _generateFlagOptions
409 {
410     local vdir=$1
411     local file
412
413     CHCONTEXT_FLAG_OPTS=()
414
415     findFile file "$vdir"/cflags "$vdir"/flags ""
416     test -z "$file" || \
417     while read flag; do
418         case x"$flag" in
419             (x|x\#*)            ;;
420             (xnamespace)        ;;
421             (xfakeinit)
422                 _IS_FAKEINIT=1
423                 ;;
424             (*)
425                 OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --flag "$flag" )
426                 CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}"
427                                       --flag "$flag" )
428                 ;;
429         esac
430     done <"$file"
431
432     isAvoidNamespace "$vdir" || {
433         USE_VNAMESPACE=1
434         CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}" --flag namespace )
435         ! $_VSERVER_INFO - FEATURE PIVOT_ROOT || \
436             OPT_VCONTEXT_CHROOT=--pivot-root
437     }
438 }
439
440 function _generateChcontextOptions
441 {
442     local vdir=$1
443     local ctx hostname domainname
444     local cap_opts
445     local flag
446
447     {
448         read ctx        <"$vdir"/context        || :
449         ## LEGACY ALERT
450         read hostname   <"$vdir"/uts/nodename   || read hostname   <"$vdir"/hostname   || :
451         read domainname <"$vdir"/uts/domainname || read domainname <"$vdir"/domainname || :
452     } 2>/dev/null
453
454     test -z "$S_CONTEXT" || ctx=$S_CONTEXT
455
456     _generateCapabilityOptions "$vdir"
457     _generateFlagOptions       "$vdir"
458     _generateUMaskOptions      "$vdir"
459
460     CHCONTEXT_OPTS=( $SILENT_OPT \
461                      "${CHCONTEXT_FLAG_OPTS[@]}" \
462                      "${CAP_OPTS[@]}" \
463                      --secure
464                      ${ctx:+--ctx "$ctx"} \
465                      ${hostname:+--hostname "$hostname"} \
466                      ${domainname:+--domainname "$domainname"} )
467
468     OPTS_VCONTEXT_CREATE=( $SILENT_OPT \
469                            ${ctx:+--xid "$ctx"} )
470     ## put '--secure' at front so that it can be overridden
471     OPTS_VATTRIBUTE=( --secure --flag default "${OPTS_VATTRIBUTE[@]}" )
472 }
473
474 function _generateScheduleOptions
475 {
476     local vdir=$1
477     if test -d "$vdir"/sched; then
478       OPTS_VSCHED=( --dir "$vdir"/sched --missingok )
479       return 0
480     fi
481
482     local f="$vdir"/schedule
483     test -e "$f" || return 0
484
485     local fill_rate interval tokens tokens_min tokens_max prio_bias
486     {
487         {
488             read fill_rate   && \
489             read interval    && \
490             read tokens      && \
491             read tokens_min  && \
492             read tokens_max  && \
493             read prio_bias   || prio_bias=
494         } <"$f"
495     } 2>/dev/null
496
497     test -n "$prio_bias" || {
498         echo $"Bad content in '$f'; aborting..." >&2
499         false
500     }
501
502     OPTS_VSCHED=( --fill-rate  "$fill_rate"  --interval "$interval" \
503                   --tokens     "$tokens"     --tokens_min "$tokens_min" \
504                   --tokens_max "$tokens_max" --priority-bias "$prio_bias" )
505 }
506
507 function _getInterfaceValue
508 {
509     local _giv_val=$1
510     local _giv_dflt=$2
511     shift 2
512     
513     local _giv_i
514     local _giv_tmp
515
516     for _giv_i; do
517         read _giv_tmp  <"$_giv_i/$_giv_val" && break || :
518     done 2>/dev/null
519
520     : ${_giv_tmp:=$_giv_dflt}
521     eval $_giv_val=\$_giv_tmp
522 }
523
524 ## Usage: _transformMask2Prefix <result-varname> <prefix> <mask>
525 function _transformMask2Prefix
526 {
527     local _tm2p_tmp=$2
528     
529     test -n "$_tm2p_tmp" || {
530         $_MASK2PREFIX "$3" || _tm2p_tmp=$?
531     }
532
533     eval $1=\$_tm2p_tmp
534     return 0
535 }
536
537 function _addInterfaceCmd
538 {
539     eval INTERFACE_CMDS_${INTERFACE_CMDS_IDX}='( "$@" )'
540     let ++INTERFACE_CMDS_IDX
541 }
542
543 ## Usage: _generateMac <var> <iface> <ctx>
544 function _generateMac
545 {
546     isNumber "$2" || {
547         echo $"Interface basename '$iface' must be either a number, or the mac must be configured explicitly" >&2
548         return 1
549     }
550
551     eval $1=$(printf "f0:ff:%02x:%02x:%02x:%02x" $[ (~($2>>8)) & 0xff ] $[ ($2 & 0xff) ] $[ ($3>>8) & 0xff ] $[ $3 & 0xff ])
552 }
553
554 function _getVLANInfo
555 {
556     case "$1" in
557         (vlan????)
558             panic "\
559 creation of VLAN_PLUS_VID devices is not supported; please create them
560 before starting the vserver and use the 'nodev' flag then"
561             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID"
562             ;;
563         (vlan*)
564             panic "\
565 creation of VLAN_PLUS_VID_NO_PAD devices is not supported; please
566 create them before starting the vserver and use the 'nodev' flag then"
567             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID_N0_PAD"
568             ;;
569         (*.????)        echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID";;
570         (*.*)           echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID_NO_PAD";;
571         (*)             return 1
572     esac
573
574     return 0
575 }
576
577 function _getTunInfo
578 {
579     local iface="$1"
580
581     test -e "$iface/tun" -o -e "$iface/tap" || return 1
582     test ! -e "$iface/tun"     || echo --tun
583     test ! -e "$iface/tap"     || echo --tap
584     test ! -e "$iface/nocsum"  || echo --~checksum
585     test   -e "$iface/shared"  || echo --nid-failure-ok "$N_CONTEXT"
586     if test -e "$iface/uid"; then
587         local uid
588         getFileValue uid "$iface/uid"
589         echo --uid "$uid"
590     fi
591     if test -e "$iface/gid"; then
592         local gid
593         getFileValue gid "$iface/gid"
594         echo --gid "$gid"
595     fi
596     if test -e "$iface/linktype"; then
597         local linktype
598         getFileValue linktype "$iface/linktype"
599         echo --linktype "$linktype"
600     fi
601     return 0
602 }
603
604 ## Usage: _processSingleInterface <interface-directory>
605 function _processSingleInterface
606 {
607     local iface=$1
608
609     local ip
610     local dev
611     local prefix
612     local mask
613     local bcast
614     local name
615     local scope
616     local mac
617     local extip
618     local up="up"
619     local peer
620
621     _getInterfaceValue ip     '' "$iface"
622     _getInterfaceValue extip  '' "$iface" "$iface/.."
623     _getInterfaceValue dev    '' "$iface" "$iface/.."
624     _getInterfaceValue prefix '' "$iface" "$iface/.."
625     _getInterfaceValue mask   '' "$iface" "$iface/.."
626     _getInterfaceValue bcast  '' "$iface" "$iface/.."
627     _getInterfaceValue name   '' "$iface"
628     _getInterfaceValue scope  '' "$iface" "$iface/.."
629     _getInterfaceValue mac    '' "$iface"
630     _getInterfaceValue peer   '' "$iface"
631
632     test -n "$ip" || { echo $"Can not read ip for '$iface'"  >&2; return 1; }
633     test -n "$dev" -o -e "$iface"/nodev || {
634         echo $"No device specified for '$iface'" >&2
635         return 1;
636     }
637
638     test ! -e "$iface"/down || up=
639
640     while true; do
641         _transformMask2Prefix prefix "$prefix" "$mask"
642         INTERFACES=( "${INTERFACES[@]}" "$ip${prefix:+/$prefix}" )
643
644         test ! -e "$iface"/nodev   || break
645         ## LEGACY ALERT
646         test ! -e "$iface"/only_ip || break
647
648         test -e "$iface/vlandev" \
649              -o \( -e "$iface/../vlandev" -a ! -e "$iface/novlandev" \) \
650              -o \( -e "$__CONFDIR/.defaults/interfaces/vlandev" \
651                    -a ! -e "$iface/novlandev" \
652                    -a ! -e "$iface/../novlandev" \) && {
653             local vlan_info
654             if vlan_info=$(_getVLANInfo "$dev"); then
655                 test -d /proc/net/vlan || {
656                     echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
657                     return 1
658                 }
659                 _addInterfaceCmd VCONFIG $vlan_info
660             fi
661         }
662
663         if ! test -e "$iface"/indirect; then
664             # XXX: IPv6 hack
665             local use_bcast="broadcast ${bcast:-+}"
666             echo "$ip" | $_GREP -q : && use_bcast=
667
668             local tun_info
669             if tun_info=$(_getTunInfo "$iface"); then
670                 _addInterfaceCmd TUNCTL "$dev" $tun_info
671             fi
672
673             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" $use_bcast ${name:+label "$dev:$name"} dev "$dev" ${peer:+peer "$peer"}
674             #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
675             _addInterfaceCmd IP_LINK  "$dev" $up
676         elif ! test -n "$N_CONTEXT"; then
677             echo $"Using 'dummy' (indirect) for interface '$dev' requires a fixed context number; dynamic ctx are not supported" >&2
678             return 1
679         else
680             test -z "$mac" || _generateMac mac "$(basename $iface)" "$N_CONTEXT" || return 1
681             _addInterfaceCmd MODPROBE dummy "$dev"
682             _addInterfaceCmd IP_LINK  dev dummy0 address "$mac"
683             _addInterfaceCmd NAMEIF   "$dev" "$mac"
684             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" dev "$dev"
685             test -z "$extip" || _addInterfaceCmd IPTABLES "$ip${prefix:+/$prefix}" ${name:+label "$dev:$name"} "$N_CONTEXT" "$extip"
686         fi
687
688         break
689     done
690 }
691
692 ## Usage: _generateInterfaceOptions <vserver-directory>
693 function _generateInterfaceOptions
694 {
695     local iface
696
697     # XXX: This is here instead of in _generateChbindOptions
698     #      to avoid a circular dependency
699     getFileValue N_CONTEXT "$1/ncontext" "$1/context"
700     test -n "$N_CONTEXT" -o -z "$S_CONTEXT" || N_CONTEXT="$S_CONTEXT"
701
702     for iface in "$1/interfaces/"*; do
703         test   -d "$iface"          || continue
704         test ! -e "$iface"/disabled || continue
705     
706         _processSingleInterface "$iface"
707     done
708     _HAVE_INTERFACE_OPTIONS=1
709 }
710
711 function enableInterfaces
712 {
713     local i=0
714     declare -a var
715
716     lock "$__LOCKDIR"/vserver.interfaces
717
718     while test $i -lt $INTERFACE_CMDS_IDX; do
719         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
720         local type=${var[0]}
721         unset var[0]
722
723         set -- "${var[@]}"
724         case "$type" in
725             IPTABLES)   ;; ## TODO
726             MODPROBE)
727                 local mod=$1
728                 local name=$2
729                 shift 2
730                 $_MODPROBE ${name:+-o "$name"} "$mod" "$@"
731                 ;;
732             NAMEIF)             $_NAMEIF   "$@";;
733             VCONFIG)            $_VCONFIG  set_name_type "$4"      >/dev/null
734                                 $_VCONFIG  add           "$2" "$3" >/dev/null;;
735             IP_ADDR)            $_IP addr  add   "$@";;
736             IP_ADDR_FLUSH)      $_IP addr  flush "$@";;
737             IP_LINK)            $_IP link  set   "$@";;
738             IP_ROUTE)           $_IP route add   "$@";;
739             TUNCTL)
740                 local dev="$1"
741                 shift
742                 $_TUNCTL --persist "$@" "$dev"
743                 ;;
744             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
745         esac
746
747         let ++i
748     done
749
750     unlock 1
751 }
752
753 function disableInterfaces
754 {
755     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
756
757     local i=$INTERFACE_CMDS_IDX
758     declare -a var
759
760     lock "$__LOCKDIR"/vserver.interfaces
761     
762     while test $i -gt 0; do
763         let --i || :
764
765         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
766         local type=${var[0]}
767         unset var[0]
768         
769         set -- "${var[@]}"
770         case "$type" in
771             IPTABLES)           ;; ## TODO
772             MODPROBE)           $_RMMOD "${2:-$1}";;
773             NAMEIF)             ;;
774             VCONFIG)            $_VCONFIG  rem "$2.$3" >/dev/null;;
775             IP_ADDR)            $_IP addr  del "$@";;
776             IP_ADDR_FLUSH)      ;;
777             IP_LINK)            ;; ## Ignore the link-down command for now
778             IP_ROUTE)           $_IP route del "$@";;
779             TUNCTL)
780                 local dev="$1"
781                 shift
782                 $_TUNCTL --~persist "$@" "$dev"
783                 ;;
784             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
785         esac
786     done
787
788     unlock 1
789 }
790
791 function _generateTagOptions
792 {
793     local vdir="$1"
794     local tag
795
796     getFileValue tag "$vdir/tag" "$vdir/context"
797     test -n "$tag" || return 0
798
799     OPTS_VTAG_CREATE=( --tag "$tag" )
800     OPTS_VTAG_ENTER=( --tag "$tag" )
801 }
802
803 function _generateMemctrlOptions
804 {
805     local vdir="$1"
806     local badness
807
808     getFileValue badness "$vdir/badness"
809     test -n "$badness" || return 0
810
811     OPTS_VMEMCTRL=( --badness "$badness" )
812 }
813
814 function _generateSpaceOptions
815 {
816     local vdir="$1"
817     local d="$vdir"/spaces
818     local shared
819     local space
820
821     for space in pid net; do
822         if test -e "$d"/$space; then
823             getFileValue shared "$d"/$space || shared=""
824             if test -z "$shared"; then
825                 OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --$space )
826             elif test "$shared" = "0"; then
827                 : # Do nothing
828             else
829                 OPTS_VSPACE_SHARED=( "${OPTS_VSPACE_SHARED[@]}" --$space )
830                 VSPACE_SHARED_CMD=( "${VSPACE_SHARED_CMD[@]}" $_VSPACE --enter "$shared" --$space -- )
831             fi
832         fi
833     done
834
835     local mask
836     getFileValue mask "$d"/mask || \
837       OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --mask "$mask" )
838 }
839
840 ## Usage: prepareInit <vserver-directory>
841 function prepareInit
842 {
843     pushd "$1/vdir" >/dev/null
844     case "$INITSTYLE" in
845         sysv)
846             { $_FIND var/run  ! -type d -print0; \
847               $_FIND var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
848             ;;
849         plain)
850             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
851             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
852             ;;
853         minit)
854             ;;
855     esac
856     "${INITCMD_PREPARE[@]}"
857     popd >/dev/null
858 }
859
860 ## Usage: prepareInit <vserver-directory>
861 function prepareStop
862 {
863     pushd "$1/vdir" >/dev/null
864     case "$INITSTYLE" in
865         (sysv)
866             export PREVLEVEL=$RUNLEVEL_START RUNLEVEL=$RUNLEVEL_STOP # required by Debian's initscripts
867             ;;
868     esac
869     "${STOPCMD_PREPARE[@]}"
870     popd >/dev/null
871 }
872
873
874 function generateOptions
875 {
876     _generateInterfaceOptions   "$1"
877     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
878     _generateNiceCommand        "$1"
879     _generateIONiceCommand      "$1"
880     _generateInitOptions        "$1"
881     _generateChcontextOptions   "$1"
882     _generateScheduleOptions    "$1"
883     _generatePersonalityOptions "$1"
884     _generateTagOptions         "$1"
885     _generateMemctrlOptions     "$1"
886     _generateSpaceOptions       "$1"
887     _generateCgroupOptions
888
889     if test -n "$_IS_FAKEINIT"; then
890         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
891         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
892     fi
893 }
894
895 function addtoCPUSET
896 {
897     local vdir=$1
898     local cpuset
899     local f="$vdir"/cpuset
900     local i
901     local configured=0
902
903     test -d "$f" || return 0
904     test -e "$f"/name || return 0
905
906     read cpuset < "$f"/name
907     test -e "$f"/nocreate || {
908        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
909        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
910            if test -e "$f"/"$i"; then
911                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" || {
912                    configured=1
913                    break
914                }
915            fi
916        done
917     }
918
919     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
920     if [ "$configured" -ne 0 ]; then
921        warning $"\
922 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
923        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
924        return 0
925     fi
926 }
927
928 function removeCPUSET
929 {
930     local vdir=$1
931     local cpuset
932     local f="$vdir"/cpuset
933
934     test -d "$f" || return 0
935     test -e "$f"/name || return 0
936
937     read cpuset < "$f"/name
938     test -e "$f"/nocreate || {
939        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
940     }
941 }
942
943 function _mountVserverInternal
944 {
945     local fstab="$1"
946     local xflag=
947     
948     test -e "$fstab" || return 0
949     shift
950
951     pushd "$vdir" >/dev/null
952     # check whether / is mounted readonly or whether there is special
953     # magic regarding the mtab file; when etc/mtab can not be touched,
954     # add the '-n' flag to mount
955     test -w etc -o -w etc/mtab || xflag=-n
956     if test -h etc/mtab; then
957         local l=$($_READLINK etc/mtab)
958         test "${l##/proc/}" = "$l" || xflag=-n
959     fi
960     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
961     popd >/dev/null
962 }
963
964 function mountRootFS
965 {
966     local cfgdir=$1
967     local vdir=$1/vdir
968     local fstab="$cfgdir"/fstab
969     local xflag=
970
971     test -e "$fstab" || return 0
972     pushd "$vdir" >/dev/null
973     # check whether / is mounted readonly or whether there is special
974     # magic regarding the mtab file; when etc/mtab can not be touched,
975     # add the '-n' flag to mount
976     test -w etc -o -w etc/mtab || xflag=-n
977     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
978     popd >/dev/null
979 }
980
981 function mountVserver
982 {
983     local cfgdir=$1
984     local ns_opt=$2
985     local vdir=$1/vdir
986     local mtab_src
987     local extra_opt=
988     local real_vdir
989
990     test -e "$cfgdir"/fstab -o \
991          -e "$cfgdir"/fstab.local -o \
992          -e "$cfgdir"/fstab.remote || return 0
993
994     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
995     
996     pushd "$vdir" >/dev/null
997     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
998     popd >/dev/null
999
1000     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
1001
1002     _mountVserverInternal "$cfgdir"/fstab
1003     _mountVserverInternal "$cfgdir"/fstab.local
1004     _mountVserverInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}"
1005
1006     isNamespaceCleanup "$cfgdir" && \
1007         _namespaceCleanup "$cfgdir"
1008
1009     real_vdir=$(getPhysicalDir "$vdir")
1010     isAvoidNamespace "$cfgdir" || \
1011         $_SECURE_MOUNT --rbind -n -o dev "$vdir" "$real_vdir"
1012     ! $_VSERVER_INFO - FEATURE PIVOT_ROOT || \
1013         $_SECURE_MOUNT -n -o shared,rec "$real_vdir" "$real_vdir"
1014 }
1015
1016 function _umountVserverInternal
1017 {
1018     local fstab="$1"
1019     test -e "$fstab" || return 0
1020     shift
1021
1022     $_TAC "$fstab" | {
1023         is_ok=1
1024         while read src dst tmp; do
1025             test -n "$tmp" || continue
1026             case x"$src" in
1027                 (x\#*)  continue;;
1028             esac
1029
1030         
1031             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
1032         done
1033         test -n "$is_ok"
1034     }
1035 }
1036
1037 function umountVserver
1038 {
1039     local cfgdir=$1
1040     local vdir=$1/vdir
1041     local is_ok=1
1042
1043     isAvoidNamespace "$cfgdir"    || return 0
1044     test -e "$cfgdir"/fstab -o \
1045          -e "$cfgdir"/fstab.local -o \
1046          -e "$cfgdir"/fstab.remote || return 0
1047     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
1048     
1049     pushd "$vdir/" >/dev/null || return 1
1050         _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
1051         _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
1052         _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
1053     popd >/dev/null           || return 1
1054
1055     test -n "$is_ok"
1056 }
1057
1058 function fsckAllFS
1059 {
1060     local cfgdir=$1
1061     local fstab="$cfgdir"/fstab
1062     local FSTAB_FILE
1063     local fsck_exitcode
1064
1065     test -e "$fstab" || return 0
1066
1067     export FSTAB_FILE="$fstab"
1068     $_FSCK -s -n -A -T
1069     fsck_exitcode=$?
1070     test "$fsck_exitcode" -eq 0 -o \
1071          "$fsck_exitcode" -eq 1 || return $fsck_exitcode
1072 }
1073
1074 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
1075 function initSync
1076 {
1077     local _is_meth=sync
1078     test -n "$_NEED_VSHELPER_SYNC" && \
1079         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
1080
1081     vshelper.initSync "$1" "$3" "$_is_meth"
1082 }
1083
1084 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
1085 function initWait
1086 {
1087     if $_VSERVER_INFO - FEATURE vwait; then
1088         local _is_tmpdir
1089         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
1090
1091         (
1092             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
1093                 --status-fd 3 "$2" \
1094                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
1095             rc=$?
1096
1097             if test "$rc" -ne 0 -a "$rc" -ne 1; then
1098                 $_VPS axf | $_EGREP -e "^[ \t]*[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
1099                 killContext "$S_CONTEXT" 9
1100             fi
1101
1102             exit $rc
1103         ) 4>$_is_tmpdir/procs &
1104             
1105         echo "$!" >$_is_tmpdir/pid
1106         eval "$3"=$_is_tmpdir
1107     fi </dev/null
1108 }
1109
1110
1111 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
1112 function _waitForVWait
1113 {
1114     wait "$3" || :
1115
1116     declare -a status
1117     declare -r procs=$(cat $4)
1118
1119     getFileArray status "$2"
1120     set -- ${status[0]}
1121
1122     case "$1" in
1123         (ERROR)         warning $"\
1124 'vwait' exited with error '$2' which indicates that vserver could not
1125 be stopped properly"
1126                         ;;
1127         (FINISHED)      ;;
1128         (KILLED)        warning $"\
1129 A timeout occured while waiting for the vserver to finish and it was
1130 killed by sending a SIGKILL signal. Please investigate the reasons
1131 and/or increase the timeout in apps/vshelper/sync-timeout."
1132                         ;;
1133
1134         (TIMEOUT)       warning $"\
1135 A timeout occured while waiting for the vserver to finish and it will
1136 be killed by sending a SIGKILL signal. The following process list
1137 might be useful for finding out the reason of this behavior:
1138
1139 ----------------------------------------------------------------------
1140 ${procs:+$procs
1141 }----------------------------------------------------------------------"
1142                         ;;
1143
1144         (\?\?\?|*)      warning $"\
1145 internal error: 'vwait' exited with an unexpected status '$1'; I will
1146 try to continue but be prepared for unexpected events."
1147                     ;;
1148     esac
1149
1150     return 0
1151 }
1152
1153 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
1154 function waitForSync
1155 {
1156     local cfgdir=$1
1157     local fifo=$2
1158     local vwait_statdir=$3
1159     local vwait_pid=$4
1160
1161     if test -d "$vwait_statdir"; then
1162         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
1163     elif test -n "$_NEED_VSHELPER_SYNC"; then
1164         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
1165             warning $"\
1166 A timeout or other error occured while waiting for the synchronization
1167 signal from vserver '$VSERVER_NAME'.
1168 The vserver will be killed nevertheless..."
1169     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
1170         "${INITCMD_STOP_SYNC[@]}" || \
1171             warning $"\
1172 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
1173 will be killed nevertheless..."
1174     fi
1175
1176     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
1177         sleep 1
1178 }
1179
1180 function _sourceWrap
1181 {
1182     local vdir name flavor start i already_handled base
1183     . "$@"
1184 }
1185
1186 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
1187 function execScriptlets
1188 {
1189     declare -r vdir=$1
1190     declare -r name=$2
1191     declare -r flavor=$3
1192     local base i
1193
1194     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
1195         local   DONT_SKIP_DEFAULTS=
1196         local   already_handled=
1197         
1198         for i in "$base/$flavor" "$base/$flavor.d"/*; do
1199             isRegularFile "$i" || continue
1200             test  -r "$i"      || continue
1201
1202             already_handled=1
1203             local start=
1204             test -x "$i" || start=_sourceWrap
1205             $start "$i" "$flavor" "$name"
1206         done
1207
1208         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
1209     done
1210 }
1211
1212
1213 function sanityCheck
1214 {
1215     declare -r cfgdir=$1
1216
1217     ! test -e "$cfgdir"/fstab.local ||
1218         warning $"\
1219 WARNING: 'fstab' will *not* be executed in the network context of the
1220   vserver anymore. Therefore, 'fstab.local' has the same functionality
1221   and is obsoleted. When you need the old behaviour, put the mounts
1222   into 'fstab.remote'"
1223
1224     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1225         warning $"\
1226 WARNING: The hostname is now configured in 'uts/nodename' but not in
1227   'hostname'."
1228
1229     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1230         warning $"\
1231 WARNING: The domainname is now configured in 'uts/domainname' but not
1232   in 'domainname'." >&2
1233
1234   
1235     local i
1236     for i in "$cfgdir"/interfaces/*/only_ip; do
1237         if test -e "$i"; then
1238             local iface
1239             iface=${i##$cfgdir/interfaces/}
1240             iface=${iface%%/only_ip}
1241             warning $"\
1242 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1243   'nodev' instead of"
1244         fi
1245     done
1246
1247     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1248         warning $"\
1249 WARNING: There is no cachedirectory configured for this vserver;
1250   please create '$cfgdir/cache' e.g. by executing
1251
1252   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1253 "
1254
1255     $_FIND "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1256
1257     vshelper.doSanityCheck
1258
1259     $_VSERVER_INFO - VERIFYCAP ||
1260         panic $"capabilities are not enabled in kernel-setup"
1261
1262     $_VSERVER_INFO - VERIFYPROC ||
1263         panic $"\
1264 /proc/uptime can not be accessed. Usually, this is caused by
1265 procfs-security. Please read the FAQ for more details
1266 http://linux-vserver.org/Proc-Security"
1267
1268     test -e "$cfgdir"/context || {
1269         TYPE=$( $_VSERVER_INFO 49152 XIDTYPE )
1270         test "$TYPE" != "static" || panic $"\
1271 The kernel does not have dynamic contexts enabled. Please configure
1272 a static one by executing
1273
1274   echo [number between 2 and 49151] > $cfgdir/context"
1275     }
1276 }
1277
1278
1279 function _setSingleDiskLimit
1280 {
1281     local vdir=$1
1282     local dlimit=$2
1283     local space_used=
1284     local space_total=
1285     local inodes_used=
1286     local inodes_total=
1287     local reserved=
1288     local directory=
1289     local ctx=
1290
1291     getFileValue ctx          "$vdir/context"
1292     getFileValue directory    "$dlimit/directory"    || return 0
1293     getFileValue space_total  "$dlimit/space_total"  || return 0
1294     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1295     getFileValue reserved     "$dlimit/reserved"     || return 0
1296
1297     local cachename=$ctx$directory
1298     cachename=dlimits/${cachename//\//_}
1299
1300     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1301     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1302     $_RM -f "$vdir/cache/$cachename"
1303
1304     if test -z "$inodes_used" -o -z "$space_used"; then
1305         local tmpvdu
1306         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1307         inodes_used=${tmpvdu##* }
1308         space_used=${tmpvdu%% *}
1309     fi
1310
1311     $_VDLIMIT --xid $ctx \
1312         --set space_used=$space_used \
1313         --set space_total=$space_total \
1314         --set inodes_used=$inodes_used \
1315         --set inodes_total=$inodes_total \
1316         --set reserved=$reserved \
1317         "$directory"
1318 }
1319
1320
1321 function setDiskLimits
1322 {
1323     local vdir=$1
1324     local dlimit
1325
1326     # Disk Limits without a static context are useless
1327     test -e "$vdir"/context || return 0
1328
1329     for dlimit in "$vdir/dlimits/"*; do
1330         test   -d "$dlimit"          || continue
1331         test ! -e "$dlimit/disabled" || continue
1332
1333         _setSingleDiskLimit "$vdir" "$dlimit"
1334     done
1335 }
1336
1337
1338 function _saveSingleDiskLimit
1339 {
1340     local vdir=$1
1341     local dlimit=$2
1342     local ctx=
1343     local directory=
1344
1345     getFileValue ctx       "$vdir/context"
1346     getFileValue directory "$dlimit/directory" || return 0
1347
1348     local cachename=$ctx$directory
1349     cachename=${cachename//\//_}
1350
1351     # Things are getting ugly here... LFS says that /var/cache (where
1352     # cachename is usually pointing to) can vanish and applications
1353     # have to deal with it. So, we have to interprete the $vdir/cache
1354     # symlink and have to create the needed directories manually.
1355     if   test -d "$vdir/cache"; then
1356         :       # ok, exists already
1357     elif test -L "$vdir/cache"; then
1358         # it's a dangling symlink
1359         local link
1360         link=$($_READLINK "$vdir/cache")
1361         ( cd $vdir && $_MKDIR -p "$link" )
1362     else
1363         return 0
1364     fi
1365
1366     test -d "$vdir/cache"
1367     $_MKDIR -p "$vdir"/cache/dlimits
1368
1369     $_VDLIMIT --xid $ctx "$directory" | \
1370         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1371
1372     $_VDLIMIT --xid $ctx --remove "$directory"
1373 }
1374
1375
1376 function saveDiskLimits
1377 {
1378     local vdir=$1
1379     local dlimit
1380
1381     test -e "$vdir"/context || return 0
1382
1383     for dlimit in "$vdir/dlimits/"*; do
1384         test   -d "$dlimit"          || continue
1385         test ! -e "$dlimit/disabled" || continue
1386
1387         _saveSingleDiskLimit "$vdir" "$dlimit"
1388     done
1389 }
1390
1391 function _namespaceCleanup
1392 {
1393     local vdir="$1"
1394     local root=$($_VSERVER_INFO "$1" VDIR 1)
1395     local -a list
1396     local -a skip
1397     local i
1398     local j
1399
1400     getFileArray skip "$vdir"/namespace-cleanup-skip \
1401         "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
1402
1403     # these are things that have to be accessible post-cleanup
1404     for i in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
1405         "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
1406         local real=`getPhysicalDir "$i"`
1407         test "$i" != "$real" || real=
1408         for j in "$i" "$real"; do
1409             while test -n "$j"; do
1410                 list=( "${list[@]}" "$j" )
1411                 j="${j%/*}"
1412             done
1413         done
1414     done
1415
1416     local -a list_umount
1417     while read -r dev path opts; do
1418         test -n "$path" || continue
1419         for i in "$root" /dev /proc; do
1420             path_dir="${path}/"
1421             test "${path_dir#${i}/}" != "${path_dir}" && continue 2
1422         done
1423         for i in "${list[@]}" /; do
1424             test "$path" = "$i" && continue 2
1425         done
1426         # unmount them in reverse order so mounts further down the tree get unmounted first
1427         list_umount=( "$path" "${list_umount[@]}" )
1428     done < /proc/mounts
1429     # separate loop to avoid races while reading /proc/mounts
1430     for i in "${list_umount[@]}"; do
1431         $_UMOUNT -l -n -i "$i"
1432     done
1433 }
1434
1435 function handleDeviceMap
1436 {
1437     local op="$1"
1438     local xid="$2"
1439     local dir="$3"
1440     local flags device target
1441
1442     test -d "$dir" || return 0
1443     test -n "$xid" || return 0
1444
1445     for i in "$dir"/*; do
1446         test -d "$i" || continue
1447
1448         local -a vdevmap_opts=()
1449         test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
1450         test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
1451         test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
1452
1453         getFileValue flags "$i/flags" || :
1454         getFileValue device "$i/device" || :
1455         getFileValue target "$i/target" || :
1456         vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
1457                         ${device:+--device "$device"} ${target:+--target "$target"} )
1458
1459         $_VDEVMAP --xid "$xid" "$op" "${vdevmap_opts[@]}" || return $?
1460     done
1461 }
1462
1463 function hasCgroup
1464 {
1465     $_GREP -q "cgroup" /proc/filesystems
1466 }
1467
1468 function _generateCgroupOptions
1469 {
1470     local file
1471
1472     hasCgroup || return 0
1473
1474     findFile file "$__CONFDIR/.defaults/cgroup/mnt" ""
1475     if test -n "$file"; then
1476         read CGROUP_MNT < "$file"
1477     fi
1478     findFile file "$__CONFDIR/.defaults/cgroup/subsys" ""
1479     if test -n "$file"; then
1480         _readFileToArray CGROUP_SUBSYS "$file"
1481     else
1482         CGROUP_SUBSYS=( $($_SED '/^#/d;/^ns[[:space:]]/d;s/[[:space:]].*//' /proc/cgroups) )
1483     fi
1484     findFile file "$__CONFDIR/.defaults/cgroup/inherit" ""
1485     if test -n "$file"; then
1486         _readFileToArray CGROUP_INHERIT "$file" ""
1487     fi
1488     findFile file "$__CONFDIR/.defaults/cgroup/base" ""
1489     if test -n "$file"; then
1490         read CGROUP_BASE < "$file"
1491         test "$CGROUP_BASE" != "${CGROUP_BASE%/}" || \
1492             CGROUP_BASE="${CGROUP_BASE}/"
1493     fi
1494     findFile file "$__CONFDIR/.defaults/cgroup/per-ss" ""
1495     if test -n "$file"; then
1496         CGROUP_MNT_PER_SS=1
1497     fi
1498
1499     return 0
1500 }
1501
1502 function useCgroup
1503 {
1504     hasCgroup || return 1
1505     if test -n "$CGROUP_MNT_PER_SS"; then
1506         local existing=0
1507         local ss
1508         for ss in "${CGROUP_SUBSYS[@]}"; do
1509             if test -e "$CGROUP_MNT/$ss/tasks"; then
1510                 let existing=${existing}+1
1511             fi
1512         done
1513         test "$existing" -gt 0 || return 1
1514     else
1515         test -e "$CGROUP_MNT/tasks" || return 1
1516     fi
1517     local memcg=""
1518     if $_VSERVER_INFO - FEATURE memcg; then
1519         memcg=1
1520     fi
1521     test -d "$1/cgroup" -o \
1522         \( \( -d "$__CONFDIR/.defaults/cgroup" -o -n "$memcg" \) -a \
1523            ! -e "$1/nocgroup" \)
1524 }
1525
1526 function _handleCgroup
1527 {
1528     local action="$1"
1529     local vdir="$2"
1530     local dir
1531     local name
1532     local i
1533     local j
1534     local parent
1535     local -a mnts
1536     local ss
1537
1538     useCgroup "$vdir" || return 0
1539
1540     findDir dir "$vdir/cgroup" "$__CONFDIR/.defaults/cgroup" ""
1541
1542     if test -n "$dir" -a -r "$dir"/name; then
1543         read name < "$dir"/name
1544     else
1545         name="$VSERVER_NAME"
1546     fi
1547
1548     if test -n "$CGROUP_MNT_PER_SS"; then
1549         mnts=()
1550         for ss in "${CGROUP_SUBSYS[@]}"; do
1551             mnts=( "${mnts[@]}" "$CGROUP_MNT/$ss" )
1552         done
1553     else
1554         mnts=( "$CGROUP_MNT" )
1555     fi
1556     for mnt in "${mnts[@]}"; do
1557         test -d "$mnt" || continue
1558         if test "$action" = "attach"; then
1559             if test -n "$CGROUP_BASE"; then
1560                 local -a dirs=()
1561                 i="$mnt/$CGROUP_BASE"
1562                 while test "$mnt" != "$i"; do
1563                     dirs=( "$i" "${dirs[@]}" )
1564                     i="${i%/*}"
1565                 done
1566                 for i in "${dirs[@]}"; do
1567                     if mkdir "$i" 2>/dev/null; then
1568                         parent="${i%/*}"
1569                         for j in "${CGROUP_INHERIT[@]}"; do
1570                             test -f "$parent/$j" || continue
1571                             cat "$parent/$j" > "$i/$j"
1572                         done
1573                     fi
1574                 done
1575             fi
1576             if mkdir "$mnt/$CGROUP_BASE$name" 2>/dev/null; then
1577                 parent="$mnt/$CGROUP_BASE$name"
1578                 parent="${parent%/*}"
1579                 for i in "${CGROUP_INHERIT[@]}"; do
1580                     test -f "$parent/$i" || continue
1581                     cat "$parent/$i" > "$mnt/$CGROUP_BASE$name/$i"
1582                 done
1583
1584                 if test -n "$dir"; then
1585                     shopt -s nullglob
1586                     for i in "$dir"/*; do
1587                         f="${i##*/}"
1588                         test "$f" != mnt -a "$f" != subsys -a \
1589                             "$f" != inherit -a "$f" != name -a "$f" != base -a \
1590                             "$f" != per-ss \
1591                             || continue
1592                         if test -n "$CGROUP_MNT_PER_SS"; then
1593                             ss="${f%%.*}"
1594                             test "$ss" = "${mnt##*/}" || continue
1595                         fi
1596                         cat "$i" > "$mnt/$CGROUP_BASE$name/$f"
1597                     done
1598                 fi
1599             fi
1600             echo "$$" > "$mnt/$CGROUP_BASE$name/tasks"
1601         elif test "$action" = "destroy"; then
1602             rmdir "$mnt/$name" 2>/dev/null || :
1603         fi
1604     done
1605
1606     return 0
1607 }
1608
1609 function attachToCgroup
1610 {
1611     _handleCgroup attach "$@"
1612 }
1613
1614 function destroyCgroup
1615 {
1616     _handleCgroup destroy "$@"
1617 }