michael@13: #!@l_prefix@/lib/openpkg/bash
michael@13: ##
michael@13: ## rpmdb -- OpenPKG RPM Database Administration Utility
michael@428: ## Copyright (c) 2000-2012 OpenPKG GmbH
michael@13: ##
michael@428: ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
michael@428: ## All rights reserved. Licenses which grant limited permission to use,
michael@428: ## copy, modify and distribute this software are available from the
michael@428: ## OpenPKG GmbH.
michael@13: ##
michael@428: ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
michael@13: ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
michael@13: ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@13: ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
michael@13: ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@13: ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@13: ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
michael@13: ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
michael@13: ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
michael@13: ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
michael@13: ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
michael@13: ## SUCH DAMAGE.
michael@13: ##
michael@13:
michael@13: # program information
michael@13: progname="rpmdb"
michael@13:
michael@13: # configuration defaults
michael@13: help=""
michael@13: prefix="@l_prefix@"
michael@13: dbpath=""
michael@428: dbapi=""
michael@428: tmpdir=""
michael@13: rpm=""
michael@13: musr="@l_musr@"
michael@13: mgrp="@l_mgrp@"
michael@13: mode=""
michael@13: force=no
michael@13: verbose=2
michael@13:
michael@13: ##
michael@13: ## PARSE COMMAND LINE
michael@13: ##
michael@13:
michael@13: # iterate over argument line
michael@428: args=""
michael@13: for opt
michael@13: do
michael@13: case $opt in
michael@13: -*=*) arg=`echo "$opt" | sed 's/^[-_a-zA-Z0-9]*=//'` ;;
michael@13: *) arg='' ;;
michael@13: esac
michael@13: case $opt in
michael@13: -h|--help ) help="Usage" ;;
michael@13: -B|--build ) mode=build ;;
michael@428: -M|--migrate ) mode=migrate ;;
michael@13: -R|--rebuild ) mode=rebuild ;;
michael@13: -C|--cleanup ) mode=cleanup ;;
michael@13: -F|--fixate ) mode=fixate ;;
michael@13: -L|--list ) mode=list ;;
michael@13: -f|--force ) force=yes ;;
michael@13: -q|--quiet ) verbose=0 ;;
michael@13: -v|--verbose ) verbose=`expr $verbose + 1` ;;
michael@13: --prefix=* ) prefix=$arg ;;
michael@13: --dbpath=* ) dbpath=$arg ;;
michael@428: --dblib=* ) dblib=$arg ;;
michael@428: --tmpdir=* ) tmpdir=$arg ;;
michael@13: --rpm=* ) rpm=$arg ;;
michael@13: --musr=* ) musr=$arg ;;
michael@13: --mgrp=* ) mgrp=$arg ;;
michael@428: -- ) ;;
michael@428: -* ) help="Invalid option \`$opt'"; break ;;
michael@428: * ) args="$args \"$opt\"" ;;
michael@13: esac
michael@13: done
michael@428: eval "set -- $args"
michael@13:
michael@428: # make sure exactly one run-time mode is specified
michael@13: if [ ".$mode" = . ]; then
michael@13: help="No or invalid run-time mode specified"
michael@13: fi
michael@13:
michael@13: # error or usage message
michael@13: if [ ".$help" != . ]; then
michael@13: if [ ".$help" != ".Usage" ]; then
michael@13: echo "$progname:ERROR: $help" 1>&2
michael@13: fi
michael@13: cat 1>&2 <&2
michael@13: exit 1
michael@13: }
michael@13:
michael@13: warning () {
michael@13: echo "$progname:WARNING: $*" 1>&2
michael@13: }
michael@13:
michael@13: verbose () {
michael@13: local level=$1
michael@13: shift
michael@13: if [ $level -le $verbose ]; then
michael@13: local lead=""
michael@13: case "$level" in
michael@13: 1 ) lead="" ;;
michael@13: 2 ) lead="" ;;
michael@13: 3 ) lead=" " ;;
michael@13: * ) lead=" " ;;
michael@13: esac
michael@13: echo "$progname: $lead$*" 1>&2
michael@13: fi
michael@13: }
michael@13:
michael@428: rpmdb_version_load () {
michael@428: if [ -f $dbpath/VERSION ]; then
michael@428: eval `(. $dbpath/VERSION || exit $?; echo "DBAPI=\"$DBAPI\"; DBLIB=\"$DBLIB\"; DBVER=\"$DBVER\"")`
michael@428: fi
michael@428: if [ ".$DBAPI" = . ]; then DBAPI="3"; fi
michael@428: if [ ".$DBLIB" = . ]; then DBLIB="db"; fi
michael@428: if [ ".$DBVER" = . ]; then DBVER="4.1.2"; fi
michael@428: }
michael@428:
michael@428: rpmdb_version_save () {
michael@428: if [ ".$DBAPI" = .3 ]; then
michael@428: DBLIB="db"
michael@428: DBVER=`$rpmdb_load -V 2>&1 |\
michael@428: grep 'Berkeley DB [0-9][0-9]*\.[0-9][0-9]*' |\
michael@428: sed -e 's;^;X;' \
michael@428: -e 's;^X[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$;\1;' \
michael@428: -e 's;^X[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\).*$;\1.0;' \
michael@428: -e 's;^X.*$;0.0.0;'`
michael@428: elif [ ".$DBAPI" = .4 ]; then
michael@428: DBLIB="sqlite"
michael@428: DBVER=`$rpmdb_sqlite --version 2>&1 |\
michael@428: grep '^[0-9][0-9]*\.[0-9][0-9]*' |\
michael@428: sed -e 's;^;X;' \
michael@428: -e 's;^X[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*$;\1;' \
michael@428: -e 's;^X[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\).*$;\1.0;' \
michael@428: -e 's;^X.*$;0.0.0;'`
michael@428: fi
michael@428: ( echo "DBAPI=$DBAPI"
michael@428: echo "DBLIB=$DBLIB"
michael@428: echo "DBVER=$DBVER"
michael@428: ) >$dbpath/VERSION.new || exit $?
michael@428: mv $dbpath/VERSION.new $dbpath/VERSION || exit $?
michael@428: chown $musr:$mgrp $dbpath/VERSION 2>/dev/null || true
michael@428: chmod 644 $dbpath/VERSION 2>/dev/null || true
michael@428: }
michael@428:
michael@13: rpm () {
michael@428: local opts="--define '_dbpath `echo $dbpath | sed -e 's;/*$;;' -e 's;$;/;'`'"
michael@428: opts="$opts --define '_repackage_all_erasures 0'"
michael@13: if [ ".$force" = .yes ]; then
michael@13: opts="$opts --define '__dbi_private yes'"
michael@13: fi
michael@13: verbose 3 "run: $rpm $opts $@"
michael@13: eval "$rpm $opts \"\$@\""
michael@13: }
michael@13:
michael@428: rpmdb_load="$prefix/lib/openpkg/db_tool load"
michael@428: rpmdb_dump="$prefix/lib/openpkg/db_tool dump"
michael@428: rpmdb_sqlite="$prefix/lib/openpkg/sqlite3"
michael@13:
michael@13: ##
michael@13: ## RPM DATABASE OPERATIONS
michael@13: ##
michael@13:
michael@13: db_wait () {
michael@13: # wait until RPM has released the database in case we are called
michael@13: # asynchronously to RPM (especially important when upgrading from
michael@13: # RPM 4.0 where concurrent access is still not possible)
michael@13: verbose 2 "waiting for RPM database to be available"
michael@13: local i=0
michael@13: while [ $i -lt 10 ]; do
michael@13: if $prefix/libexec/openpkg/rpm -q openpkg >/dev/null 2>&1; then
michael@13: break
michael@13: fi
michael@13: sleep 1
michael@13: i=`expr $i + 1`
michael@13: done
michael@13: if [ $i -eq 10 ]; then
michael@13: exit 1
michael@13: else
michael@13: exit 0
michael@13: fi
michael@13: }
michael@13:
michael@13: db_remove () {
michael@13: # remove all known files
michael@13: verbose 2 "removing (possibly existing) old RPM database DB files"
michael@13: for dbfile in $dbfiles; do
michael@428: eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\):\(.*\)$/dbapi="\1"; dbtype="\2"; dbfile="\3";/'`
michael@428: verbose 3 "removing database file: $dbpath/$dbfile ($dbapi:$dbtype)"
michael@13: rm -f $dbpath/$dbfile
michael@13: done
michael@13: }
michael@13:
michael@13: db_init () {
michael@13: # perform official "initdb" operation
michael@13: verbose 2 "creating new RPM database (built-in RPM procedure)"
michael@13: rpm --initdb
michael@428: rpmdb_version_save
michael@13: }
michael@13:
michael@13: db_unbreak () {
michael@428: # cleanup DB files
michael@428: verbose 2 "cleaning up RPM database DB files"
michael@13: for dbfile in $dbfiles; do
michael@428: eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\):\(.*\)$/dbapi="\1"; dbtype="\2"; dbfile="\3";/'`
michael@428: if [ -f $dbpath/$dbfile ]; then
michael@428: if [ ".$dbapi" = .3 -a ".$dbtype" = .region ]; then
michael@428: verbose 3 "cleaning up DB file: $dbpath/$dbfile ($dbapi:$dbtype)"
michael@428: rm -f $dbpath/$dbfile || true
michael@428: elif [ ".$dbtype" = .temporary ]; then
michael@428: verbose 3 "cleaning up DB file: $dbpath/$dbfile ($dbapi:$dbtype)"
michael@428: rm -f $dbpath/$dbfile || true
michael@428: fi
michael@13: fi
michael@13: done
michael@13: }
michael@13:
michael@428: db_convert () {
michael@428: # make sure all RPM database DB files have the correct type
michael@428: # (as the type can be different during upgrading from RPM 4 to RPM 5)
michael@428: verbose 2 "making sure RPM database are of the correct type"
michael@13: for dbfile in $dbfiles; do
michael@428: eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\):\(.*\)$/dbapi="\1"; dbtype="\2"; dbfile="\3";/'`
michael@428: if [ ".$dbapi" != ".$DBAPI" ]; then
michael@428: continue
michael@428: fi
michael@428: if [ -f $dbpath/$dbfile ]; then
michael@428: if [ ".$dbapi" = .3 ] && [ ".$dbtype" = .hash -o ".$dbtype" = .btree ]; then
michael@428: dbtype_old="`$rpmdb_dump -h $tmpdir -r $dbpath/$dbfile | grep '^type=' | sed -e 'q' | sed -e 's/^type=//'`"
michael@428: if [ ".$dbtype_old" != ".$dbtype" ]; then
michael@428: verbose 3 "converting DB file: $dbpath/$dbfile ($dbtype_old -> $dbtype)"
michael@428: rm -f $dbpath/$dbfile.new
michael@428: $rpmdb_dump -h $tmpdir -r $dbpath/$dbfile |\
michael@428: sed -e "s/^type=.*/type=$dbtype/" -e '/^h_nelem=.*/d' |\
michael@428: $rpmdb_load -h $tmpdir $dbpath/$dbfile.new
michael@428: if $rpmdb_dump -h $tmpdir -r $dbpath/$dbfile.new >/dev/null 2>&1; then
michael@428: rm -f $dbpath/$dbfile
michael@428: mv $dbpath/$dbfile.new $dbpath/$dbfile
michael@428: else
michael@428: warning "failed to convert RPM DB file \"$dbfile\""
michael@428: fi
michael@428: fi
michael@13: fi
michael@13: fi
michael@13: done
michael@13: }
michael@13:
michael@13: db_reload () {
michael@13: # rebuilding new from old RPM database DB files by dumping and
michael@13: # reloading their entire content
michael@13: verbose 2 "dumping and reloading RPM database DB file contents"
michael@13: for dbfile in $dbfiles; do
michael@428: eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\):\(.*\)$/dbapi="\1"; dbtype="\2"; dbfile="\3";/'`
michael@428: if [ ".$dbapi" != ".$DBAPI" ]; then
michael@428: continue
michael@428: fi
michael@13: if [ -f $dbpath/$dbfile ]; then
michael@428: if [ ".$dbapi" = .3 ]; then
michael@428: verbose 3 "dumping and reloading DB file: $dbpath/$dbfile ($dbtype)"
michael@428: if [ ".$dbtype" = .hash -o ".$dbtype" = .btree ]; then
michael@428: rm -f $dbpath/$dbfile.new
michael@428: $rpmdb_dump -h $tmpdir -r $dbpath/$dbfile |\
michael@428: $rpmdb_load -h $tmpdir $dbpath/$dbfile.new
michael@428: if $rpmdb_dump -h $tmpdir -r $dbpath/$dbfile.new >/dev/null 2>&1; then
michael@428: rm -f $dbpath/$dbfile
michael@428: mv $dbpath/$dbfile.new $dbpath/$dbfile
michael@428: else
michael@428: warning "failed to reload RPM DB file \"$dbfile\""
michael@428: fi
michael@428: elif [ ".$dbtype" = .region ]; then
michael@428: rm -f $dbpath/$dbfile || true
michael@428: elif [ ".$dbtype" = .temporary ]; then
michael@428: rm -f $dbpath/$dbfile || true
michael@428: fi
michael@428: elif [ ".$dbapi" = .4 ]; then
michael@428: verbose 3 "dumping and reloading DB file: $dbpath/$dbfile ($dbtype)"
michael@428: rm -f $dbpath/$dbfile.new
michael@428: $rpmdb_sqlite $dbpath/$dbfile .dump |\
michael@428: $rpmdb_sqlite $dbpath/$dbfile.new
michael@428: if $rpmdb_sqlite $dbpath/$dbfile.new >/dev/null 2>&1; then
michael@428: rm -f $dbpath/$dbfile
michael@428: mv $dbpath/$dbfile.new $dbpath/$dbfile
michael@428: else
michael@428: warning "failed to reload RPM DB file \"$dbfile\""
michael@428: fi
michael@13: fi
michael@13: fi
michael@13: done
michael@428: rpmdb_version_save
michael@428: }
michael@428:
michael@428: db_migrate () {
michael@428: # perform database migration
michael@428: if [ ".$1" != . ]; then
michael@428: dblib="$1"
michael@428: fi
michael@428: dbapi_old="$DBAPI"
michael@428: if [ ".$dblib" = .db ]; then
michael@428: dbapi_new="3"
michael@428: elif [ ".$dblib" = .sqlite ]; then
michael@428: dbapi_new="4"
michael@428: else
michael@428: error "unknown RPM database backend library \"$dblib\""
michael@428: fi
michael@428: if [ ".$dbapi_new" = ".$dbapi_old" ]; then
michael@428: error "RPM database already uses requested backend ($DBAPI:$DBLIB:$DBVER)"
michael@428: fi
michael@428: verbose 2 "migrating RPM database"
michael@428: rpm --rebuilddb --dbapi "$dbapi_old" --rebuilddbapi "$dbapi_new"
michael@428: verbose 3 "old RPM database format: $DBAPI:$DBLIB:$DBVER"
michael@428: DBAPI="$dbapi_new"
michael@428: rpmdb_version_save
michael@428: rpmdb_version_load
michael@428: verbose 3 "new RPM database format: $DBAPI:$DBLIB:$DBVER"
michael@13: }
michael@13:
michael@13: db_rebuild () {
michael@13: # perform official "rebuilddb" operation
michael@13: verbose 2 "rebuilding RPM database (built-in RPM procedure)"
michael@13: rpm --rebuilddb
michael@428: rpmdb_version_save
michael@13: }
michael@13:
michael@13: db_operate () {
michael@13: # perform some read/write operation on RPM database
michael@13: # (we have no package available, but removing and reimporting
michael@13: # the OpenPKG OpenPGP keys is a harmless thing and always possible)
michael@13: verbose 2 "performing read/write operation on RPM database"
michael@13: for spec in \
michael@13: openpkg.org.pgp:gpg-pubkey-63c4cb9f-3c591eda \
michael@13: openpkg.com.pgp:gpg-pubkey-61b7ae34-4544a6af \
michael@13: openpkg.net.pgp:gpg-pubkey-52197903-4544a74d \
michael@13: ; do
michael@13: eval `echo "$spec" | sed -e 's/^\(.*\):\(.*\)$/file="\1"; package="\2"/'`
michael@13: rpm -q $package >/dev/null 2>&1 && rpm -e $package --allmatches || true
michael@13: rpm -q $package >/dev/null 2>&1 || rpm --import $prefix/etc/openpkg/$file || true
michael@13: done
michael@13: rpm -qa >/dev/null 2>&1
michael@13: }
michael@13:
michael@13: db_fixate () {
michael@13: # fix ownership and permissions of (especially newly created)
michael@13: # RPM database files to make sure they are consistent
michael@13: verbose 2 "making sure RPM database files have consistent attributes"
michael@13: for dbfile in $dbfiles; do
michael@428: eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\):\(.*\)$/dbapi="\1"; dbtype="\2"; dbfile="\3";/'`
michael@428: if [ ".$dbapi" != ".$DBAPI" ]; then
michael@428: continue
michael@428: fi
michael@13: verbose 3 "fixating DB file: $dbpath/$dbfile ($dbtype)"
michael@428: if [ -f $dbpath/$dbfile ]; then
michael@428: chown $musr:$mgrp $dbpath/$dbfile 2>/dev/null || true
michael@428: chmod 644 $dbpath/$dbfile 2>/dev/null || true
michael@428: fi
michael@13: done
michael@428: chown $musr:$mgrp $dbpath/VERSION 2>/dev/null || true
michael@428: chmod 644 $dbpath/VERSION 2>/dev/null || true
michael@13: }
michael@13:
michael@13: db_list () {
michael@13: # list all database files
michael@13: for dbfile in $dbfiles; do
michael@428: eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\):\(.*\)$/dbapi="\1"; dbtype="\2"; dbfile="\3";/'`
michael@428: if [ ".$dbapi" != ".$DBAPI" ]; then
michael@428: continue
michael@428: fi
michael@428: if [ ! -f "$dbpath/$dbfile" ]; then
michael@428: continue
michael@428: fi
michael@13: if [ $verbose -eq 0 ]; then
michael@13: echo "$dbfile"
michael@13: elif [ $verbose -eq 1 ]; then
michael@13: echo "$dbpath/$dbfile"
michael@13: elif [ $verbose -ge 2 ]; then
michael@13: echo "$dbpath/$dbfile ($dbtype)"
michael@13: fi
michael@13: done
michael@13: }
michael@13:
michael@13: ##
michael@13: ## ENVIRONMENT CONSISTENCY CHECKS
michael@13: ##
michael@13:
michael@13: # sanity check OpenPKG instance
michael@13: if [ ".$prefix" = . ]; then
michael@13: error "OpenPKG instance directory is empty"
michael@13: fi
michael@13: if [ ! -d $prefix ]; then
michael@13: error "OpenPKG instance directory \"$prefix\" not found"
michael@13: fi
michael@13: if [ ! -x $prefix/bin/openpkg ]; then
michael@13: error "OpenPKG instance directory \"$prefix\" not valid"
michael@13: fi
michael@13:
michael@13: # sanity check OpenPKG RPM database
michael@13: if [ ".$dbpath" = . ]; then
michael@13: error "OpenPKG RPM database directory is empty"
michael@13: fi
michael@13: if [ ! -d $dbpath ]; then
michael@13: error "OpenPKG RPM database directory \"$dbpath\" not found"
michael@13: fi
michael@13: if [ ! -w $dbpath ]; then
michael@13: error "OpenPKG RPM database directory \"$dbpath\" not writable"
michael@13: fi
michael@13:
michael@428: # load database information
michael@428: rpmdb_version_load
michael@428:
michael@13: ##
michael@13: ## DISPATCH INTO COMMANDS
michael@13: ##
michael@13:
michael@13: case "$mode" in
michael@13: build )
michael@13: verbose 1 "BUILDING NEW RPM DATABASE FROM SCRATCH ($dbpath)"
michael@13: db_remove
michael@13: db_init
michael@428: db_fixate
michael@428: db_operate
michael@428: ;;
michael@428:
michael@428: migrate )
michael@428: verbose 1 "MIGRATING RPM DATABASE FORMAT ($dbpath)"
michael@428: db_unbreak
michael@428: db_convert
michael@428: db_migrate
michael@13: db_rebuild
michael@13: db_fixate
michael@13: db_operate
michael@13: ;;
michael@13:
michael@13: rebuild )
michael@13: verbose 1 "REBUILDING NEW FROM OLD RPM DATABASE ($dbpath)"
michael@13: db_unbreak
michael@428: db_convert
michael@13: db_reload
michael@13: db_rebuild
michael@13: db_fixate
michael@13: db_operate
michael@13: ;;
michael@13:
michael@13: cleanup )
michael@13: verbose 1 "CLEANING UP EXISTING RPM DATABASE ($dbpath)"
michael@13: db_unbreak
michael@428: db_convert
michael@13: db_rebuild
michael@13: db_fixate
michael@13: db_operate
michael@13: ;;
michael@13:
michael@13: fixate )
michael@13: verbose 1 "FIXATING EXISTING RPM DATABASE ($dbpath)"
michael@13: db_fixate
michael@13: ;;
michael@13:
michael@13: list )
michael@13: db_list
michael@13: ;;
michael@13: esac
michael@13:
michael@13: exit 0
michael@13: