adapted code to new getFileArray() behavior
[util-vserver.git] / util-vserver / 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_START=()
32 declare -a INITCMD_START_SYNC=()
33 declare -a INITCMD_STOP=()
34 declare -a INITCMD_STOP_SYNC=()
35 declare -a INITCMD_PREPARE=()
36 declare -a INITKILL_SEQ=()
37 declare -a ENTER_SHELL=()
38
39 declare -a OPTS_VCONTEXT_CREATE=()
40 declare -a OPTS_VCONTEXT_MIGRATE=()
41 declare -a OPTS_VATTRIBUTE=()
42 declare -a OPTS_VSCHED=()
43
44 declare -a VSERVER_EXTRA_CMDS=()
45
46 VSHELPER_SYNC_TIMEOUT=30
47 USE_VNAMESPACE=
48 INTERFACE_CMDS_IDX=0
49 _HAVE_INTERFACE_OPTIONS=
50 _HAVE_CHBIND_OPTIONS=
51 _NEED_VSHELPER_SYNC=
52 _IS_FAKEINIT=
53
54 INITSTYLE=sysv
55
56 S_CONTEXT=
57
58 SILENT_OPT=
59
60 : ${VSERVER_NAME:=$(basename "$VSERVER_DIR")}
61
62 if test -e "$VSERVER_DIR"/noisy -o "$OPTION_VERBOSE"; then
63     SILENT_OPT=
64 else
65     SILENT_OPT='--silent'
66 fi
67
68 function _generateChbindOptions
69 {
70     local vdir="$1"
71     local i
72     local bcast=
73
74     test "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$vdir"
75
76     local f=$vdir/interfaces/bcast
77     getFileValue bcast "$f"
78     
79     CHBIND_OPTS=( $SILENT_OPT ${bcast:+--bcast "$bcast"} )
80
81     for i in "${INTERFACES[@]}"; do
82         CHBIND_OPTS=( "${CHBIND_OPTS[@]}" --ip "$i" )
83     done
84
85     _HAVE_CHBIND_OPTIONS=1
86 }
87
88 function _generateNiceCommand
89 {
90     local vdir=$1
91     local nice
92
93     test -r "$vdir/nice" || return 0;
94     read nice <"$vdir"/nice
95
96     NICE_CMD=( $_NICE -$nice )
97 }
98
99 function _generateCCapabilityOptions
100 {
101     local vdir=$1
102     local cap
103     local f="$vdir"/ccapabilities
104     
105     test -e "$f" || return 0
106     while read cap; do
107         case x"$cap" in
108             (x|x\#)     ;;
109             (*)         OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --ccap "$cap" );;
110         esac
111     done <"$f"
112 }
113
114 function _generateBCapabilityOptions
115 {
116     local vdir=$1
117     local cap
118     local f="$vdir"/bcapabilities
119     
120     test -e "$f" || return 0
121     while read cap; do
122         case x"$cap" in
123             (x|x\#)     ;;
124             (*)         OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --bcap "$cap" );;
125         esac
126     done <"$f"
127 }
128
129 function _generateCapabilityOptions
130 {
131     local vdir=$1
132     local cap
133
134     _generateBCapabilityOptions "$vdir"
135     _generateCCapabilityOptions "$vdir"
136     
137     test -e "$vdir"/capabilities || return 0
138
139     CAP_OPTS=()
140     CAPCHROOT_OPTS=()
141
142     while read cap; do
143         case x"$cap" in
144             (x|x\#)     ;;
145             (!CAP_SYSCHROOT)
146                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
147                 CAPCHROOT_OPTS=( "${CAPCHROOT_OPTS[@]}" --nochroot )
148                 ;;
149             (*)
150                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
151                 ;;
152         esac
153     done <"$vdir"/capabilities
154 }
155
156 function getEnterShell
157 {
158     local vdir=$1
159
160     ENTER_SHELL=()
161     
162     getFileValue ENTER_SHELL     "$vdir"/shell
163     
164     test "$ENTER_SHELL" || {
165         local i
166         for i in "/bin/bash -login" "/bin/sh -l" /bin/csh; do
167             set -- $i
168             test -x "$vdir/vdir/$1" || continue
169             ENTER_SHELL=( "$@" )
170             break
171         done
172     }
173 }
174
175 ## Usage: sendKillSequence <ctx> <signal> [<wait> <signal>]*
176 function sendKillSequence
177 {
178     local ctx=$1
179     local wait=
180     shift
181
182     while isCtxRunning "$ctx"; do
183         test -z "$wait" || sleep "$wait"
184         
185         $_VKILL -c "$ctx" ${1:+-s "$1"}
186         test "$2" || break
187         wait="$2"
188         shift 2
189     done
190 }
191
192 function _generateInitOptions
193 {
194     local vdir=$1
195     local cfgdir=$vdir/apps/init
196     local runlevel_start
197     local runlevel_stop
198     local i f
199
200     INITCMD_START=()
201     INITCMD_STOP=()
202     INITCMD_START_SYNC=()
203     INITCMD_STOP_SYNC=()
204     INITCMD_PREPARE=()
205
206     INITKILL_SEQ=( 15 5 9 )
207     CHCONTEXT_INIT_OPTS=()
208
209
210     getFileValue INITSTYLE      "$cfgdir"/style
211     getFileValue runlevel_start "$cfgdir"/runlevel
212     getFileValue runlevel_start "$cfgdir"/runlevel.start
213     getFileValue runlevel_stop  "$cfgdir"/runlevel.stop
214     getFileArray INITKILL_SEQ   "$cfgdir"/killseq || :
215
216     case x"$INITSTYLE" in
217         (xsysv)
218             test "$runlevel_start" || runlevel_start=3
219             test "$runlevel_stop"  || runlevel_stop=6
220
221             for i in /etc/init.d/rc /etc/rc.d/rc; do
222                 test -x "$vdir/vdir/$i" || continue
223                 INITCMD_START=( "$i" "$runlevel_start" )
224                 INITCMD_STOP=(  "$i" "$runlevel_stop"  )
225             done
226             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$runlevel_start" /var/run/utmp )
227             ;;
228             
229         (xplain)
230             INITCMD_START=( /sbin/init )
231             INITCMD_STOP=(  /sbin/init )
232             _IS_FAKEINIT=1
233             _NEED_VSHELPER_SYNC=1
234             test -z "$runlevel_start" || INITCMD_START=( "${INITCMD_START[@]}" "$runlevel_start" )
235             test -z "$runlevel_stop"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$runlevel_stop"  )
236             ;;
237             
238         (xminit)
239             INITCMD_START=( /sbin/minit-start )
240             INITCMD_STOP=(  /sbin/minit-stop  )
241             _IS_FAKEINIT=1
242             INITCMD_START_SYNC=( "$_INITSYNC_MINIT_START" "$vdir" )
243             _NEED_VSHELPER_SYNC=1
244             test -z "$runlevel_start" || INITCMD_START=( "${INITCMD_START[@]}" "$runlevel_start" )
245             test -z "$runlevel_stop"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$runlevel_stop"  )
246             ;;
247
248         (xgentoo)
249             INITCMD_START=( /sbin/rc default  )
250             INITCMD_STOP=(  /sbin/rc shutdown )
251             ;;
252
253         (x) ;;
254         (*) echo "Unknown init-style '$INITSTYLE'; aborting" >&2;
255             exit 1;;
256     esac
257
258     getFileArray INITCMD_START      "$cfgdir"/cmd.start      || :
259     getFileArray INITCMD_STOP       "$cfgdir"/cmd.stop       || :
260     getFileArray INITCMD_START_SYNC "$cfgdir"/cmd.start-sync || :
261     getFileArray INITCMD_STOP_SYNC  "$cfgdir"/cmd.stop-sync  || :
262     getFileArray INITCMD_PREPARE    "$cfgdir"/cmd.prepare    || :
263
264     
265     test "$OPTION_FORCE_SYNC" -o -e "$cfgdir"/sync || {
266         INITCMD_START_SYNC=()
267         INITCMD_STOP_SYNC=()
268         _NEED_VSHELPER_SYNC=
269     }
270
271     if vshelper.isEnabled; then
272         vshelper.getSyncTimeout "$vdir" VSHELPER_SYNC_TIMEOUT || :
273     else
274         _NEED_VSHELPER_SYNC=
275     fi
276 }
277
278 function _generateFlagOptions
279 {
280     local vdir=$1
281
282     CHCONTEXT_FLAG_OPTS=()
283
284     test ! -e "$vdir"/flags || \
285     while read flag; do
286         case x"$flag" in
287             (x|x\#)             ;;
288             (xnamespace)        ;;
289             (xfakeinit)
290                 _IS_FAKEINIT=1
291                 ;;
292             (*)
293                 OPTS_VATTRIBUTE=( "${OPTS_VATTRIBUTE[@]}" --flag "$flag" )
294                 CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}"
295                                       --flag "$flag" )
296                 ;;
297         esac
298     done <"$vdir"/flags
299
300     isAvoidNamespace "$vdir" || {
301         USE_VNAMESPACE=1
302         CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}" --flag namespace )
303     }
304 }
305
306 function _generateChcontextOptions
307 {
308     local vdir=$1
309     local ctx hostname domainname
310     local cap_opts
311     local flag
312
313     {
314         read ctx        <"$vdir"/context        || :
315         ## LEGACY ALERT
316         read hostname   <"$vdir"/uts/nodename   || read hostname   <"$vdir"/hostname   || :
317         read domainname <"$vdir"/uts/domainname || read domainname <"$vdir"/domainname || :
318     } 2>/dev/null
319
320     ## LEGACY ALERT
321     test \( ! -e "$vdir"/hostname   -o -e "$vdir"/uts/nodename   \) -a \
322          \( ! -e "$vdir"/domainname -o -e "$vdir"/uts/domainname \) || {
323         echo $"\
324 The hostname is now configured in 'uts/nodename' but not in 'hostname' anymore;
325 the old way is not supported when using 2.6 kernel patches so it is strongly
326 recommended to move these files.
327
328 The same applies to 'domainname' vs. 'uts/domainname'." >&2
329     }
330
331     test -z "$S_CONTEXT" || ctx=$S_CONTEXT
332
333     _generateCapabilityOptions "$vdir"
334     _generateFlagOptions       "$vdir"
335
336     CHCONTEXT_OPTS=( $SILENT_OPT \
337                      "${CHCONTEXT_FLAG_OPTS[@]}" \
338                      "${CAP_OPTS[@]}" \
339                      --secure
340                      ${ctx:+--ctx "$ctx"} \
341                      ${hostname:+--hostname "$hostname"} \
342                      ${domainname:+--domainname "$domainname"} )
343
344     OPTS_VCONTEXT_CREATE=( $SILENT_OPT \
345                            ${ctx:+--xid "$ctx"} )
346     ## put '--secure' at front so that it can be overridden
347     OPTS_VATTRIBUTE=( --secure "${OPTS_VATTRIBUTE[@]}" )
348 }
349
350 function _generateScheduleOptions
351 {
352     local vdir=$1
353     local f="$vdir"/schedule
354     test -e "$f" || return 0
355
356     local fill_rate interval tokens tokens_min tokens_max cpu_mask
357     {
358         {
359             read fill_rate   && \
360             read interval    && \
361             read tokens      && \
362             read tokens_min  && \
363             read tokens_max  && \
364             read cpu_mask    || cpu_mask=
365         } <"$f"
366     } 2>/dev/null
367
368     test "$cpu_mask" || {
369         echo $"Bad content in '$f'; aborting..." >&2
370         false
371     }
372
373     OPTS_VSCHED=( --fill-rate  "$fill_rate"  --interval "$interval" \
374                   --tokens     "$tokens"     --tokens_min "$tokens_min" \
375                   --tokens_max "$tokens_max" --cpu_mask "$cpu_mask" )
376 }
377
378 function _getInterfaceValue
379 {
380     local val=$1
381     local dflt=$2
382     shift 2
383     
384     local i
385     local tmp
386
387     for i; do
388         read tmp  <"$i/$val" && break || :
389     done 2>/dev/null
390
391     : ${tmp:=$dflt}
392     eval $val=\$tmp
393 }
394
395 ## Usage: _transformMask2Prefix <result-varname> <prefix> <mask>
396 function _transformMask2Prefix
397 {
398     local tmp=$2
399     
400     test "$tmp" || {
401         $_MASK2PREFIX "$3" || tmp=$?
402     }
403
404     eval $1=\$tmp
405     return 0
406 }
407
408 function _addInterfaceCmd
409 {
410     eval INTERFACE_CMDS_${INTERFACE_CMDS_IDX}='( "$@" )'
411     let ++INTERFACE_CMDS_IDX
412 }
413
414 ## Usage: _processSingleInterface <interface-directory>
415 function _processSingleInterface
416 {
417     local iface=$1
418
419     local ip
420     local dev
421     local prefix
422     local mask
423     local bcast
424     local name
425     local scope
426
427     _getInterfaceValue ip     '' "$iface"
428     _getInterfaceValue dev    '' "$iface" "$iface/.."
429     _getInterfaceValue prefix '' "$iface" "$iface/.."
430     _getInterfaceValue mask   '' "$iface" "$iface/.."
431     _getInterfaceValue bcast  '' "$iface" "$iface/.."
432     _getInterfaceValue name   '' "$iface"
433     _getInterfaceValue scope  '' "$iface" "$iface/.."
434
435     test "$ip" || { echo $"Can not read ip for '$iface'"  >&2; return 1; }
436     
437     test ! -e "$iface"/only_ip || {
438         echo $"The 'only_ip' flag for interface '$iface' is deprecated; use 'nodev' instead of" >&2
439         unset dev
440     }
441     test ! -e "$iface"/nodev   || unset dev
442     
443     if test "$dev"; then
444         _transformMask2Prefix prefix "$prefix" "$mask"
445
446         case "$dev" in
447             (*.*)
448                 test -d /proc/net/vlan || {
449                     echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"  >&2
450                     return 1
451                 }
452                 test -f /proc/net/vlan || {
453                     _addInterfaceCmd VCONFIG "${dev/./ }"
454                     _addInterfaceCmd IP_ADDR 127.0.0.1/8 broadcast 127.255.255.255 dev "$dev"
455                     _addInterfaceCmd IP_LINK "$dev"
456                 }
457                 ;;
458         esac
459
460         INTERFACES=( "${INTERFACES[@]}" "$ip${prefix:+/$prefix}" )
461         _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" broadcast ${bcast:-+} ${name:+label "$dev:$name"} dev "$dev"
462         #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
463         _addInterfaceCmd IP_LINK  "$dev"
464     fi
465 }
466
467 ## Usage: _generateInterfaceOptions <vserver-directory>
468 function _generateInterfaceOptions
469 {
470     local iface
471
472     for iface in "$1/interfaces/"*; do
473         test   -d "$iface"          || continue
474         test ! -e "$iface"/disabled || continue
475     
476         _processSingleInterface "$iface"
477     done
478     _HAVE_INTERFACE_OPTIONS=1
479 }
480
481 function enableInterfaces
482 {
483     local i=0
484     declare -a var
485
486     while test $i -lt $INTERFACE_CMDS_IDX; do
487         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
488         local type=${var[0]}
489         unset var[0]
490         
491         case "$type" in
492             VCONFIG)    $_VCONFIG  add "${var[@]}";;
493             IP_ADDR)    $_IP addr  add "${var[@]}";;
494             IP_LINK)    $_IP link  set "${var[@]}" up;;
495             IP_ROUTE)   $_IP route add "${var[@]}";;
496             *)          echo "Unknown interface-command type '$type'" >&2; false;;
497         esac
498
499         let ++i
500     done
501 }
502
503 function disableInterfaces
504 {
505     test "$_HAVE_INTERFACE_OPTIONS" || _generateInterfaceOptions "$1"
506
507     local i=$INTERFACE_CMDS_IDX
508     declare -a var
509
510     while test $i -gt 0; do
511         let --i || :
512
513         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
514         local type=${var[0]}
515         unset var[0]
516         
517         case "$type" in
518             VCONFIG)    $_VCONFIG  rem "${var[@]}";;
519             IP_ADDR)    $_IP addr  del "${var[@]}";;
520             IP_LINK)    ;; ## Ignore the link-down command for now
521             IP_ROUTE)   $_IP route del "${var[@]}";;
522             *)          echo "Unknown interface-command type '$type'" >&2; false;;
523         esac
524     done
525 }
526
527 ## Usage: prepareInit <vserver-directory>
528 function prepareInit
529 {
530     pushd "$1/vdir" >/dev/null
531     case "$INITSTYLE" in
532         sysv)
533             { find var/run  ! -type d -print0; \
534               find var/lock ! -type d -print0; } | xargs -0r $_CHROOT_RM
535             ;;
536         minit)
537             ;;
538     esac
539     "${INITCMD_PREPARE[@]}"
540     popd >/dev/null
541 }
542
543 function generateOptions
544 {
545     _generateInterfaceOptions  "$1"
546     test "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1" 
547     _generateNiceCommand       "$1"
548     _generateInitOptions       "$1"
549     _generateChcontextOptions  "$1"
550     _generateScheduleOptions   "$1"
551
552     if test "$_IS_FAKEINIT"; then
553         CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
554         OPTS_VCONTEXT_MIGRATE=( "${OPTS_VCONTEXT_MIGRATE[@]}" --fakeinit --disconnect )
555     fi
556 }
557
558 function _mountVserverInternal
559 {
560     local fstab="$1"
561     test -e "$fstab" || return 0
562     shift
563     
564     "$@" $_SECURE_MOUNT -a --chroot "$vdir" --fstab "$fstab"
565 }
566
567 function mountVserver
568 {
569     local cfgdir=$1
570     local ns_opt=$2
571     local vdir=$1/vdir
572     local mtab_src
573
574     test -e "$cfgdir"/fstab -o \
575          -e "$cfgdir"/fstab.local || return 0
576
577     findObject -r mtab_src "$vdir"/apps/init/mtab "$CONFDIR"/.defaults/init/mtab "$PKGLIBDEFAULTDIR"/mtab /dev/null
578     
579     pushd "$vdir" >/dev/null
580     $_CHROOT_CAT /etc/mtab <"$mtab_src"
581     popd >/dev/null
582
583     test "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$cfgdir"
584
585     test -z "$NAMESPACE_CLEANUP" || isAvoidNamespace "$cfgdir" || \
586         $_VNAMESPACE --cleanup
587
588     _mountVserverInternal "$cfgdir"/fstab       $_CHBIND "${CHBIND_OPTS[@]}"
589     _mountVserverInternal "$cfgdir"/fstab.local
590
591     isAvoidNamespace "$cfgdir" || \
592         $_SECURE_MOUNT --rbind -n "$vdir" "/"
593 }
594
595 function _umountVserverInternal
596 {
597     local fstab="$1"
598     test -e "$fstab" || return 0
599     shift
600
601     tac "$fstab" | {
602         is_ok=1
603         while read src dst tmp; do
604             test "$tmp" || continue
605             case x"$src" in
606                 (x\#*)  continue;;
607             esac
608
609         
610             "$@" $_EXEC_CD "$dst" $_UMOUNT -lfn . || is_ok=
611         done
612         test "$is_ok"
613     }
614 }
615
616 function umountVserver
617 {
618     local cfgdir=$1
619     local vdir=$1/vdir
620     local is_ok=1
621
622     isAvoidNamespace "$cfgdir"    || return 0
623     test -e "$cfgdir"/fstab -o \
624          -e "$cfgdir"/fstab.local || return 0
625     test "$_HAVE_CHBIND_OPTIONS"  || _generateChbindOptions "$cfgdir"
626     
627     pushd "$vdir/" >/dev/null || return 1
628         _umountVserverInternal  "$cfgdir"/fstab.local                              || is_ok=
629         _umountVserverInternal  "$cfgdir"/fstab       $_CHBIND "${CHBIND_OPTS[@]}" || is_ok=
630     popd >/dev/null           || return 1
631
632     test "$is_ok"
633 }
634
635 ## Usage: waitForSync <vserver> <vshelper-fifo-varname>
636 function initSync
637 {
638     test -z "$_NEED_VSHELPER_SYNC" || vshelper.initSync "$1" "$2"
639 }
640
641 ## Usage: waitForSync <vserver> [<vshelper-fifo>]
642 function waitForSync
643 {
644     local cfgdir=$1
645     local fifo=$2
646     
647     if test "$_NEED_VSHELPER_SYNC"; then
648         $_VSHELPER_SYNC "$fifo" "$VSHELPER_SYNC_TIMEOUT" || \
649             warning $"\
650 A timeout or other error occured while waiting for the synchronization
651 signal from vserver '$VSERVER_NAME'.
652 The vserver will be killed nevertheless..."
653     elif test "${#INITCMD_STOP_SYNC[@]}" -ne 0; then
654         "${INITCMD_STOP_SYNC[@]}" || \
655             warning $"\
656 Stop-synchronization for vserver '$VSERVER_NAME' failed. The vserver
657 will be killed nevertheless..."
658     fi
659
660     test -z "$OPTION_FORCE_SYNC" -a ! -e "$cfgdir"/sync ||
661         sleep 1
662 }
663
664 function _sourceWrap
665 {
666     local vdir name flavor start i
667     . "$@"
668 }
669
670 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
671 function execScriptlets
672 {
673     local vdir=$1
674     local name=$2
675     local flavor=$3
676     local start=
677     local i
678
679     for i in "$vdir/scripts/$flavor" "$vdir/scripts/$flavor.d"/*; do
680         test -f "$i" -a -r "$i" || continue
681         test -x "$i" || start=_sourceWrap
682         $start "$i" "$flavor" "$name"
683     done
684 }