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