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