#!@l_prefix@/lib/openpkg/bash
##
##  rpmdb -- OpenPKG RPM Database Administration Utility
##  Copyright (c) 2000-2006 OpenPKG Foundation e.V. <http://openpkg.net/>
##  Copyright (c) 2000-2006 Ralf S. Engelschall <http://engelschall.com/>
##
##  Permission to use, copy, modify, and distribute this software for
##  any purpose with or without fee is hereby granted, provided that
##  the above copyright notice and this permission notice appear in all
##  copies.
##
##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
##  SUCH DAMAGE.
##

#   program information
progname="rpmdb"

#   configuration defaults
help=""
prefix="@l_prefix@"
dbpath=""
rpm=""
musr="@l_musr@"
mgrp="@l_mgrp@"
mode=""
force=no
verbose=2

##
##  PARSE COMMAND LINE
##

#   iterate over argument line
for opt
do
    case $opt in
        -*=*) arg=`echo "$opt" | sed 's/^[-_a-zA-Z0-9]*=//'` ;;
           *) arg='' ;;
    esac
    case $opt in
        -h|--help      ) help="Usage"  ;;
        -B|--build     ) mode=build    ;;
        -R|--rebuild   ) mode=rebuild  ;;
        -C|--cleanup   ) mode=cleanup  ;;
        -F|--fixate    ) mode=fixate   ;;
        -L|--list      ) mode=list     ;;
        -f|--force     ) force=yes     ;;
        -q|--quiet     ) verbose=0     ;;
        -v|--verbose   ) verbose=`expr $verbose + 1` ;;
        --prefix=*     ) prefix=$arg   ;;
        --dbpath=*     ) dbpath=$arg   ;;
        --rpm=*        ) rpm=$arg      ;;
        --musr=*       ) musr=$arg     ;;
        --mgrp=*       ) mgrp=$arg     ;;
        *              ) help="Invalid option \`$opt'"; break ;;
    esac
done

#   makre sure exactly one run-time mode is specified
if [ ".$mode" = . ]; then
    help="No or invalid run-time mode specified"
fi

#   error or usage message
if [ ".$help" != . ]; then
    if [ ".$help" != ".Usage" ]; then
        echo "$progname:ERROR: $help" 1>&2
    fi
    cat 1>&2 <<EOT
$progname:USAGE: $progname [options]

  -h, --help        print usage message (this one)
  -B, --build       build new RPM database from scratch
  -R, --rebuild     rebuild new from old RPM database
  -C, --cleanup     cleanup existing RPM database
  -F, --fixate      fixate existing RPM database
  -L, --list        list all RPM database files
  -f, --force       operate in force mode (no save operations)
  -q, --quiet       operate in quiet mode (no verbose messages at all)
  -v, --verbose     operate in more verbose mode (increase verbose level)
  --prefix=PREFIX   use OpenPKG instance under PREFIX
  --dbpath=PATH     use OpenPKG RPM database under PATH
  --rpm=PROG        use OpenPKG RPM executable PROG
  --musr=USERNAME   use OpenPKG management user USERNAME
  --mgrp=GROUPNAME  use OpenPKG management group GROUPNAME

This is OpenPKG rpmdb, an RPM database administration utility, providing
lower-level maintainance functions for the Berkeley-DB 4.1 based RPM 4.2
database. It allows building new RPM databases from scratch, rebuilding
a new from an old RPM database (content dumping and reloading),
cleaning up problems on an existing RPM database (removal of DB region
files, etc) and fixating the files in an existing RPM database (file
attributes).

EOT
    if [ ".$help" != ".Usage" ]; then
        exit 2
    else
        exit 0
    fi
fi

#   delayed determination of variables
if [ ".$dbpath" = . ]; then
    dbpath="$prefix/RPM/DB"
fi
if [ ".$rpm" = . ]; then
    rpm="$prefix/bin/openpkg rpm"
fi

##
##  DATABASE FILE INFORMATION
##

dbfiles="
    hash:Basenames
    hash:Conflictname
    hash:Depends
    btree:Dirnames
    hash:Filemd5s
    hash:Group
    btree:Installtid
    hash:Name
    hash:Packages
    hash:Providename
    btree:Provideversion
    hash:Pubkeys
    hash:Requirename
    btree:Requireversion
    hash:Sha1header
    hash:Sigmd5
    hash:Triggername
    region:__db.001
    region:__db.002
    region:__db.003
    region:__db.004
    region:__db.005
    region:__db.006
    region:__db.007
    region:__db.008
    region:__db.009
