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