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