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