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