3 # A perl script to be run from cron which creates an rpm database of
4 # all your binary RPMS. This database contains the most recent
5 # version of every branch of each package found in the package
6 # repositories. The database is useful for querying since it is as if
7 # you have installed all these packages into your system. You can
8 # find which packages hold a file or which packages satify a
9 # dependency. We are only load the information from the packages and
10 # do not save the contents of the packages into the file system.
12 # Branch is a Version Control concept and is coded into the packages
13 # rpm version by convention. We build the same packages for many
14 # different projects. Each project works on its own branch and may
15 # have different source code for the same rpm. The branch name is
16 # encouded in the version number of the package followed by a '.'.
17 # The full database needs to have the most recent copy of each package
18 # on each branch. For example rpm package version "3.43" would be the
19 # "43" release of the branch "3" and rpm package version "potato.91"
20 # would be the "91" release of the "potato" branch.
22 # written by Ken Estes kestes@staff.mail.com
30 # An rpm_package is a hash of:
31 # $package{'fqn'}="perl-5.00502-3"
32 # $package{'rpm_file'}="$RPMS_DIR/".
33 # "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
34 # $package{'srpm_file'}="$SRPMS_DIR/".
35 # "./perl-5.00502-3.src.rpm"
36 # $package{'name'}="perl"
37 # $package{'version'}="5.00502"
38 # $package{'release'}="3"
40 # fqn is "fully qualified name"
42 # the packages are stored in the datastructure
43 # $BY_NAME{$name}{$branch}{$version}{$release};
49 # This functions sets all the static variables which are often
50 # configuration parameters. Since it only sets variables to static
51 # quantites it can not fail at run time. Some of these variables are
52 # adjusted by parse_args() but asside from that none of these
53 # variables are ever written to. All global variables are defined here
54 # so we have a list of them and a comment of what they are for.
56 $VERSION = ( qw$Revision: 1.2 $ )[1];
60 # The pattern for parsing fqn into ($name, $version, $release).
61 # This is difficult to parse since some hyphens are significant and
62 # others are not, some packages have alphabetic characters in the
65 $PACKAGE_PAT ='(.*)-([^-]+)-([^-]+).solaris2.6-\w*.rpm';
67 # packages which will end up in the database match this pattern
68 # currently we are not retricting the packages which go into the
73 # the list of directories where rpms are stored
74 @RPM_ARCHIVES = ('/export/rpms/redhat',);
76 # the full path name of the database we are creating.
78 $RPM_DB_DIR = "/export/rpms/redhat/repository.rpmdb";
82 # the correct path has not been finalized yet, but this is close.
90 # taint perl requires we clean up these bad environmental
93 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
103 # this function sets variables similar to set_static variables. This
104 # function may fail only if the OS is in a very strange state. after
105 # we leave this function we should be all set up to give good error
106 # handling, should things fail.
110 $PROGRAM = basename($0);
112 $LOCALTIME = localtime($main::TIME);
116 my ($sec,$min,$hour,$mday,$mon,
117 $year,$wday,$yday,$isdst) =
120 # convert confusing perl time vars to what users expect
125 $DATE_STR = sprintf("%02u%02u%02u", $year, $mon, $mday, );
126 $TIME_STR = sprintf("%02u%02u", $hour, $min);
128 # a unique id for cache file generation
129 $UID = "$DATE_STR.$TIME_STR.$PID";
138 # This is difficult to parse since some hyphens are significant and
139 # others are not, some packages have alphabetic characters in the
142 # Also remember that the format of the file is dependent on how RPM
143 # is configured so this may not be portable to all RPM users.
145 (!("@_" =~ m/^$PACKAGE_PAT$/)) &&
146 die("rpm_package_name: '@_' is not in a valid format");
152 sub new_rpm_package {
154 # An rpm_package is a hash of:
155 # $package{'fqn'}="perl-5.00502-3"
156 # $package{'rpm_file'}="$RPMS_DIR/".
157 # "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
158 # $package{'srpm_file'}="$SRPMS_DIR/".
159 # "./perl-5.00502-3.src.rpm"
160 # $package{'name'}="perl"
161 # $package{'version'}="5.00502"
162 # $package{'release'}="3"
166 my($name, $version, $release) = main::parse_fqn(basename($rpm_file));
170 $package->{'fqn'}="$name-$version-$release";
171 $package->{'name'}=$name;
172 $package->{'version'}=$version;
173 $package->{'release'}=$release;
174 $package->{'rpm_file'}=$rpm_file;
176 # these are needed to do proper sorting of major/minor numbers in
177 # the version of the package
179 $package->{'version_cmp'}=[split(/\./, $version)];
180 $package->{'release_cmp'}=[split(/\./, $release)];
182 # our packages have a naming convention where then branch name is
183 # the first part of the version.
185 $package->{'branch'}= @{ $package->{'version_cmp'} }[0];
191 # return the most recent version of package for a given package name/branch pair
194 my ($name, $branch) =(@_);
198 foreach $version ( keys %{ $BY_NAME{$name}{$branch} }) {
199 foreach $release ( keys %{ $BY_NAME{$name}{$branch}{$version} }) {
201 push @out, $BY_NAME{$name}{$branch}{$version}{$release};
206 # the $BY_NAME datastructure is fairly good but the list can not be
207 # sorted right. Sort again using the Schwartzian Transform as
208 # discribed in perlfaq4
212 # compare the versions but make no assumptions
213 # about how many elements there are
216 my @a_version = @{ $a->{'version_cmp'} };
217 my @b_version = @{ $b->{'version_cmp'} };
219 ($#a_version > $i) &&
220 ($#b_version > $i) &&
221 ($a_version[$i] == $b_version[$i])
227 my @a_release = @{ $a->{'release_cmp'} };
228 my @b_release = @{ $b->{'release_cmp'} };
230 ($#a_release > $j) &&
231 ($#b_release > $j) &&
232 ($a_release[$j] == $b_release[$j])
238 ($b_version[$i] <=> $a_version[$i])
240 ($b_release[$j] <=> $a_release[$j])
249 # traverse the package repositories and create a data structure of all
250 # the packages we find.
252 sub parse_package_names {
253 my $db_dir = basename($RPM_DB_DIR);
254 foreach $archive (@RPM_ARCHIVES) {
257 exec("find", $archive, "-print") ||
258 die("Could not run find. $!\n");
260 while ($filename = <FILES>) {
262 # we want only the binary rpm files of interest
264 ($filename =~ m/\.rpm$/) || next;
265 ($filename =~ m/\.src\.rpm$/) && next;
266 ($filename =~ m/$PICKLIST_PAT/) || next;
268 # do not mistake database files for packages
270 ($filename =~ m!/$db_dir/!) && next;
274 $pkg = new_rpm_package($filename);
275 $BY_NAME{$pkg->{'name'}}{$pkg->{'branch'}}{$pkg->{'version'}}{$pkg->{'release'}} = $pkg;
280 die("Could not close find. $!\n");
289 # traverse the data structure of all the packages and load the most
290 # recent version from each branch into the database. We are only
291 # loading the information from the package not saving the files into
299 # eventually there will be a builder id who will run this code but
303 die("Must not run this program as root\n");
305 # set up to load the database
309 my $tmp_db = "$RPM_DB_DIR.$UID";
311 system("mkdir", "-p", $tmp_db, );
313 die("$0: System error: $! \n");
315 system("rpm", "--initdb",
316 "--dbpath", $tmp_db, );
318 die("$0: System error: $! \n");
320 open(README, ">$tmp_db/README") ||
321 die("Could not open $tmp_db/README. $! \n");
324 # This directory is updated daily by a cron job.
327 # updated ran at: $LOCALTIME
329 # This directory contains an rpm database which has been loaded with
330 # the most recent version of every package in our archive. It is
331 # intended to be used for queries to find packages. Example:
333 # rpm --dbpath /net/master-mm/export/rpms/redhat/rpmfulldb
334 # -q --whatprovides 'java(com.iname.site.context.LoginContext)'
337 # rpm --dbpath /net/master-mm/export/rpms/redhat/rpmfulldb
338 # -qf /usr/local/bin/rpmdiff
343 die("Could not close $tmp_db/README. $! \n");
346 # load the database with the packages we want.
348 foreach $pkg_name (keys %BY_NAME) {
349 foreach $branch (keys %{ $BY_NAME{$pkg_name} }) {
350 $pkg_file = get_latest_fqn($pkg_name, $branch)->{'rpm_file'};
352 system("rpm", "-i", "--nodeps", "--noscripts", "--justdb",
356 die("$0: System error: $! \n");
360 # do the update as close to atomically as is practicale.
362 system("rm", "-rf", $RPM_DB_DIR,);
364 die("$0: System error: $! \n");
366 rename($tmp_db, $RPM_DB_DIR,) ||
367 die("Could not rename file: $tmp_db => $RPM_DB_DIR. $! \n");
381 my %by_name=parse_package_names();
382 create_new_db(%by_name);