gentoo: use /var/run for new /run compatibility
[util-vserver.git] / scripts / pkgmgmt
1 #!/bin/bash
2 # $Id$
3
4 # Copyright (C) 2004,2005 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
5 #  
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
9 #  
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #  
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 : ${UTIL_VSERVER_VARS:=/usr/lib/util-vserver/util-vserver-vars}
20 test -e "$UTIL_VSERVER_VARS" || {
21     echo $"Can not find util-vserver installation (the file '$UTIL_VSERVER_VARS' would be expected); aborting..." >&2
22     exit 1
23 }
24 . "$UTIL_VSERVER_VARS"
25 . "$_LIB_FUNCTIONS"
26 . "$_LIB_VSERVER_BUILD_FUNCTIONS"
27 . "$_LIB_VSERVER_BUILD_FUNCTIONS_PKGMGMT"
28 . "$__PKGLIBDIR/vserver.functions"
29
30 function showHelp()
31 {
32     echo \
33 $"Usage: $0 --externalize|--internalize [-y] [--] <vserver-name>
34
35 Report bugs to <$PACKAGE_BUGREPORT>."
36     exit 0
37 }
38
39 function showVersion()
40 {
41     echo $"\
42 vpkg $PACKAGE_VERSION -- shows information about packages in vservers
43 This program is part of $PACKAGE_STRING
44
45 Copyright (C) 2004,2005 Enrico Scholz
46 This program is free software; you may redistribute it under the terms of
47 the GNU General Public License.  This program has absolutely no warranty."
48     exit 0
49 }
50
51 function init()
52 {
53     if test -z "$WORKAROUND_106057"; then
54         rpmdb_mntpoint=/dev
55     else
56         rpmdb_mntpoint=/.rpmdb
57     fi
58 }
59
60 function _createDirs()
61 {
62     for i; do
63         test -n "$i" || continue
64         mkdir -p -m755 "$i"
65     done
66 }
67
68 function _copySecure()
69 {
70     local chroot=$1
71     local srcdir=$2
72     local dstdir=$3
73
74     
75     ( cd "$srcdir" && tar chf - '.' ) | \
76     ( cd "$chroot" && $_EXEC_CD "$dstdir" $_TAR xf - )
77 }
78
79 function _copySecureRev()
80 {
81     local chroot=$1
82     local srcdir=$2
83     local dstdir=$3
84
85     ( cd "$chroot" && $_EXEC_CD "$srcdir" $_TAR cf - '.' ) | \
86     ( cd "$dstdir" && tar xf - )
87 }
88
89 ## Usage: _substFile <filename> <sed-expression>
90 function _substFile()
91 {
92     local file=$1
93     local expr=$2
94     
95     $_CHROOT_SH testfile "$file" || return 0
96
97     local tmp=$($_MKTEMP pkgmgmt_subst.XXXXXX)
98     trap "$_RM -f $tmp" EXIT
99
100     $_CHROOT_SH cat "$file" | \
101         $_SED -e "$expr" >$tmp
102
103     $_CHROOT_SH cat "$file" | $_CMP -s $tmp - || \
104         $_CHROOT_SH truncate "$file" <$tmp
105
106     $_RM -f $tmp
107 }
108
109 function _hashAuto()
110 {
111     local file=$1
112     local hash=$2
113
114     $_CHROOT_SH testfile "$file" || return 0
115     
116     local tmp=$($_MKTEMP apt.conf.XXXXXX)
117     trap "$_RM -f $tmp" EXIT
118
119     $_CHROOT_SH cat "$file" | \
120         $_SED -e "s|^\([^$hash].*@autogenerated@\)|$hash$hash\1|" >$tmp
121
122     $_CHROOT_SH cat "$file" | $_CMP -s $tmp - || \
123         $_CHROOT_SH truncate "$file" <$tmp
124
125     $_RM -f $tmp
126 }
127
128 function _unhashAuto()
129 {
130     test -e "$1" || return 0
131
132     local hash=$2
133     local tmp=$($_MKTEMP apt.conf.XXXXXX)
134     trap "$_RM -f $tmp" EXIT
135
136     $_SED -e "s|^$hash$hash\(.*@autogenerated@\)|\1|" "$1" >$tmp
137     $_CMP -s "$tmp" "$1" || \
138         $_CAT "$tmp" >"$1"
139
140     $_RM -f $tmp
141 }
142
143 function _mountFilesystemsInternal()
144 {
145     local fstab="$1"
146     test -e "$fstab" || return 0
147     shift
148
149     pushd "$vdir" >/dev/null
150     "$@" $_SECURE_MOUNT -n -a --chroot --fstab "$fstab"
151     popd >/dev/null
152 }
153
154 function _mountFilesystems()
155 {
156     local cfgdir
157     cfgdir=$($_VSERVER_INFO "$1" CFGDIR) || {
158         echo "Can not determine configuration directory for '$1'; ..." >&2
159         return 1
160     }
161     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1"
162     
163     _mountFilesystemsInternal "$cfgdir"/fstab                           || return 1
164     _mountFilesystemsInternal "$cfgdir"/fstab.local                     || return 1
165     _mountFilesystemsInternal "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || return 1
166 }
167
168 function _umountFilesystems()
169 {
170     local cfgdir
171     cfgdir=$($_VSERVER_INFO "$1" CFGDIR) || {
172         echo "Can not determine configuration directory for '$1'; ..." >&2
173         return 1
174     }
175     local vdir=$cfgdir/vdir
176     local is_ok=1
177     test -n "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1"
178
179     pushd "$vdir/" >/dev/null || return 1
180         _umountVserverInternal  "$cfgdir"/fstab.remote "${CHBIND_CMD[@]}" || is_ok=
181         _umountVserverInternal  "$cfgdir"/fstab.local                     || is_ok=
182         _umountVserverInternal  "$cfgdir"/fstab                           || is_ok=
183     popd >/dev/null           || return 1
184     
185     test -n "$is_ok"
186 }
187
188 # Usage: verifyInternalPackages <vserver> <style>
189 function verifyInternalPackages()
190 {
191     local pkgs res=0
192     local ERR="\
193 The following errors occured while trying to internalize the
194 packagemanagement:
195 "
196     
197     case $2 in
198         (RH)    pkgs=$(vrpm "$1" -- -q --qf '---%{NAME}---\n' rpm apt yum urpmi "${YUM_RELEASEPKGS[@]}")
199                 hasSubstring "$pkgs" ---rpm--- || {
200                     warning "$ERR
201 * The vserver does not seem to have the 'rpm' package which is required
202   for internal package management. It is suggested to install it before
203   continuing."
204                     res=1
205                     ERR=
206                 }
207                 
208                 hasSubstring "$pkgs"  ---apt--- ---yum--- ---urpmi--- || {
209                     warning "$ERR
210 * The vserver does not seem to have a depsolver like 'apt' or 'yum'
211   installed. It is suggested to install such a program before setting
212   up internal package management."
213                     res=1
214                     ERR=
215                 }
216
217                 test -n "$have_apt" || test -n "$have_urpmi" ||
218                 test -z "$have_yum" || \
219                 hasSubstring "$pkgs" "${YUM_RELEASEPKGS[@]}" || {
220                     warning "$ERR
221 * yum requires a special package which describes the version of the
222   distribution. Such a package could not be found within the vserver
223   so please install it before continuing. Usually, this package is
224   named 'redhat-release' of 'fedora-release'."
225                     res=1
226                     ERR=
227                 }
228                 ;;
229     esac
230
231     return $res
232 }
233
234 function processVserver_RH()
235 {
236     local vserver=$1
237     local is_internalize=$2
238     local have_apt
239     local have_yum
240     local have_urpmi
241     local cfgdir
242     local i
243
244     cfgdir=$($_VSERVER_INFO "$vserver" APPDIR pkgmgmt) || \
245     cfgdir=$($_VSERVER_INFO "$vserver" APPDIR)/pkgmgmt
246
247     ## Figure out the environment....
248     have_apt=1
249     have_yum=1
250     have_urpmi=1
251     pkgmgmt.isAptAvailable "$cfgdir" "$vdir" "$is_internalize" || have_apt=
252     pkgmgmt.isYumAvailable "$cfgdir" "$vdir" "$is_internalize" || have_yum=
253     pkgmgmt.isUrpmiAvailable "$cfgdir" "$vdir" "$is_internalize" || have_urpmi=
254
255     local APTETCDIR=
256     local APTSTATEDIR=
257     local APTCACHEDIR=
258     local APTARCHIVDIR=
259     local RPMETCDIR=
260     local RPMSTATEDIR=
261
262     ## Create directories and assign variables where configuration
263     ## can/will be found on the host
264     if test -n "$is_internalize"; then
265         verifyInternalPackages "$vserver" RH || test -n "$IS_FORCE" ||
266             panic "
267 Can not continue; use '--force' to override this check"
268     
269         pushd "$vdir" >/dev/null
270
271         test ! -L var/lib/rpm || {
272             $_EXEC_CD /var/lib $_RM            rpm &&
273             $_EXEC_CD /var/lib $_MKDIR -m755   rpm &&
274             $_EXEC_CD /var/lib $_CHOWN rpm:rpm rpm ||
275             :
276         } </dev/null 2>/dev/null
277
278         for i in var/cache/apt/{,archives/{,partial},genpkglist,gensrclist} \
279                  var/state/{,apt/{,lists/{,partial}}} \
280                  etc/apt etc/rpm; do
281             test -d "$i" ||
282                 $_EXEC_CD /$(dirname "$i") $_MKDIR -m755 $(basename "$i") || :
283         done #2>/dev/null
284         
285         popd >/dev/null
286         
287         if test -n "$have_apt"; then
288             findDir APTETCDIR     "$cfgdir"/aptetc "$cfgdir"/base/apt/etc /etc/apt /
289         elif test -n "$have_urpmi"; then
290             findDir URPMISTATEDIR "$cfgdir"/base/var/lib/urpmi
291             findDir URPMIETCDIR   "$cfgdir"/base/etc/urpmi
292         fi
293
294         findDir RPMETCDIR   "$cfgdir"/rpmetc   "$cfgdir"/base/rpm/etc    \
295                             "$cfgdir"/base/etc/rpm /etc/rpm /
296         findDir RPMSTATEDIR "$cfgdir"/rpmstate "$cfgdir"/base/rpm/state  \
297                             "$cfgdir"/base/var/lib/rpm
298     else
299         mkdir -m755 -p "$cfgdir"
300         local need_base=
301
302         if test -n "$have_apt"; then
303             findDir APTETCDIR    "$cfgdir"/aptetc      "$cfgdir"/base/apt/etc       /
304             findDir APTSTATEDIR  "$cfgdir"/aptstate    "$cfgdir"/base/apt/state     /
305             findDir APTCACHEDIR  "$cfgdir"/aptcache    "$cfgdir"/base/apt/cache     /
306             findDir APTARCHIVDIR "$cfgdir"/aptarchives "$cfgdir"/base/apt/archives  /
307             
308             test "$APTETCDIR"    != / || APTETCDIR=$cfgdir/base/apt/etc
309             test "$APTSTATEDIR"  != / || APTSTATEDIR=$cfgdir/base/apt/state
310             test "$APTCACHEDIR"  != / || APTCACHEDIR=$cfgdir/base/apt/cache
311             test "$APTARCHIVDIR" != / || APTARCHIVDIR=$cfgdir/base/apt/archive
312
313             test -d "$cfgdir"/aptetc   -a -d "$cfgdir"/aptstate -a \
314                  -d "$cfgdir"/aptcache -a -d "$cfgdir"/aptarchives || need_base=1
315         fi
316
317         findDir RPMETCDIR     "$cfgdir"/rpmetc   "$cfgdir"/base/rpm/etc    /
318         findDir RPMSTATEDIR   "$cfgdir"/rpmstate "$cfgdir"/base/rpm/state  /
319
320         test "$RPMETCDIR"   != / || RPMETCDIR=$cfgdir/base/rpm/etc
321         test "$RPMSTATEDIR" != / || RPMSTATEDIR=$cfgdir/base/rpm/state
322
323         test -d "$cfgdir"/rpmetc -a -d "$cfgdir"/rpmstate || need_base=1
324         test ! -e "$cfgdir"/base || need_base=
325
326         test -z "$need_base" || ln -s "$PKGCFGDIR" "$cfgdir"/base
327
328         mkdir -m755 -p "$PKGCFGDIR"
329         _createDirs "$APTETCDIR" "$APTSTATEDIR" "$APTCACHEDIR" "$APTARCHIVDIR" \
330                     "$RPMETCDIR" "$RPMSTATEDIR"
331     fi
332
333     ## Copy the files...
334     if test -n "$is_internalize"; then
335         if test -n "$have_apt"; then
336             _copySecure "$vdir" "$APTETCDIR" /etc/apt
337             pushd "$vdir" >/dev/null
338                 _hashAuto /etc/apt/apt.conf '/'
339             popd >/dev/null
340         elif test -n "$have_urpmi"; then
341             _copySecure "$vdir" "$URPMISTATEDIR" /var/lib/urpmi
342             _copySecure "$vdir" "$URPMIETCDIR" /etc/urpmi
343         fi
344
345         _copySecure "$vdir" "$RPMETCDIR"   /etc/rpm
346         _copySecure "$vdir" "$RPMSTATEDIR" /var/lib/rpm
347
348         pushd "$vdir" >/dev/null
349             ## remove %_dbpath settings
350             _substFile /etc/rpm/macros '/^%_dbpath[ \t].*/D'
351         popd >/dev/null
352     else
353         if test -n "$have_apt"; then
354             _copySecureRev "$vdir" /etc/apt "$APTETCDIR"
355             _unhashAuto "$APTETCDIR"/apt.conf '/'
356         fi
357
358         _copySecureRev "$vdir" /etc/rpm     "$RPMETCDIR"
359         _copySecureRev "$vdir" /var/lib/rpm "$RPMSTATEDIR"
360
361         echo -e "%_dbpath\t\t$rpmdb_mntpoint" >>$RPMETCDIR/macros
362     fi
363
364     ## Cleanups...
365     if test -n "$is_internalize"; then
366         :
367     else
368         tmpdir=$($_MKTEMPDIR -p /var/tmp pkgmgmt.XXXXXX)
369         trap "$_RM -rf $tmpdir" EXIT
370         pushd "$vdir" >/dev/null
371         $_EXEC_CD /var/lib $_MV rpm $tmpdir/
372         $_EXEC_CD /var/lib $_LN_S "$rpmdb_mntpoint" rpm
373         $_RM -rf $tmpdir
374     fi
375
376     ## Finish it...
377     if test -n "$is_internalize"; then
378         $_TOUCH "$cfgdir"/internal
379     else
380         $_RM -f "$cfgdir"/internal
381     fi
382 }
383
384 function processVserver_Debian()
385 {
386     local vserver=$1
387     local is_internalize=$2
388
389     if test -n "$is_internalize"; then
390         echo $"Debian vservers should be internalized everytime; do not know how to handle '$vserver'" >&2
391     else
392         echo $"External packagemanagement is not supported for Debian vserver" >&2
393     fi
394
395     return 1
396 }
397
398 function processVserver()
399 {
400     local vserver=$1
401     local is_external=
402     local skip=1
403     local vdir
404
405     ! $_VSERVER_INFO -q "$vserver" RUNNING || {
406         echo $"Can not operate on running vservers; please stop '$vserver' and retry again..."
407         return 1
408     } >&2
409
410     vdir=$($_VSERVER_INFO "$vserver" VDIR) && test -d "$vdir" || {
411         echo $"Vserver '$vserver' does not seem to exist; skipping it..."
412         return 1
413     } >&2
414
415     _setVserverDir "$vserver"
416     _setVserverName
417     _setVserverDirName
418     pkgmgmt.initVariables
419     
420     pkgmgmt.isInternal "$vserver" || is_external=1
421
422     case "$is_external"X"$IS_INTERNALIZE"X"$IS_EXTERNALIZE" in
423         (*X1X1) echo $"Can not externalize and internalize at the same time";;
424         (*XX)   echo $"No operation specified; try '--help' for more information";;
425         (1XX1)  echo $"Vserver '$vserver' has already external packagemanagment; skipping it...";;
426         (X1X)   echo $"Vserver '$vserver' has already internal packagemanagment; skipping it...";;
427         (*)     skip=
428     esac >&2
429
430     test -z "$skip" || return 1
431
432     local style
433     _mountFilesystems  "$vserver"       || return 1
434     pkgmgmt.guessStyle "$vserver" style || return 1
435
436     case "$style" in
437         (redhat|mandrake)       processVserver_RH     "$vserver" "$IS_INTERNALIZE";;
438         (debian)                processVserver_Debian "$vserver" "$IS_INTERNALIZE";;
439         (*)
440             echo $"Vserver style '$style' is not supported for packagemanagment" >&2
441             return 1
442     esac
443
444     _umountFilesystems "$vserver"       || return 1
445 }
446
447 tmp=$(getopt -o y --long debug,externalize,internalize,help,version,force -n "$0" -- "$@") || exit 1
448 eval set -- "$tmp"
449
450 IS_EXTERNALIZE=
451 IS_INTERNALIZE=
452 IS_YES=
453 IS_FORCE=
454
455 while true; do
456     case "$1" in
457         (--help)        showHelp $0;;
458         (--version)     showVersion;;
459         (--debug)       set -x;;
460         (--externalize) IS_EXTERNALIZE=1;;
461         (--internalize) IS_INTERNALIZE=1;;
462         (--force)       IS_FORCE=1;;
463         (-y)            IS_YES=1;;
464         (--)            shift; break;;
465         (*)             echo $"vserver: internal error; arg=='$1'" >&2; exit 1;;
466     esac
467     shift
468 done
469
470 test -n "$1" || {
471     echo $"No vserver specified; try '--help' for more information"
472     exit 1
473 } >&2
474
475
476 set -e
477 init
478
479 ok=1
480 passed=
481 for i; do
482     processVserver "$i" && passed=1 || ok=
483 done
484     
485 test -z "$ok"     || exit 0
486 test -z "$passed" || exit 1
487 exit 2