"

##
##  HELPER FUNCTIONS
##

error () {
    echo "$progname:ERROR: $*" 1>&2
    exit 1
}

warning () {
    echo "$progname:WARNING: $*" 1>&2
}

verbose () {
    local level=$1
    shift
    if [ $level -le $verbose ]; then
        local lead=""
        case "$level" in
            1 ) lead="" ;;
            2 ) lead="" ;;
            3 ) lead="  " ;;
            * ) lead="    " ;;
        esac
        echo "$progname: $lead$*" 1>&2
    fi
}

rpm () {
    local opts="--dbpath `echo $dbpath | sed -e 's;/*$;;' -e 's;$;/;'`"
    if [ ".$force" = .yes ]; then
        opts="$opts --define '__dbi_private yes'"
    fi
    verbose 3 "run: $rpm $opts $@"
    eval "$rpm $opts \"\$@\""
}

rpmdb_load () {
    $prefix/lib/openpkg/rpmdb_load ${1+"$@"}
}

rpmdb_dump () {
    $prefix/lib/openpkg/rpmdb_dump ${1+"$@"}
}

##
##  RPM DATABASE OPERATIONS
##

db_wait () {
    #   wait until RPM has released the database in case we are called
    #   asynchronously to RPM (especially important when upgrading from
    #   RPM 4.0 where concurrent access is still not possible)
    verbose 2 "waiting for RPM database to be available"
    local i=0
    while [ $i -lt 10 ]; do
        if $prefix/libexec/openpkg/rpm -q openpkg >/dev/null 2>&1; then
            break
        fi
        sleep 1
        i=`expr $i + 1`
    done
    if [ $i -eq 10 ]; then
        exit 1
    else
        exit 0
    fi
}

db_remove () {
    #   remove all known files
    verbose 2 "removing (possibly existing) old RPM database DB files"
    for dbfile in $dbfiles; do
        eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\)$/dbtype="\1"; dbfile="\2";/'`
        verbose 3 "removing database file: $dbpath/$dbfile ($dbtype)"
        rm -f $dbpath/$dbfile
    done
}

db_init () {
    #   perform official "initdb" operation
    #   (is mostly a no-operation in RPM 4.2, but anyway)
    verbose 2 "creating new RPM database (built-in RPM procedure)"
    rpm --initdb

    #   perform some real RPM work, so more database files
    #   magically spring into existence
    verbose 2 "operating on new RPM database"
    rpm --import $prefix/etc/openpkg/openpkg.pgp || true
    rpm -e gpg-pubkey-63c4cb9f-3c591eda --allmatches || true

    #   perform official "rebuilddb" operation in the hope it
    #   creates even more database files now that we have some content
    verbose 2 "rebuilding new RPM database (built-in RPM procedure)"
    rpm --rebuilddb
}

db_unbreak () {
    #   cleanup DB region files
    verbose 2 "cleaning up RPM database DB region files"
    for dbfile in $dbfiles; do
        eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\)$/dbtype="\1"; dbfile="\2";/'`
        if [ ".$dbtype" = .region ]; then
            verbose 3 "cleaning up DB file: $dbpath/$dbfile ($dbtype)"
            rm -f $dbpath/$dbfile || true
            touch $dbpath/$dbfile || true
        fi
    done
}

db_extend () {
    #   make sure all RPM database DB files are present
    verbose 2 "making sure RPM database contains all possible DB files"
    for dbfile in $dbfiles; do
        eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\)$/dbtype="\1"; dbfile="\2";/'`
        if [ ! -f $dbpath/$dbfile ]; then
            verbose 3 "creating DB file: $dbpath/$dbfile ($dbtype)"
            if [ ".$dbtype" = .hash -o ".$dbtype" = .btree ]; then
                ( echo "VERSION=3"
                  echo "format=bytevalue"
                  echo "type=$dbtype"
                  echo "db_pagesize=16384"
                  echo "HEADER=END"
                  echo "DATA=END"
                ) | rpmdb_load $dbpath/$dbfile || true
            else
                touch $dbpath/$dbfile || true
            fi
        fi
    done
}

