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