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