minor optimizations
[util-vserver.git] / util-vserver / scripts / pkgmgmt
1 #!/bin/bash
2 # $Id$
3
4 # Copyright (C) 2004 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_PKGMGMT"
27 . "$PKGLIBDIR/vserver.functions"
28
29 function showHelp()
30 {
31     echo \
32 $"Usage: $0 --externalize|--internalize [-y] [--] <vserver-name>
33
34 Report bugs to <$PACKAGE_BUGREPORT>."
35     exit 0
36 }
37
38 function showVersion()
39 {
40     echo $"\
41 vpkg $PACKAGE_VERSION -- shows information about packages in vservers
42 This program is part of $PACKAGE_STRING
43
44 Copyright (C) 2004 Enrico Scholz
45 This program is free software; you may redistribute it under the terms of
46 the GNU General Public License.  This program has absolutely no warranty."
47     exit 0
48 }
49
50 function init()
51 {
52     if test -z "$WORKAROUND_106057"; then
53         rpmdb_mntpoint=/dev
54     else
55         rpmdb_mntpoint=/.rpmdb
56     fi
57     pkgmgmt.initVariables
58 }
59
60 function _createDirs()
61 {
62     for i; do
63         test "$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 function _hashAuto()
90 {
91     local hash=$2
92     local dir=$(dirname "$1")
93     local file=$(basename "$1")
94
95     $_EXEC_CD "$dir" "$(which test)" "$file" || return 0
96     
97     local tmp=$(mktemp /tmp/apt.conf.XXXXXX)
98     trap "rm -f $tmp" EXIT
99
100     $_EXEC_CD "$dir" /bin/cat "$file" | \
101         sed -e "s|^\([^$hash].*@autogenerated@\)|$hash$hash\1|" >$tmp
102
103     cat $tmp | $_EXEC_CD "$dir" "$(which cmp)" -s - "$file" || \
104         $_CHROOT_CAT "$1" <$tmp
105
106     rm -f $tmp
107 }
108
109 function _unhashAuto()
110 {
111     test -e "$1" || return 0
112
113     local hash=$2
114     local tmp=$(mktemp /tmp/apt.conf.XXXXXX)
115     trap "rm -f $tmp" EXIT
116
117     sed -e "s|^$hash$hash\(.*@autogenerated@\)|\1|" "$1" >$tmp
118     cmp -s "$tmp" "$1" || \
119         cat "$tmp" >"$1"
120
121     rm -f $tmp
122 }
123
124 function _mountFilesystemsInternal()
125 {
126     local fstab="$1"
127     test -e "$fstab" || return 0
128     shift
129     
130     "$@" $_SECURE_MOUNT -n -a --chroot "$vdir" --fstab "$fstab"
131 }
132
133 function _mountFilesystems()
134 {
135     local cfgdir=$($_VSERVER_INFO "$1" CFGDIR) || {
136         echo "Can not determine configuration directory for '$1'; ..." >&2
137         return 1
138     }
139     test "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1"
140     
141     _mountFilesystemsInternal "$cfgdir"/fstab       $_CHBIND "${CHBIND_OPTS[@]}" || return 1
142     _mountFilesystemsInternal "$cfgdir"/fstab.local $_CHBIND "${CHBIND_OPTS[@]}" || return 1
143 }
144
145 function _umountFilesystems()
146 {
147     local cfgdir=$($_VSERVER_INFO "$1" CFGDIR) || {
148         echo "Can not determine configuration directory for '$1'; ..." >&2
149         return 1
150     }
151     local vdir=$cfgdir/vdir
152     local is_ok=1
153     test "$_HAVE_CHBIND_OPTIONS" || _generateChbindOptions "$1"
154
155     pushd "$vdir/" >/dev/null || return 1
156         _umountVserverInternal  "$cfgdir"/fstab.local                              || is_ok=
157         _umountVserverInternal  "$cfgdir"/fstab       $_CHBIND "${CHBIND_OPTS[@]}" || is_ok=
158     popd >/dev/null           || return 1
159     
160     test "$is_ok"
161 }
162
163 function processVserver_RH()
164 {
165     local vserver=$1
166     local is_internalize=$2
167     local have_apt
168     local cfgdir
169     local i
170
171     cfgdir=$($_VSERVER_INFO "$vserver" APPDIR pkgmgmt) || \
172     cfgdir=$($_VSERVER_INFO "$vserver" APPDIR)/pkgmgmt
173
174     ## Figure out the environment....
175     have_apt=1
176     pkgmgmt.isAptAvailable "$cfgdir" "$vdir" "$is_internalize" || have_apt=
177
178     local APTETCDIR=
179     local APTSTATEDIR=
180     local APTCACHEDIR=
181     local APTARCHIVDIR=
182     local RPMETCDIR=
183     local RPMSTATEDIR=
184
185     ## Create directories and assign variables where configuration
186     ## can/will be found on the host
187     if test "$is_internalize"; then
188         pushd "$vdir" >/dev/null
189
190         test ! -L var/lib/rpm || {
191             $_EXEC_CD /var/lib /bin/rm            rpm &&
192             $_EXEC_CD /var/lib /bin/mkdir -m755   rpm &&
193             $_EXEC_CD /var/lib /bin/chown rpm:rpm rpm ||
194             :
195         } </dev/null 2>/dev/null
196
197         for i in var/cache/apt/{,archives/{,partial},genpkglist,gensrclist} \
198                  var/state/{,apt/{,lists/{,partial}}} \
199                  etc/apt etc/rpm; do
200             test -d "$i" ||
201                 $_EXEC_CD /$(dirname "$i") /bin/mkdir -m755 $(basename "$i") || :
202         done #2>/dev/null
203         
204         popd >/dev/null
205         
206         if test "$have_apt"; then
207             findDir APTETCDIR    "$cfgdir"/aptetc "$cfgdir"/base/apt/etc /etc/apt /
208         fi
209
210         findDir RPMETCDIR   "$cfgdir"/rpmetc   "$cfgdir"/base/rpm/etc    /etc/rpm /
211         findDir RPMSTATEDIR "$cfgdir"/rpmstate "$cfgdir"/base/rpm/state
212     else
213         mkdir -m755 -p "$cfgdir"
214         local need_base=
215
216         if test "$have_apt"; then
217             findDir APTETCDIR    "$cfgdir"/aptetc      "$cfgdir"/base/apt/etc       /
218             findDir APTSTATEDIR  "$cfgdir"/aptstate    "$cfgdir"/base/apt/state     /
219             findDir APTCACHEDIR  "$cfgdir"/aptcache    "$cfgdir"/base/apt/cache     /
220             findDir APTARCHIVDIR "$cfgdir"/aptarchives "$cfgdir"/base/apt/archives  /
221             
222             test "$APTETCDIR"    != / || APTETCDIR=$cfgdir/base/apt/etc
223             test "$APTSTATEDIR"  != / || APTSTATEDIR=$cfgdir/base/apt/state
224             test "$APTCACHEDIR"  != / || APTCACHEDIR=$cfgdir/base/apt/cache
225             test "$APTARCHIVDIR" != / || APTARCHIVDIR=$cfgdir/base/apt/archive
226
227             test -d "$cfgdir"/aptetc   -a -d "$cfgdir"/aptstate -a \
228                  -d "$cfgdir"/aptcache -a -d "$cfgdir"/aptarchives || need_base=1
229         fi
230
231         findDir RPMETCDIR     "$cfgdir"/rpmetc   "$cfgdir"/base/rpm/etc    /
232         findDir RPMSTATEDIR   "$cfgdir"/rpmstate "$cfgdir"/base/rpm/state  /
233
234         test "$RPMETCDIR"   != / || RPMETCDIR=$cfgdir/base/rpm/etc
235         test "$RPMSTATEDIR" != / || RPMSTATEDIR=$cfgdir/base/rpm/state
236
237         test -d "$cfgdir"/rpmetc -a -d "$cfgdir"/rpmstate || need_base=1
238         test ! -e "$cfgdir"/base || need_base=
239
240         test -z "$need_base" || ln -s "$PKGCFGDIR" "$cfgdir"/base
241
242         mkdir -m755 -p "$PKGCFGDIR"
243         _createDirs "$APTETCDIR" "$APTSTATEDIR" "$APTCACHEDIR" "$APTARCHIVDIR" \
244                     "$RPMETCDIR" "$RPMSTATEDIR"
245     fi
246
247     ## Copy the files...
248     if test "$is_internalize"; then
249         if test "$have_apt"; then
250             _copySecure "$vdir" "$APTETCDIR" /etc/apt
251             pushd "$vdir" >/dev/null
252                 _hashAuto /etc/apt/apt.conf '/'
253             popd >/dev/null
254         fi
255
256         _copySecure "$vdir" "$RPMETCDIR"   /etc/rpm
257         _copySecure "$vdir" "$RPMSTATEDIR" /var/lib/rpm
258
259     else
260         if test "$have_apt"; then
261             _copySecureRev "$vdir" /etc/apt "$APTETCDIR"
262             _unhashAuto "$APTETCDIR"/apt.conf '/'
263         fi
264
265         _copySecureRev "$vdir" /etc/rpm     "$RPMETCDIR"
266         _copySecureRev "$vdir" /var/lib/rpm "$RPMSTATEDIR"
267     fi
268
269     ## Cleanups...
270     if test "$is_internalize"; then
271         :
272     else
273         tmpdir=$(mktemp -d /var/tmp/pgmgmt.XXXXXX)
274         trap "rm -rf $tmpdir" EXIT
275         pushd "$vdir" >/dev/null
276         $_EXEC_CD /var/lib /bin/mv rpm $tmpdir/
277         $_EXEC_CD /var/lib /bin/ln -s "$rpmdb_mntpoint" rpm
278         rm -rf $tmpdir
279     fi
280
281     ## Finish it...
282     if test "$is_internalize"; then
283         touch "$cfgdir"/internal
284     else
285         rm -f "$cfgdir"/internal
286     fi
287 }
288
289 function processVserver_Debian()
290 {
291     local vserver=$1
292     local is_internalize=$2
293
294     if test "$is_internalize"; then
295         echo $"Debian vservers should be internalized everytime; do not know how to handle '$vserver'" >&2
296     else
297         echo $"External packagemanagement is not supported for Debian vserver" >&2
298     fi
299
300     return 1
301 }
302
303 function processVserver()
304 {
305     local vserver=$1
306     local is_external=
307     local skip=1
308     local vdir
309
310     ! $_VSERVER_INFO -q "$vserver" RUNNING || {
311         echo $"Can not operate on running vservers; please stop '$vserver' and retry again..."
312         return 1
313     } >&2
314
315     vdir=$($_VSERVER_INFO "$vserver" VDIR) && test -d "$vdir" || {
316         echo $"Vserver '$vserver' does not seem to exist; skipping it..."
317         return 1
318     } >&2
319     
320     pkgmgmt.isInternal "$vserver" || is_external=1
321
322     case "$is_external"X"$IS_INTERNALIZE"X"$IS_EXTERNALIZE" in
323         (*X1X1) echo $"Can not externalize and internalize at the same time";;
324         (*XX)   echo $"No operation specified; try '--help' for more information";;
325         (1XX1)  echo $"Vserver '$vserver' has already external packagemanagment; skipping it...";;
326         (X1X)   echo $"Vserver '$vserver' has already internal packagemanagment; skipping it...";;
327         (*)     skip=
328     esac >&2
329
330     test -z "$skip" || return 1
331
332     local style
333     _mountFilesystems  "$vserver"       || return 1
334     pkgmgmt.guessStyle "$vserver" style || return 1
335
336     case "$style" in
337         (redhat|mandrake)       processVserver_RH     "$vserver" "$IS_INTERNALIZE";;
338         (debian)                processVserver_Debian "$vserver" "$IS_INTERNALIZE";;
339         (*)
340             echo $"Vserver style '$style' is not supported for packagemanagment" >&2
341             return 1
342     esac
343
344     _umountFilesystems "$vserver"       || return 1
345 }
346
347 tmp=$(getopt -o y --long debug,externalize,internalize,help,version -n "$0" -- "$@") || exit 1
348 eval set -- "$tmp"
349
350 IS_EXTERNALIZE=
351 IS_INTERNALIZE=
352 IS_YES=
353
354 while true; do
355     case "$1" in
356         (--help)        showHelp $0;;
357         (--version)     showVersion;;
358         (--debug)       set -x;;
359         (--externalize) IS_EXTERNALIZE=1;;
360         (--internalize) IS_INTERNALIZE=1;;
361         (-y)            IS_YES=1;;
362         (--)            shift; break;;
363         (*)             echo $"vserver: internal error; arg=='$1'" >&2; exit 1;;
364     esac
365     shift
366 done
367
368 test "$1" || {
369     echo $"No vserver specified; try '--help' for more information"
370     exit 1
371 } >&2
372
373
374 set -e
375 init
376
377 ok=1
378 passed=
379 for i; do
380     processVserver "$i" && passed=1 || ok=
381 done
382     
383 test -z "$ok"     || exit 0
384 test -z "$passed" || exit 1
385 exit 2