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