# This file is meant to be sourced by runme commands # vim:set syntax=sh: # $Id: //depot/ops/corp/grhat/tree/var/lib/grhat/funcs#29 $ # 2004-01-09 - External release v1.0 / Cleaned up release for LCA 2004 name=`echo "$0" | sed "s/^\.\///"` if [ `echo "$name" | grep "/" | wc -l` -gt 0 ]; then echo "You must run $name from the directory it is in" exit 99 fi if [ -f /etc/sysconfig/getupdates ]; then . /etc/sysconfig/getupdates else echo "DIE: can't read /etc/sysconfig/getupdates" exit 98 fi if [ -z "$WORKDIR" ]; then echo "DIE: can't read WORKDIR from /etc/sysconfig/getupdates" exit 97 fi LEADGOLD=`echo $TARGET | sed 's/^.*\(....\)$/\1/'` if [ -z $LEADGOLD ]; then echo "DIE: couldn't compute LEADGOLD from /etc/sysconfig/getupdates (TARGET is $TARGET)" exit 96 fi MACHTYPE=`echo $TARGET | sed 's/^\(.*\)....$/\1/'` if [ -z $MACHTYPE ]; then echo "DIE: couldn't compute MACHTYPE from /etc/sysconfig/getupdates (TARGET is $TARGET)" exit 95 fi CHANGEDFILES=$WORKDIR/state/changedfiles if [ -z $SHOWROTATED ]; then SHOWROTATED="^Rotated" fi log () { DATE=`date "+%Y/%m/%d %H:%M:%S ($$)"` echo "$DATE LOG: $1" } removefiles () { for file in "$@" do if [ -f "$file" ]; then echo "REMOVEFILE: $file" chattr -i "$file" /bin/rm "$file" fi done } rotatefile () { if [ "$#" -ne 1 ]; then echo "DIE: rotatefile file (called with $# args: $@)" >&2 exit 199 fi if [ -L "$1" ]; then echo "ROTATEFILE: $1 is a link, moving to $1.oldlink" /bin/mv -f "$1"{,.oldlink} else # savelog is a no-op if file.redhat doesn't exist savelog -c 9 -l "$1".redhat | grep -v $SHOWROTATED || true # In many cases, we will try to rotate out an immutable file. # Account for that chattr -i "$1" /bin/mv "$1"{,.redhat} fi } installlink () { if [ "$#" -ne 3 ]; then echo "DIE: installlink dir src dest (called with $# args: $@)" >&2 return 299 fi DIR="$1" SRC="$2" TARGET="$3" DIR="`echo \"$DIR\" | sed "s/\/\//\//g"`" pushd "$DIR" >/dev/null if [ -e "$TARGET" ] ; then chattr -i "$TARGET" if [ ! -L "$TARGET" ]; then echo "INSTALLLINK: $TARGET exists but isn't a link, rotating and linking $SRC to $TARGET" rotatefile "$TARGET" ln -s "$SRC" "$TARGET" else echo "INSTALLLINK: $TARGET exists and is a link, leaving alone" fi else if [ -L "$TARGET" ] ; then echo "INSTALLLINK: $TARGET is dangling. Force-linking $SRC to $TARGET" ln -snf "$SRC" "$TARGET" else echo "INSTALLLINK: $TARGET doesn't exist. Linking $SRC to $TARGET" ln -s "$SRC" "$TARGET" fi fi popd >/dev/null } chattr () { if [ "$#" -ne 2 ]; then echo "DIE: chattr [-|+]flags file (called with $# args: $@)" >&2 exit 399 fi if [ -L $2 ]; then echo "CHATTR: Not running chattr $1 $2, $2 is a symlink" return 0 fi /usr/bin/chattr $1 "$2" return $? } lsattr () { if [ "$#" -ne 1 ]; then echo "DIE: chattr file (called with $# args: $@)" >&2 exit 499 fi if [ -L $1 ]; then return 0 fi /usr/bin/lsattr "$1" return $? } # So, here's what we do: # 0) If the destination doesn't exist, the file is installed as immutable # 1) If the source and destination are identical (and aren't symlinks), nothing # happens # 2) If we we called with -m [Y|md5] # 2.1) If -m Y, install the new file as mutable (modifiable), regardless # of what the old file mode was (rotating the old file). This is most # likely bad, because it will forcibly rotate out a user modified file # 2.2) If -m md5 and matches the destination file, we rotate it out and # replace it. This is for replacing (after the initial install) a file # that a user could have modified (you make sure the md5 matches the # initial value from the initial RPM) # 2.3) If the md5 doesn't match, you're trying to replace a file you were # not expecting, so nothing happens # 3) If the destination is mutable # 3.1) if targetfile was already replaced (recorded in CHANGEDFILES), we # leave it alone # 3.2) otherwise, we replace the file once, and record this in CHANGEDFILES. # the idea is only to replace the file once (use this for system files # that you replace once, but if the user makes the file mutable and # modifies it later, we won't touch it the second time) # 3.2.1) If called with -o, we overwrite the original file (you probably # don't want to do this, unless you can't afford the rotation # (like in /etc/cron.d) # 3.2.2) the default case is otherwise to rotate the file we move out # 4) If the destination is immutable # 4.1) if we weren't called with -r, we replace the target in place # 4.2) if we were called with -r (ROT=y), we rotate out the file we replace installfile () { ROT=N OVR=N MU=N if [ z"$1" = z-r ]; then ROT=Y shift fi if [ z"$1" = z-o ]; then OVR=Y shift fi # -m, for mutable, should only be used once to replace a file while leaving # replacement mutable. You have to provide the md5sum of the file you are # hoping to replace. If the sum doesn't match, the file will not be replaced # -m Y: Do not set the file mutable, but rotate it regardless # You probably don't want to do this unless you really know why # (users will try to modify your file and won't know it can get # rotated again if you changeset runs again) # -m md5: Set mutable, but rotate only if md5sum of old file matches md5 if [ z"$1" = z-m ]; then shift # This loads the md5sum of the file we're hoping to replace # (or Y, if we are rotating regardless (dangerous) MU=$1 shift fi if [ "$#" -ne 2 -a "$#" -ne 3 ]; then echo "DIE: installfile sourcedir file [destdir] (called with \"$*\")" >&2 return 399 fi FILE="$2" SRC="$1" if [ ! -z $3 ]; then DEST="$3" else DEST="$1" fi # This allows FILE to be set to '*' for srcfile in files/$SRC/$FILE do file="`basename $srcfile`" targetfile="`echo \"/$DEST/$file\" | sed "s/\/\//\//g"`" if [ -e "$targetfile" ]; then DESTIM=`lsattr "$targetfile" | cut -b5` if ! test -L "$srcfile" && ! test -L "$targetfile" && cmp -s "$srcfile" "$targetfile"; then echo "FILEINSTALL NO: Not installing $targetfile, identical to destination" # We delete the source so that we can survey if # source files were left behind later /bin/rm "$srcfile" elif [ z"$MU" != zN ]; then if [ z"$MU" = zY ]; then echo "FILEINSTALL YES: Installing $targetfile as mutable (directly editable)" rotatefile "$targetfile" /bin/cp -a "$srcfile" "$targetfile" chmod u+w "$targetfile" /bin/rm "$srcfile" else MD5=`md5sum "$targetfile" | awk '{print $1}'` if [ z$MD5 = z$MU ]; then echo "FILEINSTALL YES: $targetfile matches given MD5SUM of $MD5, replacing with given file" rotatefile "$targetfile" /bin/cp -a "$srcfile" "$targetfile" chmod u+w "$targetfile" /bin/rm "$srcfile" else echo "FILEINSTALL NO (WARN): $targetfile doesn't match MD5SUM of $MU (MD5SUM is $MD5), not replacing with given file" # We delete the source so that we can survey if # source files were left behind later /bin/rm "$srcfile" fi fi elif [ z"$DESTIM" != zi ]; then if grep -q "$targetfile" $CHANGEDFILES; then if [ ! -L "$targetfile" ]; then echo "FILEINSTALL NO (WARN): Not rotating/overwriting user modified file $targetfile (was mutable)" fi # We delete the source so that we can survey if source # files were left behind later /bin/rm "$srcfile" else echo "FILEINSTALL YES: Very first install of $targetfile (destination was mutable)" chattr -i "$targetfile" if [ $OVR = N ]; then rotatefile "$targetfile" fi /bin/cp -a "$srcfile" "$targetfile" # We delete the source so that we can survey if # source files were left behind later /bin/rm "$srcfile" # Hint that the file is automatically maintained chattr +i "$targetfile" # This is how we track that we updated the file once # after that, if it's mutable, it's the user who changed it echo "$targetfile" >> $CHANGEDFILES fi else if [ $ROT = Y ]; then echo "FILEINSTALL YES: $targetfile installed after rotating old file" rotatefile "$targetfile" /bin/cp -a "$srcfile" "$targetfile" # We delete the source so that we can survey if # source files were left behind later /bin/rm "$srcfile" # Hint that the file is automatically maintained chattr +i "$targetfile" else echo "FILEINSTALL YES: $targetfile installed by overwriting old file" chattr -i "$targetfile" # updating getupdates causes the script to run itself while # it is being modified, so we unlink the destination # first so that bash can run the unlinked version while # we install the new one /bin/rm "$targetfile" /bin/cp -a "$srcfile" "$targetfile" # We delete the source so that we can survey if # source files were left behind later /bin/rm "$srcfile" # Hint that the file is automatically maintained chattr +i "$targetfile" fi fi else echo "FILEINSTALL YES: $targetfile newly installed" /bin/cp -a "$srcfile" "$targetfile" # We delete the source so that we can survey if # source files were left behind later /bin/rm "$srcfile" # Hint that the file is automatically maintained chattr +i "$targetfile" fi done } rpm () { TMPFILE=`mktemp /tmp/rpm$$.XXXXXX` || return 1 ( ( set +e; /bin/rpm "$@" 3>&2 2>&1 1>&3; echo $? > $TMPFILE ) | sed -e "s/\(warning:.*NOKEY, key ID.*\)/ok->\1/" -e "s/\(warning:.*\.rpmorig\)/ok->\1/" -e "s/\(warning:.*\.rpm[ns][ea][wv]\)/ok->\1/" -e "s/\(warning:.*skipping V4 signature\)/ok->\1/" ) 3>&2 2>&1 1>&3 RET=`cat $TMPFILE` /bin/rm $TMPFILE return $RET } apt-get () { TMPFILE=`mktemp /tmp/apt$$_a.XXXXXX` || return 1 TMPFILE2=`mktemp /tmp/apt$$_b.XXXXXX` || return 1 ( ( set +e; /usr/bin/apt-get -q --trivial-only "$@" 3>&2 2>&1 1>&3; echo $? > $TMPFILE ) | sed -e "s/\(warning:.*NOKEY, key ID.*\)/ok->\1/" -e "s/\(warning:.*\.rpmorig\)/ok->\1/" -e "s/\(warning:.*\.rpm[ns][ea][wv]\)/ok->\1/" -e "s/\(warning:.*skipping V4 signature\)/ok->\1/" ) 3>&2 2>&1 1>&3 | tee $TMPFILE2 RET=`cat $TMPFILE` if [ z$RET != z0 ]; then cat $TMPFILE2 >&2 fi /bin/rm $TMPFILE $TMPFILE2 return $RET } apt-get-force () { TMPFILE=`mktemp /tmp/apt$$_a.XXXXXX` || return 1 TMPFILE2=`mktemp /tmp/apt$$_b.XXXXXX` || return 1 ( ( set +e; /usr/bin/apt-get -q -y "$@" 3>&2 2>&1 1>&3; echo $? > $TMPFILE ) | sed -e "s/\(warning:.*NOKEY, key ID.*\)/ok->\1/" -e "s/\(warning:.*\.rpm[ns][ea][wv]\)/ok->\1/" -e "s/\(warning:.*\.rpmorig\)/ok->\1/" -e "s/\(warning:.*skipping V4 signature\)/ok->\1/" ) 3>&2 2>&1 1>&3 | tee $TMPFILE2 RET=`cat $TMPFILE` if [ z$RET != z0 ]; then cat $TMPFILE2 >&2 fi /bin/rm $TMPFILE $TMPFILE2 return $RET } # # example usage: # if apt-get-install-check pkgA pgkB pkgC-; then apt-get-force install pkgÀ pkgB pkgC-; fi # The above would force install of pkgA and pkgB and removal of pkgC # iff no packages other than A,B, and C would be affected. Append "-" # to packages names to be removed. Function accepts version numbers # prefaced by "=" but does not verify that apt would affect that # particular version (it merely strips them off). # apt-get-install-check () { TMPFILE=`mktemp /tmp/apt$$_a.XXXXXX` || return 1 TMPFILE2=`mktemp /tmp/apt$$_b.XXXXXX` || return 1 ( ( set +e; /usr/bin/apt-get -s install "$@" 3>&2 2>&1 1>&3; echo $? > $TMPFILE ) | sed -e "s/\(warning:.*NOKEY, key ID.*\)/ok->\1/" -e "s/\(warning:.*\.rpmorig\)/ok->\1/" -e "s/\(warning:.*\.rpm[ns][ea][wv]\)/ok->\1/" -e "s/\(warning:.*skippingV4 signature\)/ok->\1/" ) 3>&2 2>&1 1>&3 | tee $TMPFILE2 RET=`cat $TMPFILE` if [ z$RET != z0 ]; then cat $TMPFILE2 >&2 /bin/rm $TMPFILE $TMPFILE2 return $RET fi installregex="^$" removeregex="^$" for arg in "$@" do lastchar=${arg:(-1):1} if [ z"$lastchar" == z- ]; then argnodash=${arg%?} argnoversion=${argnodash%%[=]*} removeregex=$removeregex"|^Remv $argnoversion " else argnoversion=${arg%%[=]*} installregex=$installregex"|^Inst $argnoversion " fi done if egrep "^Inst " $TMPFILE2 | egrep -qv "$installregex" || egrep "^Remv " $TMPFILE2 | egrep -qv "$removeregex"; then cat $TMPFILE2 >&2 echo "apt-get-install-check: Files other than $@ would be installed or removed, failing sanity check" >&2 RET=599 else RET=0 fi /bin/rm $TMPFILE $TMPFILE2 return $RET }