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