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