added getEnterShell() function
[util-vserver.git] / util-vserver / scripts / vserver.functions
1 #! /bin/bash
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 INTERFACE_CMDS_IDX=0
40
41 INITSTYLE=sysv
42
43 S_CONTEXT=
44
45 SILENT_OPT=
46 _NEWLINE='
47 '
48 _NEWLINE=${_NEWLINE:0-1}
49
50 : ${VSERVER_NAME:=$(basename "$VSERVER_DIR")}
51
52 if test -e "$VSERVER_DIR"/noisy; then
53     SILENT_OPT=
54 else
55     SILENT_OPT='--silent'
56 fi
57
58 ## Called as '_getFileValue <varname> <filename>'
59 function _getFileValue
60 {
61     test -r "$2" || return 0
62     eval read "$1" <"$2"
63 }
64
65 ## Called as '_getFileArray <varname> <filename>'
66 function _getFileArray
67 {
68     test -r "$2" || return 0
69     
70     local IFS=$_NEWLINE
71     eval "$1"='( $(< "$2") )'
72 }
73
74 function _generateChbindOptions
75 {
76     local vdir="$1"
77     local i
78     local bcast=
79
80     local f=$vdir/interfaces/bcast
81     _getFileValue bcast "$f"
82     
83     CHBIND_OPTS=( $SILENT_OPT ${bcast:+--bcast "$bcast"} )
84
85     for i in "${INTERFACES[@]}"; do
86         CHBIND_OPTS=( "${CHBIND_OPTS[@]}" --ip "$i" )
87     done
88 }
89
90 function _generateNiceCommand
91 {
92     local vdir=$1
93     local nice
94
95     test -r "$vdir/nice" || return 0;
96     read nice <"$vdir"/nice
97
98     NICE_CMD=( $_NICE -$nice )
99 }
100
101 function _generateCapabilityOptions
102 {
103     local vdir=$1
104     local cap
105
106     test -e "$vdir"/capabilities || return 0
107
108     CAP_OPTS=()
109     CAPCHROOT_OPTS=()
110
111     while read cap; do
112         case x"$cap" in
113             x|x#)       ;;
114             !CAP_SYSCHROOT)
115                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" )
116                 CAPCHROOT_OPTS=( "${CAPCHROOT_OPTS[@]}" --nochroot )
117                 ;;
118             *)
119                 CAP_OPTS=( "${CAP_OPTS[@]}" --cap "$cap" );;
120         esac
121     done <"$vdir"/capabilities
122 }
123
124 function getEnterShell
125 {
126     local vdir=$1
127
128     ENTER_SHELL=()
129     
130     _getFileValue ENTER_SHELL    "$vdir"/shell
131     
132     test "$ENTER_SHELL" || {
133         local i
134         for i in "/bin/bash -login" "/bin/sh -l" /bin/csh; do
135             set -- $i
136             test -x "$vdir/vdir/$1" || continue
137             ENTER_SHELL=( "$@" )
138             break
139         done
140     }
141 }
142
143 function _generateInitOptions
144 {
145     local vdir=$1
146     local cfgdir=$vdir/apps/init
147     local runlevel_start=3
148     local runlevel_stop=6
149     local i f
150
151     INITCMD_START=()
152     INITCMD_STOP=()
153     INITCMD_START_SYNC=()
154     INITCMD_STOP_SYNC=()
155     INITCMD_PREPARE=()
156
157     INITKILL_SEQ=( 15 5 9 )
158     CHCONTEXT_INIT_OPTS=()
159
160
161     _getFileValue INITSTYLE      "$cfgdir"/style
162     _getFileValue runlevel_start "$cfgdir"/runlevel
163     _getFileValue runlevel_start "$cfgdir"/runlevel.start
164     _getFileValue runlevel_stop  "$cfgdir"/runlevel.stop
165     _getFileArray INITKILL_SEQ   "$cfgdir"/killseq
166
167     case x"$INITSTYLE" in
168         xsysv)
169             for i in /etc/init.d/rc /etc/rc.d/rc; do
170                 test -x "$vdir/vdir/$i" || continue
171                 INITCMD_START=( "$i" "$runlevel_start" )
172                 INITCMD_STOP=(  "$i" "$runlevel_stop"  )
173             done
174             INITCMD_PREPARE=( $_FAKE_RUNLEVEL "$runlevel_start" /var/run/utmp )
175             ;;
176             
177         xplain)
178             INITCMD_START=( /sbin/init )
179             INITCMD_STOP=(  /sbin/init )
180             CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
181             test -z "$runlevel_start" || INITCMD_START=( "${INITCMD_START[@]}" "$runlevel_start" )
182             test -z "$runlevel_stop"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$runlevel_stop"  )
183             ;;
184             
185         xminit)
186             INITCMD_START=( /sbin/minit-start )
187             INITCMD_STOP=(  /sbin/minit-stop  )
188             CHCONTEXT_INIT_OPTS=( --disconnect --flag fakeinit )
189             INITCMD_START_SYNC=( "$_INITSYNC_MINIT_START" "$vdir" )
190             INITCMD_STOP_SYNC=(  "$_INITSYNC_MINIT_STOP"  "$vdir" )
191             test -z "$runlevel_start" || INITCMD_START=( "${INITCMD_START[@]}" "$runlevel_start" )
192             test -z "$runlevel_stop"  || INITCMD_STOP=(  "${INITCMD_STOP[@]}"  "$runlevel_stop"  )
193             ;;
194
195         x)  ;;
196         *)  echo "Unknown init-style '$INITSTYLE'; aborting" >&2;
197             exit 1;;
198     esac
199
200     _getFileArray INITCMD_START      "$cfgdir"/cmd.start
201     _getFileArray INITCMD_STOP       "$cfgdir"/cmd.stop
202     _getFileArray INITCMD_START_SYNC "$cfgdir"/cmd.start-sync
203     _getFileArray INITCMD_STOP_SYNC  "$cfgdir"/cmd.stop-sync
204     _getFileArray INITCMD_PREPARE    "$cfgdir"/cmd.prepare
205
206     
207     test -e "$cfgdir"/sync -o "$OPTION_FORCE_SYNC" || {
208         INITCMD_START_SYNC=()
209         INITCMD_STOP_SYNC=()
210     }
211 }
212
213 function _generateFlagOptions
214 {
215     local vdir=$1
216
217     CHCONTEXT_FLAG_OPTS=()
218
219     test -e "$vdir"/flags || return 0
220
221     while read flag; do
222         case x"$flag" in
223             x|x#)               ;;
224             dummy)              ;;
225             fakeinit)
226                 CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}" 
227                                       --disconnect --flag --fakeinit )
228                 ;;
229             *)
230                 CHCONTEXT_FLAG_OPTS=( "${CHCONTEXT_FLAG_OPTS[@]}"
231                                       --flag "$flag" )
232                 ;;
233         esac
234     done <"$vdir"/flags
235 }
236
237 function _generateChcontextOptions
238 {
239     local vdir=$1
240     local ctx hostname domainname
241     local cap_opts
242     local flag
243
244     {
245         read ctx        <"$vdir"/context
246         read hostname   <"$vdir"/hostname
247         read domainname <"$vdir"/domainname
248     } 2>/dev/null
249
250     test -z "$S_CONTEXT" || ctx=$S_CONTEXT
251
252     _generateCapabilityOptions "$vdir"
253     _generateFlagOptions       "$vdir"
254
255     CHCONTEXT_OPTS=( $SILENT_OPT \
256                      "${CHCONTEXT_FLAG_OPTS[@]}" \
257                      "${CHCONTEXT_INIT_OPTS[@]}" \
258                      "${CAP_OPTS[@]}" \
259                      --secure
260                      ${ctx:+--ctx "$ctx"} \
261                      ${hostname:+--hostname "$hostname"} \
262                      ${domainname:+--domainname "$domainname"} )
263 }
264
265 function _getInterfaceValue
266 {
267     local val=$1
268     local dflt=$2
269     shift 2
270     
271     local i
272     local tmp
273
274     for i; do
275         read tmp  <"$i/$val" && break || :
276     done 2>/dev/null
277
278     : ${tmp:=$dflt}
279     eval $val=\$tmp
280 }
281
282 ## Usage: _transformMask2Prefix <result-varname> <prefix> <mask>
283 function _transformMask2Prefix
284 {
285     local tmp=$2
286     
287     test "$tmp" || {
288         $_MASK2PREFIX "$3" || tmp=$?
289     }
290
291     eval $1=\$tmp
292     return 0
293 }
294
295 function _addInterfaceCmd
296 {
297     eval INTERFACE_CMDS_${INTERFACE_CMDS_IDX}='( "$@" )'
298     let ++INTERFACE_CMDS_IDX
299 }
300
301 ## Usage: _processSingleInterface <interface-directory>
302 function _processSingleInterface
303 {
304     local iface=$1
305
306     local ip
307     local dev
308     local prefix
309     local mask
310     local bcast
311     local name
312     local scope
313
314     _getInterfaceValue ip     '' "$iface"
315     _getInterfaceValue dev    '' "$iface" "$iface/.."
316     _getInterfaceValue prefix '' "$iface" "$iface/.."
317     _getInterfaceValue mask   '' "$iface" "$iface/.."
318     _getInterfaceValue bcast  '' "$iface" "$iface/.."
319     _getInterfaceValue name   '' "$iface"
320     _getInterfaceValue scope  '' "$iface" "$iface/.."
321
322     test "$ip" || { echo $"Can not read ip for '$iface'"; return 1; }
323     
324     test ! -e "$iface"/only_ip   || unset dev
325     
326     if test "$dev"; then
327         _transformMask2Prefix prefix "$prefix" "$mask"
328
329         case "$dev" in
330             *.*)
331                 test -d /proc/net/vlan || {
332                     echo -e $"VLAN device-name used, but vlan subsystem not enabled.\nTry to execute 'modprobe 8021q' before starting the vservers"
333                     return 1
334                 }
335                 test -f /proc/net/vlan || {
336                     _addInterfaceCmd VCONFIG "${dev/./ }"
337                     _addInterfaceCmd IP_ADDR 127.0.0.1/8 broadcast 127.255.255.255 dev "$dev"
338                     _addInterfaceCmd IP_LINK "$dev"
339                 }
340                 ;;
341         esac
342
343         INTERFACES=( "${INTERFACES[@]}" "$ip${prefix:+/$prefix}" )
344         _addInterfaceCmd IP_ADDR  "$ip${prefix:+/$prefix}" broadcast ${bcast:-+} ${name:+label "$dev:$name"} dev "$dev"
345         #_addInterfaceCmd IP_ROUTE "$ip${prefix:+/$prefix}" dev "$dev"
346         _addInterfaceCmd IP_LINK  "$dev"
347     fi
348 }
349
350 ## Usage: _generateInterfaceOptions <vserver-directory>
351 function _generateInterfaceOptions
352 {
353     local iface
354
355     for iface in "$1/interfaces/"*; do
356         test   -d "$iface"          || continue
357         test ! -e "$iface"/disabled || continue
358     
359         _processSingleInterface "$iface"
360     done
361 }
362
363 function enableInterfaces
364 {
365     local i=0
366     declare -a var
367
368     while test $i -lt $INTERFACE_CMDS_IDX; do
369         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
370         local type=${var[0]}
371         unset var[0]
372         
373         case "$type" in
374             VCONFIG)    $_VCONFIG  add "${var[@]}";;
375             IP_ADDR)    $_IP addr  add "${var[@]}";;
376             IP_LINK)    $_IP link  set "${var[@]}" up;;
377             IP_ROUTE)   $_IP route add "${var[@]}";;
378             *)          echo "Unknown interface-command type '$type'" >&2; false;;
379         esac
380
381         let ++i
382     done
383 }
384
385 function disableInterfaces
386 {
387     local i=$INTERFACE_CMDS_IDX
388     declare -a var
389
390     while test $i -gt 0; do
391         let --i
392
393         eval var='( "${INTERFACE_CMDS_'$i'[@]}" )'
394         local type=${var[0]}
395         unset var[0]
396         
397         case "$type" in
398             VCONFIG)    $_VCONFIG  rem "${var[@]}";;
399             IP_ADDR)    $_IP addr  del "${var[@]}";;
400             IP_LINK)    ;; ## Ignore the link-down command for now
401             IP_ROUTE)   $_IP route del "${var[@]}";;
402             *)          echo "Unknown interface-command type '$type'" >&2; false;;
403         esac
404     done
405 }
406
407 ## Usage: prepareInit <vserver-directory>
408 function prepareInit
409 {
410     pushd "$1/vdir" >/dev/null
411     case "$INITSTYLE" in
412         sysv)
413             { find var/run  ! -type d -print0; \
414               find var/lock ! -type d -print0; } | xargs -0r $_CHROOT_RM
415             ;;
416         minit)
417             ;;
418     esac
419     "${INITCMD_PREPARE[@]}"
420     popd >/dev/null
421 }
422
423 function generateOptions
424 {
425     _generateInterfaceOptions  "$1"
426     _generateChbindOptions     "$1"
427     _generateNiceCommand       "$1"
428     _generateInitOptions       "$1"
429     _generateChcontextOptions  "$1"
430 }
431
432 function mountVserver
433 {
434     local cfgdir=$1
435     local vdir=$1/vdir
436
437     test -e "$cfgdir"/fstab || return 0
438     $_CHBIND "${CHBIND_OPTS[@]}" \
439         $_SECURE_MOUNT -a --chroot "$vdir" --fstab "$cfgdir"/fstab
440 }
441
442 function umountVserver
443 {
444     local cfgdir=$1
445     local vdir=$1/vdir
446
447     is_ok=1
448
449     test -e "$cfgdir"/fstab || return 0
450     
451     pushd "$vdir/" >/dev/null
452     tac "$cfgdir"/fstab | while read src dst tmp; do
453         test "$tmp" || continue
454         case x"$src" in
455             x#*)        continue;;
456         esac
457
458         $_CHBIND "${CHBIND_OPTS[@]}" \
459             $_SECURE_UMOUNT "$dst" || is_ok=
460     done
461     popd >/dev/null
462
463     test "$is_ok"
464 }
465
466 function _sourceWrap
467 {
468     local vdir name flavor start i
469     . "$@"
470 }
471
472 ## Usage: execScriptlets <vserver-cfgdir> <vserver-name> <script-flavor>
473 function execScriptlets
474 {
475     local vdir=$1
476     local name=$2
477     local flavor=$3
478     local start=
479     local i
480
481     for i in "$vdir/scripts/$flavor" "$vdir/scripts/$flavor.d"/*; do
482         test -f "$i" -a -r "$i" || continue
483         test -x "$i" || start=_sourceWrap
484         $start "$i" "$flavor" "$name"
485     done
486 }