Hi Tony, I was recently very frustrated by the add-rpm.py script alluded to in your very helpful Customized-installer mini-HOWTO, so I wrote my own addrpm script. It is a bash/shell script, so i expect it is easier to understand and hack about with. I have tested it and it does what I want it to (i.e., you can have openssh-*{2.9p1,3.0p1,3.3p1,3.4p1} all hanging about in the updates directory yet it will figure out that only the 3.4p1 version is worth trying to wedge into the real tree. If it gets confused it will ask for help. In case this is of use to you (and others on the kickstart list) here it is. As per usual, if it breaks you can keep the pieces. Paul Knowles. phone: 41 26 300 90 64 email: Paul.Knowles@xxxxxxxx Fax: 41 26 300 97 47 finger me at pexppc33.unifr.ch for more contact information ################################################## #!/bin/bash # # addrpm Adds RPM(s) to a Redhat Tree # Copyright (C) 2002 Paul Knowles <Paul.Knowles@xxxxxxxx> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # NOTE: I'm not a bash shell guru. Its syntax is weird # and often difficult to understand for comparisons # (something to do with interpreting > as redirection # versus comparison problems I expect). If there is a # real bash wizard with interest in correcting all the # weird ways I've made the comparisons work, I would # appreciate the comments. # # NOTE2: You are running this at your own risk. If it # deletes the contents of your hard disk, drinks all your # beer, and tries to shave your cat I will disavow having # ever seen it before in my life. # # I use a RedHat layout that looks like the tree pictured below. # This is very convenient since the updating commands are just # $> cd $UPDATES/redhat-updates # $> ncftpget -FRT ftp://really.fast.server/linux/mirror/.../redhat-updates/7.3 # and all is magically updated. In fact, cron does this for me at 3 am. # # The Redhat 7.3 tree is set out like: #redhat #`-- 7.3 # `-- en # `-- os # `-- i386 # |-- RedHat # | |-- RPMS # | `-- base # |-- SRPMS # |-- dosutils # | |-- autoboot # | |-- fips15c # | | |-- restorrb # | | `-- source # | |-- fips20 # | | |-- restorrb # | | `-- source # | |-- fipsdocs # | `-- rawritewin # |-- images # | |-- de # | |-- es # | |-- fr # | |-- it # | `-- pxeboot # `-- megroup <--- Medium Energy Group specific RPMs # # and the updates tree looks like # #redhat-updates #|-- 7.3 #| `-- en #| |-- megroup #| | |-- SRPMS #| | `-- i386 #| `-- os #| |-- SRPMS #| |-- athlon #| |-- i386 #| |-- i486 #| |-- i586 #| |-- i686 #| `-- noarch #|-- attic #| |-- SRPMS #| |-- athlon #| |-- i386 #| |-- i486 #| |-- i586 #| |-- i686 #| `-- noarch #`-- tosort # `-- movie # # This shell script has been very successful in sorting through all the # updates in 7.3/en/os/*/*.rpm, figuring out the latest in the bunch # and then replacing the RedHat/RPMS/*.rpm packages # # If you call it with an argument, it puts itself into test mode # and prints out what it would do. # # e.g., to really update the trees # $> addrpm # too see what it will do # $> addrpm poo #or ``1'' or ``t'' or ``flibber'' or ... # # see the lines: # export putdir=/home/updates/redhat/7.3/en/os/i386/RedHat/RPMS/ # export extradir=/home/updates/redhat/7.3/en/os/i386/megroup/ # export getdir=/home/updates/redhat-updates/7.3/en/os/$u # export atticdir=/home/updates/redhat-updates/attic/$u # and the big for loop over SRPMS i386 etc # if your directory structure is different. # # cheers, # P. Knowles, July 11, 2002 ###################################################### # the comparison function # called with two strings of form a.b.c.d.e.f # first is the champion, second is the challenger # return 0 if $2 is ``above'' $1, i.e., 1.2.3 is above # 1.2.2, and 3.0.0 is above 2.99.194 # return 2 if $1 and $2 have differing composition (punt value!) # i.e., comparing 3.0.0 and 3.0.0rc8 is a hard problem # 3.1.0 and 3.0.foo is not so much of a problem though # return 1 if $1 <= $2 # valcomp() { # echo Comparing champion $1 and challenger $2 if [ $1 == $2 ]; then return 1 fi # # bugger, we have to work declare -a champ declare -a chall champ=(`echo $1 | sed s/\\\./\ /g`) chall=(`echo $2 | sed s/\\\./\ /g`) # echo broke champ into ${champ[@]} # echo broke chall into ${chall[@]} if [ ${#champ[@]} -ne ${#chall[@]} ]; then # the strings don't have the same composition; punt # echo Punting due to length comparison return 2 fi local -i i=0 local -i j=${#champ[*]} for (( i=0 ; i < j ; i++ )) do # either we have just number to compare # or we must use strings dostringcomp=0 if [ ${chall[$i]} != `echo ${chall[$i]} | sed -e s/[[:alpha:]]//g` ]; then dostringcomp=$(( $dostringcomp + 1 )) fi if [ ${champ[$i]} != `echo ${champ[$i]} | sed -e s/[[:alpha:]]//g` ]; then dostringcomp=$(( $dostringcomp + 1 )) fi if [ "$dostringcomp" == "1" ]; then # must punt here, someone has letters, other just numbers # echo returning punt return 2 fi # # directly comparing ${champ[$i]} and ${chall[$i]} fails # some weird shell syntax problem I don't yet understand? # use this intermediate step form poo=${chall[$i]} bar=${champ[$i]} if [ $dostringcomp -eq 2 ]; then # compare via strings if [ "$poo" \> "$bar" ]; then # echo strings found that ${chall[$i]} is greater than ${champ[$i]} return 0 fi fi if [ $poo -gt $bar ]; then # echo numerically found ${chall[$i]} is greater than ${champ[$i]} return 0 elif [ $(( ${chall[$i]} == ${champ[$i]} )) == "1" ]; then # echo at i=$i, ${chall[$i]} is the same as ${champ[$i]} continue fi # this occurs when ${chall[$i]} is less than ${champ[$i]} # claiming ``equality'' is enough to ignore the rpm return 1 done # we should never get here: the initial comparison should catch this case! # at end, no seen difference return 1 } # # # doreplace() is called with the full pathways of the new file, # the old file, and the test flag doreplace() { new=$1 old=$2 local test=$3 if [ $test == "n" ]; then mv $old $atticdir/ || exit 1 cp $new `dirname $old`/ || exit 1 else echo mv $old $atticdir/ echo cp $new `dirname $old`/ fi } # # called when the program gets too confused # ask for operator intervention directly punt() { echo "Oops: comparing " echo " $2" echo " and" echo " $4" echo "is too confusing for me: Should I replace $4 with $2" echo "(default is no)?" ans="" read -r -p y/N -n1 ans if [ $ans == "y" ]; then echo echo O.k., upgrading to $1/$2 doreplace $1/$2 $3/$4 $test else echo echo O.k., skipping $2 fi } # # Done with function definitions # The action starts here # if [ ! -z $1 ]; then test="y" echo Test mode else test="n" fi export test # for u in SRPMS i386 i486 i586 i686 athlon do export putdir=/home/updates/redhat/7.3/en/os/i386/RedHat/RPMS/ export extradir=/home/updates/redhat/7.3/en/os/i386/megroup/ export getdir=/home/updates/redhat-updates/7.3/en/os/$u export atticdir=/home/updates/redhat-updates/attic/$u echo $getdir tag=$u if [ $u == "SRPMS" ]; then tag=src putdir=/home/updates/redhat/7.3/en/os/i386/SRPMS fi # # these are the possible replacement package names # there may be more than one replacement available # we need to find the latest one for f in `rpm -qp --qf '%{name} \n' $getdir/*.rpm | sort | uniq ` do echo echo $u:$f # the current version and release vcurr=`rpm -qp --qf '%{version}' $putdir/$f-[0123456789]*$tag.rpm` rcurr=`rpm -qp --qf '%{release}' $putdir/$f-[0123456789]*$tag.rpm` # the candidate replacements echo "Current version is: $vcurr-$rcurr" declare -a vlist declare -a rlist # # rpm doesn't separate error messages and regular output # so scuttling errors to /dev/null won't work here # If one of the .rpm files isn't a real rpm # the script will get very confused # since weird things will show up in the version and release lists vlist=(`rpm -qp --qf '%{version} ' $getdir/$f-[0123456789]*$tag.rpm`) rlist=(`rpm -qp --qf '%{release} ' $getdir/$f-[0123456789]*$tag.rpm`) echo "Candidate versions: ${vlist[@]}" echo "Candidate releases: ${rlist[@]}" tst=$((${#vlist[*]}+${#rlist[*]})) vi=0 ri=0 if [ $tst -gt 2 ]; then # loop over the versions first # need to find the biggest version and release of the bunch # set i to the index # # use vi as sentinal vi=-1 declare -i i=1 declare -i j=${#vlist[*]} declare -i verdex=0 for (( i=1 ; $(( i < j)) ; i++ )) do # echo testing ${vlist[$i]} valcomp ${vlist[$verdex]} ${vlist[$i]} ret=$? if [ $ret -eq 0 ] ; then # echo found ${vlist[$i]} bigger than ${vlist[$verdex]} verdex=$i reldex=$i vi=$i continue fi if [ $ret -eq 2 ] ; then echo ERROR: This is too hard a hard case for me break 2 # give up and get another file fi done if [ $vi == "-1" ]; then # versions are all the same declare -i i=1 declare -i j=${#rlist[*]} declare -i reldex=0 for (( i=1 ; $(( i < j)) ; i++ )) do # echo testing ${rlist[$i]} valcomp ${rlist[$reldex]} ${rlist[$i]} ret=$? if [ $ret -eq 0 ] ; then # echo found ${rlist[$i]} bigger than ${rlist[$reldex]} reldex=$i verdex=$i continue fi if [ $ret -eq 2 ] ; then echo ERROR: This is too hard a hard case for me break 2 # give up and get another file fi done fi echo Found ${vlist[$verdex]}-${rlist[$reldex]} is best candidate vi=$verdex ri=$reldex fi vcandidate=${vlist[$vi]} rcandidate=${rlist[$ri]} # # see if candidate should replace curr # three cases # - curr doesn't exists: ask user if we add the rpm # (and where: $putdir or $extradir) # - curr needs replacing: # move curr to atticdir then # cp candidate into place # - curr doesn't need replacing: do nothing # # # deal with the case that curr is missing # this is a bit of a hack since it doesn't # handle existing rpm's in $extradir very gracefully # you probably want to ``hand craft'' extradir in any case # if [ ! -f $putdir/$f-$vcurr-$rcurr.$tag.rpm -o -f $extradir/$f-$vcandidate-$rcandidate.$tag.rpm ]; then moved="" echo "Oops: $f doesn't exist in $tag flavour in $putdir" echo "or is already in $extradir" echo "Shall I add it to $putdir" echo "(default is no)?" read -r -p y/N -n1 ans if [ $ans == "y" ]; then ans="" moved=1 echo echo O.k., Adding $f-$vcandidate-$rcandidate.$tag.rpm if [ $test == "n" ]; then cp $getdir/$f-$vcandidate-$rcandidate.$tag.rpm $putdir/ || exit 1 else echo cp $getdir/$f-$vcandidate-$rcandidate.$tag.rpm $putdir/ fi # else echo echo "Shall I add it to $extradir" echo "(default is no)?" read -r -p y/N -n1 ans if [ $ans == y ]; then ans="" moved=1 echo echo O.k., Adding $f-$vcandidate-$rcandidate.$tag.rpm if [ $test == "n" ]; then cp $getdir/$f-$vcandidate-$rcandidate.$tag.rpm $extradir/ || exit 1 else echo cp $getdir/$f-$vcandidate-$rcandidate.$tag.rpm $extradir/ fi fi fi if [ -z $moved ]; then echo echo O.k., skipping $f-$vcandidate-$rcandidate.$tag.rpm fi # # finished with name isolated $f, get another name now continue fi # # because of the continue above, we know curr and candidate both exist valcomp $vcurr $vcandidate ret=$? if [ $ret -eq 0 ] ; then echo $f-$vcandidate-$rcandidate.$tag.rpm replaces $f-$vcurr-$rcurr.$tag.rpm doreplace $getdir/$f-$vcandidate-$rcandidate.$tag.rpm $putdir/$f-$vcurr-$rcurr.$tag.rpm $test continue fi if [ $ret -eq 2 ] ; then punt $getdir $f-$vcandidate-$rcandidate.$tag.rpm $putdir $f-$vcurr-$rcurr.$tag.rpm continue fi # # if we got here, $ret=1, so versions are the same # compare releases valcomp $rcurr $rcandidate ret=$? if [ $ret -eq 0 ] ; then echo $f-$vcandidate-$rcandidate.$tag.rpm replaces $f-$vcurr-$rcurr.$tag.rpm doreplace $getdir/$f-$vcandidate-$rcandidate.$tag.rpm $putdir/$f-$vcurr-$rcurr.$tag.rpm $test continue fi if [ $ret -eq 2 ] ; then punt $getdir $f-$vcandidate-$rcandidate.$tag.rpm $putdir $f-$vcurr-$rcurr.$tag.rpm continue fi # # if we get here both versions and releases # are the same; this means there is nothing to do. echo No reason to change $f-$vcurr-$rcurr.$tag.rpm echo done done