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