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