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