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