Make novlandev the default, but easily overridable for all guests.
[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     getFileValue INITSTYLE      "$cfgdir"/style
251     getFileValue RUNLEVEL_START "$cfgdir"/runlevel
252     getFileValue RUNLEVEL_START "$cfgdir"/runlevel.start
253     getFileValue RUNLEVEL_STOP  "$cfgdir"/runlevel.stop
254     getFileArray INITKILL_SEQ   "$cfgdir"/killseq || :
255
256     findFile _gio_env           "$cfgdir"/environment \
257         "$__CONFDIR"/.defaults/apps/init/environment \
258         "$__PKGLIBDEFAULTDIR"/environment
259     getFileArray OPTS_ENV       "$_gio_env" || :
260
261     case x"$INITSTYLE" in
262         (xrescue)
263             INITCMD_START=( "${INITCMD_RESCUE[@]}" )
264             INITCMD_STOP=( /sbin/killall5 )
265             _IS_FAKEINIT=1
266             _NEED_VSHELPER_SYNC=
267             ;;
268             
269         (xsysv)
270             test -n "$RUNLEVEL_START" || RUNLEVEL_START=3
271             test -n "$RUNLEVEL_STOP"  || RUNLEVEL_STOP=6
272
273             for i in /etc/init.d/rc /etc/rc.d/rc; do
274                 test -x "$vdir/vdir/$i" || continue
275                 INITCMD_START=( "$i" "$RUNLEVEL_START" )
276                 INITCMD_STOP=(  "$i" "$RUNLEVEL_STOP"  )
277             done
278             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
279             ;;
280             
281         (xplain)
282             INITCMD_START=( /sbin/init )
283             INITCMD_STOP=(  /sbin/init )
284             _IS_FAKEINIT=1
285             _NEED_VSHELPER_SYNC=1
286             test -z "$RUNLEVEL_START" || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
287             test -z "$RUNLEVEL_STOP"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
288             ;;
289             
290         (xminit)
291             INITCMD_START=( /sbin/minit-start )
292             INITCMD_STOP=(  /sbin/minit-stop  )
293             _IS_FAKEINIT=1
294             INITCMD_START_SYNC=( "$_INITSYNC_MINIT_START" "$vdir" )
295             _NEED_VSHELPER_SYNC=1
296             test -z "$RUNLEVEL_START"         || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
297             test -z "$RUNLEVEL_STOP"          || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
298             ! isNumber "${RUNLEVEL_START:-3}" || INITCMD_PREPARE=( $_FAKE_RUNLEVEL "${RUNLEVEL_START:-3}" /var/run/utmp )
299             ;;
300
301         (xgentoo)
302             panic "init-style '$INITSTYLE' is no longer supported; please use plain instead; aborting";;
303
304         (x) ;;
305         (*) panic "Unknown init-style '$INITSTYLE'; aborting";;
306     esac
307
308     if test x"$INITSTYLE" != xrescue; then
309         getFileArray INITCMD_START      "$cfgdir"/cmd.start      || :
310         getFileArray INITCMD_STOP       "$cfgdir"/cmd.stop       || :
311         getFileArray INITCMD_START_SYNC "$cfgdir"/cmd.start-sync || :
312         getFileArray INITCMD_STOP_SYNC  "$cfgdir"/cmd.stop-sync  || :
313         getFileArray INITCMD_PREPARE    "$cfgdir"/cmd.prepare    || :
314     fi
315     
316     test -n "$OPTION_FORCE_SYNC" -o -e "$cfgdir"/sync || {
317         INITCMD_START_SYNC=()
318         INITCMD_STOP_SYNC=()
319         _NEED_VSHELPER_SYNC=
320     }
321
322     if vshelper.isEnabled; then
323         vshelper.getSyncTimeout "$vdir" VSHELPER_SYNC_TIMEOUT || :
324     else
325         _NEED_VSHELPER_SYNC=
326     fi
327 }
328
329 function _generateFlagOptions
330 {
331     local vdir=$1
332
333     CHCONTEXT_FLAG_OPTS=()
334
335     test ! -e "$vdir"/flags || \
336     while read flag; do
337         case x"$flag" in
338             (x|x\#*)            ;;
339             (xnamespace)        ;;
340             (xfakeinit)
341                 _IS_FAKEINIT=1
342                 ;;
343             (*)
344                 OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --flag "$flag" )
345                 CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}"
346                                       --flag "$flag" )
347                 ;;
348         esac
349     done <"$vdir"/flags
350
351     isAvoidNamespace "$vdir" || {
352         USE_VNAMESPACE=1
353         CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}" --flag namespace )
354     }
355 }
356
357 function _generateChcontextOptions
358 {
359     local vdir=$1
360     local ctx hostname domainname
361     local cap_opts
362     local flag
363
364     {
365         read ctx        <"$vdir"/context        || :
366         ## LEGACY ALERT
367         read hostname   <"$vdir"/uts/nodename   || read hostname   <"$vdir"/hostname   || :
368         read domainname <"$vdir"/uts/domainname || read domainname <"$vdir"/domainname || :
369     } 2>/dev/null
370
371     test -z "$S_CONTEXT" || ctx=$S_CONTEXT
372
373     _generateCapabilityOptions "$vdir"
374     _generateFlagOptions       "$vdir"
375
376     CHCONTEXT_OPTS=( $SILENT_OPT \
377                      "${CHCONTEXT_FLAG_OPTS[@]}" \
378                      "${CAP_OPTS[@]}" \
379                      --secure
380                      ${ctx:+--ctx "$ctx"} \
381                      ${hostname:+--hostname "$hostname"} \
382                      ${domainname:+--domainname "$domainname"} )
383
384     OPTS_VCONTEXT_CREATE=( $SILENT_OPT \
385                            ${ctx:+--xid "$ctx"} )
386     ## put '--secure' at front so that it can be overridden
387     OPTS_VATTRIBUTE=( --secure --flag default "${OPTS_VATTRIBUTE[@]}" )
388 }
389
390 function _generateScheduleOptions
391 {
392     local vdir=$1
393     local f="$vdir"/schedule
394     test -e "$f" || return 0
395
396     local fill_rate interval tokens tokens_min tokens_max prio_bias
397     {
398         {
399             read fill_rate   && \
400             read interval    && \
401             read tokens      && \
402             read tokens_min  && \
403             read tokens_max  && \
404             read prio_bias   || prio_bias=
405         } <"$f"
406     } 2>/dev/null
407
408     test -n "$prio_bias" || {
409         echo $"Bad content in '$f'; aborting..." >&2
410         false
411     }
412
413     OPTS_VSCHED=( --fill-rate  "$fill_rate"  --interval "$interval" \
414                   --tokens     "$tokens"     --tokens_min "$tokens_min" \
415                   --tokens_max "$tokens_max" --priority-bias "$prio_bias" )
416 }
417
418 function _getInterfaceValue
419 {
420     local _giv_val=$1
421     local _giv_dflt=$2
422     shift 2
423     
424     local _giv_i
425     local _giv_tmp
426
427     for _giv_i; do
428         read _giv_tmp  <"$_giv_i/$_giv_val" && break || :
429     done 2>/dev/null
430
431     : ${_giv_tmp:=$_giv_dflt}
432     eval $_giv_val=\$_giv_tmp
433 }
434
435 ## Usage: _transformMask2Prefix <result-varname> <prefix> <mask>
436 function _transformMask2Prefix
437 {
438     local _tm2p_tmp=$2
439     
440     test -n "$_tm2p_tmp" || {
441         $_MASK2PREFIX "$3" || _tm2p_tmp=$?
442     }
443
444     eval $1=\$_tm2p_tmp
445     return 0
446 }
447
448 function _addInterfaceCmd
449 {
450     eval INTERFACE_CMDS_${INTERFACE_CMDS_IDX}='( "$@" )'
451     let ++INTERFACE_CMDS_IDX
452 }
453
454 ## Usage: _generateMac <var> <iface> <ctx>
455 function _generateMac
456 {
457     isNumber "$2" || {
458         echo $"Interface basename '$iface' must be either a number, or the mac must be configured explicitly" >&2
459         return 1
460     }
461
462     eval $1=$(printf "f0:ff:%02x:%02x:%02x:%02x" $[ (~($2>>8)) & 0xff ] $[ ($2 & 0xff) ] $[ ($3>>8) & 0xff ] $[ $3 & 0xff ])
463 }
464
465 function _getVLANInfo
466 {
467     case "$1" in
468         (vlan????)
469             panic "\
470 creation of VLAN_PLUS_VID devices is not supported; please create them
471 before starting the vserver and use the 'nodev' flag then"
472             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID"
473             ;;
474         (vlan*)
475             panic "\
476 creation of VLAN_PLUS_VID_NO_PAD devices is not supported; please
477 create them before starting the vserver and use the 'nodev' flag then"
478             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID_N0_PAD"
479             ;;
480         (*.????)        echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID";;
481         (*.*)           echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID_NO_PAD";;
482         (*)             return 1
483     esac
484
485     return 0
486 }
487
488 ## Usage: _processSingleInterface <interface-directory>
489 function _processSingleInterface
490 {
491     local iface=$1
492
493     local ip
494     local dev
495     local prefix
496     local mask
497     local bcast
498     local name
499     local scope
500     local mac
501     local extip
502     local up="up"
503
504     _getInterfaceValue ip     '' "$iface"
505     _getInterfaceValue extip  '' "$iface" "$iface/.."
506     _getInterfaceValue dev    '' "$iface" "$iface/.."
507     _getInterfaceValue prefix '' "$iface" "$iface/.."
508     _getInterfaceValue mask   '' "$iface" "$iface/.."
509     _getInterfaceValue bcast  '' "$iface" "$iface/.."
510     _getInterfaceValue name   '' "$iface"
511     _getInterfaceValue scope  '' "$iface" "$iface/.."
512     _getInterfaceValue mac    '' "$iface"
513
514     test -n "$ip" || { echo $"Can not read ip for '$iface'"  >&2; return 1; }
515     test -n "$dev" -o -e "$iface"/nodev || {
516         echo $"No device specified for '$iface'" >&2
517         return 1;
518     }
519
520     test ! -e "$iface"/down || up=
521
522     while true; do
523         _transformMask2Prefix prefix "$prefix" "$mask"
524         INTERFACES=( "${INTERFACES[@]}" "$ip${prefix:+/$prefix}" )
525
526         test ! -e "$iface"/nodev   || break
527         ## LEGACY ALERT
528         test ! -e "$iface"/only_ip || break
529
530         local vlan_info
531         if vlan_info=$(_getVLANInfo "$dev"); then
532             test -d /proc/net/vlan || {
533                 echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
534                 return 1
535             }
536             test -e "$iface/vlandev" \
537                  -o \( -e "$iface/../vlandev" -a ! -e "$iface/novlandev" \) \
538                  -o \( -e "$__CONFDIR/.defaults/interfaces/vlandev" \
539                        -a ! -e "$iface/novlandev" \
540                        -a ! -e "$iface/../novlandev" \) && {
541                 _addInterfaceCmd VCONFIG $vlan_info
542             }
543         fi
544
545         if ! test -e "$iface"/indirect; then
546             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" broadcast ${bcast:-+} ${name:+label "$dev:$name"} dev "$dev"
547             #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
548             _addInterfaceCmd IP_LINK  "$dev" $up
549         elif ! test -n "$ctx"; then
550             echo $"Using 'dummy' (indirect) for interface '$dev' requires a fixed context number; dynamic ctx are not supported" >&2
551             return 1
552         else
553             test -z "$mac" || _generateMac mac "$(basename $iface)" "$ctx" || return 1
554             _addInterfaceCmd MODPROBE dummy "$dev"
555             _addInterfaceCmd IP_LINK  dev dummy0 address "$mac"
556             _addInterfaceCmd NAMEIF   "$dev" "$mac"
557             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" dev "$dev"
558             test -z "$extip" || _addInterfaceCmd IPTABLES "$ip${prefix:+/$prefix}" ${name:+label "$dev:$name"} "$ctx" "$extip"
559         fi
560
561         break
562     done
563 }
564
565 ## Usage: _generateInterfaceOptions <vserver-directory>
566 function _generateInterfaceOptions
567 {
568     local iface
569     local ctx
570
571     test ! -e "$1"/context || read ctx <"$1"/context
572
573     for iface in "$1/interfaces/"*; do
574         test   -d "$iface"          || continue
575         test ! -e "$iface"/disabled || continue
576     
577         _processSingleInterface "$iface"
578     done
579     _HAVE_INTERFACE_OPTIONS=1
580 }
581
582 function enableInterfaces
583 {
584     local i=0
585     declare -a var
586
587     lock "$__LOCKDIR"/vserver.interfaces
588
589     while test $i -lt $INTERFACE_CMDS_IDX; do
590         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
591         local type=${var[0]}
592         unset var[0]
593
594         set -- "${var[@]}"
595         case "$type" in
596             IPTABLES)   ;; ## TODO
597             MODPROBE)
598                 local mod=$1
599                 local name=$2
600                 shift 2
601                 $_MODPROBE ${name:+-o "$name"} "$mod" "$@"
602                 ;;
603             NAMEIF)             $_NAMEIF   "$@";;
604             VCONFIG)            $_VCONFIG  set_name_type "$4"      >/dev/null
605                                 $_VCONFIG  add           "$2" "$3" >/dev/null;;
606             IP_ADDR)            $_IP addr  add   "$@";;
607             IP_ADDR_FLUSH)      $_IP addr  flush "$@";;
608             IP_LINK)            $_IP link  set   "$@";;
609             IP_ROUTE)           $_IP route add   "$@";;
610             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
611         esac
612
613         let ++i
614     done
615
616     unlock 1
617 }
618
619 function disableInterfaces
620 {
621     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
622
623     local i=$INTERFACE_CMDS_IDX
624     declare -a var
625
626     lock "$__LOCKDIR"/vserver.interfaces
627     
628     while test $i -gt 0; do
629         let --i || :
630
631         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
632         local type=${var[0]}
633         unset var[0]
634         
635         set -- "${var[@]}"
636         case "$type" in
637             IPTABLES)           ;; ## TODO
638             MODPROBE)           $_RMMOD "${2:-$1}";;
639             NAMEIF)             ;;
640             VCONFIG)            $_VCONFIG  rem "$2.$3" >/dev/null;;
641             IP_ADDR)            $_IP addr  del "$@";;
642             IP_ADDR_FLUSH)      ;;
643             IP_LINK)            ;; ## Ignore the link-down command for now
644             IP_ROUTE)           $_IP route del "$@";;
645             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
646         esac
647     done
648
649     unlock 1
650 }
651
652 ## Usage: prepareInit <vserver-directory>
653 function prepareInit
654 {
655     pushd "$1/vdir" >/dev/null
656     case "$INITSTYLE" in
657         sysv)
658             { find var/run  ! -type d -print0; \
659               find var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
660             ;;
661         plain)
662             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
663             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
664             ;;
665         minit)
666             ;;
667     esac
668     "${INITCMD_PREPARE[@]}"
669     popd >/dev/null
670 }
671
672 ## Usage: prepareInit <vserver-directory>
673 function prepareStop
674 {
675     pushd "$1/vdir" >/dev/null
676     case "$INITSTYLE" in
677         (sysv)
678             export PREVLEVEL=$RUNLEVEL_START # required by Debian's initscripts
679             ;;
680     esac
681     "${STOPCMD_PREPARE[@]}"
682     popd >/dev/null
683 }
684
685
686 function generateOptions
687 {
688     _generateInterfaceOptions   "$1"
689     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
690     _generateNiceCommand        "$1"
691     _generateInitOptions        "$1"
692     _generateChcontextOptions   "$1"
693     _generateScheduleOptions    "$1"
694     _generatePersonalityOptions "$1"
695
696     if test -n "$_IS_FAKEINIT"; then
697         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
698         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
699     fi
700 }
701
702 function addtoCPUSET
703 {
704     local vdir=$1
705     local cpuset
706     local f="$vdir"/cpuset
707     local i
708     local configured=0
709
710     test -d "$f" || return 0
711     test -e "$f"/name || return 0
712
713     read cpuset < "$f"/name
714     test -e "$f"/nocreate || {
715        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
716        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
717            if test -e "$f"/"$i"; then
718                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" 2>/dev/null || {
719                    configured=1
720                    break
721                }
722            fi
723        done
724     }
725
726     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
727     if [ "$configured" -ne 0 ]; then
728        warning $"\
729 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
730        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
731        return 0
732     fi
733 }
734
735 function removeCPUSET
736 {
737     local vdir=$1
738     local cpuset
739     local f="$vdir"/cpuset
740
741     test -d "$f" || return 0
742     test -e "$f"/name || return 0
743
744     read cpuset < "$f"/name
745     test -e "$f"/nocreate || {
746        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
747     }
748 }
749
750 function _mountVserverInternal
751 {
752     local fstab="$1"
753     local xflag=
754     
755     test -e "$fstab" || return 0
756     shift
757
758     pushd "$vdir" >/dev/null
759     # check whether / is mounted readonly or whether there is special
760     # magic regarding the mtab file; when etc/mtab can not be touched,
761     # add the '-n' flag to mount
762     test -w etc -o -w etc/mtab || xflag=-n
763     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
764     popd >/dev/null
765 }
766
767 function mountRootFS
768 {
769     local cfgdir=$1
770     local vdir=$1/vdir
771     local fstab="$cfgdir"/fstab
772     local xflag=
773
774     test -e "$fstab" || return 0
775     pushd "$vdir" >/dev/null
776     # check whether / is mounted readonly or whether there is special
777     # magic regarding the mtab file; when etc/mtab can not be touched,
778     # add the '-n' flag to mount
779     test -w etc -o -w etc/mtab || xflag=-n
780     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
781     popd >/dev/null
782 }
783
784 function mountVserver
785 {
786     local cfgdir=$1
787     local ns_opt=$2
788     local vdir=$1/vdir
789     local mtab_src
790
791     test -e "$cfgdir"/fstab -o \
792          -e "$cfgdir"/fstab.local -o \
793          -e "$cfgdir"/fstab.remote || return 0
794
795     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
796     
797     pushd "$vdir" >/dev/null
798     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
799     popd >/dev/null
800
801     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
802
803     _mountVserverInternal "$cfgdir"/fstab
804     _mountVserverInternal "$cfgdir"/fstab.local
805     _mountVserverInternal "$cfgdir"/fstab.remote $_CHBIND "${CHBIND_OPTS[@]}"
806
807     isNamespaceCleanup "$cfgdir" && \
808         _namespaceCleanup
809
810     isAvoidNamespace "$cfgdir" || \
811         $_SECURE_MOUNT --rbind -n "$vdir" "/"
812 }
813
814 function _umountVserverInternal
815 {
816     local fstab="$1"
817     test -e "$fstab" || return 0
818     shift
819
820     $_TAC "$fstab" | {
821         is_ok=1
822         while read src dst tmp; do
823             test -n "$tmp" || continue
824             case x"$src" in
825                 (x\#*)  continue;;
826             esac
827
828         
829             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
830         done
831         test -n "$is_ok"
832     }
833 }
834
835 function umountVserver
836 {
837     local cfgdir=$1
838     local vdir=$1/vdir
839     local is_ok=1
840
841     isAvoidNamespace "$cfgdir"    || return 0
842     test -e "$cfgdir"/fstab -o \
843          -e "$cfgdir"/fstab.local -o \
844          -e "$cfgdir"/fstab.remote || return 0
845     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
846     
847     pushd "$vdir/" >/dev/null || return 1
848         _umountVserverInternal  "$cfgdir"/fstab.remote $_CHBIND "${CHBIND_OPTS[@]}" || is_ok=
849         _umountVserverInternal  "$cfgdir"/fstab.local                               || is_ok=
850         _umountVserverInternal  "$cfgdir"/fstab                                     || is_ok=
851     popd >/dev/null           || return 1
852
853     test -n "$is_ok"
854 }
855
856 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
857 function initSync
858 {
859     local _is_meth=sync
860     test -n "$_NEED_VSHELPER_SYNC" && \
861         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
862
863     vshelper.initSync "$1" "$3" "$_is_meth"
864 }
865
866 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
867 function initWait
868 {
869     if $_VSERVER_INFO - FEATURE vwait; then
870         local _is_tmpdir
871         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
872
873         (
874             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
875                 --status-fd 3 "$2" \
876                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
877             rc=$?
878
879             if test "$rc" -ne 0 -a "$rc" -ne 1; then
880                 $_VPS axf | $_EGREP -e "^[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
881                 killContext "$S_CONTEXT" 9
882             fi
883
884             exit $rc
885         ) 4>$_is_tmpdir/procs &
886             
887         echo "$!" >$_is_tmpdir/pid
888         eval "$3"=$_is_tmpdir
889     fi </dev/null
890 }
891
892
893 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
894 function _waitForVWait
895 {
896     wait "$3" || :
897
898     declare -a status
899     declare -r procs=$(cat $4)
900
901     getFileArray status "$2"
902     set -- ${status[0]}
903
904     case "$1" in
905         (ERROR)         warning $"\
906 'vwait' exited with error '$2' which indicates that vserver could not
907 be stopped properly"
908                         ;;
909         (FINISHED)      ;;
910         (KILLED)        warning $"\
911 A timeout occured while waiting for the vserver to finish and it was
912 killed by sending a SIGKILL signal. Please investigate the reasons
913 and/or increase the timeout in apps/vshelper/sync-timeout."
914                         ;;
915
916         (TIMEOUT)       warning $"\
917 A timeout occured while waiting for the vserver to finish and it will
918 be killed by sending a SIGKILL signal. The following process list
919 might be useful for finding out the reason of this behavior:
920
921 ----------------------------------------------------------------------
922 ${procs:+$procs
923 }----------------------------------------------------------------------"
924                         ;;
925
926         (\?\?\?|*)      warning $"\
927 internal error: 'vwait' exited with an unexpected status '$1'; I will
928 try to continue but be prepared for unexpected events."
929                     ;;
930     esac
931
932     return 0
933 }
934
935 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
936 function waitForSync
937 {
938     local cfgdir=$1
939     local fifo=$2
940     local vwait_statdir=$3
941     local vwait_pid=$4
942
943     if test -d "$vwait_statdir"; then
944         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
945     elif test -n "$_NEED_VSHELPER_SYNC"; then
946         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
947             warning $"\
948 A timeout or other error occured while waiting for the synchronization
949 signal from vserver '$VSERVER_NAME'.
950 The vserver will be killed nevertheless..."
951     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
952         "${INITCMD_STOP_SYNC[@]}" || \
953             warning $"\
954 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
955 will be killed nevertheless..."
956     fi
957
958     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
959         sleep 1
960 }
961
962 function _sourceWrap
963 {
964     local vdir name flavor start i already_handled base
965     . "$@"
966 }
967
968 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
969 function execScriptlets
970 {
971     declare -r vdir=$1
972     declare -r name=$2
973     declare -r flavor=$3
974     local base i
975
976     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
977         local   DONT_SKIP_DEFAULTS=
978         local   already_handled=
979         
980         for i in "$base/$flavor" "$base/$flavor.d"/*; do
981             isRegularFile "$i" || continue
982             test  -r "$i"      || continue
983
984             already_handled=1
985             local start=
986             test -x "$i" || start=_sourceWrap
987             $start "$i" "$flavor" "$name"
988         done
989
990         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
991     done
992 }
993
994
995 function sanityCheck
996 {
997     declare -r cfgdir=$1
998
999     ! test -e "$cfgdir"/fstab.local ||
1000         warning $"\
1001 WARNING: 'fstab' will *not* be executed in the network context of the
1002   vserver anymore. Therefore, 'fstab.local' has the same functionality
1003   and is obsoleted. When you need the old behaviour, put the mounts
1004   into 'fstab.remote'"
1005
1006     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1007         warning $"\
1008 WARNING: The hostname is now configured in 'uts/nodename' but not in
1009   'hostname'."
1010
1011     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1012         warning $"\
1013 WARNING: The domainname is now configured in 'uts/domainname' but not
1014   in 'domainname'." >&2
1015
1016   
1017     local i
1018     for i in "$cfgdir"/interfaces/*/only_ip; do
1019         if test -e "$i"; then
1020             local iface
1021             iface=${i##$cfgdir/interfaces/}
1022             iface=${iface%%/only_ip}
1023             warning $"\
1024 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1025   'nodev' instead of"
1026         fi
1027     done
1028
1029     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1030         warning $"\
1031 WARNING: There is no cachedirectory configured for this vserver;
1032   please create '$cfgdir/cache' e.g. by executing
1033
1034   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1035 "
1036
1037     find "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1038
1039     vshelper.doSanityCheck
1040
1041     $_VSERVER_INFO - VERIFYCAP ||
1042         panic $"capabilities are not enabled in kernel-setup"
1043
1044     $_VSERVER_INFO - VERIFYPROC ||
1045         panic $"\
1046 /proc/uptime can not be accessed. Usually, this is caused by
1047 procfs-security. Please read the FAQ for more details
1048 http://www.linux-vserver.org/index.php?page=Linux-Vserver+FAQ"
1049 }
1050
1051
1052 function _setSingleDiskLimit
1053 {
1054     local vdir=$1
1055     local dlimit=$2
1056     local space_used=
1057     local space_total=
1058     local inodes_used=
1059     local inodes_total=
1060     local reserved=
1061     local directory=
1062     local ctx=
1063
1064     getFileValue ctx          "$vdir/context"
1065     getFileValue directory    "$dlimit/directory"    || return 0
1066     getFileValue space_total  "$dlimit/space_total"  || return 0
1067     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1068     getFileValue reserved     "$dlimit/reserved"     || return 0
1069
1070     local cachename=$ctx$directory
1071     cachename=dlimits/${cachename//\//_}
1072
1073     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1074     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1075     $_RM -f "$vdir/cache/$cachename"
1076
1077     if test -z "$inodes_used" -o -z "$space_used"; then
1078         local tmpvdu
1079         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1080         inodes_used=${tmpvdu##* }
1081         space_used=${tmpvdu%% *}
1082     fi
1083
1084     $_VDLIMIT --xid $ctx \
1085         --set space_used=$space_used \
1086         --set space_total=$space_total \
1087         --set inodes_used=$inodes_used \
1088         --set inodes_total=$inodes_total \
1089         --set reserved=$reserved \
1090         "$directory"
1091 }
1092
1093
1094 function setDiskLimits
1095 {
1096     local vdir=$1
1097     local dlimit
1098
1099     # Disk Limits without a static context are useless
1100     test -e "$vdir"/context || return 0
1101
1102     for dlimit in "$vdir/dlimits/"*; do
1103         test   -d "$dlimit"          || continue
1104         test ! -e "$dlimit/disabled" || continue
1105
1106         _setSingleDiskLimit "$vdir" "$dlimit"
1107     done
1108 }
1109
1110
1111 function _saveSingleDiskLimit
1112 {
1113     local vdir=$1
1114     local dlimit=$2
1115     local ctx=
1116     local directory=
1117
1118     getFileValue ctx       "$vdir/context"
1119     getFileValue directory "$dlimit/directory" || return 0
1120
1121     local cachename=$ctx$directory
1122     cachename=${cachename//\//_}
1123
1124     # Things are getting ugly here... LFS says that /var/cache (where
1125     # cachename is usually pointing to) can vanish and applications
1126     # have to deal with it. So, we have to interprete the $vdir/cache
1127     # symlink and have to create the needed directories manually.
1128     if   test -d "$vdir/cache"; then
1129         :       # ok, exists already
1130     elif test -L "$vdir/cache"; then
1131         # it's a dangling symlink
1132         local link
1133         link=$($_READLINK "$vdir/cache")
1134         ( cd $vdir && $_MKDIR -p "$link" )
1135     else
1136         return 0
1137     fi
1138
1139     test -d "$vdir/cache"
1140     $_MKDIR -p "$vdir"/cache/dlimits
1141
1142     $_VDLIMIT --xid $ctx "$directory" | \
1143         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1144 }
1145
1146
1147 function saveDiskLimits
1148 {
1149     local vdir=$1
1150     local dlimit
1151
1152     test -e "$vdir"/context || return 0
1153
1154     for dlimit in "$vdir/dlimits/"*; do
1155         test   -d "$dlimit"          || continue
1156         test ! -e "$dlimit/disabled" || continue
1157
1158         _saveSingleDiskLimit "$vdir" "$dlimit"
1159     done
1160 }
1161
1162 function _namespaceCleanup
1163 {
1164     local root=$(readlink -f "$vdir")
1165     local tmp="$root"
1166     local -a list
1167     while [ "$tmp" ]; do
1168         list=( "${list[@]}" "$tmp" )
1169         tmp="${tmp%/*}"
1170     done
1171     local -a list_umount
1172     while read dev path opts; do
1173         [ "$path" ] || continue
1174         for i in "$root" /dev /proc; do
1175             [ "${path#$i}" != "$path" ] && continue 2
1176         done
1177         for i in "${list[@]}" /; do
1178             [ "$path" = "$i" ] && continue 2
1179         done
1180         list_umount=( "${list_umount[@]}" "$path" )
1181     done < /proc/mounts
1182     for i in "${list_umount[@]}"; do
1183         umount -l -n "$i"
1184     done
1185 }
1186