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