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