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