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