Add initial environment support. Lets users work around their broken
[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 -f /proc/net/vlan -o -e "$iface"/novlandev || {
537                 _addInterfaceCmd VCONFIG $vlan_info
538             }
539         fi
540
541         if ! test -e "$iface"/indirect; then
542             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" broadcast ${bcast:-+} ${name:+label "$dev:$name"} dev "$dev"
543             #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
544             _addInterfaceCmd IP_LINK  "$dev" $up
545         elif ! test -n "$ctx"; then
546             echo $"Using 'dummy' (indirect) for interface '$dev' requires a fixed context number; dynamic ctx are not supported" >&2
547             return 1
548         else
549             test -z "$mac" || _generateMac mac "$(basename $iface)" "$ctx" || return 1
550             _addInterfaceCmd MODPROBE dummy "$dev"
551             _addInterfaceCmd IP_LINK  dev dummy0 address "$mac"
552             _addInterfaceCmd NAMEIF   "$dev" "$mac"
553             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" dev "$dev"
554             test -z "$extip" || _addInterfaceCmd IPTABLES "$ip${prefix:+/$prefix}" ${name:+label "$dev:$name"} "$ctx" "$extip"
555         fi
556
557         break
558     done
559 }
560
561 ## Usage: _generateInterfaceOptions <vserver-directory>
562 function _generateInterfaceOptions
563 {
564     local iface
565     local ctx
566
567     test ! -e "$1"/context || read ctx <"$1"/context
568
569     for iface in "$1/interfaces/"*; do
570         test   -d "$iface"          || continue
571         test ! -e "$iface"/disabled || continue
572     
573         _processSingleInterface "$iface"
574     done
575     _HAVE_INTERFACE_OPTIONS=1
576 }
577
578 function enableInterfaces
579 {
580     local i=0
581     declare -a var
582
583     lock "$__LOCKDIR"/vserver.interfaces
584
585     while test $i -lt $INTERFACE_CMDS_IDX; do
586         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
587         local type=${var[0]}
588         unset var[0]
589
590         set -- "${var[@]}"
591         case "$type" in
592             IPTABLES)   ;; ## TODO
593             MODPROBE)
594                 local mod=$1
595                 local name=$2
596                 shift 2
597                 $_MODPROBE ${name:+-o "$name"} "$mod" "$@"
598                 ;;
599             NAMEIF)             $_NAMEIF   "$@";;
600             VCONFIG)            $_VCONFIG  set_name_type "$4"      >/dev/null
601                                 $_VCONFIG  add           "$2" "$3" >/dev/null;;
602             IP_ADDR)            $_IP addr  add   "$@";;
603             IP_ADDR_FLUSH)      $_IP addr  flush "$@";;
604             IP_LINK)            $_IP link  set   "$@";;
605             IP_ROUTE)           $_IP route add   "$@";;
606             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
607         esac
608
609         let ++i
610     done
611
612     unlock 1
613 }
614
615 function disableInterfaces
616 {
617     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
618
619     local i=$INTERFACE_CMDS_IDX
620     declare -a var
621
622     lock "$__LOCKDIR"/vserver.interfaces
623     
624     while test $i -gt 0; do
625         let --i || :
626
627         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
628         local type=${var[0]}
629         unset var[0]
630         
631         set -- "${var[@]}"
632         case "$type" in
633             IPTABLES)           ;; ## TODO
634             MODPROBE)           $_RMMOD "${2:-$1}";;
635             NAMEIF)             ;;
636             VCONFIG)            $_VCONFIG  rem "$2.$3" >/dev/null;;
637             IP_ADDR)            $_IP addr  del "$@";;
638             IP_ADDR_FLUSH)      ;;
639             IP_LINK)            ;; ## Ignore the link-down command for now
640             IP_ROUTE)           $_IP route del "$@";;
641             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
642         esac
643     done
644
645     unlock 1
646 }
647
648 ## Usage: prepareInit <vserver-directory>
649 function prepareInit
650 {
651     pushd "$1/vdir" >/dev/null
652     case "$INITSTYLE" in
653         sysv)
654             { find var/run  ! -type d -print0; \
655               find var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
656             ;;
657         plain)
658             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
659             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
660             ;;
661         minit)
662             ;;
663     esac
664     "${INITCMD_PREPARE[@]}"
665     popd >/dev/null
666 }
667
668 ## Usage: prepareInit <vserver-directory>
669 function prepareStop
670 {
671     pushd "$1/vdir" >/dev/null
672     case "$INITSTYLE" in
673         (sysv)
674             export PREVLEVEL=$RUNLEVEL_START # required by Debian's initscripts
675             ;;
676     esac
677     "${STOPCMD_PREPARE[@]}"
678     popd >/dev/null
679 }
680
681
682 function generateOptions
683 {
684     _generateInterfaceOptions   "$1"
685     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
686     _generateNiceCommand        "$1"
687     _generateInitOptions        "$1"
688     _generateChcontextOptions   "$1"
689     _generateScheduleOptions    "$1"
690     _generatePersonalityOptions "$1"
691
692     if test -n "$_IS_FAKEINIT"; then
693         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
694         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
695     fi
696 }
697
698 function addtoCPUSET
699 {
700     local vdir=$1
701     local cpuset
702     local f="$vdir"/cpuset
703     local i
704     local configured=0
705
706     test -d "$f" || return 0
707     test -e "$f"/name || return 0
708
709     read cpuset < "$f"/name
710     test -e "$f"/nocreate || {
711        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
712        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
713            if test -e "$f"/"$i"; then
714                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" 2>/dev/null || {
715                    configured=1
716                    break
717                }
718            fi
719        done
720     }
721
722     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
723     if [ "$configured" -ne 0 ]; then
724        warning $"\
725 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
726        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
727        return 0
728     fi
729 }
730
731 function removeCPUSET
732 {
733     local vdir=$1
734     local cpuset
735     local f="$vdir"/cpuset
736
737     test -d "$f" || return 0
738     test -e "$f"/name || return 0
739
740     read cpuset < "$f"/name
741     test -e "$f"/nocreate || {
742        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
743     }
744 }
745
746 function _mountVserverInternal
747 {
748     local fstab="$1"
749     local xflag=
750     
751     test -e "$fstab" || return 0
752     shift
753
754     pushd "$vdir" >/dev/null
755     # check whether / is mounted readonly or whether there is special
756     # magic regarding the mtab file; when etc/mtab can not be touched,
757     # add the '-n' flag to mount
758     test -w etc -o -w etc/mtab || xflag=-n
759     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
760     popd >/dev/null
761 }
762
763 function mountRootFS
764 {
765     local cfgdir=$1
766     local vdir=$1/vdir
767     local fstab="$cfgdir"/fstab
768     local xflag=
769
770     test -e "$fstab" || return 0
771     pushd "$vdir" >/dev/null
772     # check whether / is mounted readonly or whether there is special
773     # magic regarding the mtab file; when etc/mtab can not be touched,
774     # add the '-n' flag to mount
775     test -w etc -o -w etc/mtab || xflag=-n
776     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
777     popd >/dev/null
778 }
779
780 function mountVserver
781 {
782     local cfgdir=$1
783     local ns_opt=$2
784     local vdir=$1/vdir
785     local mtab_src
786
787     test -e "$cfgdir"/fstab -o \
788          -e "$cfgdir"/fstab.local -o \
789          -e "$cfgdir"/fstab.remote || return 0
790
791     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
792     
793     pushd "$vdir" >/dev/null
794     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
795     popd >/dev/null
796
797     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
798
799     _mountVserverInternal "$cfgdir"/fstab
800     _mountVserverInternal "$cfgdir"/fstab.local
801     _mountVserverInternal "$cfgdir"/fstab.remote $_CHBIND "${CHBIND_OPTS[@]}"
802
803     isNamespaceCleanup "$cfgdir" && \
804         _namespaceCleanup
805
806     isAvoidNamespace "$cfgdir" || \
807         $_SECURE_MOUNT --rbind -n "$vdir" "/"
808 }
809
810 function _umountVserverInternal
811 {
812     local fstab="$1"
813     test -e "$fstab" || return 0
814     shift
815
816     $_TAC "$fstab" | {
817         is_ok=1
818         while read src dst tmp; do
819             test -n "$tmp" || continue
820             case x"$src" in
821                 (x\#*)  continue;;
822             esac
823
824         
825             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
826         done
827         test -n "$is_ok"
828     }
829 }
830
831 function umountVserver
832 {
833     local cfgdir=$1
834     local vdir=$1/vdir
835     local is_ok=1
836
837     isAvoidNamespace "$cfgdir"    || return 0
838     test -e "$cfgdir"/fstab -o \
839          -e "$cfgdir"/fstab.local -o \
840          -e "$cfgdir"/fstab.remote || return 0
841     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
842     
843     pushd "$vdir/" >/dev/null || return 1
844         _umountVserverInternal  "$cfgdir"/fstab.remote $_CHBIND "${CHBIND_OPTS[@]}" || is_ok=
845         _umountVserverInternal  "$cfgdir"/fstab.local                               || is_ok=
846         _umountVserverInternal  "$cfgdir"/fstab                                     || is_ok=
847     popd >/dev/null           || return 1
848
849     test -n "$is_ok"
850 }
851
852 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
853 function initSync
854 {
855     local _is_meth=sync
856     test -n "$_NEED_VSHELPER_SYNC" && \
857         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
858
859     vshelper.initSync "$1" "$3" "$_is_meth"
860 }
861
862 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
863 function initWait
864 {
865     if $_VSERVER_INFO - FEATURE vwait; then
866         local _is_tmpdir
867         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
868
869         (
870             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
871                 --status-fd 3 "$2" \
872                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
873             rc=$?
874
875             if test "$rc" -ne 0 -a "$rc" -ne 1; then
876                 $_VPS axf | $_EGREP -e "^[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
877                 killContext "$S_CONTEXT" 9
878             fi
879
880             exit $rc
881         ) 4>$_is_tmpdir/procs &
882             
883         echo "$!" >$_is_tmpdir/pid
884         eval "$3"=$_is_tmpdir
885     fi </dev/null
886 }
887
888
889 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
890 function _waitForVWait
891 {
892     wait "$3" || :
893
894     declare -a status
895     declare -r procs=$(cat $4)
896
897     getFileArray status "$2"
898     set -- ${status[0]}
899
900     case "$1" in
901         (ERROR)         warning $"\
902 'vwait' exited with error '$2' which indicates that vserver could not
903 be stopped properly"
904                         ;;
905         (FINISHED)      ;;
906         (KILLED)        warning $"\
907 A timeout occured while waiting for the vserver to finish and it was
908 killed by sending a SIGKILL signal. Please investigate the reasons
909 and/or increase the timeout in apps/vshelper/sync-timeout."
910                         ;;
911
912         (TIMEOUT)       warning $"\
913 A timeout occured while waiting for the vserver to finish and it will
914 be killed by sending a SIGKILL signal. The following process list
915 might be useful for finding out the reason of this behavior:
916
917 ----------------------------------------------------------------------
918 ${procs:+$procs
919 }----------------------------------------------------------------------"
920                         ;;
921
922         (\?\?\?|*)      warning $"\
923 internal error: 'vwait' exited with an unexpected status '$1'; I will
924 try to continue but be prepared for unexpected events."
925                     ;;
926     esac
927
928     return 0
929 }
930
931 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
932 function waitForSync
933 {
934     local cfgdir=$1
935     local fifo=$2
936     local vwait_statdir=$3
937     local vwait_pid=$4
938
939     if test -d "$vwait_statdir"; then
940         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
941     elif test -n "$_NEED_VSHELPER_SYNC"; then
942         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
943             warning $"\
944 A timeout or other error occured while waiting for the synchronization
945 signal from vserver '$VSERVER_NAME'.
946 The vserver will be killed nevertheless..."
947     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
948         "${INITCMD_STOP_SYNC[@]}" || \
949             warning $"\
950 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
951 will be killed nevertheless..."
952     fi
953
954     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
955         sleep 1
956 }
957
958 function _sourceWrap
959 {
960     local vdir name flavor start i already_handled base
961     . "$@"
962 }
963
964 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
965 function execScriptlets
966 {
967     declare -r vdir=$1
968     declare -r name=$2
969     declare -r flavor=$3
970     local base i
971
972     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
973         local   DONT_SKIP_DEFAULTS=
974         local   already_handled=
975         
976         for i in "$base/$flavor" "$base/$flavor.d"/*; do
977             isRegularFile "$i" || continue
978             test  -r "$i"      || continue
979
980             already_handled=1
981             local start=
982             test -x "$i" || start=_sourceWrap
983             $start "$i" "$flavor" "$name"
984         done
985
986         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
987     done
988 }
989
990
991 function sanityCheck
992 {
993     declare -r cfgdir=$1
994
995     ! test -e "$cfgdir"/fstab.local ||
996         warning $"\
997 WARNING: 'fstab' will *not* be executed in the network context of the
998   vserver anymore. Therefore, 'fstab.local' has the same functionality
999   and is obsoleted. When you need the old behaviour, put the mounts
1000   into 'fstab.remote'"
1001
1002     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1003         warning $"\
1004 WARNING: The hostname is now configured in 'uts/nodename' but not in
1005   'hostname'."
1006
1007     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1008         warning $"\
1009 WARNING: The domainname is now configured in 'uts/domainname' but not
1010   in 'domainname'." >&2
1011
1012   
1013     local i
1014     for i in "$cfgdir"/interfaces/*/only_ip; do
1015         if test -e "$i"; then
1016             local iface
1017             iface=${i##$cfgdir/interfaces/}
1018             iface=${iface%%/only_ip}
1019             warning $"\
1020 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1021   'nodev' instead of"
1022         fi
1023     done
1024
1025     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1026         warning $"\
1027 WARNING: There is no cachedirectory configured for this vserver;
1028   please create '$cfgdir/cache' e.g. by executing
1029
1030   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1031 "
1032
1033     find "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1034
1035     vshelper.doSanityCheck
1036
1037     $_VSERVER_INFO - VERIFYCAP ||
1038         panic $"capabilities are not enabled in kernel-setup"
1039
1040     $_VSERVER_INFO - VERIFYPROC ||
1041         panic $"\
1042 /proc/uptime can not be accessed. Usually, this is caused by
1043 procfs-security. Please read the FAQ for more details
1044 http://www.linux-vserver.org/index.php?page=Linux-Vserver+FAQ"
1045 }
1046
1047
1048 function _setSingleDiskLimit
1049 {
1050     local vdir=$1
1051     local dlimit=$2
1052     local space_used=
1053     local space_total=
1054     local inodes_used=
1055     local inodes_total=
1056     local reserved=
1057     local directory=
1058     local ctx=
1059
1060     getFileValue ctx          "$vdir/context"
1061     getFileValue directory    "$dlimit/directory"    || return 0
1062     getFileValue space_total  "$dlimit/space_total"  || return 0
1063     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1064     getFileValue reserved     "$dlimit/reserved"     || return 0
1065
1066     local cachename=$ctx$directory
1067     cachename=dlimits/${cachename//\//_}
1068
1069     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1070     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1071     $_RM -f "$vdir/cache/$cachename"
1072
1073     if test -z "$inodes_used" -o -z "$space_used"; then
1074         local tmpvdu
1075         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1076         inodes_used=${tmpvdu##* }
1077         space_used=${tmpvdu%% *}
1078     fi
1079
1080     $_VDLIMIT --xid $ctx \
1081         --set space_used=$space_used \
1082         --set space_total=$space_total \
1083         --set inodes_used=$inodes_used \
1084         --set inodes_total=$inodes_total \
1085         --set reserved=$reserved \
1086         "$directory"
1087 }
1088
1089
1090 function setDiskLimits
1091 {
1092     local vdir=$1
1093     local dlimit
1094
1095     # Disk Limits without a static context are useless
1096     test -e "$vdir"/context || return 0
1097
1098     for dlimit in "$vdir/dlimits/"*; do
1099         test   -d "$dlimit"          || continue
1100         test ! -e "$dlimit/disabled" || continue
1101
1102         _setSingleDiskLimit "$vdir" "$dlimit"
1103     done
1104 }
1105
1106
1107 function _saveSingleDiskLimit
1108 {
1109     local vdir=$1
1110     local dlimit=$2
1111     local ctx=
1112     local directory=
1113
1114     getFileValue ctx       "$vdir/context"
1115     getFileValue directory "$dlimit/directory" || return 0
1116
1117     local cachename=$ctx$directory
1118     cachename=${cachename//\//_}
1119
1120     # Things are getting ugly here... LFS says that /var/cache (where
1121     # cachename is usually pointing to) can vanish and applications
1122     # have to deal with it. So, we have to interprete the $vdir/cache
1123     # symlink and have to create the needed directories manually.
1124     if   test -d "$vdir/cache"; then
1125         :       # ok, exists already
1126     elif test -L "$vdir/cache"; then
1127         # it's a dangling symlink
1128         local link
1129         link=$($_READLINK "$vdir/cache")
1130         ( cd $vdir && $_MKDIR -p "$link" )
1131     else
1132         return 0
1133     fi
1134
1135     test -d "$vdir/cache"
1136     $_MKDIR -p "$vdir"/cache/dlimits
1137
1138     $_VDLIMIT --xid $ctx "$directory" | \
1139         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1140 }
1141
1142
1143 function saveDiskLimits
1144 {
1145     local vdir=$1
1146     local dlimit
1147
1148     test -e "$vdir"/context || return 0
1149
1150     for dlimit in "$vdir/dlimits/"*; do
1151         test   -d "$dlimit"          || continue
1152         test ! -e "$dlimit/disabled" || continue
1153
1154         _saveSingleDiskLimit "$vdir" "$dlimit"
1155     done
1156 }
1157
1158 function _namespaceCleanup
1159 {
1160     local root=$(readlink -f "$vdir")
1161     local tmp="$root"
1162     local -a list
1163     while [ "$tmp" ]; do
1164         list=( "${list[@]}" "$tmp" )
1165         tmp="${tmp%/*}"
1166     done
1167     local -a list_umount
1168     while read dev path opts; do
1169         [ "$path" ] || continue
1170         for i in "$root" /dev /proc; do
1171             [ "${path#$i}" != "$path" ] && continue 2
1172         done
1173         for i in "${list[@]}" /; do
1174             [ "$path" = "$i" ] && continue 2
1175         done
1176         list_umount=( "${list_umount[@]}" "$path" )
1177     done < /proc/mounts
1178     for i in "${list_umount[@]}"; do
1179         umount -l -n "$i"
1180     done
1181 }
1182