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