-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrpmcompare.sh
executable file
·549 lines (488 loc) · 16.5 KB
/
rpmcompare.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
#!/bin/bash
# rpmcompare.sh was tmverifyrpms.sh
# from http://mirror.centos.org/centos/4/build/distro/tmverifyrpms
# Check a set of rpms against a reference.
# Ideas from David Cox's perl script ported to bash
# creates tmverifyrpms.log in repository where it is run in the form of:
# rpmname-cmp OK|OK-PERFECT|FAIL ref:refsize +/-:sizediffabs %:sizediff%
# Where
# - refsize is the rpm -q --qf %{SIZE} of the ref rpm
# - sizediff% is the % difference between cmp and ref
# - sizediffabs is the difference in bytes
# If there are library, or file name/perm differences, create
# reports/rpm.out
#
# Version 4.1.5-CentOS
# 2007-02-27 Johnny Hughes <[email protected]>
# - Modified so that it will match some of the standard file
# extensions we use (.centos, .c4. .el4.centos) with their
# reference counterparts w/o those extensions
# 2005-07-13 Johnny Hughes <[email protected]>
# - Modified David's great script to run standalone
# for using in CentOS compares.
# - Removed many of the features (run in background,
# srpms maps, etc.) which are not needed in this
# implementation of the script
#
# You can get all of David's tmtools as a group
# from the /tmtools directory of any taolinux
# mirror. See http://www.taolinux.com for details
#
# Thanks to David for a great script :)
#
# Version 4.1.4
# 2004-12-23 David L. Parsley <[email protected]>
# - Sort rpm --requires output
# 2004-12-21 [email protected]
# - Findrepo & cd there first (to run in subdirs)
# - Put error checking before the fork-to-background
# 2004-12-03 [email protected]
# - Discard rpm errors
# - Fix sign error in SZDIFF
# 2004-11-30 [email protected]
# - Strip (xxx) from end (ld-linux.so mainly)
# - Add checking of rpm --requires
# 2004-09-30 David L. Parsley <[email protected]>
# - use mktemp -u, or files will be mode 600
# 2004-05-16 David L. Parsley <[email protected]>
# - Add user & group to fingerprinting
# - Strip right-hand-side of library linkages (after =>)
# - dump stderr by default (can override locally)
# 2004-05-01 David L. Parsley <[email protected]>j
# - use rpm --dump for files, can run unpriv
#set -x
#
# This has been modified to become a standalone compare tool
# by Johnny Hughes for CentOS compares on 7/13/2005
#
# more modifications at CERN:
# - take some input arguments to cope with different layouts, allow for several directories to compare with
# - remove dependency on current directory, quote everything to cope with whitespace in path
# - make "workdir" temporary.
# - extend patterns for modified RPM names to include SL/CERN
# - check rpm integrity+signatures
# - summary check at end
# - optionally skip debuginfo RPMs from comparison (since they need explicit mirroring from ftp.redhat.com)
STARTDIR=`pwd`
#This is where the reference files are
REFDIRS="$STARTDIR/REFERENCE"
#This is the directory with files to be compared
CMPDIR="$STARTDIR/COMPARE"
#Reporting goes here
RPTDIR="$STARTDIR/rpmcomparereports"
# optional: only consider a subset/pattern
RPMPATTERN='*.rpm'
# optional: want signed RPMs
RPMNEEDSIGN=
# optional: insist on this signing key
RPMGPGKEY=
# indicator that something bad was found.
GLOBALFAIL=0
# dont bother with really old RPMs
RPMAGE=
# dont bother with debuginfo
SKIPDEBUG=
# prefer getting a real mail?
MAILTO=
# SKIP report into a file?
SKIPLIST=
#percentage to use to determine if the filesize is ok
PERCENTDIFFOK=10
#log file name
LOGFILE=$RPTDIR/rpmcompare.log
rpmarch(){
STRIPPED=${1%.rpm}
echo ${STRIPPED##*.}
}
genfilelist(){
rpm -qp --dump "$1" 2>/dev/null | LC_ALL=C sort | while read DUMPLINE
do
DUMPLINE=${DUMPLINE/ [01] [01] /TMLINESEP}
FSYMLINK=${DUMPLINE#*TMLINESEP* }
DUMPLINE=${DUMPLINE%TMLINESEP*}
FILENAME="${DUMPLINE% * * * * * *}"
REST=${DUMPLINE#$FILENAME }
set $REST
echo "$FILENAME mode:$4 owner:$5 group:$6 linkto:$FSYMLINK"
done
}
comprpms(){
CMPRPM="$1"
REFRPM="$2"
MODIFIED="$3"
RPM=`basename "$CMPRPM"`
PKGOK="OK"
CMPRPMDIR=`mktemp -t -d cmprpm.XXXXXX`
REFRPMDIR=`mktemp -t -d refrpm.XXXXXX`
WORKDIR=`mktemp -t -d workdir.XXXXXX`
REASON=""
rm -f "$RPTDIR/$RPM.out"
rpm2cpio "$CMPRPM" | ( cd "$CMPRPMDIR"; cpio -id --no-absolute-filenames --quiet )
rpm2cpio "$REFRPM" | ( cd "$REFRPMDIR"; cpio -id --no-absolute-filenames --quiet )
cd "$CMPRPMDIR"
find . ! -type d | while read FILENAME
do
if [ -x "$FILENAME" -a -f "$FILENAME" ]
then
echo $FILENAME >> $WORKDIR/CMPDIR-libs
ldd "$FILENAME" 2>&1 | LC_ALL=C sort >> "$WORKDIR/CMPDIR-libs"
# check for libraries compiled without -fPIC, SELinux issue
case "$FILENAME" in
*.so*)
eu-findtextrel "$FILENAME" >& /dev/null && echo "eu-findtextrel: $FILENAME needs text relocation" >> "$WORKDIR/CMPDIR-libs"
;;
esac
fi
done
cd "$REFRPMDIR"
find . ! -type d | while read FILENAME
do
if [ -x "$FILENAME" -a -f "$FILENAME" ]
then
echo "$FILENAME" >> "$WORKDIR/REFDIR-libs"
ldd "$FILENAME" 2>&1 | LC_ALL=C sort >> "$WORKDIR/REFDIR-libs"
# check for libraries compiled without -fPIC, SELinux issue
case "$FILENAME" in
*.so*)
eu-findtextrel "$FILENAME" >& /dev/null && echo "eu-findtextrel: $FILENAME needs text relocation" >> "$WORKDIR/CMPDIR-libs"
;;
esac
fi
done
genfilelist "$CMPRPM" > "$WORKDIR/CMPDIR-list"
genfilelist "$REFRPM" > "$WORKDIR/REFDIR-list"
diff --unified=1 "$WORKDIR/CMPDIR-list" "$WORKDIR/REFDIR-list" > "$WORKDIR/rpmdiff"
if [ $? -eq 1 ]
then
if [ "$MODIFIED" = 0 ]
then
PKGOK="FAIL"
else
PKGOK="WARN"
fi
REASON="$REASON fingerprints"
RPMNAME=`basename $CMPRPM`
# echo "$RPMNAME:Differing file fingerprints:" >> $LOGFILE
echo "Differing file fingerprints:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/rpmdiff" >> "$RPTDIR/$RPM.out"
fi
rpm -qp --requires "$CMPRPM" 2>/dev/null | LC_ALL=C sort >"$WORKDIR/CMPDIR-req"
rpm -qp --requires "$REFRPM" 2>/dev/null | LC_ALL=C sort >"$WORKDIR/REFDIR-req"
diff --unified=1 "$WORKDIR/CMPDIR-req" "$WORKDIR/REFDIR-req" > "$WORKDIR/reqdiff"
if [ $? -eq 1 -a "$MODIFIED" -eq 0 ]
then
if [ "$MODIFIED" = 0 ]
then
PKGOK="FAIL"
else
PKGOK="WARN"
fi
REASON="$REASON RPM-requires"
# echo "$CMPRPM:Differing rpm requires requirements:" >> $LOGFILE
echo "Differing rpm requires requirements:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/reqdiff" >> "$RPTDIR/$RPM.out"
fi
rpm -qp --scripts --triggers "$CMPRPM" 2>/dev/null | LC_ALL=C sort >"$WORKDIR/CMPDIR-scripts"
rpm -qp --scripts --triggers "$REFRPM" 2>/dev/null | LC_ALL=C sort >"$WORKDIR/REFDIR-scripts"
diff --unified=1 "$WORKDIR/CMPDIR-scripts" "$WORKDIR/REFDIR-scripts" > "$WORKDIR/scriptsdiff"
if [ $? -eq 1 -a "$MODIFIED" -eq 0 ]
then
if [ "$MODIFIED" = 0 ]
then
PKGOK="FAIL"
else
PKGOK="WARN"
fi
REASON="$REASON RPM-scripts"
# echo "$CMPRPM:Differing scripts:" >> $LOGFILE
echo "Differing scripts :">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/scriptsdiff" >> "$RPTDIR/$RPM.out"
fi
if [ -e "$WORKDIR/REFDIR-libs" ]
then
# Only want what it's looking for, not what it actually gets
perl -pi -e 's/ =>.*//' "$WORKDIR/CMPDIR-libs"
perl -pi -e 's/ =>.*//' "$WORKDIR/REFDIR-libs"
perl -pi -e 's/ \(.*//' "$WORKDIR/CMPDIR-libs"
perl -pi -e 's/ \(.*//' "$WORKDIR/REFDIR-libs"
diff --unified=1 --show-function-line='^\.' "$WORKDIR/CMPDIR-libs" "$WORKDIR/REFDIR-libs" > "$WORKDIR/libdiff"
if [ $? -eq 1 ]
then
if [ "$MODIFIED" = 0 ]
then
PKGOK="FAIL"
else
PKGOK="WARN"
fi
REASON="$REASON diff.libraries"
RPMNAME=`basename $CMPRPM`
# echo "$RPMNAME:Differing libraries:" >> $LOGFILE
echo "Differing libraries:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/libdiff" >> "$RPTDIR/$RPM.out"
echo "ldd for CMPDIR-rpm:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/CMPDIR-libs" >> "$RPTDIR/$RPM.out"
echo "ldd for REFDIR-rpm:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/REFDIR-libs" >> "$RPTDIR/$RPM.out"
fi
fi
if [ -e "$WORKDIR/CMPDIR-libs" ]
then
grep -q 'not found' "$WORKDIR/CMPDIR-libs"
if [ $? -eq 0 ]
then
if [ "$MODIFIED" = 0 ]
then
PKGOK="FAIL"
else
PKGOK="WARN"
fi
REASON="$REASON missing.libraries"
# echo "$CMPRPM:Missing libraries:" >> $LOGFILE
echo "Missing libraries:" >> "$RPTDIR/$RPM.out"
cat "$WORKDIR/CMPDIR-libs" >> "$RPTDIR/$RPM.out"
fi
fi
rm -rf $WORKDIR CMPDIR-libs REFDIR-libs
# MORE TESTS HERE
CMPSIZE=`rpm -qp --qf %{SIZE} $CMPRPM 2>/dev/null`
REFSIZE=`rpm -qp --qf %{SIZE} $REFRPM 2>/dev/null`
SZDIFF=$(($CMPSIZE - $REFSIZE))
ABSDIFF=$((SZDIFF<0?-$SZDIFF:$SZDIFF))
if [ $REFSIZE -ne 0 ]
then
PCTDIFF=$(($ABSDIFF * 100 / $REFSIZE))
if [ "$PCTDIFF" -gt $PERCENTDIFFOK ]
then
if [ "$MODIFIED" = 0 ]
then
PKGOK="FAIL"
else
PKGOK="WARN"
fi
REASON="$REASON diff.size"
RPMNAME=`basename $CMPRPM`
# echo "$RPMNAME:More than $PERCENTDIFFOK % size difference (%:$PCTDIFF) to reference RPM (reference: $REFSIZE, this: $CMPSIZE)" >> $LOGFILE
echo "More than $PERCENTDIFFOK % size difference (%:$PCTDIFF) to reference RPM (reference: $REFSIZE, this: $CMPSIZE) " \
>> "$RPTDIR/$RPM.out"
fi
else
PCTDIFF=-0-
fi
if [ $PKGOK = "OK" -a $SZDIFF -eq 0 ]
then
PKGOK="OK-PERFECT"
fi
rm -rf $CMPRPMDIR $REFRPMDIR
cd /
if [ "$PKGOK" = "FAIL" ]
then
let GLOBALFAIL++
fi
RESULTS="$RPM: ${PKGOK}(${REASON}) ref:$REFSIZE +/-:$SZDIFF %:$PCTDIFF"
}
# read in a config file to save ourselves from extremely-long command lines.
[ -e "/etc/sysconfig/tmverify" ] && . /etc/sysconfig/tmverify
help() {
cat <<EOFhelp
Usage: rpmcompare.sh [-h] [-d] [-s] [-g keyid] [-a age] [-p pat] [-m address] [-r refdir1,refdir2,..] [-c cmpdir] [-l logdir]
compare RPMs in cmpdir (default:$CMPDIR) to those in refdir (default:$REFDIRS),
list differences into logdir (default:$RPTDIR).
-a: only consider RPMs less than age days old
-p: only consider RPMs that have a certain pattern, default: ${RPMPATTERN}
-d: dont bother looking for -debuginfo RPMs
-m: just send mail to address, no (normal) output
-h: help
-s: want signed RPMs
-g: like -s, but accept only this keyid
EOFhelp
exit 0
}
while getopts "hdsvg:r:m:c:l:w:a:p:" flag
do
[ "$flag" == "h" ] && help
[ "$flag" == "s" ] && RPMNEEDSIGN=1
[ "$flag" == "g" ] && RPMGPGKEY="${OPTARG#0x}"
[ "$flag" == "r" ] && REFDIRS="${OPTARG//,/ }"
[ "$flag" == "c" ] && CMPDIR="${OPTARG%\/}"
[ "$flag" == "l" ] && RPTDIR="${OPTARG%\/}"
[ "$flag" == "p" ] && RPMPATTERN="${OPTARG}"
[ "$flag" == "a" ] && RPMAGE="-mtime -${OPTARG}"
[ "$flag" == "d" ] && SKIPDEBUG=1
[ "$flag" == "m" ] && MAILTO="${OPTARG}"
done
[ -n "$RPMGPGKEY" ] && RPMNEEDSIGN=1
[ -z "$SKIPLIST" ] && SKIPLIST="$RPTDIR/.rpmcompare-skiplist"
mkdir -p "$RPTDIR"
cd "$RPTDIR"
if [ -s $LOGFILE ] ; then
rm $LOGFILE
else
touch $LOGFILE
fi
echo "Using ${CMPDIR} as the COMPARE DIR and ${REFDIRS} as the REFERENCE DIRS"
echo "Using ${CMPDIR} as the COMPARE DIR and ${REFDIRS} as the REFERENCE DIRS" >> $LOGFILE
# Get list of rpms to compare
for CMPRPM in `find "${CMPDIR}" -name "${RPMPATTERN}" ${RPMAGE} | sort`
do
cd "$RPTDIR"
CMPRPM="${CMPRPM##${CMPDIR}/}"
echo -n "Verifying $CMPRPM ..."
# tests done directly on the RPM, i.e. not comparing with something
# readable?
if [ ! -r "${CMPDIR}/$CMPRPM" ]
then
echo ""
echo "$CMPRPM FAIL(unreadable)" >> $LOGFILE
let GLOBALFAIL++
continue
fi
# corrupt, signed etc - try to run rpmk only once...
verify=`rpm -Kv "${CMPDIR}/$CMPRPM" 2>&1`
if ! echo "$verify" | grep -q "Header SHA1 digest: OK"
then
echo ""
echo "$CMPRPM FAIL(corrupted header or not an RPM)" >> $LOGFILE
echo "corrupted header or not an RPM:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
let GLOBALFAIL++
continue
fi
echo -n "."
if ! echo "$verify" | grep -q "MD5 digest: OK"
then
echo ""
echo "$CMPRPM FAIL(corrupted payload)" >> $LOGFILE
echo "corrupted payload:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
let GLOBALFAIL++
continue
fi
echo -n "."
# do we insist on signed RPMs?
if [ -n "$RPMNEEDSIGN" ]
then
if ! echo "$verify" | grep -q "Header V3 DSA signature" ||\
! echo "$verify" | grep -q "V3 DSA signature"
then
echo ""
echo "$CMPRPM FAIL(not signed)" >> $LOGFILE
echo "not signed:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
let GLOBALFAIL++
continue
fi
echo -n "."
if ! echo "$verify" | grep -q "V3 DSA signature: OK" ||\
! echo "$verify" | grep -q "Header V3 DSA signature: OK"
then
echo ""
echo "$CMPRPM FAIL(signed with unknown key or bad signature)" >> $LOGFILE
echo "signed with unknown key or bad signature:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
let GLOBALFAIL++
continue
fi
echo -n "."
# do we insist on a specific key?
if [ -n "$RPMGPGKEY" ]
then
if ! echo "$verify" | grep -q "V3 DSA signature: OK, key ID $RPMGPGKEY" ||\
! echo "$verify" | grep -q "Header V3 DSA signature: OK, key ID $RPMGPGKEY"
then
echo ""
echo "$CMPRPM FAIL(not signed with distro key $RPMGPGKEY)" >> $LOGFILE
echo "not signed with distro key $RPMGPGKEY:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
let GLOBALFAIL++
continue
fi
echo -n "."
fi
fi
if [ -n "$SKIPDEBUG" -a "$CMPRPM" != "${CMPRPM/-debuginfo-}" ]
then
echo "skipping debuginfo"
continue
fi
# look for world-writeable files. always a bad idea
# OK: symlinks are lrwxrwxrwx,
# sticky dirs are rwxrwxrwt
# lots of /dev/ devices are world-writeable?
RPMWORLDWRITE=`rpm -qlvp "${CMPDIR}/$CMPRPM" | grep '^[^lcb].......w[^t]'`
if [ -n "$RPMWORLDWRITE" ]
then
echo ""
echo "$CMPRPM FAIL(contains world-writeable files)" >> $LOGFILE
echo "world-writeable files:" >> "$RPTDIR/$CMPRPM.out"
echo "$RPMWORLDWRITE" >> "$RPTDIR/$CMPRPM.out"
let GLOBALFAIL++
# falltrough: can still continue with comparisons
fi
##################################################################
# end of invidiual test, rest is comparisons only
##################################################################
# try with the original-non-distro name
REFRPM="$CMPRPM"
for pat in .el4.centos .el5.centos .centos4 .centos .c4 .SL4 .cern .slc4 .slc .sl6 ; do
# should be able to use {,pattern,pattern,..} but doesn't work with bash-2
REFRPM="${REFRPM//${pat}/}"
done
# more CERN-specific RPMs go here, for things where the author cannot be bothered..
for pat in castor- CASTOR- hwraidtools- ; do
# should be able to use {,pattern,pattern,..} but doesn't work with bash-2
REFRPM="${REFRPM//${pat}/}"
done
# comparisons to some upstream RPM
for REFDIR in $REFDIRS
do
# remove trailing slashes
REFDIR="${REFDIR%%/%}"
# prefer to look for exactly-named RPMs
if [ -e "$REFDIR/$CMPRPM" ]; then
comprpms "$REFDIR/$CMPRPM" "$CMPDIR/$CMPRPM" 0
echo ""
echo $RESULTS >> $LOGFILE
continue 2
fi
if [ -e "$REFDIR/$REFRPM" ]; then
echo -n "(distro tag)... "
comprpms "$REFDIR/$REFRPM" "$CMPDIR/$CMPRPM" 1
echo ""
echo $RESULTS >> $LOGFILE
continue 2
fi
done
# nothing found? error unless we have a clearly-tagged RPM
if [ "$CMPRPM" = "$REFRPM" ]
then
echo ""
echo "$CMPRPM: FAIL(no match found and no distrotag)" >> $LOGFILE
echo "no matching reference RPM found, but $CMPRPM has no distrotag" >> "$RPTDIR/$CMPRPM.out"
let GLOBALFAIL++
else
echo "$CMPRPM: no match found" >> $LOGFILE
fi
done
echo "Finished with $GLOBALFAIL errors"
#exec 1>&6
if [ "$GLOBALFAIL" -gt 0 ]
then
if [ -n "$SKIPLIST" ]
then
rm -rf "$SKIPLIST"
for badrpm in `cat $LOGFILE | grep FAIL | cut -f1 -d:`
do
echo "$badrpm" >> "$SKIPLIST"
rpm -qp --queryformat '%{SOURCERPM}\n' "${CMPDIR}/${badrpm}" >> "$SKIPLIST"
done
fi
if [ -z "$MAILTO" ]
then
echo "There were $GLOBALFAIL errors, see $LOGFILE:"
cat $LOGFILE | grep FAIL
exit 1
else
( echo -e "The following RPMs have problems:\n"; cat $LOGFILE | grep FAIL | LC_ALL=C sort; echo -e "\n\nIndividual per-RPM reports are in $RPTDIR/\n\nFull summary report:\n"; cat $LOGFILE ) | mail -s "rpmcompare.sh: $GLOBALFAIL errors found for $CMPDIR" "$MAILTO"
exit 0
fi
fi