Run fsck on filesystems in the guest's fstab.
[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"
615     test -n "$tag" || return 0
616
617     OPTS_VTAG_CREATE=( --tag "$tag" )
618     OPTS_VTAG_ENTER=( --tag "$tag" )
619 }
620
621 function enableInterfaces
622 {
623     local i=0
624     declare -a var
625
626     lock "$__LOCKDIR"/vserver.interfaces
627
628     while test $i -lt $INTERFACE_CMDS_IDX; do
629         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
630         local type=${var[0]}
631         unset var[0]
632
633         set -- "${var[@]}"
634         case "$type" in
635             IPTABLES)   ;; ## TODO
636             MODPROBE)
637                 local mod=$1
638                 local name=$2
639                 shift 2
640                 $_MODPROBE ${name:+-o "$name"} "$mod" "$@"
641                 ;;
642             NAMEIF)             $_NAMEIF   "$@";;
643             VCONFIG)            $_VCONFIG  set_name_type "$4"      >/dev/null
644                                 $_VCONFIG  add           "$2" "$3" >/dev/null;;
645             IP_ADDR)            $_IP addr  add   "$@";;
646             IP_ADDR_FLUSH)      $_IP addr  flush "$@";;
647             IP_LINK)            $_IP link  set   "$@";;
648             IP_ROUTE)           $_IP route add   "$@";;
649             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
650         esac
651
652         let ++i
653     done
654
655     unlock 1
656 }
657
658 function disableInterfaces
659 {
660     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
661
662     local i=$INTERFACE_CMDS_IDX
663     declare -a var
664
665     lock "$__LOCKDIR"/vserver.interfaces
666     
667     while test $i -gt 0; do
668         let --i || :
669
670         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
671         local type=${var[0]}
672         unset var[0]
673         
674         set -- "${var[@]}"
675         case "$type" in
676             IPTABLES)           ;; ## TODO
677             MODPROBE)           $_RMMOD "${2:-$1}";;
678             NAMEIF)             ;;
679             VCONFIG)            $_VCONFIG  rem "$2.$3" >/dev/null;;
680             IP_ADDR)            $_IP addr  del "$@";;
681             IP_ADDR_FLUSH)      ;;
682             IP_LINK)            ;; ## Ignore the link-down command for now
683             IP_ROUTE)           $_IP route del "$@";;
684             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
685         esac
686     done
687
688     unlock 1
689 }
690
691 ## Usage: prepareInit <vserver-directory>
692 function prepareInit
693 {
694     pushd "$1/vdir" >/dev/null
695     case "$INITSTYLE" in
696         sysv)
697             { find var/run  ! -type d -print0; \
698               find var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
699             ;;
700         plain)
701             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
702             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
703             ;;
704         minit)
705             ;;
706     esac
707     "${INITCMD_PREPARE[@]}"
708     popd >/dev/null
709 }
710
711 ## Usage: prepareInit <vserver-directory>
712 function prepareStop
713 {
714     pushd "$1/vdir" >/dev/null
715     case "$INITSTYLE" in
716         (sysv)
717             export PREVLEVEL=$RUNLEVEL_START RUNLEVEL=$RUNLEVEL_STOP # required by Debian's initscripts
718             ;;
719     esac
720     "${STOPCMD_PREPARE[@]}"
721     popd >/dev/null
722 }
723
724
725 function generateOptions
726 {
727     _generateInterfaceOptions   "$1"
728     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
729     _generateNiceCommand        "$1"
730     _generateInitOptions        "$1"
731     _generateChcontextOptions   "$1"
732     _generateScheduleOptions    "$1"
733     _generatePersonalityOptions "$1"
734     _generateTagOptions         "$1"
735
736     if test -n "$_IS_FAKEINIT"; then
737         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
738         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
739     fi
740 }
741
742 function addtoCPUSET
743 {
744     local vdir=$1
745     local cpuset
746     local f="$vdir"/cpuset
747     local i
748     local configured=0
749
750     test -d "$f" || return 0
751     test -e "$f"/name || return 0
752
753     read cpuset < "$f"/name
754     test -e "$f"/nocreate || {
755        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
756        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
757            if test -e "$f"/"$i"; then
758                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" || {
759                    configured=1
760                    break
761                }
762            fi
763        done
764     }
765
766     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
767     if [ "$configured" -ne 0 ]; then
768        warning $"\
769 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
770        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
771        return 0
772     fi
773 }
774
775 function removeCPUSET
776 {
777     local vdir=$1
778     local cpuset
779     local f="$vdir"/cpuset
780
781     test -d "$f" || return 0
782     test -e "$f"/name || return 0
783
784     read cpuset < "$f"/name
785     test -e "$f"/nocreate || {
786        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
787     }
788 }
789
790 function _mountVserverInternal
791 {
792     local fstab="$1"
793     local xflag=
794     
795     test -e "$fstab" || return 0
796     shift
797
798     pushd "$vdir" >/dev/null
799     # check whether / is mounted readonly or whether there is special
800     # magic regarding the mtab file; when etc/mtab can not be touched,
801     # add the '-n' flag to mount
802     test -w etc -o -w etc/mtab || xflag=-n
803     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
804     popd >/dev/null
805 }
806
807 function mountRootFS
808 {
809     local cfgdir=$1
810     local vdir=$1/vdir
811     local fstab="$cfgdir"/fstab
812     local xflag=
813
814     test -e "$fstab" || return 0
815     pushd "$vdir" >/dev/null
816     # check whether / is mounted readonly or whether there is special
817     # magic regarding the mtab file; when etc/mtab can not be touched,
818     # add the '-n' flag to mount
819     test -w etc -o -w etc/mtab || xflag=-n
820     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
821     popd >/dev/null
822 }
823
824 function mountVserver
825 {
826     local cfgdir=$1
827     local ns_opt=$2
828     local vdir=$1/vdir
829     local mtab_src
830
831     test -e "$cfgdir"/fstab -o \
832          -e "$cfgdir"/fstab.local -o \
833          -e "$cfgdir"/fstab.remote || return 0
834
835     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
836     
837     pushd "$vdir" >/dev/null
838     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
839     popd >/dev/null
840
841     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
842
843     _mountVserverInternal "$cfgdir"/fstab
844     _mountVserverInternal "$cfgdir"/fstab.local
845     _mountVserverInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}"
846
847     isNamespaceCleanup "$cfgdir" && \
848         _namespaceCleanup "$cfgdir"
849
850     isAvoidNamespace "$cfgdir" || \
851         $_SECURE_MOUNT --rbind -n "$vdir" "/"
852 }
853
854 function _umountVserverInternal
855 {
856     local fstab="$1"
857     test -e "$fstab" || return 0
858     shift
859
860     $_TAC "$fstab" | {
861         is_ok=1
862         while read src dst tmp; do
863             test -n "$tmp" || continue
864             case x"$src" in
865                 (x\#*)  continue;;
866             esac
867
868         
869             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
870         done
871         test -n "$is_ok"
872     }
873 }
874
875 function umountVserver
876 {
877     local cfgdir=$1
878     local vdir=$1/vdir
879     local is_ok=1
880
881     isAvoidNamespace "$cfgdir"    || return 0
882     test -e "$cfgdir"/fstab -o \
883          -e "$cfgdir"/fstab.local -o \
884          -e "$cfgdir"/fstab.remote || return 0
885     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
886     
887     pushd "$vdir/" >/dev/null || return 1
888         _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
889         _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
890         _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
891     popd >/dev/null           || return 1
892
893     test -n "$is_ok"
894 }
895
896 function fsckAllFS
897 {
898     local cfgdir=$1
899     local fstab="$cfgdir"/fstab
900     local FSTAB_FILE
901     local fsck_exitcode
902
903     test -e "$fstab" || return 0
904
905     export FSTAB_FILE="$fstab"
906     $_FSCK -s -n -A -T
907     fsck_exitcode=$?
908     test "$fsck_exitcode" -eq 0 -o \
909          "$fsck_exitcode" -eq 1 || return $fsck_exitcode
910 }
911
912 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
913 function initSync
914 {
915     local _is_meth=sync
916     test -n "$_NEED_VSHELPER_SYNC" && \
917         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
918
919     vshelper.initSync "$1" "$3" "$_is_meth"
920 }
921
922 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
923 function initWait
924 {
925     if $_VSERVER_INFO - FEATURE vwait; then
926         local _is_tmpdir
927         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
928
929         (
930             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
931                 --status-fd 3 "$2" \
932                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
933             rc=$?
934
935             if test "$rc" -ne 0 -a "$rc" -ne 1; then
936                 $_VPS axf | $_EGREP -e "^[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
937                 killContext "$S_CONTEXT" 9
938             fi
939
940             exit $rc
941         ) 4>$_is_tmpdir/procs &
942             
943         echo "$!" >$_is_tmpdir/pid
944         eval "$3"=$_is_tmpdir
945     fi </dev/null
946 }
947
948
949 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
950 function _waitForVWait
951 {
952     wait "$3" || :
953
954     declare -a status
955     declare -r procs=$(cat $4)
956
957     getFileArray status "$2"
958     set -- ${status[0]}
959
960     case "$1" in
961         (ERROR)         warning $"\
962 'vwait' exited with error '$2' which indicates that vserver could not
963 be stopped properly"
964                         ;;
965         (FINISHED)      ;;
966         (KILLED)        warning $"\
967 A timeout occured while waiting for the vserver to finish and it was
968 killed by sending a SIGKILL signal. Please investigate the reasons
969 and/or increase the timeout in apps/vshelper/sync-timeout."
970                         ;;
971
972         (TIMEOUT)       warning $"\
973 A timeout occured while waiting for the vserver to finish and it will
974 be killed by sending a SIGKILL signal. The following process list
975 might be useful for finding out the reason of this behavior:
976
977 ----------------------------------------------------------------------
978 ${procs:+$procs
979 }----------------------------------------------------------------------"
980                         ;;
981
982         (\?\?\?|*)      warning $"\
983 internal error: 'vwait' exited with an unexpected status '$1'; I will
984 try to continue but be prepared for unexpected events."
985                     ;;
986     esac
987
988     return 0
989 }
990
991 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
992 function waitForSync
993 {
994     local cfgdir=$1
995     local fifo=$2
996     local vwait_statdir=$3
997     local vwait_pid=$4
998
999     if test -d "$vwait_statdir"; then
1000         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
1001     elif test -n "$_NEED_VSHELPER_SYNC"; then
1002         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
1003             warning $"\
1004 A timeout or other error occured while waiting for the synchronization
1005 signal from vserver '$VSERVER_NAME'.
1006 The vserver will be killed nevertheless..."
1007     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
1008         "${INITCMD_STOP_SYNC[@]}" || \
1009             warning $"\
1010 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
1011 will be killed nevertheless..."
1012     fi
1013
1014     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
1015         sleep 1
1016 }
1017
1018 function _sourceWrap
1019 {
1020     local vdir name flavor start i already_handled base
1021     . "$@"
1022 }
1023
1024 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
1025 function execScriptlets
1026 {
1027     declare -r vdir=$1
1028     declare -r name=$2
1029     declare -r flavor=$3
1030     local base i
1031
1032     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
1033         local   DONT_SKIP_DEFAULTS=
1034         local   already_handled=
1035         
1036         for i in "$base/$flavor" "$base/$flavor.d"/*; do
1037             isRegularFile "$i" || continue
1038             test  -r "$i"      || continue
1039
1040             already_handled=1
1041             local start=
1042             test -x "$i" || start=_sourceWrap
1043             $start "$i" "$flavor" "$name"
1044         done
1045
1046         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
1047     done
1048 }
1049
1050
1051 function sanityCheck
1052 {
1053     declare -r cfgdir=$1
1054
1055     ! test -e "$cfgdir"/fstab.local ||
1056         warning $"\
1057 WARNING: 'fstab' will *not* be executed in the network context of the
1058   vserver anymore. Therefore, 'fstab.local' has the same functionality
1059   and is obsoleted. When you need the old behaviour, put the mounts
1060   into 'fstab.remote'"
1061
1062     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1063         warning $"\
1064 WARNING: The hostname is now configured in 'uts/nodename' but not in
1065   'hostname'."
1066
1067     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1068         warning $"\
1069 WARNING: The domainname is now configured in 'uts/domainname' but not
1070   in 'domainname'." >&2
1071
1072   
1073     local i
1074     for i in "$cfgdir"/interfaces/*/only_ip; do
1075         if test -e "$i"; then
1076             local iface
1077             iface=${i##$cfgdir/interfaces/}
1078             iface=${iface%%/only_ip}
1079             warning $"\
1080 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1081   'nodev' instead of"
1082         fi
1083     done
1084
1085     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1086         warning $"\
1087 WARNING: There is no cachedirectory configured for this vserver;
1088   please create '$cfgdir/cache' e.g. by executing
1089
1090   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1091 "
1092
1093     find "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1094
1095     vshelper.doSanityCheck
1096
1097     $_VSERVER_INFO - VERIFYCAP ||
1098         panic $"capabilities are not enabled in kernel-setup"
1099
1100     $_VSERVER_INFO - VERIFYPROC ||
1101         panic $"\
1102 /proc/uptime can not be accessed. Usually, this is caused by
1103 procfs-security. Please read the FAQ for more details
1104 http://linux-vserver.org/Proc-Security"
1105
1106     test -e "$cfgdir"/context || {
1107         TYPE=$( $_VSERVER_INFO 49152 XIDTYPE )
1108         test "$TYPE" != "static" || panic $"\
1109 The kernel does not have dynamic contexts enabled. Please configure
1110 a static one by executing
1111
1112   echo [number between 2 and 49151] > $cfgdir/context"
1113     }
1114 }
1115
1116
1117 function _setSingleDiskLimit
1118 {
1119     local vdir=$1
1120     local dlimit=$2
1121     local space_used=
1122     local space_total=
1123     local inodes_used=
1124     local inodes_total=
1125     local reserved=
1126     local directory=
1127     local ctx=
1128
1129     getFileValue ctx          "$vdir/context"
1130     getFileValue directory    "$dlimit/directory"    || return 0
1131     getFileValue space_total  "$dlimit/space_total"  || return 0
1132     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1133     getFileValue reserved     "$dlimit/reserved"     || return 0
1134
1135     local cachename=$ctx$directory
1136     cachename=dlimits/${cachename//\//_}
1137
1138     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1139     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1140     $_RM -f "$vdir/cache/$cachename"
1141
1142     if test -z "$inodes_used" -o -z "$space_used"; then
1143         local tmpvdu
1144         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1145         inodes_used=${tmpvdu##* }
1146         space_used=${tmpvdu%% *}
1147     fi
1148
1149     $_VDLIMIT --xid $ctx \
1150         --set space_used=$space_used \
1151         --set space_total=$space_total \
1152         --set inodes_used=$inodes_used \
1153         --set inodes_total=$inodes_total \
1154         --set reserved=$reserved \
1155         "$directory"
1156 }
1157
1158
1159 function setDiskLimits
1160 {
1161     local vdir=$1
1162     local dlimit
1163
1164     # Disk Limits without a static context are useless
1165     test -e "$vdir"/context || return 0
1166
1167     for dlimit in "$vdir/dlimits/"*; do
1168         test   -d "$dlimit"          || continue
1169         test ! -e "$dlimit/disabled" || continue
1170
1171         _setSingleDiskLimit "$vdir" "$dlimit"
1172     done
1173 }
1174
1175
1176 function _saveSingleDiskLimit
1177 {
1178     local vdir=$1
1179     local dlimit=$2
1180     local ctx=
1181     local directory=
1182
1183     getFileValue ctx       "$vdir/context"
1184     getFileValue directory "$dlimit/directory" || return 0
1185
1186     local cachename=$ctx$directory
1187     cachename=${cachename//\//_}
1188
1189     # Things are getting ugly here... LFS says that /var/cache (where
1190     # cachename is usually pointing to) can vanish and applications
1191     # have to deal with it. So, we have to interprete the $vdir/cache
1192     # symlink and have to create the needed directories manually.
1193     if   test -d "$vdir/cache"; then
1194         :       # ok, exists already
1195     elif test -L "$vdir/cache"; then
1196         # it's a dangling symlink
1197         local link
1198         link=$($_READLINK "$vdir/cache")
1199         ( cd $vdir && $_MKDIR -p "$link" )
1200     else
1201         return 0
1202     fi
1203
1204     test -d "$vdir/cache"
1205     $_MKDIR -p "$vdir"/cache/dlimits
1206
1207     $_VDLIMIT --xid $ctx "$directory" | \
1208         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1209
1210     $_VDLIMIT --xid $ctx --remove "$directory"
1211 }
1212
1213
1214 function saveDiskLimits
1215 {
1216     local vdir=$1
1217     local dlimit
1218
1219     test -e "$vdir"/context || return 0
1220
1221     for dlimit in "$vdir/dlimits/"*; do
1222         test   -d "$dlimit"          || continue
1223         test ! -e "$dlimit/disabled" || continue
1224
1225         _saveSingleDiskLimit "$vdir" "$dlimit"
1226     done
1227 }
1228
1229 function _namespaceCleanup
1230 {
1231     local vdir="$1"
1232     local root=$($_VSERVER_INFO "$1" VDIR 1)
1233     local -a list
1234     local -a skip
1235     local i
1236     local j
1237
1238     getFileArray skip "$vdir"/namespace-cleanup-skip \
1239         "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
1240
1241     # these are things that have to be accessible post-cleanup
1242     for i in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
1243         "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
1244         local real=`readlink -f "$i"`
1245         test "$i" != "$real" || real=
1246         for j in "$i" "$real"; do
1247             while test -n "$j"; do
1248                 list=( "${list[@]}" "$j" )
1249                 j="${j%/*}"
1250             done
1251         done
1252     done
1253
1254     local -a list_umount
1255     while read dev path opts; do
1256         test -n "$path" || continue
1257         for i in "$root" /dev /proc; do
1258             test "${path#$i}" != "$path" && continue 2
1259         done
1260         for i in "${list[@]}" /; do
1261             test "$path" = "$i" && continue 2
1262         done
1263         # unmount them in reverse order so mounts further down the tree get unmounted first
1264         list_umount=( "$path" "${list_umount[@]}" )
1265     done < /proc/mounts
1266     # separate loop to avoid races while reading /proc/mounts
1267     for i in "${list_umount[@]}"; do
1268         $_UMOUNT -l -n "$i"
1269     done
1270 }
1271
1272 function loadDeviceMap
1273 {
1274     local xid="$1"
1275     local dir="$2"
1276     local flags device target
1277
1278     test -d "$dir" || return 0
1279
1280     for i in "$dir"/*; do
1281         test -d "$i" || continue
1282
1283         local -a vdevmap_opts=()
1284         test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
1285         test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
1286         test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
1287
1288         getFileValue flags "$i/flags" || :
1289         getFileValue device "$i/device" || :
1290         getFileValue target "$i/target" || :
1291         vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
1292                         ${device:+--device "$device"} ${target:+--target "$target"} )
1293
1294         $_VDEVMAP --xid "$xid" --set "${vdevmap_opts[@]}" || return $?
1295     done
1296 }