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