use 'findObject -e' instead of 'findFile' to find /dev/null also
[util-vserver.git] / util-vserver / scripts / 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 _VS_LOGFILE=
19 _VS_ERRFILE=
20
21 _VS_NEWLINE='
22 '
23 declare -r _VS_NEWLINE=${_VS_NEWLINE:0-1}
24
25 function findObject
26 {
27     local mod=$1
28     local var=$2
29     local file=
30     local i=X
31     shift 2
32     
33     for i; do
34         test "$i"        || continue
35         test ! $mod "$i" || { file=$i; break; }
36     done
37
38     test -z "$i" -o "$file" || {
39         echo "Can not find file for '$var'; aborting"
40         exit 1
41     } >&2
42
43     eval "$var=\"$file\""
44 }
45
46 function findFile
47 {
48     findObject -f "$@"
49 }
50
51 function findDir
52 {
53     findObject -d "$@"
54 }
55
56 function findAndCopy
57 {
58     local dst=$1
59     test ! -s "$dst"         || return 0
60     
61     local tmp
62     shift
63     findFile tmp "$@"
64
65     test "$tmp" -a -s "$tmp" || return 0
66     cp -af "$tmp" "$dst"
67 }
68
69 function getPhysicalDir
70 {
71     ( set -P && cd "$1" && pwd )
72 }
73
74 function logging
75 {
76     if test "$_VS_LOGFILE"; then
77         echo "$@" >>"$_VS_LOGFILE"
78     else
79         echo "$@"
80     fi
81 }
82
83 function warning
84 {
85     if test "$_VS_ERRFILE"; then
86         echo "$@" >>"$_VS_ERRFILE"
87     else
88         echo "$@" >&2
89     fi
90 }
91
92 function panic
93 {
94     if test "$_VS_ERRFILE"; then
95         echo "$@" >>"$_VS_ERRFILE"
96     else
97         echo "$@" >&2
98     fi
99
100     exit 1
101 }
102
103 function execute
104 {
105     test -z "${DEBUG_EXEC:-}"       || echo "$@"
106     test "${DEBUG_EXEC:-}" = noexec || exec "$@"
107     exit 0
108 }
109
110
111 function _pkgMountBindDir()
112 {
113     test "$1" != "$2" || return 0
114
115     mount -n --bind "$1" "$2"
116 }
117
118 function _pkgSetVarsBase
119 {
120     case "$vserver" in
121         ./*|/*)
122             if test -d "$vserver/vdir"; then
123                 BASEDIR=$vserver
124                 VDIR=$(getPhysicalDir "$vserver/vdir")
125                 
126                 PKGDIR=$BASEDIR/apps/pkgmgmt
127                 test -d "$PKGDIR" || {
128                     echo "Can not find configuration-directory for package-managment tools"
129                     exit 1
130                 } >&2
131                 findDir EXECDIR      $PKGDIR/execdir     /
132             else
133                 VDIR=$(getPhysicalDir "$vserver")
134                 PKGDIR=
135             fi
136             ;;
137         *)
138             BASEDIR=$CONFDIR/$vserver
139             test -d "$BASEDIR" || {
140                 echo "Can not find configuration-directory"
141                 exit 1
142             } >&2
143             
144             VDIR=$BASEDIR/vdir
145             test -d "$VDIR"   || VDIR=$DEFAULT_VSERVERDIR/$vserver
146             VDIR=$(getPhysicalDir "$VDIR")
147             
148             PKGDIR=$BASEDIR/apps/pkgmgmt
149             test -d "$PKGDIR" || {
150                 echo "Can not find configuration-directory for package-managment tools"
151                 exit 1
152             } >&2
153
154             findDir EXECDIR      $PKGDIR/execdir     /
155
156             ;;
157     esac
158
159     if test -z "$WORKAROUND_106057"; then
160         _rpmdb_mntpoint=/dev
161     else
162         _rpmdb_mntpoint=/.rpmdb
163     fi
164 }
165
166 function _pkgSetVarsRPM
167 {
168     if test "$PKGDIR"; then
169         findDir RPMETCDIR    $PKGDIR/rpmetc      $PKGDIR/base/rpm/etc       /etc/rpm
170         findDir RPMSTATEDIR  $PKGDIR/rpmstate    $PKGDIR/base/rpm/state
171
172         findDir RPMLIBDIR    $PKGDIR/rpmlib      /
173
174     else
175         findDir RPMETCDIR    "$VDIR"/etc/rpm     /etc/rpm
176         findDir RPMSTATEDIR  "$VDIR"/var/lib/rpm
177         RPMLIBDIR=/
178     fi
179     
180     RPMSTATEDIR=$(getPhysicalDir "$RPMSTATEDIR")
181     RPMETCDIR=$(getPhysicalDir "$RPMETCDIR")
182 }
183
184 function _pkgSetVarsApt
185 {
186     if test "$PKGDIR"; then
187         findDir APTETCDIR    $PKGDIR/aptetc      $PKGDIR/base/apt/etc       /etc/apt
188         findDir APTSTATEDIR  $PKGDIR/aptstate    $PKGDIR/base/apt/state
189         findDir APTCACHEDIR  $PKGDIR/aptcache    $PKGDIR/base/apt/cache
190         findDir APTARCHIVDIR $PKGDIR/aptarchives $PKGDIR/base/apt/archives  /var/cache/apt/archives
191     else
192         findDir APTETCDIR    "$VDIR"/etc/apt            /etc/apt
193         findDir APTSTATEDIR  "$VDIR"/var/state/apt
194         findDir APTCACHEDIR  "$VDIR"/var/cache/apt
195         findDir APTARCHIVDIR "$VDIR"/var/cache/apt/archives /var/cache/apt/archives
196     fi
197
198     findFile APT_CONFIG "$APTETCDIR"/apt.conf ""
199     test -z "$APT_CONFIG" || export APT_CONFIG
200 }
201
202 function _pkgMountBase
203 {
204     :
205 }
206
207 function _pkgMountApt
208 {
209     :
210 }
211
212 function _pkgMountRPM
213 {
214     _pkgMountBindDir "$RPMETCDIR" /etc/rpm
215     test "$RPMLIBDIR" = "/" || _pkgMountBindDir "$RPMLIBDIR" /usr/lib/rpm
216
217     "$_SECURE_MOUNT" --chroot "$VDIR" -n --secure --bind "$RPMSTATEDIR" "$_rpmdb_mntpoint"
218     test -z "$WORKAROUND_106057" || mount -n --bind "$RPMSTATEDIR" "$_rpmdb_mntpoint"
219
220     test -e "$VDIR"/proc/self/status || \
221         "$_SECURE_MOUNT" --chroot "$VDIR" -n --secure -t proc none /proc
222 }
223
224 function _pkgSetEnvBase
225 {
226     test "$EXECDIR"   = "/" || {
227         PATH=$EXECDIR:$PATH
228         LD_LIBRARY_PATH=$EXECDIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
229     }
230
231     export PATH LD_LIBRARY_PATH
232 }
233
234 function _pkgSetEnvApt
235 {
236     :
237 }
238
239 function _pkgSetEnvRPM
240 {
241     CUR_VSERVER=$vserver
242     RPM_FAKE_NAMESPACE_MOUNTS=$_rpmdb_mntpoint
243     RPM_BINARY=$_VRPM_PRELOAD
244
245     export CUR_VSERVER RPM_FAKE_NAMESPACE_MOUNTS RPM_BINARY
246 }
247
248 function pkgInit
249 {
250     local i
251     local vserver=$1
252     shift
253     
254     _pkgSetVarsBase
255     for i; do
256         case "$i" in
257             rpm)        _pkgSetVarsRPM;;
258             apt)        _pkgSetVarsApt;;
259             *)          echo "Unknown packaging flavor" >&2; exit 1;;
260         esac
261     done
262
263     _pkgMountBase
264     for i; do
265         case "$i" in
266             rpm)        _pkgMountRPM;;
267             apt)        _pkgMountApt;;
268         esac
269     done
270
271     _pkgSetEnvBase
272     for i; do
273         case "$i" in
274             rpm)        _pkgSetEnvRPM;;
275             apt)        _pkgSetEnvApt;;
276         esac
277     done
278
279     _PKG_FLAVORS="$@"
280     _PKG_VSERVER=$vserver
281 }
282
283 function isAvoidNamespace
284 {
285     local cfgdir
286
287     $_VSERVER_INFO - FEATURE namespace   || return 0
288     cfgdir=$($_VSERVER_INFO "$1" CFGDIR) || return 0
289     test ! -e "$cfgdir"/namespace        || return 1
290     test -e "$CONFDIR"/.defaults/nonamespace -o \
291          -e "$cfgdir"/nonamespace
292 }
293
294 function getAllVservers
295 {
296     local i
297     declare -a _tmp=()
298
299     for i in $CONFDIR/*; do
300         test   -d "$i"          || continue
301         test ! -e "$i"/disabled || continue
302         test   -d "$i"/vdir     || continue
303         case "$i" in
304             *.~*~) continue;;
305         esac
306
307         _tmp=( "${_tmp[@]}" "${i##$CONFDIR/}")
308     done
309
310     eval $1='( "${_tmp[@]}" )'
311 }
312
313 ## Usage: getVserverCtx <vdir> <result-varname> [<procnumber-varname> [<do-cleanup>]]
314 ## Returns: 0 iff vserver is running
315 function getVserverStatus
316 {
317     test -r "$1"/run || return 1
318
319     local _ctx
320     read _ctx <"$1"/run
321     eval "$2"=\$_ctx
322
323     test "$3"        || return 0
324     local _tmp=$($_VPS ax | $_AWK '{print $2}' | $_GREP -x "$_ctx" | $_WC -l )
325     let _tmp=_tmp+0
326     eval "$3"=\$_tmp
327
328     if test "$_tmp" = 0; then
329         local runfile=$($_READLINK "$1/run")
330         test -z "$4" || rm -f "$runfile"
331         return 1
332     fi
333
334     return 0
335 }
336
337 ## Usage: isCtxRunning <ctx>
338 function isCtxRunning
339 {
340     local _tmp=$($_VPS ax | $_AWK '{print $2}' | $_GREP -x "$1" | $_WC -l )
341     test $_tmp -gt 0
342 }
343
344 ## Usage: isVserverRunning <vdir> [<ctx-varname>]
345 function isVserverRunning
346 {
347     local ctx procnum
348
349     getVserverStatus "$1" ctx procnum 1 || return 1
350     test $procnum != 0                  || return 1
351     test -z "$2" || eval "$2"=\$ctx
352     return 0
353 }
354
355 ## Called as 'getFileValue <varname> <filename>'
356 function getFileValue
357 {
358     test -r "$2" || return 0
359     eval read "$1" <"$2"
360 }
361
362 ## Called as 'getFileArray <varname> <filename>'
363 function getFileArray
364 {
365     test -r "$2" || return 1
366     
367     local IFS=$_VS_NEWLINE
368     eval "$1"='( $(< "$2") )'
369 }
370
371 function checkComponents
372 {
373     local       i
374     local       msg=$1
375     local       x_failed=
376
377     shift
378     
379     for i; do
380         local failed=
381         case "$i" in
382             core)       test -x "$_CHBIND"           || failed=1;;
383             build)      test -x "$_VSERVER_BUILD"    || failed=1;;
384             sysv)       test -x "$INITRDDIR/vserver" || failed=1;;
385             devel)      test -d "$INCLUDEDIR/vserver.h" || failed=1;;
386             *)          echo "Unknown component '$i'" >&2
387                         return false
388                         ;;
389         esac
390
391         test -z "$failed" || {
392             echo "$msg: $i"
393             x_failed=1
394         } >&2
395     done
396
397     test -z "$x_failed"
398 }
399
400 ## Usage: isKernelAPI <ver> [<cmp-modifier>]
401 function isKernelAPI
402 {
403     local api=$($_VSERVER_INFO - APIVER) || api=0
404     test $[ $api ] -${2:-ge} $[ $1 ]
405 }
406
407 ## Usage: callInNamespace <vserver> <command> <args>*
408 function callInNamespace
409 {
410     local ctx=
411     
412     isAvoidNamespace "$1" || \
413     ctx=$( $_VSERVER_INFO "$1" CONTEXT t ) || ctx=
414
415     shift
416     if test "$ctx"; then
417         $_VNAMESPACE --enter "$ctx" -- "$@"
418     else
419         "$@"
420     fi
421 }
422
423 ## Usage: pkgmgmt.guessStyle <vserver> <resultvar>
424 function pkgmgmt.guessStyle()
425 {
426     local vdir=$($_VSERVER_INFO "$1" VDIR) || {
427         echo $"Can not determine vserver-root" >&2
428         return 1
429     }
430     local cfgdir=$($_VSERVER_INFO "$1" APPDIR pkgmgmt) || :
431
432     if test "$cfgdir" -a -e "$cfgdir"/style; then
433         read style <"$cfgdir"/style
434     elif test -e "$vdir"/etc/redhat-release -o -e "$vdir"/etc/fedora-release; then
435         style=redhat
436     elif test -e "$vdir"/etc/mandrake-release; then
437         style=mandrake
438     elif test -e "$vdir"/etc/debian_version; then
439         style=debian
440     elif test -e "$vdir"/etc/SuSE-release; then
441         style=suse
442     else
443         echo $"Can not determine packagemanagement style" >&2
444         return 1
445     fi
446
447     eval $2=\$style
448     return 0
449 }
450
451 ## Usage: pkgmgmt.isInternal <vserver>
452 ## returns true iff <vserver> is configured for internal packagemanagement
453 ## A typical application is
454 ## | is_external=
455 ## | pkgmgmt.isInternal "$vserver" || is_external=1
456 function pkgmgmt.isInternal
457 {
458     local cfgdir=$($_VSERVER_INFO "$1" APPDIR pkgmgmt) || :
459
460     test -z "$cfgdir" -o ! -d "$cfgdir" -o -e "$cfgdir"/internal
461 }
462
463 ## Usage: pkgmgmt.isAptAvailable <cfgdir> <vdir> [<is-internal>]
464 function pkgmgmt.isAptAvailable
465 {
466     local cfgdir="$1"
467     local vdir="$2"
468     local is_internal="$3"
469     
470     local have_apt i
471     if test "$is_internal"; then
472         have_apt=1
473         test -d "$cfgdir"/base/apt -o -d "$cfgdir"/aptetc || have_apt=
474     else
475         have_apt=
476         for i in /bin /usr/bin /usr/local/bin; do
477             test ! -x "$vdir$i"/apt-get || { have_apt=1; break; }
478         done
479     fi
480
481     test "$have_apt" && return 0 || return 1
482 }
483
484 function vshelper.doSanityCheck
485 {
486     local vshelper this_xid i
487     declare -a warnings=()
488     local solution_disable=
489     local solution_sysctl=
490
491     vshelper.isEnabled && vshelper.isEnabled warning || return 0
492     
493     this_xid=$($_VSERVER_INFO - XID) ||
494         panic $"Failed to determine current context; aborting..."
495
496     ## Do nothing in other xid's; the helper will be executed in xid 0 only
497     test "$this_xid" = 0 || return 0
498
499     local proc_file=/proc/sys/kernel/vshelper
500
501     if ! test -r "$proc_file"; then
502         vshelper=
503         warnings=( "${warnings[@]}"
504                    $"File '$proc_file' does not exists but is required for vshelper setup" )
505         solution_disable=1
506     else
507         vshelper=$(cat "$proc_file")
508
509         cmp -s "$vshelper" "$_VSHELPER" || {
510             warnings=( "${warnings[@]}"
511                        $"The configured vshelper '$vshelper' does not match the 'vshelper'
512   script of the util-vserver package"
513                      )
514             solution_disable=1
515             solution_sysctl=1
516         }
517     fi
518
519     test -d "$VSHELPERSTATEDIR" || {
520         warnings=( "${warnings[@]}"
521                    $"\
522 The vshelper state-directory '$VSHELPERSTATEDIR' does not exist;
523   since it is created by 'make install', this indicates a serious
524   problem with your util-vserver installation" )
525         solution_disable=1
526     }
527
528     test "${#warnings[@]}" -eq 0 || {
529         warning $"\
530 The following problem(s) were encountered while verifying vshelper
531 functionality:"
532
533         for i in "${warnings[@]}"; do
534             warning "* $i"
535         done
536
537         warning $"\
538         
539 To fix this, you can:"
540
541         test -z "$solution_disable" || warning $"\
542 * disable vshelper entirely by executing
543   | touch \"$CONFDIR/.defaults/apps/vshelper/disabled\"
544 * disable only this message by executing
545   | touch \"$CONFDIR/.defaults/apps/vshelper/warning-disabled\""
546
547         test -x "$solution_sysctl" || warning $"\
548 * configure the util-vserver vshelper script, e.g. by adding
549   | kernel.vshelper = \"$_VSHELPER\"
550   to /etc/sysctl.conf and rebooting the machine, or by executing
551   | echo \"$_VSHELPER\" >$proc_file"
552
553         warning ""
554
555         return 1
556     }
557
558     return 0
559 }
560
561 ## Usage: vshelper.isEnabled [<style>] [<vserver>]
562 function vshelper.isEnabled
563 {
564     local f=${1:+$1-}disabled
565     test ! -e "$CONFDIR"/.defaults/apps/vshelper/"$f"       || return 1
566     $_VSERVER_INFO - FEATURE vshelper                       || return 1
567     if test "$2"; then
568         local appdir=$($_VSERVER_INFO "$2" APPDIR vshelper) || return 0
569         test -z "$2" -o ! -e "$appdir/$f"                   || return 1
570     fi
571
572     return 0
573 }
574
575 function vshelper._getHandlerInternal
576 {
577     local var=$1
578     local tmp
579     shift
580     shift       ## HACK: see below the note about the 'set -u' mode
581     
582     while test "$#" -ge 2; do
583         local mod=$1
584         local obj=$2
585         shift 2
586
587         test "$mod" "$obj" || continue
588         case "$mod" in
589             (-x)
590                 eval $var=\$obj
591                 ;;
592             (-e)
593                 read tmp <"$obj"
594                 eval $var=:\$tmp
595                 ;;
596             (*) panic $"Internal error, unexpected modifier '$mod'"
597         esac
598         return 0
599     done
600     
601     return 1
602 }
603
604 ## Usage: vshelper.getHandler <result-var> <vserver> <action>
605 function vshelper.getHandler
606 {
607     local appdir=$($_VSERVER_INFO "$2" APPDIR vshelper) || appdir=
608     local script
609     declare -a search_list=( X )
610     ## HACK: when we are in 'set -u' mode, empty lists are causing errors
611
612     test -z "$appdir" || search_list=( "${search_list[@]}" -x "$appdir/$3" )
613     test -z "$appdir" || search_list=( "${search_list[@]}" -e "$appdir/action" )
614     search_list=( "${search_list[@]}" -x "$CONFDIR"/.defaults/apps/vshelper/"$3" )
615     search_list=( "${search_list[@]}" -e "$CONFDIR"/.defaults/apps/vshelper/action )
616
617     ! vshelper._getHandlerInternal "$1" "${search_list[@]}" || return 0
618     eval $1=':restart'
619 }
620
621 ## Usage: vshelper.init <vserver> [<method> <args>*]
622 function vshelper.doInit
623 {
624     vshelper.isEnabled || return 0
625     
626     local xid=$($_VSERVER_INFO "$1" CONTEXT false) || {
627         warning $"vshelper.init: can not determine xid of vserver '$vserver'"
628         return 1
629     }
630
631     local f="$VSHELPERSTATEDIR/$xid"
632     
633     set -C
634     rm -f "$f"
635     echo "$1" >"$f"
636     set +C
637     
638     if test "$2"; then
639         shift 1
640         local i
641         for i; do
642             echo "$i" 
643         done
644     else
645         echo "default"
646     fi >>"$f"
647
648     return 0
649 }
650
651 ## Usage: vshelper.doDestroy <vserver> <xid>
652 function vshelper.doDestroy
653 {
654     vshelper.isEnabled || return 0
655
656     rm -f "$VSHELPERSTATEDIR/$2"
657 }
658
659 ## Usage: vshelper.initSync <vserver> <pipe-varname>
660 function vshelper.initSync
661 {
662     local tmpdir=$(mktemp -d /tmp/vserver-stop.XXXXXX) || {
663         warning $"Failed to generate temporary directory for vshelper sync"
664         return 1
665     }
666
667     local fifo="$tmpdir"/pipe
668     mkfifo -m700 "$fifo"
669     vshelper.doInit "$VSERVER_DIR" sync "$fifo"
670     eval $2=\$fifo
671 }
672
673 ## Usage: vshelper.getSyncTimeout <vserver> <varname>
674 function vshelper.getSyncTimeout
675 {
676     local appdir=$($_VSERVER_INFO "$1" APPDIR vshelper) || appdir=
677     local file tmp
678
679     findFile file ${appdir:+"$appdir"/sync-timeout} "$CONFDIR"/.defaults/apps/vshelper/sync-timeout ''
680     test "$file" || return 1
681     read tmp <"$file"
682     eval $2=\$tmp
683 }