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