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