Tagging module util-vserver - util-vserver-0.30.215-6
[util-vserver.git] / scripts / vserver.functions
1 # $Id: vserver.functions 2702 2008-03-11 10:07:26Z hollow $  --*- 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
565     _getInterfaceValue ip     '' "$iface"
566     _getInterfaceValue extip  '' "$iface" "$iface/.."
567     _getInterfaceValue dev    '' "$iface" "$iface/.."
568     _getInterfaceValue prefix '' "$iface" "$iface/.."
569     _getInterfaceValue mask   '' "$iface" "$iface/.."
570     _getInterfaceValue bcast  '' "$iface" "$iface/.."
571     _getInterfaceValue name   '' "$iface"
572     _getInterfaceValue scope  '' "$iface" "$iface/.."
573     _getInterfaceValue mac    '' "$iface"
574
575     test -n "$ip" || { echo $"Can not read ip for '$iface'"  >&2; return 1; }
576     test -n "$dev" -o -e "$iface"/nodev || {
577         echo $"No device specified for '$iface'" >&2
578         return 1;
579     }
580
581     test ! -e "$iface"/down || up=
582
583     while true; do
584         _transformMask2Prefix prefix "$prefix" "$mask"
585         INTERFACES=( "${INTERFACES[@]}" "$ip${prefix:+/$prefix}" )
586
587         test ! -e "$iface"/nodev   || break
588         ## LEGACY ALERT
589         test ! -e "$iface"/only_ip || break
590
591         test -e "$iface/vlandev" \
592              -o \( -e "$iface/../vlandev" -a ! -e "$iface/novlandev" \) \
593              -o \( -e "$__CONFDIR/.defaults/interfaces/vlandev" \
594                    -a ! -e "$iface/novlandev" \
595                    -a ! -e "$iface/../novlandev" \) && {
596             local vlan_info
597             if vlan_info=$(_getVLANInfo "$dev"); then
598                 test -d /proc/net/vlan || {
599                     echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
600                     return 1
601                 }
602                 _addInterfaceCmd VCONFIG $vlan_info
603             fi
604         }
605
606         if ! test -e "$iface"/indirect; then
607             # XXX: IPv6 hack
608             local use_bcast="broadcast ${bcast:-+}"
609             echo "$ip" | $_GREP -q : && use_bcast=
610
611             local tun_info
612             if tun_info=$(_getTunInfo "$iface"); then
613                 _addInterfaceCmd TUNCTL "$dev" $tun_info
614             fi
615
616             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" $use_bcast ${name:+label "$dev:$name"} dev "$dev"
617             #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
618             _addInterfaceCmd IP_LINK  "$dev" $up
619         elif ! test -n "$N_CONTEXT"; then
620             echo $"Using 'dummy' (indirect) for interface '$dev' requires a fixed context number; dynamic ctx are not supported" >&2
621             return 1
622         else
623             test -z "$mac" || _generateMac mac "$(basename $iface)" "$N_CONTEXT" || return 1
624             _addInterfaceCmd MODPROBE dummy "$dev"
625             _addInterfaceCmd IP_LINK  dev dummy0 address "$mac"
626             _addInterfaceCmd NAMEIF   "$dev" "$mac"
627             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" dev "$dev"
628             test -z "$extip" || _addInterfaceCmd IPTABLES "$ip${prefix:+/$prefix}" ${name:+label "$dev:$name"} "$N_CONTEXT" "$extip"
629         fi
630
631         break
632     done
633 }
634
635 ## Usage: _generateInterfaceOptions <vserver-directory>
636 function _generateInterfaceOptions
637 {
638     local iface
639
640     # XXX: This is here instead of in _generateChbindOptions
641     #      to avoid a circular dependency
642     getFileValue N_CONTEXT "$1/ncontext" "$1/context"
643     test -n "$N_CONTEXT" -o -z "$S_CONTEXT" || N_CONTEXT="$S_CONTEXT"
644
645     for iface in "$1/interfaces/"*; do
646         test   -d "$iface"          || continue
647         test ! -e "$iface"/disabled || continue
648     
649         _processSingleInterface "$iface"
650     done
651     _HAVE_INTERFACE_OPTIONS=1
652 }
653
654 function enableInterfaces
655 {
656     local i=0
657     declare -a var
658
659     lock "$__LOCKDIR"/vserver.interfaces
660
661     while test $i -lt $INTERFACE_CMDS_IDX; do
662         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
663         local type=${var[0]}
664         unset var[0]
665
666         set -- "${var[@]}"
667         case "$type" in
668             IPTABLES)   ;; ## TODO
669             MODPROBE)
670                 local mod=$1
671                 local name=$2
672                 shift 2
673                 $_MODPROBE ${name:+-o "$name"} "$mod" "$@"
674                 ;;
675             NAMEIF)             $_NAMEIF   "$@";;
676             VCONFIG)            $_VCONFIG  set_name_type "$4"      >/dev/null
677                                 $_VCONFIG  add           "$2" "$3" >/dev/null;;
678             IP_ADDR)            $_IP addr  add   "$@";;
679             IP_ADDR_FLUSH)      $_IP addr  flush "$@";;
680             IP_LINK)            $_IP link  set   "$@";;
681             IP_ROUTE)           $_IP route add   "$@";;
682             TUNCTL)
683                 local dev="$1"
684                 shift
685                 $_TUNCTL --persist "$@" "$dev"
686                 ;;
687             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
688         esac
689
690         let ++i
691     done
692
693     unlock 1
694 }
695
696 function disableInterfaces
697 {
698     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
699
700     local i=$INTERFACE_CMDS_IDX
701     declare -a var
702
703     lock "$__LOCKDIR"/vserver.interfaces
704     
705     while test $i -gt 0; do
706         let --i || :
707
708         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
709         local type=${var[0]}
710         unset var[0]
711         
712         set -- "${var[@]}"
713         case "$type" in
714             IPTABLES)           ;; ## TODO
715             MODPROBE)           $_RMMOD "${2:-$1}";;
716             NAMEIF)             ;;
717             VCONFIG)            $_VCONFIG  rem "$2.$3" >/dev/null;;
718             IP_ADDR)            $_IP addr  del "$@";;
719             IP_ADDR_FLUSH)      ;;
720             IP_LINK)            ;; ## Ignore the link-down command for now
721             IP_ROUTE)           $_IP route del "$@";;
722             TUNCTL)             $_TUNCTL --~persist "$1";;
723             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
724         esac
725     done
726
727     unlock 1
728 }
729
730 function _generateTagOptions
731 {
732     local vdir="$1"
733     local tag
734
735     getFileValue tag "$vdir/tag" "$vdir/context"
736     test -n "$tag" || return 0
737
738     OPTS_VTAG_CREATE=( --tag "$tag" )
739     OPTS_VTAG_ENTER=( --tag "$tag" )
740 }
741
742 function _generateMemctrlOptions
743 {
744     local vdir="$1"
745     local badness
746
747     getFileValue badness "$vdir/badness"
748     test -n "$badness" || return 0
749
750     OPTS_VMEMCTRL=( --badness "$badness" )
751 }
752
753 function _generateSpaceOptions
754 {
755     local vdir="$1"
756     local d="$vdir"/spaces
757
758     test ! -e "$d"/pid || \
759       OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --pid )
760
761     test ! -e "$d"/net || {
762         OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --net )
763         # network context and namespace don't make much sense
764         _HAVE_CHBIND_OPTIONS=1
765         CHBIND_CMD=()
766     }
767
768     local mask
769     getFileValue mask "$d"/mask || \
770       OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --mask "$mask" )
771 }
772
773 ## Usage: prepareInit <vserver-directory>
774 function prepareInit
775 {
776     pushd "$1/vdir" >/dev/null
777     case "$INITSTYLE" in
778         sysv)
779             { find var/run  ! -type d -print0; \
780               find var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
781             ;;
782         plain)
783             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
784             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
785             ;;
786         minit)
787             ;;
788     esac
789     "${INITCMD_PREPARE[@]}"
790     popd >/dev/null
791 }
792
793 ## Usage: prepareInit <vserver-directory>
794 function prepareStop
795 {
796     pushd "$1/vdir" >/dev/null
797     case "$INITSTYLE" in
798         (sysv)
799             export PREVLEVEL=$RUNLEVEL_START RUNLEVEL=$RUNLEVEL_STOP # required by Debian's initscripts
800             ;;
801     esac
802     "${STOPCMD_PREPARE[@]}"
803     popd >/dev/null
804 }
805
806
807 function generateOptions
808 {
809     _generateInterfaceOptions   "$1"
810     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
811     _generateNiceCommand        "$1"
812     _generateInitOptions        "$1"
813     _generateChcontextOptions   "$1"
814     _generateScheduleOptions    "$1"
815     _generatePersonalityOptions "$1"
816     _generateTagOptions         "$1"
817     _generateMemctrlOptions     "$1"
818     _generateSpaceOptions       "$1"
819
820     if test -n "$_IS_FAKEINIT"; then
821         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
822         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
823     fi
824 }
825
826 function addtoCPUSET
827 {
828     local vdir=$1
829     local cpuset
830     local f="$vdir"/cpuset
831     local i
832     local configured=0
833
834     test -d "$f" || return 0
835     test -e "$f"/name || return 0
836
837     read cpuset < "$f"/name
838     test -e "$f"/nocreate || {
839        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
840        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
841            if test -e "$f"/"$i"; then
842                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" || {
843                    configured=1
844                    break
845                }
846            fi
847        done
848     }
849
850     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
851     if [ "$configured" -ne 0 ]; then
852        warning $"\
853 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
854        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
855        return 0
856     fi
857 }
858
859 function removeCPUSET
860 {
861     local vdir=$1
862     local cpuset
863     local f="$vdir"/cpuset
864
865     test -d "$f" || return 0
866     test -e "$f"/name || return 0
867
868     read cpuset < "$f"/name
869     test -e "$f"/nocreate || {
870        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
871     }
872 }
873
874 function _mountVserverInternal
875 {
876     local fstab="$1"
877     local xflag=
878     
879     test -e "$fstab" || return 0
880     shift
881
882     pushd "$vdir" >/dev/null
883     # check whether / is mounted readonly or whether there is special
884     # magic regarding the mtab file; when etc/mtab can not be touched,
885     # add the '-n' flag to mount
886     test -w etc -o -w etc/mtab || xflag=-n
887     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
888     popd >/dev/null
889 }
890
891 function mountRootFS
892 {
893     local cfgdir=$1
894     local vdir=$1/vdir
895     local fstab="$cfgdir"/fstab
896     local xflag=
897
898     test -e "$fstab" || return 0
899     pushd "$vdir" >/dev/null
900     # check whether / is mounted readonly or whether there is special
901     # magic regarding the mtab file; when etc/mtab can not be touched,
902     # add the '-n' flag to mount
903     test -w etc -o -w etc/mtab || xflag=-n
904     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
905     popd >/dev/null
906 }
907
908 function mountVserver
909 {
910     local cfgdir=$1
911     local ns_opt=$2
912     local vdir=$1/vdir
913     local mtab_src
914
915     test -e "$cfgdir"/fstab -o \
916          -e "$cfgdir"/fstab.local -o \
917          -e "$cfgdir"/fstab.remote || return 0
918
919     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
920     
921     pushd "$vdir" >/dev/null
922     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
923     popd >/dev/null
924
925     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
926
927     _mountVserverInternal "$cfgdir"/fstab
928     _mountVserverInternal "$cfgdir"/fstab.local
929     _mountVserverInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}"
930
931     isNamespaceCleanup "$cfgdir" && \
932         _namespaceCleanup "$cfgdir"
933
934     isAvoidNamespace "$cfgdir" || \
935         $_SECURE_MOUNT --rbind -n "$vdir" "/"
936 }
937
938 function _umountVserverInternal
939 {
940     local fstab="$1"
941     test -e "$fstab" || return 0
942     shift
943
944     $_TAC "$fstab" | {
945         is_ok=1
946         while read src dst tmp; do
947             test -n "$tmp" || continue
948             case x"$src" in
949                 (x\#*)  continue;;
950             esac
951
952         
953             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
954         done
955         test -n "$is_ok"
956     }
957 }
958
959 function umountVserver
960 {
961     local cfgdir=$1
962     local vdir=$1/vdir
963     local is_ok=1
964
965     isAvoidNamespace "$cfgdir"    || return 0
966     test -e "$cfgdir"/fstab -o \
967          -e "$cfgdir"/fstab.local -o \
968          -e "$cfgdir"/fstab.remote || return 0
969     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
970     
971     pushd "$vdir/" >/dev/null || return 1
972         _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
973         _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
974         _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
975     popd >/dev/null           || return 1
976
977     test -n "$is_ok"
978 }
979
980 function fsckAllFS
981 {
982     local cfgdir=$1
983     local fstab="$cfgdir"/fstab
984     local FSTAB_FILE
985     local fsck_exitcode
986
987     test -e "$fstab" || return 0
988
989     export FSTAB_FILE="$fstab"
990     $_FSCK -s -n -A -T
991     fsck_exitcode=$?
992     test "$fsck_exitcode" -eq 0 -o \
993          "$fsck_exitcode" -eq 1 || return $fsck_exitcode
994 }
995
996 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
997 function initSync
998 {
999     local _is_meth=sync
1000     test -n "$_NEED_VSHELPER_SYNC" && \
1001         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
1002
1003     vshelper.initSync "$1" "$3" "$_is_meth"
1004 }
1005
1006 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
1007 function initWait
1008 {
1009     if $_VSERVER_INFO - FEATURE vwait; then
1010         local _is_tmpdir
1011         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
1012
1013         (
1014             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
1015                 --status-fd 3 "$2" \
1016                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
1017             rc=$?
1018
1019             if test "$rc" -ne 0 -a "$rc" -ne 1; then
1020                 $_VPS axf | $_EGREP -e "^[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
1021                 killContext "$S_CONTEXT" 9
1022             fi
1023
1024             exit $rc
1025         ) 4>$_is_tmpdir/procs &
1026             
1027         echo "$!" >$_is_tmpdir/pid
1028         eval "$3"=$_is_tmpdir
1029     fi </dev/null
1030 }
1031
1032
1033 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
1034 function _waitForVWait
1035 {
1036     wait "$3" || :
1037
1038     declare -a status
1039     declare -r procs=$(cat $4)
1040
1041     getFileArray status "$2"
1042     set -- ${status[0]}
1043
1044     case "$1" in
1045         (ERROR)         warning $"\
1046 'vwait' exited with error '$2' which indicates that vserver could not
1047 be stopped properly"
1048                         ;;
1049         (FINISHED)      ;;
1050         (KILLED)        warning $"\
1051 A timeout occured while waiting for the vserver to finish and it was
1052 killed by sending a SIGKILL signal. Please investigate the reasons
1053 and/or increase the timeout in apps/vshelper/sync-timeout."
1054                         ;;
1055
1056         (TIMEOUT)       warning $"\
1057 A timeout occured while waiting for the vserver to finish and it will
1058 be killed by sending a SIGKILL signal. The following process list
1059 might be useful for finding out the reason of this behavior:
1060
1061 ----------------------------------------------------------------------
1062 ${procs:+$procs
1063 }----------------------------------------------------------------------"
1064                         ;;
1065
1066         (\?\?\?|*)      warning $"\
1067 internal error: 'vwait' exited with an unexpected status '$1'; I will
1068 try to continue but be prepared for unexpected events."
1069                     ;;
1070     esac
1071
1072     return 0
1073 }
1074
1075 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
1076 function waitForSync
1077 {
1078     local cfgdir=$1
1079     local fifo=$2
1080     local vwait_statdir=$3
1081     local vwait_pid=$4
1082
1083     if test -d "$vwait_statdir"; then
1084         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
1085     elif test -n "$_NEED_VSHELPER_SYNC"; then
1086         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
1087             warning $"\
1088 A timeout or other error occured while waiting for the synchronization
1089 signal from vserver '$VSERVER_NAME'.
1090 The vserver will be killed nevertheless..."
1091     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
1092         "${INITCMD_STOP_SYNC[@]}" || \
1093             warning $"\
1094 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
1095 will be killed nevertheless..."
1096     fi
1097
1098     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
1099         sleep 1
1100 }
1101
1102 function _sourceWrap
1103 {
1104     local vdir name flavor start i already_handled base
1105     . "$@"
1106 }
1107
1108 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
1109 function execScriptlets
1110 {
1111     declare -r vdir=$1
1112     declare -r name=$2
1113     declare -r flavor=$3
1114     local base i
1115
1116     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
1117         local   DONT_SKIP_DEFAULTS=
1118         local   already_handled=
1119         
1120         for i in "$base/$flavor" "$base/$flavor.d"/*; do
1121             isRegularFile "$i" || continue
1122             test  -r "$i"      || continue
1123
1124             already_handled=1
1125             local start=
1126             test -x "$i" || start=_sourceWrap
1127             $start "$i" "$flavor" "$name"
1128         done
1129
1130         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
1131     done
1132 }
1133
1134
1135 function sanityCheck
1136 {
1137     declare -r cfgdir=$1
1138
1139     ! test -e "$cfgdir"/fstab.local ||
1140         warning $"\
1141 WARNING: 'fstab' will *not* be executed in the network context of the
1142   vserver anymore. Therefore, 'fstab.local' has the same functionality
1143   and is obsoleted. When you need the old behaviour, put the mounts
1144   into 'fstab.remote'"
1145
1146     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1147         warning $"\
1148 WARNING: The hostname is now configured in 'uts/nodename' but not in
1149   'hostname'."
1150
1151     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1152         warning $"\
1153 WARNING: The domainname is now configured in 'uts/domainname' but not
1154   in 'domainname'." >&2
1155
1156   
1157     local i
1158     for i in "$cfgdir"/interfaces/*/only_ip; do
1159         if test -e "$i"; then
1160             local iface
1161             iface=${i##$cfgdir/interfaces/}
1162             iface=${iface%%/only_ip}
1163             warning $"\
1164 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1165   'nodev' instead of"
1166         fi
1167     done
1168
1169     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1170         warning $"\
1171 WARNING: There is no cachedirectory configured for this vserver;
1172   please create '$cfgdir/cache' e.g. by executing
1173
1174   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1175 "
1176
1177     find "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1178
1179     vshelper.doSanityCheck
1180
1181     $_VSERVER_INFO - VERIFYCAP ||
1182         panic $"capabilities are not enabled in kernel-setup"
1183
1184     $_VSERVER_INFO - VERIFYPROC ||
1185         panic $"\
1186 /proc/uptime can not be accessed. Usually, this is caused by
1187 procfs-security. Please read the FAQ for more details
1188 http://linux-vserver.org/Proc-Security"
1189
1190     test -e "$cfgdir"/context || {
1191         TYPE=$( $_VSERVER_INFO 49152 XIDTYPE )
1192         test "$TYPE" != "static" || panic $"\
1193 The kernel does not have dynamic contexts enabled. Please configure
1194 a static one by executing
1195
1196   echo [number between 2 and 49151] > $cfgdir/context"
1197     }
1198 }
1199
1200
1201 function _setSingleDiskLimit
1202 {
1203     local vdir=$1
1204     local dlimit=$2
1205     local space_used=
1206     local space_total=
1207     local inodes_used=
1208     local inodes_total=
1209     local reserved=
1210     local directory=
1211     local ctx=
1212
1213     getFileValue ctx          "$vdir/context"
1214     getFileValue directory    "$dlimit/directory"    || return 0
1215     getFileValue space_total  "$dlimit/space_total"  || return 0
1216     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1217     getFileValue reserved     "$dlimit/reserved"     || return 0
1218
1219     local cachename=$ctx$directory
1220     cachename=dlimits/${cachename//\//_}
1221
1222     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1223     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1224     $_RM -f "$vdir/cache/$cachename"
1225
1226     if test -z "$inodes_used" -o -z "$space_used"; then
1227         local tmpvdu
1228         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1229         inodes_used=${tmpvdu##* }
1230         space_used=${tmpvdu%% *}
1231     fi
1232
1233     $_VDLIMIT --xid $ctx \
1234         --set space_used=$space_used \
1235         --set space_total=$space_total \
1236         --set inodes_used=$inodes_used \
1237         --set inodes_total=$inodes_total \
1238         --set reserved=$reserved \
1239         "$directory"
1240 }
1241
1242
1243 function setDiskLimits
1244 {
1245     local vdir=$1
1246     local dlimit
1247
1248     # Disk Limits without a static context are useless
1249     test -e "$vdir"/context || return 0
1250
1251     for dlimit in "$vdir/dlimits/"*; do
1252         test   -d "$dlimit"          || continue
1253         test ! -e "$dlimit/disabled" || continue
1254
1255         _setSingleDiskLimit "$vdir" "$dlimit"
1256     done
1257 }
1258
1259
1260 function _saveSingleDiskLimit
1261 {
1262     local vdir=$1
1263     local dlimit=$2
1264     local ctx=
1265     local directory=
1266
1267     getFileValue ctx       "$vdir/context"
1268     getFileValue directory "$dlimit/directory" || return 0
1269
1270     local cachename=$ctx$directory
1271     cachename=${cachename//\//_}
1272
1273     # Things are getting ugly here... LFS says that /var/cache (where
1274     # cachename is usually pointing to) can vanish and applications
1275     # have to deal with it. So, we have to interprete the $vdir/cache
1276     # symlink and have to create the needed directories manually.
1277     if   test -d "$vdir/cache"; then
1278         :       # ok, exists already
1279     elif test -L "$vdir/cache"; then
1280         # it's a dangling symlink
1281         local link
1282         link=$($_READLINK "$vdir/cache")
1283         ( cd $vdir && $_MKDIR -p "$link" )
1284     else
1285         return 0
1286     fi
1287
1288     test -d "$vdir/cache"
1289     $_MKDIR -p "$vdir"/cache/dlimits
1290
1291     $_VDLIMIT --xid $ctx "$directory" | \
1292         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1293
1294     $_VDLIMIT --xid $ctx --remove "$directory"
1295 }
1296
1297
1298 function saveDiskLimits
1299 {
1300     local vdir=$1
1301     local dlimit
1302
1303     test -e "$vdir"/context || return 0
1304
1305     for dlimit in "$vdir/dlimits/"*; do
1306         test   -d "$dlimit"          || continue
1307         test ! -e "$dlimit/disabled" || continue
1308
1309         _saveSingleDiskLimit "$vdir" "$dlimit"
1310     done
1311 }
1312
1313 function _namespaceCleanup
1314 {
1315     local vdir="$1"
1316     local root=$($_VSERVER_INFO "$1" VDIR 1)
1317     local -a list
1318     local -a skip
1319     local i
1320     local j
1321
1322     getFileArray skip "$vdir"/namespace-cleanup-skip \
1323         "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
1324
1325     # these are things that have to be accessible post-cleanup
1326     for i in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
1327         "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
1328         local real=`getPhysicalDir "$i"`
1329         test "$i" != "$real" || real=
1330         for j in "$i" "$real"; do
1331             while test -n "$j"; do
1332                 list=( "${list[@]}" "$j" )
1333                 j="${j%/*}"
1334             done
1335         done
1336     done
1337
1338     local -a list_umount
1339     while read -r dev path opts; do
1340         test -n "$path" || continue
1341         for i in "$root" /dev /proc; do
1342             test "${path#$i}" != "$path" && continue 2
1343         done
1344         for i in "${list[@]}" /; do
1345             test "$path" = "$i" && continue 2
1346         done
1347         # unmount them in reverse order so mounts further down the tree get unmounted first
1348         list_umount=( "$path" "${list_umount[@]}" )
1349     done < /proc/mounts
1350     # separate loop to avoid races while reading /proc/mounts
1351     for i in "${list_umount[@]}"; do
1352         $_UMOUNT -l -n "$i"
1353     done
1354 }
1355
1356 function handleDeviceMap
1357 {
1358     local op="$1"
1359     local xid="$2"
1360     local dir="$3"
1361     local flags device target
1362
1363     test -d "$dir" || return 0
1364     test -n "$xid" || return 0
1365
1366     for i in "$dir"/*; do
1367         test -d "$i" || continue
1368
1369         local -a vdevmap_opts=()
1370         test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
1371         test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
1372         test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
1373
1374         getFileValue flags "$i/flags" || :
1375         getFileValue device "$i/device" || :
1376         getFileValue target "$i/target" || :
1377         vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
1378                         ${device:+--device "$device"} ${target:+--target "$target"} )
1379
1380         $_VDEVMAP --xid "$xid" "$op" "${vdevmap_opts[@]}" || return $?
1381     done
1382 }