db_reload () {
    #   rebuilding new from old RPM database DB files by dumping and
    #   reloading their entire content
    verbose 2 "dumping and reloading RPM database DB file contents"
    for dbfile in $dbfiles; do
        eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\)$/dbtype="\1"; dbfile="\2";/'`
        verbose 3 "dumping and reloading DB file: $dbpath/$dbfile ($dbtype)"
        if [ -f $dbpath/$dbfile ]; then
            if [ ".$dbtype" = .hash -o ".$dbtype" = .btree ]; then
                rpmdb_dump $dbpath/$dbfile |\
                rpmdb_load $dbpath/$dbfile.new
                rm -f $dbpath/$dbfile
                mv $dbpath/$dbfile.new $dbpath/$dbfile
            else
                rm -f $dbpath/$dbfile || true
                touch $dbpath/$dbfile || true
            fi
        fi
    done
}

db_rebuild () {
    #   perform official "rebuilddb" operation
    verbose 2 "rebuilding RPM database (built-in RPM procedure)"
    rpm --rebuilddb
}

db_operate () {
    #   perform some read/write operation on RPM database
    #   (we have no package available, but removing and reimporting
    #   the OpenPKG OpenPGP key is a harmless thing and always possible)
    verbose 2 "performing read/write operation on RPM database"
    rpm -q gpg-pubkey-63c4cb9f-3c591eda >/dev/null 2>&1 && \
        rpm -e gpg-pubkey-63c4cb9f-3c591eda --allmatches || true
    rpm -q gpg-pubkey-63c4cb9f-3c591eda >/dev/null 2>&1 || \
        rpm --import $prefix/etc/openpkg/openpkg.pgp || true
    rpm -qa >/dev/null 2>&1
}

db_fixate () {
    #   fix ownership and permissions of (especially newly created)
    #   RPM database files to make sure they are consistent
    verbose 2 "making sure RPM database files have consistent attributes"
    for dbfile in $dbfiles; do
        eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\)$/dbtype="\1"; dbfile="\2";/'`
        verbose 3 "fixating DB file: $dbpath/$dbfile ($dbtype)"
        chown $musr:$mgrp $dbpath/$dbfile 2>/dev/null || true
        chmod 644 $dbpath/$dbfile 2>/dev/null || true
    done
}

db_list () {
    #   list all database files
    for dbfile in $dbfiles; do
        eval `echo $dbfile | sed -e 's/^\(.*\):\(.*\)$/dbtype="\1"; dbfile="\2";/'`
        if [ $verbose -eq 0 ]; then
            echo "$dbfile"
        elif [ $verbose -eq 1 ]; then
            echo "$dbpath/$dbfile"
        elif [ $verbose -ge 2 ]; then
            echo "$dbpath/$dbfile ($dbtype)"
        fi
    done
}

##
##  ENVIRONMENT CONSISTENCY CHECKS
##

#   sanity check OpenPKG instance
if [ ".$prefix" = . ]; then
    error "OpenPKG instance directory is empty"
fi
if [ ! -d $prefix ]; then
    error "OpenPKG instance directory \"$prefix\" not found"
fi
if [ ! -x $prefix/bin/openpkg ]; then
    error "OpenPKG instance directory \"$prefix\" not valid"
fi

#   sanity check OpenPKG RPM database
if [ ".$dbpath" = . ]; then
    error "OpenPKG RPM database directory is empty"
fi
if [ ! -d $dbpath ]; then
    error "OpenPKG RPM database directory \"$dbpath\" not found"
fi
if [ ! -w $dbpath ]; then
    error "OpenPKG RPM database directory \"$dbpath\" not writable"
fi

##
##  DISPATCH INTO COMMANDS
##

case "$mode" in
    build )
        verbose 1 "BUILDING NEW RPM DATABASE FROM SCRATCH ($dbpath)"
        db_remove
        db_init
        db_extend
        db_rebuild
        db_operate
        db_fixate
        ;;

    rebuild )
        verbose 1 "REBUILDING NEW FROM OLD RPM DATABASE ($dbpath)"
        db_unbreak
        db_extend
        db_reload
        db_rebuild
        db_operate
        db_fixate
        ;;

    cleanup )
        verbose 1 "CLEANING UP EXISTING RPM DATABASE ($dbpath)"
        db_unbreak
        db_extend
        db_rebuild
        db_operate
        db_fixate
        ;;

    fixate )
        verbose 1 "FIXATING EXISTING RPM DATABASE ($dbpath)"
        db_extend
        db_fixate
        ;;

    list )
        db_list
        ;;
esac

exit 0

