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