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