Fix types
[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)             $_TUNCTL --~persist "$1";;
766             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
767         esac
768     done
769
770     unlock 1
771 }
772
773 function _generateTagOptions
774 {
775     local vdir="$1"
776     local tag
777
778     getFileValue tag "$vdir/tag" "$vdir/context"
779     test -n "$tag" || return 0
780
781     OPTS_VTAG_CREATE=( --tag "$tag" )
782     OPTS_VTAG_ENTER=( --tag "$tag" )
783 }
784
785 function _generateMemctrlOptions
786 {
787     local vdir="$1"
788     local badness
789
790     getFileValue badness "$vdir/badness"
791     test -n "$badness" || return 0
792
793     OPTS_VMEMCTRL=( --badness "$badness" )
794 }
795
796 function _generateSpaceOptions
797 {
798     local vdir="$1"
799     local d="$vdir"/spaces
800     local shared
801     local space
802
803     for space in pid net; do
804         if test -e "$d"/$space; then
805             getFileValue shared "$d"/$space || shared=""
806             if test -z "$shared"; then
807                 OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --$space )
808             elif test "$shared" = "0"; then
809                 : # Do nothing
810             else
811                 OPTS_VSPACE_SHARED=( "${OPTS_VSPACE_SHARED[@]}" --$space )
812                 VSPACE_SHARED_CMD=( "${VSPACE_SHARED_CMD[@]}" $_VSPACE --enter "$shared" --$space -- )
813             fi
814         fi
815     done
816
817     local mask
818     getFileValue mask "$d"/mask || \
819       OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --mask "$mask" )
820 }
821
822 ## Usage: prepareInit <vserver-directory>
823 function prepareInit
824 {
825     pushd "$1/vdir" >/dev/null
826     case "$INITSTYLE" in
827         sysv)
828             { $_FIND var/run  ! -type d -print0; \
829               $_FIND var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
830             ;;
831         plain)
832             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
833             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
834             ;;
835         minit)
836             ;;
837     esac
838     "${INITCMD_PREPARE[@]}"
839     popd >/dev/null
840 }
841
842 ## Usage: prepareInit <vserver-directory>
843 function prepareStop
844 {
845     pushd "$1/vdir" >/dev/null
846     case "$INITSTYLE" in
847         (sysv)
848             export PREVLEVEL=$RUNLEVEL_START RUNLEVEL=$RUNLEVEL_STOP # required by Debian's initscripts
849             ;;
850     esac
851     "${STOPCMD_PREPARE[@]}"
852     popd >/dev/null
853 }
854
855
856 function generateOptions
857 {
858     _generateInterfaceOptions   "$1"
859     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
860     _generateNiceCommand        "$1"
861     _generateIONiceCommand      "$1"
862     _generateInitOptions        "$1"
863     _generateChcontextOptions   "$1"
864     _generateScheduleOptions    "$1"
865     _generatePersonalityOptions "$1"
866     _generateTagOptions         "$1"
867     _generateMemctrlOptions     "$1"
868     _generateSpaceOptions       "$1"
869     _generateCgroupOptions
870
871     if test -n "$_IS_FAKEINIT"; then
872         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
873         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
874     fi
875 }
876
877 function addtoCPUSET
878 {
879     local vdir=$1
880     local cpuset
881     local f="$vdir"/cpuset
882     local i
883     local configured=0
884
885     test -d "$f" || return 0
886     test -e "$f"/name || return 0
887
888     read cpuset < "$f"/name
889     test -e "$f"/nocreate || {
890        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
891        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
892            if test -e "$f"/"$i"; then
893                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" || {
894                    configured=1
895                    break
896                }
897            fi
898        done
899     }
900
901     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
902     if [ "$configured" -ne 0 ]; then
903        warning $"\
904 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
905        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
906        return 0
907     fi
908 }
909
910 function removeCPUSET
911 {
912     local vdir=$1
913     local cpuset
914     local f="$vdir"/cpuset
915
916     test -d "$f" || return 0
917     test -e "$f"/name || return 0
918
919     read cpuset < "$f"/name
920     test -e "$f"/nocreate || {
921        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
922     }
923 }
924
925 function _mountVserverInternal
926 {
927     local fstab="$1"
928     local xflag=
929     
930     test -e "$fstab" || return 0
931     shift
932
933     pushd "$vdir" >/dev/null
934     # check whether / is mounted readonly or whether there is special
935     # magic regarding the mtab file; when etc/mtab can not be touched,
936     # add the '-n' flag to mount
937     test -w etc -o -w etc/mtab || xflag=-n
938     if test -h etc/mtab; then
939         local l=$($_READLINK etc/mtab)
940         test "${l##/proc/}" = "$l" || xflag=-n
941     fi
942     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
943     popd >/dev/null
944 }
945
946 function mountRootFS
947 {
948     local cfgdir=$1
949     local vdir=$1/vdir
950     local fstab="$cfgdir"/fstab
951     local xflag=
952
953     test -e "$fstab" || return 0
954     pushd "$vdir" >/dev/null
955     # check whether / is mounted readonly or whether there is special
956     # magic regarding the mtab file; when etc/mtab can not be touched,
957     # add the '-n' flag to mount
958     test -w etc -o -w etc/mtab || xflag=-n
959     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
960     popd >/dev/null
961 }
962
963 function mountVserver
964 {
965     local cfgdir=$1
966     local ns_opt=$2
967     local vdir=$1/vdir
968     local mtab_src
969     local extra_opt=
970     local real_vdir
971
972     test -e "$cfgdir"/fstab -o \
973          -e "$cfgdir"/fstab.local -o \
974          -e "$cfgdir"/fstab.remote || return 0
975
976     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
977     
978     pushd "$vdir" >/dev/null
979     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
980     popd >/dev/null
981
982     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
983
984     _mountVserverInternal "$cfgdir"/fstab
985     _mountVserverInternal "$cfgdir"/fstab.local
986     _mountVserverInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}"
987
988     isNamespaceCleanup "$cfgdir" && \
989         _namespaceCleanup "$cfgdir"
990
991     real_vdir=$(getPhysicalDir "$vdir")
992     isAvoidNamespace "$cfgdir" || \
993         $_SECURE_MOUNT --rbind -n -o dev "$vdir" "$real_vdir"
994     ! $_VSERVER_INFO - FEATURE PIVOT_ROOT || \
995         $_SECURE_MOUNT -n -o shared,rec "$real_vdir" "$real_vdir"
996 }
997
998 function _umountVserverInternal
999 {
1000     local fstab="$1"
1001     test -e "$fstab" || return 0
1002     shift
1003
1004     $_TAC "$fstab" | {
1005         is_ok=1
1006         while read src dst tmp; do
1007             test -n "$tmp" || continue
1008             case x"$src" in
1009                 (x\#*)  continue;;
1010             esac
1011
1012         
1013             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
1014         done
1015         test -n "$is_ok"
1016     }
1017 }
1018
1019 function umountVserver
1020 {
1021     local cfgdir=$1
1022     local vdir=$1/vdir
1023     local is_ok=1
1024
1025     isAvoidNamespace "$cfgdir"    || return 0
1026     test -e "$cfgdir"/fstab -o \
1027          -e "$cfgdir"/fstab.local -o \
1028          -e "$cfgdir"/fstab.remote || return 0
1029     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
1030     
1031     pushd "$vdir/" >/dev/null || return 1
1032         _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
1033         _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
1034         _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
1035     popd >/dev/null           || return 1
1036
1037     test -n "$is_ok"
1038 }
1039
1040 function fsckAllFS
1041 {
1042     local cfgdir=$1
1043     local fstab="$cfgdir"/fstab
1044     local FSTAB_FILE
1045     local fsck_exitcode
1046
1047     test -e "$fstab" || return 0
1048
1049     export FSTAB_FILE="$fstab"
1050     $_FSCK -s -n -A -T
1051     fsck_exitcode=$?
1052     test "$fsck_exitcode" -eq 0 -o \
1053          "$fsck_exitcode" -eq 1 || return $fsck_exitcode
1054 }
1055
1056 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
1057 function initSync
1058 {
1059     local _is_meth=sync
1060     test -n "$_NEED_VSHELPER_SYNC" && \
1061         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
1062
1063     vshelper.initSync "$1" "$3" "$_is_meth"
1064 }
1065
1066 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
1067 function initWait
1068 {
1069     if $_VSERVER_INFO - FEATURE vwait; then
1070         local _is_tmpdir
1071         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
1072
1073         (
1074             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
1075                 --status-fd 3 "$2" \
1076                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
1077             rc=$?
1078
1079             if test "$rc" -ne 0 -a "$rc" -ne 1; then
1080                 $_VPS axf | $_EGREP -e "^[ \t]*[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
1081                 killContext "$S_CONTEXT" 9
1082             fi
1083
1084             exit $rc
1085         ) 4>$_is_tmpdir/procs &
1086             
1087         echo "$!" >$_is_tmpdir/pid
1088         eval "$3"=$_is_tmpdir
1089     fi </dev/null
1090 }
1091
1092
1093 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
1094 function _waitForVWait
1095 {
1096     wait "$3" || :
1097
1098     declare -a status
1099     declare -r procs=$(cat $4)
1100
1101     getFileArray status "$2"
1102     set -- ${status[0]}
1103
1104     case "$1" in
1105         (ERROR)         warning $"\
1106 'vwait' exited with error '$2' which indicates that vserver could not
1107 be stopped properly"
1108                         ;;
1109         (FINISHED)      ;;
1110         (KILLED)        warning $"\
1111 A timeout occured while waiting for the vserver to finish and it was
1112 killed by sending a SIGKILL signal. Please investigate the reasons
1113 and/or increase the timeout in apps/vshelper/sync-timeout."
1114                         ;;
1115
1116         (TIMEOUT)       warning $"\
1117 A timeout occured while waiting for the vserver to finish and it will
1118 be killed by sending a SIGKILL signal. The following process list
1119 might be useful for finding out the reason of this behavior:
1120
1121 ----------------------------------------------------------------------
1122 ${procs:+$procs
1123 }----------------------------------------------------------------------"
1124                         ;;
1125
1126         (\?\?\?|*)      warning $"\
1127 internal error: 'vwait' exited with an unexpected status '$1'; I will
1128 try to continue but be prepared for unexpected events."
1129                     ;;
1130     esac
1131
1132     return 0
1133 }
1134
1135 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
1136 function waitForSync
1137 {
1138     local cfgdir=$1
1139     local fifo=$2
1140     local vwait_statdir=$3
1141     local vwait_pid=$4
1142
1143     if test -d "$vwait_statdir"; then
1144         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
1145     elif test -n "$_NEED_VSHELPER_SYNC"; then
1146         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
1147             warning $"\
1148 A timeout or other error occured while waiting for the synchronization
1149 signal from vserver '$VSERVER_NAME'.
1150 The vserver will be killed nevertheless..."
1151     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
1152         "${INITCMD_STOP_SYNC[@]}" || \
1153             warning $"\
1154 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
1155 will be killed nevertheless..."
1156     fi
1157
1158     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
1159         sleep 1
1160 }
1161
1162 function _sourceWrap
1163 {
1164     local vdir name flavor start i already_handled base
1165     . "$@"
1166 }
1167
1168 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
1169 function execScriptlets
1170 {
1171     declare -r vdir=$1
1172     declare -r name=$2
1173     declare -r flavor=$3
1174     local base i
1175
1176     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
1177         local   DONT_SKIP_DEFAULTS=
1178         local   already_handled=
1179         
1180         for i in "$base/$flavor" "$base/$flavor.d"/*; do
1181             isRegularFile "$i" || continue
1182             test  -r "$i"      || continue
1183
1184             already_handled=1
1185             local start=
1186             test -x "$i" || start=_sourceWrap
1187             $start "$i" "$flavor" "$name"
1188         done
1189
1190         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
1191     done
1192 }
1193
1194
1195 function sanityCheck
1196 {
1197     declare -r cfgdir=$1
1198
1199     ! test -e "$cfgdir"/fstab.local ||
1200         warning $"\
1201 WARNING: 'fstab' will *not* be executed in the network context of the
1202   vserver anymore. Therefore, 'fstab.local' has the same functionality
1203   and is obsoleted. When you need the old behaviour, put the mounts
1204   into 'fstab.remote'"
1205
1206     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1207         warning $"\
1208 WARNING: The hostname is now configured in 'uts/nodename' but not in
1209   'hostname'."
1210
1211     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1212         warning $"\
1213 WARNING: The domainname is now configured in 'uts/domainname' but not
1214   in 'domainname'." >&2
1215
1216   
1217     local i
1218     for i in "$cfgdir"/interfaces/*/only_ip; do
1219         if test -e "$i"; then
1220             local iface
1221             iface=${i##$cfgdir/interfaces/}
1222             iface=${iface%%/only_ip}
1223             warning $"\
1224 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1225   'nodev' instead of"
1226         fi
1227     done
1228
1229     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1230         warning $"\
1231 WARNING: There is no cachedirectory configured for this vserver;
1232   please create '$cfgdir/cache' e.g. by executing
1233
1234   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1235 "
1236
1237     $_FIND "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1238
1239     vshelper.doSanityCheck
1240
1241     $_VSERVER_INFO - VERIFYCAP ||
1242         panic $"capabilities are not enabled in kernel-setup"
1243
1244     $_VSERVER_INFO - VERIFYPROC ||
1245         panic $"\
1246 /proc/uptime can not be accessed. Usually, this is caused by
1247 procfs-security. Please read the FAQ for more details
1248 http://linux-vserver.org/Proc-Security"
1249
1250     test -e "$cfgdir"/context || {
1251         TYPE=$( $_VSERVER_INFO 49152 XIDTYPE )
1252         test "$TYPE" != "static" || panic $"\
1253 The kernel does not have dynamic contexts enabled. Please configure
1254 a static one by executing
1255
1256   echo [number between 2 and 49151] > $cfgdir/context"
1257     }
1258 }
1259
1260
1261 function _setSingleDiskLimit
1262 {
1263     local vdir=$1
1264     local dlimit=$2
1265     local space_used=
1266     local space_total=
1267     local inodes_used=
1268     local inodes_total=
1269     local reserved=
1270     local directory=
1271     local ctx=
1272
1273     getFileValue ctx          "$vdir/context"
1274     getFileValue directory    "$dlimit/directory"    || return 0
1275     getFileValue space_total  "$dlimit/space_total"  || return 0
1276     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1277     getFileValue reserved     "$dlimit/reserved"     || return 0
1278
1279     local cachename=$ctx$directory
1280     cachename=dlimits/${cachename//\//_}
1281
1282     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1283     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1284     $_RM -f "$vdir/cache/$cachename"
1285
1286     if test -z "$inodes_used" -o -z "$space_used"; then
1287         local tmpvdu
1288         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1289         inodes_used=${tmpvdu##* }
1290         space_used=${tmpvdu%% *}
1291     fi
1292
1293     $_VDLIMIT --xid $ctx \
1294         --set space_used=$space_used \
1295         --set space_total=$space_total \
1296         --set inodes_used=$inodes_used \
1297         --set inodes_total=$inodes_total \
1298         --set reserved=$reserved \
1299         "$directory"
1300 }
1301
1302
1303 function setDiskLimits
1304 {
1305     local vdir=$1
1306     local dlimit
1307
1308     # Disk Limits without a static context are useless
1309     test -e "$vdir"/context || return 0
1310
1311     for dlimit in "$vdir/dlimits/"*; do
1312         test   -d "$dlimit"          || continue
1313         test ! -e "$dlimit/disabled" || continue
1314
1315         _setSingleDiskLimit "$vdir" "$dlimit"
1316     done
1317 }
1318
1319
1320 function _saveSingleDiskLimit
1321 {
1322     local vdir=$1
1323     local dlimit=$2
1324     local ctx=
1325     local directory=
1326
1327     getFileValue ctx       "$vdir/context"
1328     getFileValue directory "$dlimit/directory" || return 0
1329
1330     local cachename=$ctx$directory
1331     cachename=${cachename//\//_}
1332
1333     # Things are getting ugly here... LFS says that /var/cache (where
1334     # cachename is usually pointing to) can vanish and applications
1335     # have to deal with it. So, we have to interprete the $vdir/cache
1336     # symlink and have to create the needed directories manually.
1337     if   test -d "$vdir/cache"; then
1338         :       # ok, exists already
1339     elif test -L "$vdir/cache"; then
1340         # it's a dangling symlink
1341         local link
1342         link=$($_READLINK "$vdir/cache")
1343         ( cd $vdir && $_MKDIR -p "$link" )
1344     else
1345         return 0
1346     fi
1347
1348     test -d "$vdir/cache"
1349     $_MKDIR -p "$vdir"/cache/dlimits
1350
1351     $_VDLIMIT --xid $ctx "$directory" | \
1352         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1353
1354     $_VDLIMIT --xid $ctx --remove "$directory"
1355 }
1356
1357
1358 function saveDiskLimits
1359 {
1360     local vdir=$1
1361     local dlimit
1362
1363     test -e "$vdir"/context || return 0
1364
1365     for dlimit in "$vdir/dlimits/"*; do
1366         test   -d "$dlimit"          || continue
1367         test ! -e "$dlimit/disabled" || continue
1368
1369         _saveSingleDiskLimit "$vdir" "$dlimit"
1370     done
1371 }
1372
1373 function _namespaceCleanup
1374 {
1375     local vdir="$1"
1376     local root=$($_VSERVER_INFO "$1" VDIR 1)
1377     local -a list
1378     local -a skip
1379     local i
1380     local j
1381
1382     getFileArray skip "$vdir"/namespace-cleanup-skip \
1383         "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
1384
1385     # these are things that have to be accessible post-cleanup
1386     for i in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
1387         "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
1388         local real=`getPhysicalDir "$i"`
1389         test "$i" != "$real" || real=
1390         for j in "$i" "$real"; do
1391             while test -n "$j"; do
1392                 list=( "${list[@]}" "$j" )
1393                 j="${j%/*}"
1394             done
1395         done
1396     done
1397
1398     local -a list_umount
1399     while read -r dev path opts; do
1400         test -n "$path" || continue
1401         for i in "$root" /dev /proc; do
1402             path_dir="${path}/"
1403             test "${path_dir#${i}/}" != "${path_dir}" && continue 2
1404         done
1405         for i in "${list[@]}" /; do
1406             test "$path" = "$i" && continue 2
1407         done
1408         # unmount them in reverse order so mounts further down the tree get unmounted first
1409         list_umount=( "$path" "${list_umount[@]}" )
1410     done < /proc/mounts
1411     # separate loop to avoid races while reading /proc/mounts
1412     for i in "${list_umount[@]}"; do
1413         $_UMOUNT -l -n -i "$i"
1414     done
1415 }
1416
1417 function handleDeviceMap
1418 {
1419     local op="$1"
1420     local xid="$2"
1421     local dir="$3"
1422     local flags device target
1423
1424     test -d "$dir" || return 0
1425     test -n "$xid" || return 0
1426
1427     for i in "$dir"/*; do
1428         test -d "$i" || continue
1429
1430         local -a vdevmap_opts=()
1431         test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
1432         test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
1433         test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
1434
1435         getFileValue flags "$i/flags" || :
1436         getFileValue device "$i/device" || :
1437         getFileValue target "$i/target" || :
1438         vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
1439                         ${device:+--device "$device"} ${target:+--target "$target"} )
1440
1441         $_VDEVMAP --xid "$xid" "$op" "${vdevmap_opts[@]}" || return $?
1442     done
1443 }
1444
1445 function hasCgroup
1446 {
1447     $_GREP -q "cgroup" /proc/filesystems
1448 }
1449
1450 function _generateCgroupOptions
1451 {
1452     local file
1453
1454     hasCgroup || return 0
1455
1456     findFile file "$__CONFDIR/.defaults/cgroup/mnt" ""
1457     if test -n "$file"; then
1458         read CGROUP_MNT < "$file"
1459     fi
1460     findFile file "$__CONFDIR/.defaults/cgroup/subsys" ""
1461     if test -n "$file"; then
1462         _readFileToArray CGROUP_SUBSYS "$file"
1463     else
1464         CGROUP_SUBSYS=( $($_SED '/^#/d;/^ns[[:space:]]/d;s/[[:space:]].*//' /proc/cgroups) )
1465     fi
1466     findFile file "$__CONFDIR/.defaults/cgroup/inherit" ""
1467     if test -n "$file"; then
1468         _readFileToArray CGROUP_INHERIT "$file" ""
1469     fi
1470     findFile file "$__CONFDIR/.defaults/cgroup/base" ""
1471     if test -n "$file"; then
1472         read CGROUP_BASE < "$file"
1473         test "$CGROUP_BASE" != "${CGROUP_BASE%/}" || \
1474             CGROUP_BASE="${CGROUP_BASE}/"
1475     fi
1476     findFile file "$__CONFDIR/.defaults/cgroup/per-ss" ""
1477     if test -n "$file"; then
1478         CGROUP_MNT_PER_SS=1
1479     fi
1480
1481     return 0
1482 }
1483
1484 function useCgroup
1485 {
1486     hasCgroup || return 1
1487     if test -n "$CGROUP_MNT_PER_SS"; then
1488         local existing=0
1489         local ss
1490         for ss in "${CGROUP_SUBSYS[@]}"; do
1491             if test -e "$CGROUP_MNT/$ss/tasks"; then
1492                 let existing=${existing}+1
1493             fi
1494         done
1495         test "$existing" -gt 0 || return 1
1496     else
1497         test -e "$CGROUP_MNT/tasks" || return 1
1498     fi
1499     local memcg=""
1500     if $_VSERVER_INFO - FEATURE memcg; then
1501         memcg=1
1502     fi
1503     test -d "$1/cgroup" -o \
1504         \( \( -d "$__CONFDIR/.defaults/cgroup" -o -n "$memcg" \) -a \
1505            ! -e "$1/nocgroup" \)
1506 }
1507
1508 function _handleCgroup
1509 {
1510     local action="$1"
1511     local vdir="$2"
1512     local dir
1513     local name
1514     local i
1515     local j
1516     local parent
1517     local -a mnts
1518     local ss
1519
1520     useCgroup "$vdir" || return 0
1521
1522     findDir dir "$vdir/cgroup" "$__CONFDIR/.defaults/cgroup" ""
1523
1524     if test -n "$dir" -a -r "$dir"/name; then
1525         read name < "$dir"/name
1526     else
1527         name="$VSERVER_NAME"
1528     fi
1529
1530     if test -n "$CGROUP_MNT_PER_SS"; then
1531         mnts=()
1532         for ss in "${CGROUP_SUBSYS[@]}"; do
1533             mnts=( "${mnts[@]}" "$CGROUP_MNT/$ss" )
1534         done
1535     else
1536         mnts=( "$CGROUP_MNT" )
1537     fi
1538     for mnt in "${mnts[@]}"; do
1539         test -d "$mnt" || continue
1540         if test "$action" = "attach"; then
1541             if test -n "$CGROUP_BASE"; then
1542                 local -a dirs=()
1543                 i="$mnt/$CGROUP_BASE"
1544                 while test "$mnt" != "$i"; do
1545                     dirs=( "$i" "${dirs[@]}" )
1546                     i="${i%/*}"
1547                 done
1548                 for i in "${dirs[@]}"; do
1549                     if mkdir "$i" 2>/dev/null; then
1550                         parent="${i%/*}"
1551                         for j in "${CGROUP_INHERIT[@]}"; do
1552                             test -f "$parent/$j" || continue
1553                             cat "$parent/$j" > "$i/$j"
1554                         done
1555                     fi
1556                 done
1557             fi
1558             if mkdir "$mnt/$CGROUP_BASE$name" 2>/dev/null; then
1559                 parent="$mnt/$CGROUP_BASE$name"
1560                 parent="${parent%/*}"
1561                 for i in "${CGROUP_INHERIT[@]}"; do
1562                     test -f "$parent/$i" || continue
1563                     cat "$parent/$i" > "$mnt/$CGROUP_BASE$name/$i"
1564                 done
1565
1566                 if test -n "$dir"; then
1567                     shopt -s nullglob
1568                     for i in "$dir"/*; do
1569                         f="${i##*/}"
1570                         test "$f" != mnt -a "$f" != subsys -a \
1571                             "$f" != inherit -a "$f" != name -a "$f" != base -a \
1572                             "$f" != per-ss \
1573                             || continue
1574                         if test -n "$CGROUP_MNT_PER_SS"; then
1575                             ss="${f%%.*}"
1576                             test "$ss" = "${mnt##*/}" || continue
1577                         fi
1578                         cat "$i" > "$mnt/$CGROUP_BASE$name/$f"
1579                     done
1580                 fi
1581             fi
1582             echo "$$" > "$mnt/$CGROUP_BASE$name/tasks"
1583         elif test "$action" = "destroy"; then
1584             rmdir "$mnt/$name" 2>/dev/null || :
1585         fi
1586     done
1587
1588     return 0
1589 }
1590
1591 function attachToCgroup
1592 {
1593     _handleCgroup attach "$@"
1594 }
1595
1596 function destroyCgroup
1597 {
1598     _handleCgroup destroy "$@"
1599 }