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