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