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