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