Add preliminary support for pid and network namespaces.
[util-vserver.git] / scripts / vserver.functions
1 # $Id$  --*- sh -*--
2
3 # Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 #  
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License.
8 #  
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #  
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 ## Expected env:
19 #  $VSERVER_DIR   ... path to vserver-cfg dir
20 #  $VSERVER_NAME  ... name of vserver
21
22 declare -a NICE_CMD=()
23 declare -a CHBIND_CMD=()
24 declare -a CAP_OPTS=()
25 declare -a CHCONTEXT_INIT_OPTS=()
26 declare -a CHCONTEXT_FLAG_OPTS=()
27 declare -a CHCONTEXT_OPTS=()
28 declare -a CAPCHROOT_OPTS=()
29 declare -a INTERFACES=()
30
31 declare -a INITCMD_RESCUE=( /bin/sleep 900 )
32 declare -a INITCMD_START=()
33 declare -a INITCMD_START_SYNC=()
34 declare -a INITCMD_STOP=()
35 declare -a INITCMD_STOP_SYNC=()
36 declare -a INITCMD_PREPARE=()
37 declare -a INITKILL_SEQ=()
38 declare -a ENTER_SHELL=()
39
40 declare -a OPTS_VCONTEXT_CREATE=()
41 declare -a OPTS_VCONTEXT_MIGRATE=()
42 declare -a OPTS_VCONTEXT_ENTER=()
43 declare -a OPTS_VATTRIBUTE=( --flag fakeinit )
44 declare -a OPTS_VSCHED=()
45 declare -a OPTS_ENV=()
46 declare -a OPTS_VTAG_CREATE=()
47 declare -a OPTS_VTAG_ENTER=()
48 declare -a OPTS_VMEMCTRL=()
49 declare -a OPTS_VSPACE=()
50
51 declare -a STOPCMD_PREPARE=()
52
53 declare -a VSERVER_EXTRA_CMDS=()
54
55 INIT_RESCUE=
56 VSHELPER_SYNC_TIMEOUT=30
57 USE_VNAMESPACE=
58 INTERFACE_CMDS_IDX=0
59 RUNLEVEL_START=
60 RUNLEVEL_STOP=
61 _HAVE_INTERFACE_OPTIONS=
62 _HAVE_CHBIND_OPTIONS=
63 _NEED_VSHELPER_SYNC=
64 _IS_FAKEINIT=
65
66 INITSTYLE=sysv
67
68 S_CONTEXT=
69
70 SILENT_OPT=
71
72 : ${VSERVER_NAME:=$(basename "$VSERVER_DIR")}
73
74 if test -e "$VSERVER_DIR"/noisy -o -n "$OPTION_VERBOSE"; then
75     SILENT_OPT=
76 else
77     SILENT_OPT='--silent'
78 fi
79
80 function _readFileToArray
81 {
82     local _rfta_f="$1"
83     local _rfta_a="$2"
84     local _rfta_p="$3"
85     local _rfta_v
86
87     test -e "$_rfta_f" || return 0
88     while read _rfta_v; do
89         case x"$_rfta_v" in
90             (x|x\#*)    ;;
91             (*)         eval "$_rfta_a=( \"\${$_rfta_a[@]}\" $_rfta_p \"$_rfta_v\" )";;
92         esac
93     done <"$_rfta_f"
94 }
95
96 function _generateChbindOptions
97 {
98     local vdir="$1"
99     local i
100     local bcast=
101     local lback=
102     local nid=
103
104     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$vdir"
105
106     local f="$vdir"/interfaces/bcast
107     getFileValue bcast "$f"
108     f="$vdir"/interfaces/lback
109     getFileValue lback "$f"
110
111     getFileValue nid "$vdir/ncontext" "$vdir/context"
112     
113     CHBIND_CMD=( $_CHBIND $SILENT_OPT --secure ${nid:+--nid "$nid"}
114                  ${bcast:+--bcast "$bcast"} ${lback:+--lback "$lback"}
115                 )
116
117     for i in "${INTERFACES[@]}"; do
118         CHBIND_CMD=( "${CHBIND_CMD[@]}" --ip "$i" )
119     done
120
121     _readFileToArray "$vdir"/nflags        CHBIND_CMD --flag
122     _readFileToArray "$vdir"/ncapabilities CHBIND_CMD --ncap
123
124     _HAVE_CHBIND_OPTIONS=1
125 }
126
127 function _generateNiceCommand
128 {
129     local vdir=$1
130     local nice=0
131     local current_nice=`$_NICE`
132
133     test -r "$vdir/nice" && read nice <"$vdir"/nice
134
135     let nice=$nice-$current_nice || :
136     NICE_CMD=( $_NICE -n $nice )
137 }
138
139
140 function _generatePersonalityOptions
141 {
142     local vdir="$1"
143     local f="$vdir"/personality
144     local type flags
145
146     test -s "$f" || return 0
147
148     {
149         local delim tmp
150
151         read type
152         while read tmp; do
153             case x$tmp in
154                 (x\#*|x)        ;;
155                 (*)             flags=$flags$delim$tmp
156                                 delim=,
157                                 ;;
158             esac
159         done
160     } <"$f"
161
162     OPTS_VCONTEXT_ENTER=( "${OPTS_VCONTEXT_ENTER[@]}"
163                           --personality-type "$type"
164                           ${flags:+--personality-flags "$flags"} )
165 }
166
167 function _generateCCapabilityOptions
168 {
169     local vdir=$1
170
171     _readFileToArray "$vdir"/ccapabilities OPTS_VATTRIBUTE --ccap
172 }
173
174 function _generateBCapabilityOptions
175 {
176     local vdir=$1
177
178     _readFileToArray "$vdir"/bcapabilities OPTS_VATTRIBUTE --bcap
179 }
180
181 function _generateCapabilityOptions
182 {
183     local vdir=$1
184     local cap
185
186     _generateBCapabilityOptions "$vdir"
187     _generateCCapabilityOptions "$vdir"
188     
189     test -e "$vdir"/capabilities || return 0
190
191     CAP_OPTS=()
192     CAPCHROOT_OPTS=()
193
194     while read cap; do
195         case x"$cap" in
196             (x|x\#*)    ;;
197             (!CAP_SYSCHROOT)
198                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
199                 CAPCHROOT_OPTS=( "${CAPCHROOT_OPTS[@]}" --nochroot )
200                 ;;
201             (*)
202                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
203                 ;;
204         esac
205     done <"$vdir"/capabilities
206 }
207
208 function getEnterShell
209 {
210     local vdir=$1
211     local shell_file
212
213     ENTER_SHELL=()
214
215     getFileValue ENTER_SHELL "$vdir"/shell "$__CONFDIR"/.defaults/shell
216     
217     test -n "$ENTER_SHELL" || {
218         local i
219         for i in "/bin/bash -login" "/bin/sh -l" /bin/csh; do
220             set -- $i
221             test -x "$vdir/vdir/$1" || continue
222             ENTER_SHELL=( "$@" )
223             break
224         done
225     }
226 }
227
228 ## Usage: sendKillSequence <ctx> <signal> [<wait> <signal>]*
229 function sendKillSequence
230 {
231     local ctx=$1
232     local wait=
233     shift
234
235     while isCtxRunning "$ctx"; do
236         test -z "$wait" || sleep "$wait"
237
238         killContext "$ctx" "$1"
239         test -n "$2" || break
240         wait="$2"
241         shift 2
242     done
243 }
244
245 function _generateInitOptions
246 {
247     local vdir=$1
248     local cfgdir=$vdir/apps/init
249     local i f
250
251     INITCMD_START=()
252     INITCMD_STOP=()
253     INITCMD_START_SYNC=()
254     INITCMD_STOP_SYNC=()
255     INITCMD_PREPARE=()
256     STOPCMD_PREPARE=()
257
258     INITKILL_SEQ=( 15 5 9 )
259     CHCONTEXT_INIT_OPTS=()
260
261
262     test x"$INITSTYLE" = xrescue || \
263       getFileValue INITSTYLE    "$cfgdir"/style
264     getFileValue RUNLEVEL_START "$cfgdir"/runlevel
265     getFileValue RUNLEVEL_START "$cfgdir"/runlevel.start
266     getFileValue RUNLEVEL_STOP  "$cfgdir"/runlevel.stop
267     getFileArray INITKILL_SEQ   "$cfgdir"/killseq || :
268
269     findFile _gio_env           "$cfgdir"/environment \
270         "$__CONFDIR"/.defaults/apps/init/environment \
271         "$__PKGLIBDEFAULTDIR"/environment
272     getFileArray OPTS_ENV       "$_gio_env" || :
273
274     case x"$INITSTYLE" in
275         (xrescue)
276             INITCMD_START=( "${INITCMD_RESCUE[@]}" )
277             INITCMD_STOP=( /sbin/killall5 )
278             ;;
279             
280         (xsysv)
281             test -n "$RUNLEVEL_START" || RUNLEVEL_START=3
282             test -n "$RUNLEVEL_STOP"  || RUNLEVEL_STOP=6
283
284             for i in /etc/init.d/rc /etc/rc.d/rc; do
285                 test -x "$vdir/vdir/$i" || continue
286                 INITCMD_START=( "$i" "$RUNLEVEL_START" )
287                 INITCMD_STOP=(  "$i" "$RUNLEVEL_STOP"  )
288             done
289             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$RUNLEVEL_START" /var/run/utmp )
290             OPTS_ENV=( "${OPTS_ENV[@]}" PREVLEVEL=N RUNLEVEL="$RUNLEVEL_START" )
291             if test -n "$OPTION_DEBUG_SYSV"; then
292                 INITCMD_START=( /bin/bash -x "${INITCMD_START[@]}" )
293                 INITCMD_STOP=( /bin/bash -x "${INITCMD_STOP[@]}" )
294             fi
295             ;;
296             
297         (xplain)
298             INITCMD_START=( /sbin/init )
299             INITCMD_STOP=(  /sbin/init )
300             _IS_FAKEINIT=1
301             _NEED_VSHELPER_SYNC=1
302             test -z "$RUNLEVEL_START" || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
303             test -z "$RUNLEVEL_STOP"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
304             ;;
305             
306         (xminit)
307             INITCMD_START=( /sbin/minit-start )
308             INITCMD_STOP=(  /sbin/minit-stop  )
309             _IS_FAKEINIT=1
310             INITCMD_START_SYNC=( "$_INITSYNC_MINIT_START" "$vdir" )
311             _NEED_VSHELPER_SYNC=1
312             test -z "$RUNLEVEL_START"         || INITCMD_START=( "${INITCMD_START[@]}" "$RUNLEVEL_START" )
313             test -z "$RUNLEVEL_STOP"          || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$RUNLEVEL_STOP"  )
314             ! isNumber "${RUNLEVEL_START:-3}" || INITCMD_PREPARE=( $_FAKE_RUNLEVEL "${RUNLEVEL_START:-3}" /var/run/utmp )
315             ;;
316
317         (xgentoo)
318             test -n "$RUNLEVEL_START" || RUNLEVEL_START="default"
319             RC_PATH=/usr/sbin:/usr/bin:/sbin:/bin
320
321             INITCMD_START=( env TERM=$TERM /lib/rcscripts/sh/init-vserver.sh "$RUNLEVEL_START" )
322             INITCMD_STOP=( env -i PATH=$RC_PATH TERM=$TERM RUNLEVEL=0 /sbin/rc shutdown )
323             INITCMD_PREPARE=( $_FAKE_RUNLEVEL 3 /var/run/utmp )
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         test -e "$iface/vlandev" \
558              -o \( -e "$iface/../vlandev" -a ! -e "$iface/novlandev" \) \
559              -o \( -e "$__CONFDIR/.defaults/interfaces/vlandev" \
560                    -a ! -e "$iface/novlandev" \
561                    -a ! -e "$iface/../novlandev" \) && {
562             local vlan_info
563             if vlan_info=$(_getVLANInfo "$dev"); then
564                 test -d /proc/net/vlan || {
565                     echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
566                     return 1
567                 }
568                 _addInterfaceCmd VCONFIG $vlan_info
569             fi
570         }
571
572         if ! test -e "$iface"/indirect; then
573             # XXX: IPv6 hack
574             use_bcast="broadcast ${bcast:-+}"
575             echo "$ip" | $_GREP -q : && use_bcast=
576             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" $use_bcast ${name:+label "$dev:$name"} dev "$dev"
577             #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
578             _addInterfaceCmd IP_LINK  "$dev" $up
579         elif ! test -n "$ctx"; then
580             echo $"Using 'dummy' (indirect) for interface '$dev' requires a fixed context number; dynamic ctx are not supported" >&2
581             return 1
582         else
583             test -z "$mac" || _generateMac mac "$(basename $iface)" "$ctx" || return 1
584             _addInterfaceCmd MODPROBE dummy "$dev"
585             _addInterfaceCmd IP_LINK  dev dummy0 address "$mac"
586             _addInterfaceCmd NAMEIF   "$dev" "$mac"
587             _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" dev "$dev"
588             test -z "$extip" || _addInterfaceCmd IPTABLES "$ip${prefix:+/$prefix}" ${name:+label "$dev:$name"} "$ctx" "$extip"
589         fi
590
591         break
592     done
593 }
594
595 ## Usage: _generateInterfaceOptions <vserver-directory>
596 function _generateInterfaceOptions
597 {
598     local iface
599     local ctx
600
601     test ! -e "$1"/context || read ctx <"$1"/context
602
603     for iface in "$1/interfaces/"*; do
604         test   -d "$iface"          || continue
605         test ! -e "$iface"/disabled || continue
606     
607         _processSingleInterface "$iface"
608     done
609     _HAVE_INTERFACE_OPTIONS=1
610 }
611
612 function enableInterfaces
613 {
614     local i=0
615     declare -a var
616
617     lock "$__LOCKDIR"/vserver.interfaces
618
619     while test $i -lt $INTERFACE_CMDS_IDX; do
620         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
621         local type=${var[0]}
622         unset var[0]
623
624         set -- "${var[@]}"
625         case "$type" in
626             IPTABLES)   ;; ## TODO
627             MODPROBE)
628                 local mod=$1
629                 local name=$2
630                 shift 2
631                 $_MODPROBE ${name:+-o "$name"} "$mod" "$@"
632                 ;;
633             NAMEIF)             $_NAMEIF   "$@";;
634             VCONFIG)            $_VCONFIG  set_name_type "$4"      >/dev/null
635                                 $_VCONFIG  add           "$2" "$3" >/dev/null;;
636             IP_ADDR)            $_IP addr  add   "$@";;
637             IP_ADDR_FLUSH)      $_IP addr  flush "$@";;
638             IP_LINK)            $_IP link  set   "$@";;
639             IP_ROUTE)           $_IP route add   "$@";;
640             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
641         esac
642
643         let ++i
644     done
645
646     unlock 1
647 }
648
649 function disableInterfaces
650 {
651     test -n "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
652
653     local i=$INTERFACE_CMDS_IDX
654     declare -a var
655
656     lock "$__LOCKDIR"/vserver.interfaces
657     
658     while test $i -gt 0; do
659         let --i || :
660
661         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
662         local type=${var[0]}
663         unset var[0]
664         
665         set -- "${var[@]}"
666         case "$type" in
667             IPTABLES)           ;; ## TODO
668             MODPROBE)           $_RMMOD "${2:-$1}";;
669             NAMEIF)             ;;
670             VCONFIG)            $_VCONFIG  rem "$2.$3" >/dev/null;;
671             IP_ADDR)            $_IP addr  del "$@";;
672             IP_ADDR_FLUSH)      ;;
673             IP_LINK)            ;; ## Ignore the link-down command for now
674             IP_ROUTE)           $_IP route del "$@";;
675             *)                  echo "Unknown interface-command type '$type'" >&2; false;;
676         esac
677     done
678
679     unlock 1
680 }
681
682 function _generateTagOptions
683 {
684     local vdir="$1"
685     local tag
686
687     getFileValue tag "$vdir/tag" "$vdir/context"
688     test -n "$tag" || return 0
689
690     OPTS_VTAG_CREATE=( --tag "$tag" )
691     OPTS_VTAG_ENTER=( --tag "$tag" )
692 }
693
694 function _generateMemctrlOptions
695 {
696     local vdir="$1"
697     local badness
698
699     getFileValue badness "$vdir/badness"
700     test -n "$badness" || return 0
701
702     OPTS_VMEMCTRL=( --badness "$badness" )
703 }
704
705 function _generateSpaceOptions
706 {
707     local vdir="$1"
708     local d="$vdir"/spaces
709
710     test ! -e "$d"/pid || \
711       OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --pid )
712
713     test ! -e "$d"/net || {
714         OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --net )
715         # network context and namespace don't make much sense
716         _HAVE_CHBIND_OPTIONS=1
717         CHBIND_CMD=()
718     }
719
720     local mask
721     getFileValue mask "$d"/mask || \
722       OPTS_VSPACE=( "${OPTS_VSPACE[@]}" --mask "$mask" )
723 }
724
725 ## Usage: prepareInit <vserver-directory>
726 function prepareInit
727 {
728     pushd "$1/vdir" >/dev/null
729     case "$INITSTYLE" in
730         sysv)
731             { find var/run  ! -type d -print0; \
732               find var/lock ! -type d -print0; } | xargs -0r $_CHROOT_SH rm
733             ;;
734         plain)
735             $_CHROOT_SH rm .autofsck forcefsck 2>/dev/null || :
736             : | $_CHROOT_SH truncate fastboot  2>/dev/null || :
737             ;;
738         minit)
739             ;;
740     esac
741     "${INITCMD_PREPARE[@]}"
742     popd >/dev/null
743 }
744
745 ## Usage: prepareInit <vserver-directory>
746 function prepareStop
747 {
748     pushd "$1/vdir" >/dev/null
749     case "$INITSTYLE" in
750         (sysv)
751             export PREVLEVEL=$RUNLEVEL_START RUNLEVEL=$RUNLEVEL_STOP # required by Debian's initscripts
752             ;;
753     esac
754     "${STOPCMD_PREPARE[@]}"
755     popd >/dev/null
756 }
757
758
759 function generateOptions
760 {
761     _generateInterfaceOptions   "$1"
762     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
763     _generateNiceCommand        "$1"
764     _generateInitOptions        "$1"
765     _generateChcontextOptions   "$1"
766     _generateScheduleOptions    "$1"
767     _generatePersonalityOptions "$1"
768     _generateTagOptions         "$1"
769     _generateMemctrlOptions     "$1"
770     _generateSpaceOptions       "$1"
771
772     if test -n "$_IS_FAKEINIT"; then
773         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
774         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --initpid --disconnect )
775     fi
776 }
777
778 function addtoCPUSET
779 {
780     local vdir=$1
781     local cpuset
782     local f="$vdir"/cpuset
783     local i
784     local configured=0
785
786     test -d "$f" || return 0
787     test -e "$f"/name || return 0
788
789     read cpuset < "$f"/name
790     test -e "$f"/nocreate || {
791        test -d /dev/cpuset/"$cpuset" || mkdir /dev/cpuset/"$cpuset" || configured=1
792        for i in cpus mems cpu_exclusive mem_exclusive virtualized; do
793            if test -e "$f"/"$i"; then
794                cat "$f"/"$i" >/dev/cpuset/"$cpuset"/"$i" || {
795                    configured=1
796                    break
797                }
798            fi
799        done
800     }
801
802     echo $$ >/dev/cpuset/"$cpuset"/tasks || configured=1
803     if [ "$configured" -ne 0 ]; then
804        warning $"\
805 WARNING: Failed to create or CPUSET \"$cpuset\" does not exist! Not using it!" >&2
806        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
807        return 0
808     fi
809 }
810
811 function removeCPUSET
812 {
813     local vdir=$1
814     local cpuset
815     local f="$vdir"/cpuset
816
817     test -d "$f" || return 0
818     test -e "$f"/name || return 0
819
820     read cpuset < "$f"/name
821     test -e "$f"/nocreate || {
822        rmdir /dev/cpuset/"$cpuset" 2>/dev/null || :
823     }
824 }
825
826 function _mountVserverInternal
827 {
828     local fstab="$1"
829     local xflag=
830     
831     test -e "$fstab" || return 0
832     shift
833
834     pushd "$vdir" >/dev/null
835     # check whether / is mounted readonly or whether there is special
836     # magic regarding the mtab file; when etc/mtab can not be touched,
837     # add the '-n' flag to mount
838     test -w etc -o -w etc/mtab || xflag=-n
839     "$@" $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs no
840     popd >/dev/null
841 }
842
843 function mountRootFS
844 {
845     local cfgdir=$1
846     local vdir=$1/vdir
847     local fstab="$cfgdir"/fstab
848     local xflag=
849
850     test -e "$fstab" || return 0
851     pushd "$vdir" >/dev/null
852     # check whether / is mounted readonly or whether there is special
853     # magic regarding the mtab file; when etc/mtab can not be touched,
854     # add the '-n' flag to mount
855     test -w etc -o -w etc/mtab || xflag=-n
856     $_SECURE_MOUNT -a $xflag --chroot --fstab "$fstab" --rootfs only -n
857     popd >/dev/null
858 }
859
860 function mountVserver
861 {
862     local cfgdir=$1
863     local ns_opt=$2
864     local vdir=$1/vdir
865     local mtab_src
866
867     test -e "$cfgdir"/fstab -o \
868          -e "$cfgdir"/fstab.local -o \
869          -e "$cfgdir"/fstab.remote || return 0
870
871     findObject -r mtab_src "$cfgdir"/apps/init/mtab "$__CONFDIR"/.defaults/init/mtab "$__PKGLIBDEFAULTDIR"/mtab /dev/null
872     
873     pushd "$vdir" >/dev/null
874     $_CHROOT_SH truncate /etc/mtab <"$mtab_src"
875     popd >/dev/null
876
877     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
878
879     _mountVserverInternal "$cfgdir"/fstab
880     _mountVserverInternal "$cfgdir"/fstab.local
881     _mountVserverInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}"
882
883     isNamespaceCleanup "$cfgdir" && \
884         _namespaceCleanup "$cfgdir"
885
886     isAvoidNamespace "$cfgdir" || \
887         $_SECURE_MOUNT --rbind -n "$vdir" "/"
888 }
889
890 function _umountVserverInternal
891 {
892     local fstab="$1"
893     test -e "$fstab" || return 0
894     shift
895
896     $_TAC "$fstab" | {
897         is_ok=1
898         while read src dst tmp; do
899             test -n "$tmp" || continue
900             case x"$src" in
901                 (x\#*)  continue;;
902             esac
903
904         
905             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
906         done
907         test -n "$is_ok"
908     }
909 }
910
911 function umountVserver
912 {
913     local cfgdir=$1
914     local vdir=$1/vdir
915     local is_ok=1
916
917     isAvoidNamespace "$cfgdir"    || return 0
918     test -e "$cfgdir"/fstab -o \
919          -e "$cfgdir"/fstab.local -o \
920          -e "$cfgdir"/fstab.remote || return 0
921     test -n "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
922     
923     pushd "$vdir/" >/dev/null || return 1
924         _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
925         _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
926         _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
927     popd >/dev/null           || return 1
928
929     test -n "$is_ok"
930 }
931
932 function fsckAllFS
933 {
934     local cfgdir=$1
935     local fstab="$cfgdir"/fstab
936     local FSTAB_FILE
937     local fsck_exitcode
938
939     test -e "$fstab" || return 0
940
941     export FSTAB_FILE="$fstab"
942     $_FSCK -s -n -A -T
943     fsck_exitcode=$?
944     test "$fsck_exitcode" -eq 0 -o \
945          "$fsck_exitcode" -eq 1 || return $fsck_exitcode
946 }
947
948 ## Usage: waitForSync <vserver> <context> <vshelper-fifo-varname>
949 function initSync
950 {
951     local _is_meth=sync
952     test -n "$_NEED_VSHELPER_SYNC" && \
953         ! $_VSERVER_INFO - FEATURE vwait || _is_meth=async
954
955     vshelper.initSync "$1" "$3" "$_is_meth"
956 }
957
958 ## Usage: initWait <vserver> <context> <vwait-tmpdir-varname>
959 function initWait
960 {
961     if $_VSERVER_INFO - FEATURE vwait; then
962         local _is_tmpdir
963         _is_tmpdir=$($_MKTEMPDIR vwaitstat.XXXXXX)
964
965         (
966             $_VWAIT --timeout "$VSHELPER_SYNC_TIMEOUT" \
967                 --status-fd 3 "$2" \
968                 >>$_is_tmpdir/out 2>$_is_tmpdir/err 3>$_is_tmpdir/fifo
969             rc=$?
970
971             if test "$rc" -ne 0 -a "$rc" -ne 1; then
972                 $_VPS axf | $_EGREP -e "^[^ \t]+[ \t]+$S_CONTEXT[ \t]+" >&4
973                 killContext "$S_CONTEXT" 9
974             fi
975
976             exit $rc
977         ) 4>$_is_tmpdir/procs &
978             
979         echo "$!" >$_is_tmpdir/pid
980         eval "$3"=$_is_tmpdir
981     fi </dev/null
982 }
983
984
985 ## Usage: _waitForVWait <vserver> <fifo> <pid> <procs>
986 function _waitForVWait
987 {
988     wait "$3" || :
989
990     declare -a status
991     declare -r procs=$(cat $4)
992
993     getFileArray status "$2"
994     set -- ${status[0]}
995
996     case "$1" in
997         (ERROR)         warning $"\
998 'vwait' exited with error '$2' which indicates that vserver could not
999 be stopped properly"
1000                         ;;
1001         (FINISHED)      ;;
1002         (KILLED)        warning $"\
1003 A timeout occured while waiting for the vserver to finish and it was
1004 killed by sending a SIGKILL signal. Please investigate the reasons
1005 and/or increase the timeout in apps/vshelper/sync-timeout."
1006                         ;;
1007
1008         (TIMEOUT)       warning $"\
1009 A timeout occured while waiting for the vserver to finish and it will
1010 be killed by sending a SIGKILL signal. The following process list
1011 might be useful for finding out the reason of this behavior:
1012
1013 ----------------------------------------------------------------------
1014 ${procs:+$procs
1015 }----------------------------------------------------------------------"
1016                         ;;
1017
1018         (\?\?\?|*)      warning $"\
1019 internal error: 'vwait' exited with an unexpected status '$1'; I will
1020 try to continue but be prepared for unexpected events."
1021                     ;;
1022     esac
1023
1024     return 0
1025 }
1026
1027 ## Usage: waitForSync <vserver> [<vshelper-fifo>] [<vwait-statdir>]
1028 function waitForSync
1029 {
1030     local cfgdir=$1
1031     local fifo=$2
1032     local vwait_statdir=$3
1033     local vwait_pid=$4
1034
1035     if test -d "$vwait_statdir"; then
1036         _waitForVWait "$cfgdir" "$vwait_statdir/fifo" "$( <$vwait_statdir/pid )" "$vwait_statdir/procs"
1037     elif test -n "$_NEED_VSHELPER_SYNC"; then
1038         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
1039             warning $"\
1040 A timeout or other error occured while waiting for the synchronization
1041 signal from vserver '$VSERVER_NAME'.
1042 The vserver will be killed nevertheless..."
1043     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
1044         "${INITCMD_STOP_SYNC[@]}" || \
1045             warning $"\
1046 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
1047 will be killed nevertheless..."
1048     fi
1049
1050     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
1051         sleep 1
1052 }
1053
1054 function _sourceWrap
1055 {
1056     local vdir name flavor start i already_handled base
1057     . "$@"
1058 }
1059
1060 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
1061 function execScriptlets
1062 {
1063     declare -r vdir=$1
1064     declare -r name=$2
1065     declare -r flavor=$3
1066     local base i
1067
1068     for base in "$vdir"/scripts "$__CONFDIR"/.defaults/scripts; do
1069         local   DONT_SKIP_DEFAULTS=
1070         local   already_handled=
1071         
1072         for i in "$base/$flavor" "$base/$flavor.d"/*; do
1073             isRegularFile "$i" || continue
1074             test  -r "$i"      || continue
1075
1076             already_handled=1
1077             local start=
1078             test -x "$i" || start=_sourceWrap
1079             $start "$i" "$flavor" "$name"
1080         done
1081
1082         test -z "$already_handled" -o -n "$DONT_SKIP_DEFAULTS" || break
1083     done
1084 }
1085
1086
1087 function sanityCheck
1088 {
1089     declare -r cfgdir=$1
1090
1091     ! test -e "$cfgdir"/fstab.local ||
1092         warning $"\
1093 WARNING: 'fstab' will *not* be executed in the network context of the
1094   vserver anymore. Therefore, 'fstab.local' has the same functionality
1095   and is obsoleted. When you need the old behaviour, put the mounts
1096   into 'fstab.remote'"
1097
1098     ! test -e "$cfgdir"/hostname -a ! -L "$cfgdir"/hostname ||
1099         warning $"\
1100 WARNING: The hostname is now configured in 'uts/nodename' but not in
1101   'hostname'."
1102
1103     ! test -e "$cfgdir"/domainname -a ! -L "$cfgdir"/domainname ||
1104         warning $"\
1105 WARNING: The domainname is now configured in 'uts/domainname' but not
1106   in 'domainname'." >&2
1107
1108   
1109     local i
1110     for i in "$cfgdir"/interfaces/*/only_ip; do
1111         if test -e "$i"; then
1112             local iface
1113             iface=${i##$cfgdir/interfaces/}
1114             iface=${iface%%/only_ip}
1115             warning $"\
1116 WARNING: The 'only_ip' flag for interface '$iface' is deprecated; use
1117   'nodev' instead of"
1118         fi
1119     done
1120
1121     test ! -d "$cfgdir"/dlimits -o -L "$cfgdir/cache" || \
1122         warning $"\
1123 WARNING: There is no cachedirectory configured for this vserver;
1124   please create '$cfgdir/cache' e.g. by executing
1125
1126   ln -s ../.defaults/cachebase/$VSERVER_NAME $cfgdir/cache
1127 "
1128
1129     find "$cfgdir" -type f -exec "$_CHECK_UNIXFILE" '{}' ';'
1130
1131     vshelper.doSanityCheck
1132
1133     $_VSERVER_INFO - VERIFYCAP ||
1134         panic $"capabilities are not enabled in kernel-setup"
1135
1136     $_VSERVER_INFO - VERIFYPROC ||
1137         panic $"\
1138 /proc/uptime can not be accessed. Usually, this is caused by
1139 procfs-security. Please read the FAQ for more details
1140 http://linux-vserver.org/Proc-Security"
1141
1142     test -e "$cfgdir"/context || {
1143         TYPE=$( $_VSERVER_INFO 49152 XIDTYPE )
1144         test "$TYPE" != "static" || panic $"\
1145 The kernel does not have dynamic contexts enabled. Please configure
1146 a static one by executing
1147
1148   echo [number between 2 and 49151] > $cfgdir/context"
1149     }
1150 }
1151
1152
1153 function _setSingleDiskLimit
1154 {
1155     local vdir=$1
1156     local dlimit=$2
1157     local space_used=
1158     local space_total=
1159     local inodes_used=
1160     local inodes_total=
1161     local reserved=
1162     local directory=
1163     local ctx=
1164
1165     getFileValue ctx          "$vdir/context"
1166     getFileValue directory    "$dlimit/directory"    || return 0
1167     getFileValue space_total  "$dlimit/space_total"  || return 0
1168     getFileValue inodes_total "$dlimit/inodes_total" || return 0
1169     getFileValue reserved     "$dlimit/reserved"     || return 0
1170
1171     local cachename=$ctx$directory
1172     cachename=dlimits/${cachename//\//_}
1173
1174     test -e "$vdir/cache/$cachename" && . "$vdir/cache/$cachename"
1175     # Remove the cache so if the machine goes down unexpectedly, we won't have a stale cache
1176     $_RM -f "$vdir/cache/$cachename"
1177
1178     if test -z "$inodes_used" -o -z "$space_used"; then
1179         local tmpvdu
1180         tmpvdu=`$_VDU --xid $ctx --space --inodes --script "$directory"`
1181         inodes_used=${tmpvdu##* }
1182         space_used=${tmpvdu%% *}
1183     fi
1184
1185     $_VDLIMIT --xid $ctx \
1186         --set space_used=$space_used \
1187         --set space_total=$space_total \
1188         --set inodes_used=$inodes_used \
1189         --set inodes_total=$inodes_total \
1190         --set reserved=$reserved \
1191         "$directory"
1192 }
1193
1194
1195 function setDiskLimits
1196 {
1197     local vdir=$1
1198     local dlimit
1199
1200     # Disk Limits without a static context are useless
1201     test -e "$vdir"/context || return 0
1202
1203     for dlimit in "$vdir/dlimits/"*; do
1204         test   -d "$dlimit"          || continue
1205         test ! -e "$dlimit/disabled" || continue
1206
1207         _setSingleDiskLimit "$vdir" "$dlimit"
1208     done
1209 }
1210
1211
1212 function _saveSingleDiskLimit
1213 {
1214     local vdir=$1
1215     local dlimit=$2
1216     local ctx=
1217     local directory=
1218
1219     getFileValue ctx       "$vdir/context"
1220     getFileValue directory "$dlimit/directory" || return 0
1221
1222     local cachename=$ctx$directory
1223     cachename=${cachename//\//_}
1224
1225     # Things are getting ugly here... LFS says that /var/cache (where
1226     # cachename is usually pointing to) can vanish and applications
1227     # have to deal with it. So, we have to interprete the $vdir/cache
1228     # symlink and have to create the needed directories manually.
1229     if   test -d "$vdir/cache"; then
1230         :       # ok, exists already
1231     elif test -L "$vdir/cache"; then
1232         # it's a dangling symlink
1233         local link
1234         link=$($_READLINK "$vdir/cache")
1235         ( cd $vdir && $_MKDIR -p "$link" )
1236     else
1237         return 0
1238     fi
1239
1240     test -d "$vdir/cache"
1241     $_MKDIR -p "$vdir"/cache/dlimits
1242
1243     $_VDLIMIT --xid $ctx "$directory" | \
1244         $_GREP '_used=' > "$vdir/cache/dlimits/$cachename"
1245
1246     $_VDLIMIT --xid $ctx --remove "$directory"
1247 }
1248
1249
1250 function saveDiskLimits
1251 {
1252     local vdir=$1
1253     local dlimit
1254
1255     test -e "$vdir"/context || return 0
1256
1257     for dlimit in "$vdir/dlimits/"*; do
1258         test   -d "$dlimit"          || continue
1259         test ! -e "$dlimit/disabled" || continue
1260
1261         _saveSingleDiskLimit "$vdir" "$dlimit"
1262     done
1263 }
1264
1265 function _namespaceCleanup
1266 {
1267     local vdir="$1"
1268     local root=$($_VSERVER_INFO "$1" VDIR 1)
1269     local -a list
1270     local -a skip
1271     local i
1272     local j
1273
1274     getFileArray skip "$vdir"/namespace-cleanup-skip \
1275         "$__CONFDIR"/.defaults/namespace-cleanup-skip || :
1276
1277     # these are things that have to be accessible post-cleanup
1278     for i in "$root" "$__SBINDIR" "$__PKGLIBDIR" "$vdir" \
1279         "$__PKGSTATEDIR" "$__LOCKDIR" /usr/local /tmp "${skip[@]}"; do
1280         local real=`getPhysicalDir "$i"`
1281         test "$i" != "$real" || real=
1282         for j in "$i" "$real"; do
1283             while test -n "$j"; do
1284                 list=( "${list[@]}" "$j" )
1285                 j="${j%/*}"
1286             done
1287         done
1288     done
1289
1290     local -a list_umount
1291     while read -r dev path opts; do
1292         test -n "$path" || continue
1293         for i in "$root" /dev /proc; do
1294             test "${path#$i}" != "$path" && continue 2
1295         done
1296         for i in "${list[@]}" /; do
1297             test "$path" = "$i" && continue 2
1298         done
1299         # unmount them in reverse order so mounts further down the tree get unmounted first
1300         list_umount=( "$path" "${list_umount[@]}" )
1301     done < /proc/mounts
1302     # separate loop to avoid races while reading /proc/mounts
1303     for i in "${list_umount[@]}"; do
1304         $_UMOUNT -l -n "$i"
1305     done
1306 }
1307
1308 function handleDeviceMap
1309 {
1310     local op="$1"
1311     local xid="$2"
1312     local dir="$3"
1313     local flags device target
1314
1315     test -d "$dir" || return 0
1316     test -n "$xid" || return 0
1317
1318     for i in "$dir"/*; do
1319         test -d "$i" || continue
1320
1321         local -a vdevmap_opts=()
1322         test -e "$i/create" && vdevmap_opts=( "${vdevmap_opts[@]}" --create )
1323         test -e "$i/open"   && vdevmap_opts=( "${vdevmap_opts[@]}" --open )
1324         test -e "$i/remap"  && vdevmap_opts=( "${vdevmap_opts[@]}" --remap )
1325
1326         getFileValue flags "$i/flags" || :
1327         getFileValue device "$i/device" || :
1328         getFileValue target "$i/target" || :
1329         vdevmap_opts=(  "${vdevmap_opts[@]}" ${flags:+--flags "$flags"} \
1330                         ${device:+--device "$device"} ${target:+--target "$target"} )
1331
1332         $_VDEVMAP --xid "$xid" "$op" "${vdevmap_opts[@]}" || return $?
1333     done
1334 }