Add --strace and --debug-sysv options to further debug problems with starting a guest.
[util-vserver.git] / scripts / vserver.functions
1 # $Id$  --*- sh -*--
2
3 # Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 #  
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License.
8 #  
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #  
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 ## Expected env:
19 #  $VSERVER_DIR   ... path to vserver-cfg dir
20 #  $VSERVER_NAME  ... name of vserver
21
22 declare -a NICE_CMD=()
23 declare -a CHBIND_OPTS=()
24 declare -a CAP_OPTS=()
25 declare -a CHCONTEXT_INIT_OPTS=()
26 declare -a CHCONTEXT_FLAG_OPTS=()
27 declare -a CHCONTEXT_OPTS=()
28 declare -a CAPCHROOT_OPTS=()
29 declare -a INTERFACES=()
30
31 declare -a INITCMD_RESCUE=( /bin/sleep 900 )
32 declare -a INITCMD_START=()
33 declare -a INITCMD_START_SYNC=()
34 declare -a INITCMD_STOP=()
35 declare -a INITCMD_STOP_SYNC=()
36 declare -a INITCMD_PREPARE=()
37 declare -a INITKILL_SEQ=()
38 declare -a ENTER_SHELL=()
39
40 declare -a OPTS_VCONTEXT_CREATE=()
41 declare -a OPTS_VCONTEXT_MIGRATE=()
42 declare -a OPTS_VCONTEXT_ENTER=()
43 declare -a OPTS_VATTRIBUTE=( --flag fakeinit )
44 declare -a OPTS_VSCHED=()
45 declare -a OPTS_ENV=()
46
47 declare -a STOPCMD_PREPARE=()
48
49 declare -a VSERVER_EXTRA_CMDS=()
50
51 INIT_RESCUE=
52 VSHELPER_SYNC_TIMEOUT=30
53 USE_VNAMESPACE=
54 INTERFACE_CMDS_IDX=0
55 RUNLEVEL_START=
56 RUNLEVEL_STOP=
57 _HAVE_INTERFACE_OPTIONS=
58 _HAVE_CHBIND_OPTIONS=
59 _NEED_VSHELPER_SYNC=
60 _IS_FAKEINIT=
61
62 INITSTYLE=sysv
63
64 S_CONTEXT=
65
66 SILENT_OPT=
67
68 : ${VSERVER_NAME:=$(basename "$VSERVER_DIR")}
69
70 if test -e "$VSERVER_DIR"/noisy -o -n "$OPTION_VERBOSE"; then
71     SILENT_OPT=
72 else
73     SILENT_OPT='--silent'
74 fi
75
76 function _readFileToArray
77 {
78     local _rfta_f="$1"
79     local _rfta_a="$2"
80     local _rfta_p="$3"
81     local _rfta_v
82
83     test -e "$_rfta_f" || return 0
84     while read _rfta_v; do
85         case x"$_rfta_v" in
86             (x|x\#*)    ;;
87             (*)         eval "$_rfta_a=( \"\${$_rfta_a[@]}\" $_rfta_p \"$_rfta_v\" )";;
88         esac
89     done <"$_rfta_f"
90 }
91
92 function _generateChbindOptions
93 {
94     local vdir="$1"
95     local i
96     local bcast=
97     local nid=
98
99     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$vdir"
100
101     local f=$vdir/interfaces/bcast
102     getFileValue bcast "$f"
103
104     getFileValue nid "$vdir/ncontext" "$vdir/context"
105     
106     CHBIND_OPTS=( $SILENT_OPT --secure ${nid:+--nid "$nid"} ${bcast:+--bcast "$bcast"} )
107
108     for i in "${INTERFACES[@]}"; do
109         CHBIND_OPTS=( "${CHBIND_OPTS[@]}" --ip "$i" )
110     done
111
112     _readFileToArray "$vdir"/nflags        CHBIND_OPTS --flag
113     _readFileToArray "$vdir"/ncapabilities CHBIND_OPTS --ncap
114
115     _HAVE_CHBIND_OPTIONS=1
116 }
117
118 function _generateNiceCommand
119 {
120     local vdir=$1
121     local nice=0
122     local current_nice=`$_NICE`
123
124     test -r "$vdir/nice" && read nice <"$vdir"/nice
125
126     let nice=$nice-$current_nice || :
127     NICE_CMD=( $_NICE -n $nice )
128 }
129
130
131 function _generatePersonalityOptions
132 {
133     local vdir="$1"
134     local f="$vdir"/personality
135     local type flags
136
137     test -s "$f" || return 0
138
139     {
140         local delim tmp
141
142         read type
143         while read tmp; do
144             case x$tmp in
145                 (x\#*|x)        ;;
146                 (*)             flags=$flags$delim$tmp
147                                 delim=,
148                                 ;;
149             esac
150         done
151     } <"$f"
152
153     OPTS_VCONTEXT_ENTER=( "${OPTS_VCONTEXT_ENTER[@]}"
154                           --personality-type "$type"
155                           ${flags:+--personality-flags "$flags"} )
156 }
157
158 function _generateCCapabilityOptions
159 {
160     local vdir=$1
161
162     _readFileToArray "$vdir"/ccapabilities OPTS_VATTRIBUTE --ccap
163 }
164
165 function _generateBCapabilityOptions
166 {
167     local vdir=$1
168
169     _readFileToArray "$vdir"/bcapabilities OPTS_VATTRIBUTE --bcap
170 }
171
172 function _generateCapabilityOptions
173 {
174     local vdir=$1
175     local cap
176
177     _generateBCapabilityOptions "$vdir"
178     _generateCCapabilityOptions "$vdir"
179     
180     test -e "$vdir"/capabilities || return 0
181
182     CAP_OPTS=()
183     CAPCHROOT_OPTS=()
184
185     while read cap; do
186         case x"$cap" in
187             (x|x\#*)    ;;
188             (!CAP_SYSCHROOT)
189                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
190                 CAPCHROOT_OPTS=( "${CAPCHROOT_OPTS[@]}" --nochroot )
191                 ;;
192             (*)
193                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
194                 ;;
195         esac
196     done <"$vdir"/capabilities
197 }
198
199 function getEnterShell
200 {
201     local vdir=$1
202     local shell_file
203
204     ENTER_SHELL=()
205
206     getFileValue ENTER_SHELL "$vdir"/shell "$__CONFDIR"/.defaults/shell
207     
208     test -n "$ENTER_SHELL" || {
209         local i
210         for i in "/bin/bash -login" "/bin/sh -l" /bin/csh; do
211             set -- $i
212             test -x "$vdir/vdir/$1" || continue
213             ENTER_SHELL=( "$@" )
214             break
215         done
216     }
217 }
218
219 ## Usage: sendKillSequence <ctx> <signal> [<wait> <signal>]*
220 function sendKillSequence
221 {
222     local ctx=$1
223     local wait=
224     shift
225
226     while isCtxRunning "$ctx"; do
227         test -z "$wait" || sleep "$wait"
228
229         killContext "$ctx" "$1"
230         test -n "$2" || break
231         wait="$2"
232         shift 2
233     done
234 }
235
236 function _generateInitOptions
237 {
238     local vdir=$1
239     local cfgdir=$vdir/apps/init
240     local i f
241
242     INITCMD_START=()
243     INITCMD_STOP=()
244     INITCMD_START_SYNC=()
245     INITCMD_STOP_SYNC=()
246     INITCMD_PREPARE=()
247     STOPCMD_PREPARE=()
248
249     INITKILL_SEQ=( 15 5 9 )
250     CHCONTEXT_INIT_OPTS=()
251
252
253     test x"$INITSTYLE" = xrescue || \
254       getFileValue INITSTYLE    "$cfgdir"/style
255     getFileValue RUNLEVEL_START "$cfgdir"/runlevel
256     getFileValue RUNLEVEL_START "$cfgdir"/runlevel.start
257     getFileValue RUNLEVEL_STOP  "$cfgdir"/runlevel.stop
258     getFileArray INITKILL_SEQ   "$cfgdir"/killseq || :
259
260     findFile _gio_env           "$cfgdir"/environment \
261         "$__CONFDIR"/.defaults/apps/init/environment \
262         "$__PKGLIBDEFAULTDIR"/environment
263     getFileArray OPTS_ENV       "$_gio_env" || :
264
265     case x"$INITSTYLE" in
266         (xrescue)
267             INITCMD_START=( "${INITCMD_RESCUE[@]}" )
268             INITCMD_STOP=( /sbin/killall5 )
269             ;;
270             
271         (xsysv)
272             test -n "$RUNLEVEL_START" || RUNLEVEL_START=3
273             test -n "$RUNLEVEL_STOP"  || RUNLEVEL_STOP=6
274
275             for i in /etc/init.d/rc /etc/rc.d/rc; do
276                 test -x "$vdir/vdir/$i" || continue
277                 INITCMD_START=( "$i" "$RUNLEVEL_START" )
278                 INITCMD_STOP=(  "$i" "$RUNLEVEL_STOP"  )
279             done
280             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
281             OPTS_ENV=( "${OPTS_ENV[@]}" PREVLEVEL=N RUNLEVEL="$RUNLEVEL_START" )
282             if test -n "$OPTION_DEBUG_SYSV"; then
283                 INITCMD_START=( /bin/bash -x "${INITCMD_START[@]}" )
284                 INITCMD_STOP=( /bin/bash -x "${INITCMD_STOP[@]}" )
285             fi
286             ;;
287             
288         (xplain)
289             INITCMD_START=( /sbin/init )
290             INITCMD_STOP=(  /sbin/init )
291             _IS_FAKEINIT=1
292             _NEED_VSHELPER_SYNC=1
293             test -z "$RUNLEVEL_START" || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
294             test -z "$RUNLEVEL_STOP"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
295             ;;
296             
297         (xminit)
298             INITCMD_START=( /sbin/minit-start )
299             INITCMD_STOP=(  /sbin/minit-stop  )
300             _IS_FAKEINIT=1
301             INITCMD_START_SYNC=( "$_INITSYNC_MINIT_START" "$vdir" )
302             _NEED_VSHELPER_SYNC=1
303             test -z "$RUNLEVEL_START"         || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
304             test -z "$RUNLEVEL_STOP"          || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
305             ! isNumber "${RUNLEVEL_START:-3}" || INITCMD_PREPARE=( $_FAKE_RUNLEVEL "${RUNLEVEL_START:-3}" /var/run/utmp )
306             ;;
307
308         (xgentoo)
309             test -n "$RUNLEVEL_START" || RUNLEVEL_START="default"
310
311             INITCMD_START=( env TERM=$TERM /lib/rcscripts/sh/init-vserver.sh "$RUNLEVEL_START" )
312             INITCMD_STOP=( env -i TERM=$TERM RUNLEVEL=0 /sbin/rc shutdown )
313             INITCMD_PREPARE=( $_FAKE_RUNLEVEL 3 /var/run/utmp )
314             ;;
315
316         (x) ;;
317         (*) panic "Unknown init-style '$INITSTYLE'; aborting";;
318     esac
319
320     if test x"$INITSTYLE" != xrescue; then
321         getFileArray INITCMD_START      "$cfgdir"/cmd.start      || :
322         getFileArray INITCMD_STOP       "$cfgdir"/cmd.stop       || :
323         getFileArray INITCMD_START_SYNC "$cfgdir"/cmd.start-sync || :
324         getFileArray INITCMD_STOP_SYNC  "$cfgdir"/cmd.stop-sync  || :
325         getFileArray INITCMD_PREPARE    "$cfgdir"/cmd.prepare    || :
326     fi
327     
328     test -n "$OPTION_FORCE_SYNC" -o -e "$cfgdir"/sync || {
329         INITCMD_START_SYNC=()
330         INITCMD_STOP_SYNC=()
331         _NEED_VSHELPER_SYNC=
332     }
333
334     if vshelper.isEnabled; then
335         vshelper.getSyncTimeout "$vdir" VSHELPER_SYNC_TIMEOUT || :
336     else
337         _NEED_VSHELPER_SYNC=
338     fi
339 }
340
341 function _generateFlagOptions
342 {
343     local vdir=$1
344
345     CHCONTEXT_FLAG_OPTS=()
346
347     test ! -e "$vdir"/flags || \
348     while read flag; do
349         case x"$flag" in
350             (x|x\#*)            ;;
351             (xnamespace)        ;;
352             (xfakeinit)
353                 _IS_FAKEINIT=1
354                 ;;
355             (*)
356                 OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --flag "$flag" )
357                 CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}"
358                                       --flag "$flag" )
359                 ;;
360         esac
361     done <"$vdir"/flags
362
363     isAvoidNamespace "$vdir" || {
364         USE_VNAMESPACE=1
365         CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}" --flag namespace )
366     }
367 }
368
369 function _generateChcontextOptions
370 {
371     local vdir=$1
372     local ctx hostname domainname
373     local cap_opts
374     local flag
375
376     {
377         read ctx        <"$vdir"/context        || :
378         ## LEGACY ALERT
379         read hostname   <"$vdir"/uts/nodename   || read hostname   <"$vdir"/hostname   || :
380         read domainname <"$vdir"/uts/domainname || read domainname <"$vdir"/domainname || :
381     } 2>/dev/null
382
383     test -z "$S_CONTEXT" || ctx=$S_CONTEXT
384
385     _generateCapabilityOptions "$vdir"
386     _generateFlagOptions       "$vdir"
387
388     CHCONTEXT_OPTS=( $SILENT_OPT \
389                      "${CHCONTEXT_FLAG_OPTS[@]}" \
390                      "${CAP_OPTS[@]}" \
391                      --secure
392                      ${ctx:+--ctx "$ctx"} \
393                      ${hostname:+--hostname "$hostname"} \
394                      ${domainname:+--domainname "$domainname"} )
395
396     OPTS_VCONTEXT_CREATE=( $SILENT_OPT \
397                            ${ctx:+--xid "$ctx"} )
398     ## put '--secure' at front so that it can be overridden
399     OPTS_VATTRIBUTE=( --secure --flag default "${OPTS_VATTRIBUTE[@]}" )
400 }
401
402 function _generateScheduleOptions
403 {
404     local vdir=$1
405     if test -d "$vdir"/sched; then
406       OPTS_VSCHED=( --dir "$vdir"/sched --missingok )
407       return 0
408     fi
409
410     local f="$vdir"/schedule
411     test -e "$f" || return 0
412
413     local fill_rate interval tokens tokens_min tokens_max prio_bias
414     {
415         {
416             read fill_rate   && \
417             read interval    && \
418             read tokens      && \
419             read tokens_min  && \
420             read tokens_max  && \
421             read prio_bias   || prio_bias=
422         } <"$f"
423     } 2>/dev/null
424
425     test -n "$prio_bias" || {
426         echo $"Bad content in '$f'; aborting..." >&2
427         false
428     }
429
430     OPTS_VSCHED=( --fill-rate  "$fill_rate"  --interval "$interval" \
431                   --tokens     "$tokens"     --tokens_min "$tokens_min" \
432                   --tokens_max "$tokens_max" --priority-bias "$prio_bias" )
433 }
434
435 function _getInterfaceValue
436 {
437     local _giv_val=$1
438     local _giv_dflt=$2
439     shift 2
440     
441     local _giv_i
442     local _giv_tmp
443
444     for _giv_i; do
445         read _giv_tmp  <"$_giv_i/$_giv_val" && break || :
446     done 2>/dev/null
447
448     : ${_giv_tmp:=$_giv_dflt}
449     eval $_giv_val=\$_giv_tmp
450 }
451
452 ## Usage: _transformMask2Prefix <result-varname> <prefix> <mask>
453 function _transformMask2Prefix
454 {
455     local _tm2p_tmp=$2
456     
457     test -n "$_tm2p_tmp" || {
458         $_MASK2PREFIX "$3" || _tm2p_tmp=$?
459     }
460
461     eval $1=\$_tm2p_tmp
462     return 0
463 }
464
465 function _addInterfaceCmd
466 {
467     eval INTERFACE_CMDS_${INTERFACE_CMDS_IDX}='( "$@" )'
468     let ++INTERFACE_CMDS_IDX
469 }
470
471 ## Usage: _generateMac <var> <iface> <ctx>
472 function _generateMac
473 {
474     isNumber "$2" || {
475         echo $"Interface basename '$iface' must be either a number, or the mac must be configured explicitly" >&2
476         return 1
477     }
478
479     eval $1=$(printf "f0:ff:%02x:%02x:%02x:%02x" $[ (~($2>>8)) & 0xff ] $[ ($2 & 0xff) ] $[ ($3>>8) & 0xff ] $[ $3 & 0xff ])
480 }
481
482 function _getVLANInfo
483 {
484     case "$1" in
485         (vlan????)
486             panic "\
487 creation of VLAN_PLUS_VID devices is not supported; please create them
488 before starting the vserver and use the 'nodev' flag then"
489             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID"
490             ;;
491         (vlan*)
492             panic "\
493 creation of VLAN_PLUS_VID_NO_PAD devices is not supported; please
494 create them before starting the vserver and use the 'nodev' flag then"
495             echo "$1 vlan ${1##vlan} VLAN_PLUS_VID_N0_PAD"
496             ;;
497         (*.????)        echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID";;
498         (*.*)           echo "$1 ${1%%.*} ${1##*.} DEV_PLUS_VID_NO_PAD";;
499         (*)             return 1
500     esac
501
502     return 0
503 }
504
505 ## Usage: _processSingleInterface <interface-directory>
506 function _processSingleInterface
507 {
508     local iface=$1
509
510     local ip
511     local dev
512     local prefix
513     local mask
514     local bcast
515     local name
516     local scope
517     local mac
518     local extip
519     local up="up"
520
521     _getInterfaceValue ip     '' "$iface"
522     _getInterfaceValue extip  '' "$iface" "$iface/.."
523     _getInterfaceValue dev    '' "$iface" "$iface/.."
524     _getInterfaceValue prefix '' "$iface" "$iface/.."
525     _getInterfaceValue mask   '' "$iface" "$iface/.."
526     _getInterfaceValue bcast  '' "$iface" "$iface/.."
527     _getInterfaceValue name   '' "$iface"
528     _getInterfaceValue scope  '' "$iface" "$iface/.."
529     _getInterfaceValue mac    '' "$iface"
530
531     test -n "$ip" || { echo $"Can not read ip for '$iface'"  >&2; return 1; }
532     test -n "$dev" -o -e "$iface"/nodev || {
533         echo $"No device specified for '$iface'" >&2
534         return 1;
535     }
536
537     test ! -e "$iface"/down || up=
538
539     while true; do
540         _transformMask2Prefix prefix "$prefix" "$mask"
541         INTERFACES=( "${INTERFACES[@]}" "$ip${prefix:+/$prefix}" )
542
543         test ! -e "$iface"/nodev   || break
544         ## LEGACY ALERT
545         test ! -e "$iface"/only_ip || break
546
547         test -e "$iface/vlandev" \
548              -o \( -e "$iface/../vlandev" -a ! -e "$iface/novlandev" \) \
549              -o \( -e "$__CONFDIR/.defaults/interfaces/vlandev" \
550                    -a ! -e "$iface/novlandev" \
551                    -a ! -e "$iface/../novlandev" \) && {
552             local vlan_info
553             if vlan_info=$(_getVLANInfo "$dev"); then
554                 test -d /proc/net/vlan || {
555                     echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
556                     return 1
557                 }
558                 _addInterfaceCmd VCONFIG $vlan_info
559             fi
560         }
561
562         if ! test -e "$iface"/indirect; then
563             # XXX: IPv6 hack
564             use_bcast="broadcast ${bcast:-+}"
565             echo "$ip" | $_GREP -q : && use_bcast=
566             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" $use_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 RUNLEVEL=$RUNLEVEL_STOP # 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://linux-vserver.org/Proc-Security"
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     $_VDLIMIT --xid $ctx --remove "$directory"
1166 }
1167
1168
1169 function saveDiskLimits
1170 {
1171     local vdir=$1
1172     local dlimit
1173
1174     test -e "$vdir"/context || return 0
1175
1176     for dlimit in "$vdir/dlimits/"*; do
1177         test   -d "$dlimit"          || continue
1178         test ! -e "$dlimit/disabled" || continue
1179
1180         _saveSingleDiskLimit "$vdir" "$dlimit"
1181     done
1182 }
1183
1184 function _namespaceCleanup
1185 {
1186     local vdir="$1"
1187     local root=$($_VSERVER_INFO "$1" VDIR 1)
1188     local -a list
1189     local -a skip
1190     local tmp
1191
1192     getFileArray skip "$vdir"/namespace-cleanup-skip \
1193         "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
1194
1195     # these are things that have to be accessible post-cleanup
1196     for tmp in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
1197         "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
1198         while test -n "$tmp"; do
1199             list=( "${list[@]}" "$tmp" )
1200             tmp="${tmp%/*}"
1201         done
1202     done
1203
1204     local -a list_umount
1205     while read dev path opts; do
1206         test -n "$path" || continue
1207         for i in "$root" /dev /proc; do
1208             test "${path#$i}" != "$path" && continue 2
1209         done
1210         for i in "${list[@]}" /; do
1211             test "$path" = "$i" && continue 2
1212         done
1213         # unmount them in reverse order so mounts further down the tree get unmounted first
1214         list_umount=( "$path" "${list_umount[@]}" )
1215     done < /proc/mounts
1216     # separate loop to avoid races while reading /proc/mounts
1217     for i in "${list_umount[@]}"; do
1218         $_UMOUNT -l -n "$i"
1219     done
1220 }
1221
1222 function loadDeviceMap
1223 {
1224     local xid="$1"
1225     local dir="$2"
1226     local flags device target
1227
1228     test -d "$dir" || return 0
1229
1230     for i in "$dir"/*; do
1231         test -d "$i" || continue
1232
1233         local -a vdevmap_opts=()
1234         test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
1235         test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
1236         test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
1237
1238         getFileValue flags "$i/flags" || :
1239         getFileValue device "$i/device" || :
1240         getFileValue target "$i/target" || :
1241         vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
1242                         ${device:+--device "$device"} ${target:+--target "$target"} )
1243
1244         $_VDEVMAP --xid "$xid" "${vdevmap_opts[@]}" || return $?
1245     done
1246 }