summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorjbj <devnull@localhost>2001-03-15 13:58:16 +0000
committerjbj <devnull@localhost>2001-03-15 13:58:16 +0000
commitbd80ac253d70e8da19e5634f0ab6f3e8aedf8eb5 (patch)
tree4e089a543eb57e7d7c600e6cafc33cadfcbb8092 /scripts
parentfc920e3ac326473d884ffc6cfc86225d98442ea0 (diff)
downloadrpm-bd80ac253d70e8da19e5634f0ab6f3e8aedf8eb5.tar.gz
rpm-bd80ac253d70e8da19e5634f0ab6f3e8aedf8eb5.tar.bz2
rpm-bd80ac253d70e8da19e5634f0ab6f3e8aedf8eb5.zip
Updated dependency scripts (#20295).
CVS patchset: 4625 CVS date: 2001/03/15 13:58:16
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.am6
-rwxr-xr-xscripts/http.req57
-rwxr-xr-xscripts/perllocate246
-rwxr-xr-xscripts/perllocate.cgi287
-rwxr-xr-xscripts/print_deps45
-rw-r--r--scripts/rpm_fulldb_update386
-rw-r--r--scripts/rpmsync1625
-rwxr-xr-xscripts/sql.prov115
-rwxr-xr-xscripts/sql.req108
-rw-r--r--scripts/tcl.req101
-rwxr-xr-xscripts/u_pkg.sh10
-rwxr-xr-xscripts/vpkg-provides.sh97
-rwxr-xr-xscripts/vpkg-provides2.sh5
13 files changed, 3049 insertions, 39 deletions
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index 971de3e79..2b2a764c6 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -9,7 +9,8 @@ EXTRA_DIST = \
find-prov.pl find-req.pl cpanflute find-provides.perl \
find-requires.perl get_magic.pl getpo.sh http.req \
magic.prov magic.req perl.prov perl.req rpmdiff rpmdiff.cgi \
- trpm u_pkg.sh vpkg-provides.sh vpkg-provides2.sh
+ sql.prov sql.req tcl.req trpm u_pkg.sh \
+ vpkg-provides.sh vpkg-provides2.sh
installprefix = $(DESTDIR)
@@ -22,5 +23,6 @@ config_SCRIPTS = \
brp-sparc64-linux check-prereqs convertrpmrc.sh find-lang.sh \
find-prov.pl find-req.pl cpanflute find-provides.perl \
find-requires.perl get_magic.pl getpo.sh http.req \
- magic.prov magic.req perl.prov perl.req rpmdiff rpmdiff.cgi u_pkg.sh \
+ magic.prov magic.req perl.prov perl.req rpmdiff rpmdiff.cgi \
+ sql.prov sql.req tcl.req u_pkg.sh \
vpkg-provides.sh vpkg-provides2.sh
diff --git a/scripts/http.req b/scripts/http.req
index 617958893..5d04d0c63 100755
--- a/scripts/http.req
+++ b/scripts/http.req
@@ -25,12 +25,42 @@
# (html with embedded java) since jhtml is deprecated so is this part
# of the code.
+# These references create dependencies:
+
+# <form action="signup.jhtml" method="POST">
+#
+# <img src="images/spacer.gif" width=1>
+#
+# <A HREF="signup.jhtml">
+#
+# adWidget.writeAd(out, "login.html", "expired");
+#
+# response.sendRedirect("http://"+request.getServerName()+"/mailcom/login.jhtml");
+
+
+# Notice how we look for strings WITH the proper ending. This is
+# because the java sometimes has really strange double quoting
+# conventions. Look at how splitting out the strings in this
+# fragment would get you the wrong text.
+
+# <img src="`c.getImage("bhunterlogo.gif")`" width=217 >
+
+# Ignore non relative references since these dependencies can not be
+# met. (ie, no package you install will ever provide
+# 'http://www.yahoo.com').
+
+# I use basename since I have seen too many http references which
+# begin with '../' and I can not figure out where the document root
+# is for the webserver this would just kill the dependnecy tracking
+# mechanism.
+
+
use File::Basename;
# this is the pattern of extensions to call requirements
-$DEPS_PAT = '\.((cgi)|(ps)|(pdf)|(png)|(jpg)|(gif)|(tiff)|(tif)|(xbm)|(html)|(htm)|(shtml)|(jhtml))$'; #'
+$DEPS_PAT = '\.((cgi)|(ps)|(pdf)|(png)|(jpg)|(gif)|(tiff)|(tif)|(xbm)|(html)|(htm)|(shtml)|(jhtml))'; #'
if ("@ARGV") {
foreach (@ARGV) {
@@ -81,18 +111,27 @@ sub process_file {
s!/\*.*?\*/!!g;
s/<!--.*?-->//g;
- # html references other html documents inside strings. Ignore non
- # relative references since these dependencies can not be met. (ie,
- # no package you install will ever provide 'http://www.yahoo.com').
+ # Ignore non relative references since these dependencies can not be
+ # met. (ie, no package you install will ever provide
+ # 'http://www.yahoo.com').
+
# I use basename since I have seen too many http references which
- # begin with '../' this would just kill the dependnecy tracking
+ # begin with '../' and I can not figure out where the document root
+ # is for the webserver this would just kill the dependnecy tracking
# mechanism.
- while ( m{\"([^\"]+)\"}g ) {
+
+ # Notice how we look for strings WITH the proper ending. This is
+ # because the java sometimes has really strange double quoting
+ # conventions. Look at how splitting out the strings in this
+ # fragment would get you the wrong text.
+
+ # <img src="`c.getImage("bhunterlogo.gif")`" width=217 >
+
+ while ( m{\"([^\"]+$DEPS_PAT)\"}g ) {
my $string = $1;
chomp $string;
- if ( ( $string !~ m!http://! ) &&
- ( $string =~ m!$DEPS_PAT! ) ) {
+ if ( $string !~ m!http://! ) {
$string = basename($string);
$string =~ s!\s+!!g;
$seen{"http(${string})"} = 1;
@@ -119,7 +158,7 @@ sub process_file {
}
- close(FILE, "<$file")||
+ close(FILE)||
die("$0: Could not close file: '$file' : $!\n");
return ;
diff --git a/scripts/perllocate b/scripts/perllocate
new file mode 100755
index 000000000..6cbe0e169
--- /dev/null
+++ b/scripts/perllocate
@@ -0,0 +1,246 @@
+#!/usr/bin/perl
+
+# perllocate - a perl replacement for GNU locate. This allows perl
+# regular expressions instead of shell globs.
+
+# Written by Ken Estes, Mail.com.
+
+use Getopt::Long;
+
+
+sub usage {
+
+ my $usage =<<EOF;
+
+$0 [--version] [--help]
+ [-d path] [--database=path] pattern...
+
+Arguments
+
+
+--version Print version information for this program
+
+--help Show this usage page
+
+-d path
+--database=path
+ Instead of searching the default file name database,
+ search the file name databases in path, which is a
+ colon-separated list of database file names. You can
+ also use the environment variable LOCATE_PATH to set
+ the list of database files to search. The option over-
+ rides the environment variable if both are used. If
+ neither are used the default database file is $DEFAULT_DB.
+
+
+Synopsis
+
+A perl5 based replacement for GNU locate. The arguments accepted are
+identical but the patterns matched are perl5 instead of the
+traditional locate glob patterns. This program reads 'LOCATE02'
+databases which were first introduced with locate version 4.0.
+
+For each given pattern, locate searches one or more databases of file
+names and displays the file names that contain the pattern. Patterns
+that contain metacharacters should be quoted to protect them from
+expansion by the shell.
+
+Patterns are perl5 regular expressions; see perlre(1). The database
+entries are a stored as a case-insensitive (lowercase) sorted list.
+
+The file name databases contain lists of files that were on the system
+when the databases were last updated. The system administrator can
+choose the file name of the default database, the frequency with
+which the databases are updated, and the directories for which they
+contain entries; see updatedb(1L).
+
+
+
+Environment
+
+ LOCATE_PATH
+ Colon-separated list of databases to search.
+
+Usage Example
+
+
+$0 --help
+$0 --version
+
+$0 gcc
+$0 perl5
+$0 'rpm$' 'tar$' 'gz$' 'ps$'
+$0 '^\s*'
+$0 '/RPMS/'
+
+
+EOF
+
+ print $usage;
+ exit 0;
+
+}
+
+
+
+sub set_static_vars {
+
+# This functions sets all the static variables which are often
+# configuration parameters. Since it only sets variables to static
+# quantites it can not fail at run time. Some of these variables are
+# adjusted by parse_args() but asside from that none of these
+# variables are ever written to. All global variables are defined here
+# so we have a list of them and a comment of what they are for.
+
+ $DB_FILE_MAGIC = "\0LOCATE02\0";
+
+ $DEFAULT_DB = '/usr/local/var/locatedb';
+
+ $VERSION = (qw$Revision: 1.1 $)[1];
+
+ # set a known path.
+
+ $ENV{'PATH'}= (
+ '/opt/gnu/bin'.
+ ':/usr/local/bin'.
+ ':/usr/bin'.
+ ':/bin'.
+ '');
+
+ # taint perl requires we clean up these bad environmental variables.
+
+ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+ return ;
+}
+
+
+sub parse_args{
+
+ if( !GetOptions("version", "help", "d=s", "database=s",) ) {
+ print("Illegal options in \@ARGV: '@ARGV'\n");
+ usage() ;
+ exit 1 ;
+ }
+
+ if($opt_version) {
+ print "$0: Version: $VERSION\n";
+ exit 0;
+ }
+
+ if ($opt_help) {
+ usage();
+ }
+
+ ($#ARGV == -1) &&
+ die("Must supply a pattern argument.\n");
+
+ $DB_PATH = ( $opt_database ||
+ $opt_d ||
+ ENV{'LOCATE_PATH'} ||
+ $DEFAULT_DB );
+
+ return ;
+}
+
+
+# read the locatedb file into memory
+
+sub read_database {
+ my ($filename) = @_;
+
+ # read whole file into memory
+ {
+ open (DBFILE, "<$filename")||
+ die("$0: Could not open: $filename for reading. $!\n");
+
+ # not needed on unix but lets be very clear
+ binmode (DBFILE);
+
+ # slurp whole file
+ my $old_irs = $/;
+ undef $/;
+
+ $FILE = <DBFILE>;
+
+ $/ = $old_irs;
+
+ close(DBFILE)||
+ die("$0: Could not close: $filename. $!\n");
+
+ $FILE =~ m/^$DB_FILE_MAGIC/ ||
+ die("$0: file: $filename is not an GNU locatedb file. ".
+ "No magic number found.\n");
+ }
+ return ;
+}
+
+
+sub parse_database {
+ my ($pattern) = @_;
+
+ my $file_size = length($FILE);
+ my $position = length($DB_FILE_MAGIC);
+
+ my ( $new_prefix_size, $new_filename,
+ $old_prefix_size, $old_filename, ) = ();
+
+ while ($position < $file_size) {
+ my ($offset, $suffix) = ();
+
+ # read offset
+
+ ($offset) = unpack("c", substr($FILE, $position, 1));
+ $position++;
+ if ($offest == 0x80) {
+
+ # offset is too large to store in one byte, the data we want is
+ # in the next two bytes.
+
+ ($offset) = unpack("n", substr($FILE, $position, 2));
+ $position += 2;
+ }
+
+ # read suffix
+
+ {
+ my $null_position = index ($FILE, "\0", $position);
+ my $length = $null_position - $position;
+ $suffix = substr($FILE, $position, $length);
+ $position += $length + 1;
+ }
+
+ # new values depend on old values and the contents of the database.
+
+ $new_prefix_size = $offset + $old_prefix_size;
+
+ $new_filename = substr($old_filename, 0, $new_prefix_size)
+ .$suffix;
+
+ if ( $new_filename =~ m/$pattern/ ) {
+ print "$new_filename\n";
+ }
+
+ $old_prefix_size = $new_prefix_size;
+ $old_filename = $new_filename;
+ }
+
+ return ;
+}
+
+
+# -------------- main --------------
+{
+
+ set_static_vars();
+ parse_args();
+
+ foreach $file ( split(/:/, $DB_PATH) ) {
+ read_database($file);
+ my $pattern = '('.join(')|(', @ARGV).')';
+ parse_database($pattern);
+ }
+
+ exit 0;
+}
+
diff --git a/scripts/perllocate.cgi b/scripts/perllocate.cgi
new file mode 100755
index 000000000..cabc0a844
--- /dev/null
+++ b/scripts/perllocate.cgi
@@ -0,0 +1,287 @@
+#!/usr/bin/perl
+
+# perllocate.cgi - a web interface to a perl version of the Unix
+# locate command. This script makes it easy to query the RPM
+# repository and find out what packages are availible using Perl5
+# Patterns.
+
+# written by Ken Estes kestes@staff.mail.com
+
+use CGI ':standard';
+use File::Basename;
+use File::stat;
+
+
+
+sub usage {
+
+ # If they are asking for help then they are clueless so reset all
+ # their parameters for them, in case they are in a bad state.
+
+ param(-name=>'Defaults', -value=>'on');
+ my $rpmdiff_version = `perllocate --version`;
+
+ $usage =<<EOF;
+
+ $0 version: $VERSION
+ $perllocate_version
+
+This is a web interface into the perllocate command.
+
+
+EOF
+ print pre($usage);
+
+ return ;
+}
+
+
+sub set_static_vars {
+
+# This functions sets all the static variables which are often
+# configuration parameters. Since it only sets variables to static
+# quantites it can not fail at run time. Some of these variables are
+# adjusted by parse_args() but asside from that none of these
+# variables are ever written to. All global variables are defined here
+# so we have a list of them and a comment of what they are for.
+
+
+ $VERSION = ( qw$Revision: 1.1 $ )[1];
+
+ @ORIG_ARGV = @ARGV;
+
+ $LOCATEDB = '/net/master-mm/export/rpms/redhat/rpmarchive.locatedb';
+
+ # a pattern which matches something inside the rpm archive mount point.
+
+ $MOUNT_DIR_PATTERN = '/redhat/';
+
+ # a pattern to limit the files which are displayed.
+
+ $FILES_TO_DISPLAY_PATTERN = '/RPMS/';
+
+
+ # set a known path.
+
+ # the correct path has not been finalized yet, but this is close.
+
+ $ENV{'PATH'}= (
+ '/usr/local/bin'.
+ ':/usr/bin'.
+ ':/bin'.
+ ':/usr/apache/cgibins/cgi-forms'.
+ '');
+
+ # taint perl requires we clean up these bad environmental
+ # variables.
+
+ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+ return 1;
+} #set_static_vars
+
+
+
+
+sub get_env {
+
+# this function sets variables similar to set_static variables. This
+# function may fail only if the OS is in a very strange state. after
+# we leave this function we should be all set up to give good error
+# handling, should things fail.
+
+ umask 0022;
+ $| = 1;
+ $PID = $$;
+ $PROGRAM = basename($0);
+ $TIME = time();
+ $LOCALTIME = localtime($main::TIME);
+ $START_TIME = $TIME;
+
+ {
+
+ # We show the results as if they were located in the same
+ # directory as the locatedb appears to the cgi script. The
+ # directory is exported from the repository machine in a slightly
+ # different place so that all its outputs would look wrong if we
+ # displayed it raw. This hack is what $DISPLAY_PREFIX is used
+ # for, we try and compute it automatically.
+
+ $LOCATEDB =~ m!$MOUNT_DIR_PATTERN! ||
+ die("db file: $LOCATEDB must be located in a '$MOUNT_DIR_PATTERN' directory");
+
+ $DISPLAY_PREFIX = $LOCATEDB;
+ $DISPLAY_PREFIX =~ s!$MOUNT_DIR_PATTERN(.*)!$MOUNT_DIR_PATTERN!;
+
+ (-r $LOCATEDB) ||
+ die("The file: $LOCATEDB, must exists and be readable.");
+
+ my ($mtime) = stat($LOCATEDB)->mtime;
+
+ $DB_UPDATE_TIME = localtime($mtime);
+ }
+
+ return 1;
+} # get_env
+
+
+# fatal errors need to be valid HTML
+
+sub fatal_error {
+ my @error = @_;
+
+ print header;
+
+ foreach $_ (@error) {
+ print $_;
+ }
+
+ print end_html;
+ print "\n\n\n";
+
+ die("\n");
+}
+
+
+
+
+sub print_query_page {
+
+ my @out;
+
+ push @out, start_form;
+
+ push @out, (
+ "This page allows you to search for all packages ".
+ "in the RPM repository which match a particular pattern.",
+ p(),
+ );
+
+ push @out, (
+
+ h3("Pattern",),
+ "Enter a valid Perl5 Pattern: ",
+ textfield(-name=>'pattern',
+ -size=>30,),
+ p(),
+ );
+
+
+ push @out, (
+ defaults(-name=>'Defaults'),
+ submit(-name=>'Submit'),
+ p(),
+ );
+
+
+ push @out, (
+ "Locate database created at: $DB_UPDATE_TIME\n",
+ p(),
+ "The time is now: $LOCALTIME\n",
+ p(),
+ );
+
+ push @out, (
+ end_form(),
+ );
+
+ print @out;
+
+ return ;
+}
+
+
+
+# given a pattern remove any "tainted" characters.
+
+sub clean_pattern {
+ my ($data) = @_;
+ my $out = '(none)';
+
+ # we do not allow single quotes in the pattern because of the way we
+ # invoke perllocate. If we allowed \' then users could introduce
+ # strings like "'; rm -rf /' echo 'done". Unfortunatly we can not
+ # be too strict about other characters because most characters are
+ # needed to specify regular expressions.
+
+ $data =~ s/\'//g;
+
+ if ( $data =~ m/(.*)/ ) {
+ $out = $1;
+ }
+ return $out;
+}
+
+
+
+# show the results of running perllocate on the chosen pattern.
+
+sub print_perllocate {
+ my($pattern, @args) = @_;
+
+ $pattern =~ s/\'/\\\'/g;
+
+ my $cmd = (
+ "perllocate -d $LOCATEDB '$pattern' 2>&1 ".
+ " | sed -e 's!.*$MOUNT_DIR_PATTERN!$DISPLAY_PREFIX!' ".
+ " | grep '$FILES_TO_DISPLAY_PATTERN' ".
+ "");
+
+ print $cmd, p();
+
+ my $result = "\n".qx{$cmd}."\n";
+
+ print pre($result);
+
+ return ;
+}
+
+
+# Main
+{
+
+ set_static_vars();
+ get_env();
+
+ my ($pattern) = clean_pattern(param("pattern"));
+
+ my (@perllocate_args) = param("perllocate arguments");
+ @perllocate_args = split(/\s+/,
+ '--'.(join(" --", @perllocate_args)));
+ push @perllocate_args, '--';
+
+
+ print (
+ header(),
+ start_html(-title=>"perllocate"),
+ h2({-align=>'CENTER'},"perllocate"),
+ p(),
+ );
+
+
+ if (param("Help Screen")) {
+
+ usage();
+
+ } elsif ( grep {/^(\-\-)((help)|(version))$/} @perllocate_args ) {
+
+ print_perllocate(@perllocate_args);
+
+ } else {
+
+ print_query_page();
+
+ ($pattern) &&
+ print_perllocate($pattern);
+
+ }
+
+ print (
+ end_html(),
+ "\n\n\n",
+ );
+
+
+ exit 0;
+}
+
diff --git a/scripts/print_deps b/scripts/print_deps
new file mode 100755
index 000000000..9e7689718
--- /dev/null
+++ b/scripts/print_deps
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+
+# a glue program for print out dependencies based on filenames
+# by Ken Estes kestes@staff.mail.com
+
+use File::Basename;
+use Getopt::Long;
+
+
+GetOptions (
+ qw( identifier=s basename! )
+ );
+
+
+if ("@ARGV") {
+ foreach (@ARGV) {
+ process_file($_);
+ }
+} else {
+
+ # notice we are passed a list of filenames NOT as common in unix the
+ # contents of the file.
+
+ foreach (<>) {
+ process_file($_);
+ }
+}
+
+sub process_file {
+ my ($str) = @_;
+ chomp $str;
+
+ if ($opt_basename) {
+ $str = basename($str);
+ }
+
+ if ($opt_identifier) {
+ print "${opt_identifier}(${str})\n";
+ } else {
+ print "$str\n";
+ }
+
+ return ;
+}
+
diff --git a/scripts/rpm_fulldb_update b/scripts/rpm_fulldb_update
new file mode 100644
index 000000000..55886abd9
--- /dev/null
+++ b/scripts/rpm_fulldb_update
@@ -0,0 +1,386 @@
+#!/usr/bin/perl
+
+# A perl script to be run from cron which creates an rpm database of
+# all your binary RPMS. This database contains the most recent
+# version of every branch of each package found in the package
+# repositories. The database is useful for querying since it is as if
+# you have installed all these packages into your system. You can
+# find which packages hold a file or which packages satify a
+# dependency. We are only load the information from the packages and
+# do not save the contents of the packages into the file system.
+
+# Branch is a Version Control concept and is coded into the packages
+# rpm version by convention. We build the same packages for many
+# different projects. Each project works on its own branch and may
+# have different source code for the same rpm. The branch name is
+# encouded in the version number of the package followed by a '.'.
+# The full database needs to have the most recent copy of each package
+# on each branch. For example rpm package version "3.43" would be the
+# "43" release of the branch "3" and rpm package version "potato.91"
+# would be the "91" release of the "potato" branch.
+
+# written by Ken Estes kestes@staff.mail.com
+
+
+use File::Basename;
+use File::stat;
+
+
+
+# An rpm_package is a hash of:
+# $package{'fqn'}="perl-5.00502-3"
+# $package{'rpm_file'}="$RPMS_DIR/".
+# "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
+# $package{'srpm_file'}="$SRPMS_DIR/".
+# "./perl-5.00502-3.src.rpm"
+# $package{'name'}="perl"
+# $package{'version'}="5.00502"
+# $package{'release'}="3"
+
+# fqn is "fully qualified name"
+
+# the packages are stored in the datastructure
+# $BY_NAME{$name}{$branch}{$version}{$release};
+
+
+
+sub set_static_vars {
+
+# This functions sets all the static variables which are often
+# configuration parameters. Since it only sets variables to static
+# quantites it can not fail at run time. Some of these variables are
+# adjusted by parse_args() but asside from that none of these
+# variables are ever written to. All global variables are defined here
+# so we have a list of them and a comment of what they are for.
+
+ $VERSION = ( qw$Revision: 1.1 $ )[1];
+
+ @ORIG_ARGV = @ARGV;
+
+ # The pattern for parsing fqn into ($name, $version, $release).
+ # This is difficult to parse since some hyphens are significant and
+ # others are not, some packages have alphabetic characters in the
+ # version number.
+
+ $PACKAGE_PAT ='(.*)-([^-]+)-([^-]+).solaris2.6-\w*.rpm';
+
+ # packages which will end up in the database match this pattern
+ # currently we are not retricting the packages which go into the
+ # database.
+
+ $PICKLIST_PAT = '.';
+
+ # the list of directories where rpms are stored
+ @RPM_ARCHIVES = ('/export/rpms/redhat',);
+
+ # the full path name of the database we are creating.
+
+ $RPM_DB_DIR = "/export/rpms/redhat/repository.rpmdb";
+
+ # set a known path.
+
+ # the correct path has not been finalized yet, but this is close.
+
+ $ENV{'PATH'}= (
+ '/usr/local/bin'.
+ ':/usr/bin'.
+ ':/bin'.
+ '');
+
+ # taint perl requires we clean up these bad environmental
+ # variables.
+
+ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+ return 1;
+} #set_static_vars
+
+
+
+
+sub get_env {
+
+# this function sets variables similar to set_static variables. This
+# function may fail only if the OS is in a very strange state. after
+# we leave this function we should be all set up to give good error
+# handling, should things fail.
+
+ $| = 1;
+ $PID = $$;
+ $PROGRAM = basename($0);
+ $TIME = time();
+ $LOCALTIME = localtime($main::TIME);
+ $START_TIME = $TIME;
+
+ {
+ my ($sec,$min,$hour,$mday,$mon,
+ $year,$wday,$yday,$isdst) =
+ localtime(time());
+
+ # convert confusing perl time vars to what users expect
+
+ $year += 1900;
+ $mon++;
+
+ $DATE_STR = sprintf("%02u%02u%02u", $year, $mon, $mday, );
+ $TIME_STR = sprintf("%02u%02u", $hour, $min);
+ }
+ # a unique id for cache file generation
+ $UID = "$DATE_STR.$TIME_STR.$PID";
+
+ return 1;
+} # get_env
+
+
+
+sub parse_fqn {
+
+ # This is difficult to parse since some hyphens are significant and
+ # others are not, some packages have alphabetic characters in the
+ # version number.
+
+ # Also remember that the format of the file is dependent on how RPM
+ # is configured so this may not be portable to all RPM users.
+
+ (!("@_" =~ m/^$PACKAGE_PAT$/)) &&
+ die("rpm_package_name: '@_' is not in a valid format");
+
+ return ($1, $2, $3);
+}
+
+
+sub new_rpm_package {
+
+# An rpm_package is a hash of:
+# $package{'fqn'}="perl-5.00502-3"
+# $package{'rpm_file'}="$RPMS_DIR/".
+# "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
+# $package{'srpm_file'}="$SRPMS_DIR/".
+# "./perl-5.00502-3.src.rpm"
+# $package{'name'}="perl"
+# $package{'version'}="5.00502"
+# $package{'release'}="3"
+
+ my ($rpm_file) = @_;
+ my $error = '';
+ my($name, $version, $release) = main::parse_fqn(basename($rpm_file));
+
+ my ($package) = ();
+
+ $package->{'fqn'}="$name-$version-$release";
+ $package->{'name'}=$name;
+ $package->{'version'}=$version;
+ $package->{'release'}=$release;
+ $package->{'rpm_file'}=$rpm_file;
+
+ # these are needed to do proper sorting of major/minor numbers in
+ # the version of the package
+
+ $package->{'version_cmp'}=[split(/\./, $version)];
+ $package->{'release_cmp'}=[split(/\./, $release)];
+
+ # our packages have a naming convention where then branch name is
+ # the first part of the version.
+
+ $package->{'branch'}= @{ $package->{'version_cmp'} }[0];
+
+ return $package;
+}
+
+
+# return the most recent version of package for a given package name/branch pair
+
+sub get_latest_fqn {
+ my ($name, $branch) =(@_);
+
+ my @out = ();
+
+ foreach $version ( keys %{ $BY_NAME{$name}{$branch} }) {
+ foreach $release ( keys %{ $BY_NAME{$name}{$branch}{$version} }) {
+
+ push @out, $BY_NAME{$name}{$branch}{$version}{$release};
+
+ }
+ }
+
+ # the $BY_NAME datastructure is fairly good but the list can not be
+ # sorted right. Sort again using the Schwartzian Transform as
+ # discribed in perlfaq4
+
+ my @sorted = sort {
+
+ # compare the versions but make no assumptions
+ # about how many elements there are
+
+ my $i=0;
+ my @a_version = @{ $a->{'version_cmp'} };
+ my @b_version = @{ $b->{'version_cmp'} };
+ while (
+ ($#a_version > $i) &&
+ ($#b_version > $i) &&
+ ($a_version[$i] == $b_version[$i])
+ ) {
+ $i++;
+ }
+
+ my $j = 0;
+ my @a_release = @{ $a->{'release_cmp'} };
+ my @b_release = @{ $b->{'release_cmp'} };
+ while (
+ ($#a_release > $j) &&
+ ($#b_release > $j) &&
+ ($a_release[$j] == $b_release[$j])
+ ) {
+ $j++;
+ }
+
+ return (
+ ($b_version[$i] <=> $a_version[$i])
+ ||
+ ($b_release[$j] <=> $a_release[$j])
+ );
+ }
+ @out;
+
+ return @sorted[0];
+}
+
+
+# traverse the package repositories and create a data structure of all
+# the packages we find.
+
+sub parse_package_names {
+ my $db_dir = basename($RPM_DB_DIR);
+ foreach $archive (@RPM_ARCHIVES) {
+
+ open(FILES, "-|") ||
+ exec("find", $archive, "-print") ||
+ die("Could not run find. $!\n");
+
+ while ($filename = <FILES>) {
+
+ # we want only the binary rpm files of interest
+
+ ($filename =~ m/\.rpm$/) || next;
+ ($filename =~ m/\.src\.rpm$/) && next;
+ ($filename =~ m/$PICKLIST_PAT/) || next;
+
+ # do not mistake database files for packages
+
+ ($filename =~ m!/$db_dir/!) && next;
+
+ chomp $filename;
+
+ $pkg = new_rpm_package($filename);
+ $BY_NAME{$pkg->{'name'}}{$pkg->{'branch'}}{$pkg->{'version'}}{$pkg->{'release'}} = $pkg;
+
+ }
+
+ close(FILES) ||
+ die("Could not close find. $!\n");
+
+ }
+
+ return %BY_NAME;
+}
+
+
+
+# traverse the data structure of all the packages and load the most
+# recent version from each branch into the database. We are only
+# loading the information from the package not saving the files into
+# the file system.
+
+sub create_new_db {
+
+
+ my $uid = $<;
+
+ # eventually there will be a builder id who will run this code but
+ # for now.
+
+ ($uid == 0 ) &&
+ die("Must not run this program as root\n");
+
+ # set up to load the database
+
+ {
+
+ my $tmp_db = "$RPM_DB_DIR.$UID";
+
+ system("mkdir", "-p", $tmp_db, );
+ ($?) &&
+ die("$0: System error: $! \n");
+
+ system("rpm", "--initdb",
+ "--dbpath", $tmp_db, );
+ ($?) &&
+ die("$0: System error: $! \n");
+
+ open(README, ">$tmp_db/README") ||
+ die("Could not open $tmp_db/README. $! \n");
+ print README <<EOF;
+#
+# This directory is updated daily by a cron job.
+# program: $0
+# version: $VERSION
+# updated ran at: $LOCALTIME
+
+# This directory contains an rpm database which has been loaded with
+# the most recent version of every package in our archive. It is
+# intended to be used for queries to find packages. Example:
+
+# rpm --dbpath /net/master-mm/export/rpms/redhat/rpmfulldb
+# -q --whatprovides 'java(com.iname.site.context.LoginContext)'
+
+
+# rpm --dbpath /net/master-mm/export/rpms/redhat/rpmfulldb
+# -qf /usr/local/bin/rpmdiff
+
+
+EOF
+ close(README) ||
+ die("Could not close $tmp_db/README. $! \n");
+ }
+
+ # load the database with the packages we want.
+
+ foreach $pkg_name (keys %BY_NAME) {
+ foreach $branch (keys %{ $BY_NAME{$pkg_name} }) {
+ $pkg_file = get_latest_fqn($pkg_name, $branch)->{'rpm_file'};
+
+ system("rpm", "-i", "--nodeps", "--noscripts", "--justdb",
+ "--dbpath", $tmp_db,
+ $pkg_file);
+ ($?) &&
+ die("$0: System error: $! \n");
+ }
+ }
+
+ # do the update as close to atomically as is practicale.
+
+ system("rm", "-rf", $RPM_DB_DIR,);
+ ($?) &&
+ die("$0: System error: $! \n");
+
+ rename($tmp_db, $RPM_DB_DIR,) ||
+ die("Could not rename file: $tmp_db => $RPM_DB_DIR. $! \n");
+
+ return ;
+}
+
+
+
+
+
+# Main
+{
+ set_static_vars();
+ get_env();
+
+ my %by_name=parse_package_names();
+ create_new_db(%by_name);
+
+ exit 0;
+}
+
diff --git a/scripts/rpmsync b/scripts/rpmsync
new file mode 100644
index 000000000..63902c94f
--- /dev/null
+++ b/scripts/rpmsync
@@ -0,0 +1,1625 @@
+#!/usr/bin/perl
+
+
+# rpmsync - written by Ken Estes kestes@staff.mail.com
+
+# $Revision: 1.1 $
+# $Date: 2001/03/15 13:58:16 $
+# $Author: jbj $
+# $Source: /home/boston/jkeating/rpmcvs/cvs/devel/rpm/scripts/rpmsync,v $
+# $Name: $
+
+use Fcntl;
+use File::Basename;
+use Getopt::Long;
+use IO::Socket;
+use IPC::Open3;
+use POSIX;
+use Symbol;
+use Sys::Hostname;
+use Sys::Syslog;
+
+
+# An rpm_package is a hash of:
+# $package{'fqn'}="perl-5.00502-3"
+# $package{'rpm_file'}="$RPMS_DIR/".
+# "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
+# $package{'srpm_file'}="$SRPMS_DIR/".
+# "./perl-5.00502-3.src.rpm"
+# $package{'name'}="perl"
+# $package{'version'}="5.00502"
+# $package{'release'}="3"
+
+# fqn is "fully qualified name"
+
+# The state of the system is a orderd list (topologically sorted by
+# dependendencies) of fqn's. The list may contain additional RPM flags
+# to be used on a particular list entry.
+
+# we are going to compare two states the actual state of the machine
+# %INSTALLED_BY_NAME this is indexed by package names and gives a list
+# of rpm_package objects which are installed currently on the machine.
+# Each entry is a list of the packages with the given name which are
+# installed.
+
+# The keys of the hash %LISTED_BY_FQN are the fqn's which are listed
+# in the manifest package list.
+
+
+# Here are a bunch of interesting RPM error messages:
+# rpm: --oldpackage may only be used during upgrades
+
+sub usage {
+my $usage = <<"EOF";
+Usage:
+ $PROGRAM --update | --force |--force_and_verify | --rollback | --test
+ [--log_file file] [--manifest_file file]
+ [--script_file file]
+ [--skip_check] [--verbose] [--silent]
+ [--help] [--version]
+
+
+Required Aguments:
+
+
+--test test what an update would change. Compare the installed
+ packges with the packages listed in the manifest file.
+ This option will show what commands would be executed if
+ we were to run an update without actually changing anything.
+ When an --update finishes it automatically runs --test
+ and exits with error if there is any work not
+ completed. This command has nothing to do with the
+ '--verify' (-V) option to rpm.
+
+--update Update the packages installed on the system with the newer
+ versions listed in the manifest file. This will not reinstall
+ packages which are listed and already installed but are
+ corrupted or were installed with the wrong set of arguments.
+ It will erase packages which are installed on the system but
+ not listed in the package list. All packages must have a
+ later version number then the previous packages.
+
+--rollback Rollback a previously installed update. This command
+ requires that the pervious manfest file be reinstalled. All
+ update commands are run in the reverse order from the --update,
+ this ensures that the packages are undone exactly as they were
+ installed.
+
+--force Ensure that the packages installed on the machine are
+ exactly those packages listed in the manifest file and that no
+ installed files are currpted. First each package in the
+ manifest file is installed using --force (even if it is already
+ installed) then each package which is installed but not listed
+ in the manifest list is removed from the machine.
+
+--force_and_verify This command behaves as if you ran this program first with
+ --force then ran rpm -Va. The program will exit with
+ error if either of these steps fail. This allows you to
+ perform unsafe operations (changing the name of a package
+ in a manifest list via a force) in a relatively safe
+ manner.
+
+
+Optional Aguments:
+
+
+--rpm_args Specify additional arguments to pass to rpm for all package
+ operations. This option is used by both the update and erase
+ commands. This option can appear more then once on the
+ command line and the concatination of all options will be sent
+ to rpm. This option should not be need somtimes it is useful,
+ in an emergency, to install packges with broken dependencies
+ or packages with duplicate files. This is a quick way of
+ getting the --nodeps and --force and any other needed
+ arguments to rpm.
+
+--log_file specify a log file different from the default:
+ $LOG_FILE
+
+--manifest_file specify a manifest file different from the default:
+ $MANIFEST_FILE
+
+--skip_check turn off internal sanity checks used by this script. This
+ is not related to the --check option or to rpm -V.
+
+--script_file do not run any commands on this machine instead create a
+ shell file which can be used to install all the packages
+ in the manifest. This script is useful during machine
+ creation. To use this option you must specify
+ --update or --force.
+
+--verbose provide verbose output, only useful for debugging
+ this program.
+
+--silent Do not send any output to stdout/stderr messages will
+ still go to $LOG_FILE or syslog
+
+--help show this usage page.
+
+--version print the version number of this program.
+
+
+This program is used to ensure that the RPM packages installed on a
+system match the list of packages in a manifest. The package list
+looks like the output of 'rpm -qa' but is required to be in a
+tolological order. If special flags are needed for particular
+packages (like --nodeps or --force or --oldpackage or --noscriopts or
+--root <dir> or --relocate oldpath=newpath or --rcfile <file>) they
+can be added on the line after package name with a space separating
+the two. Shell style comments (starting with \# and lasting till the
+next \\n) are legal in the package list. The default package list
+file is $MANIFEST_FILE.
+
+It is expected that most updates will use the --update command with
+--force saved for those rare situations where the machine is known to
+be in a very bad state or there are installed packages which are
+currupted.
+
+
+
+Examples:
+
+
+rpmsync --help
+rpmsync --version
+rpmsync --update
+rpmsync --force
+rpmsync --test
+
+rpmsync --force --rpm_args nodeps
+
+rpmsync --update --rpm_args nodeps --rpm_args noscripts \\
+ --skip_check --verbose
+
+rpmsync --update --script_file /tmp/rpmpkg.bootstrap.sh
+
+EOF
+
+print $usage;
+exit 0;
+}
+
+
+
+
+
+sub new_rpm_package {
+
+# An rpm_package is a hash of:
+# $package{'fqn'}="perl-5.00502-3"
+# $package{'rpm_file'}="$RPMS_DIR/".
+# "./sparc/perl-5.00502-3.solaris2.6-sparc.rpm"
+# $package{'srpm_file'}="$SRPMS_DIR/".
+# "./perl-5.00502-3.src.rpm"
+# $package{'name'}="perl"
+# $package{'version'}="5.00502"
+# $package{'release'}="3"
+
+ my ($fqn, $error_context) = @_;
+ my $error = '';
+ my($name, $version, $release) = main::parse_fqn($fqn, $error_context);
+
+ my ($rpm_file, $install_script_file) =
+ main::which_binary_package_path($name, $version, $release);
+ ($rpm_file) ||
+ ($error .= "Could not find binary file for package: '$fqn'\n");
+
+# my ($srpm_file) = main::which_source_package_path($name, $version, $release);
+# ($srpm_file) ||
+# ($error .= "Could not find source file for package: '$fqn'\n");
+
+ if ($error) {
+ if (!$SKIP_CHECK) {
+ die($error);
+ } else {
+ warn($error);
+ }
+ }
+
+ my ($package) = ();
+
+ $package->{'fqn'}=$fqn;
+ $package->{'name'}=$name;
+ $package->{'version'}=$version;
+ $package->{'release'}=$release;
+ $package->{'rpm_file'}=$rpm_file;
+ $package->{'install_script_file'}=$install_script_file;
+ $package->{'srpm_file'}=$srpm_file;
+
+ return bless($package, $class);
+}
+
+
+sub is_installed {
+
+# returns true iff the package passed in is in fact installed on the
+# machine.
+
+ my ($required_pkg) = @_;
+ my $installed_pkgs = $INSTALLED_BY_NAME{$required_pkg->{'name'}};
+ # look for the right version/release of this package
+ foreach $installed_pkg ( @{ $installed_pkgs } ) {
+ ($installed_pkg->{'fqn'} eq $required_pkg->{'fqn'}) &&
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+sub clean_up {
+
+# any cleanup actions to be performed on exit should go here
+
+ closelog();
+ close(STDERR);
+ close(STDOUT);
+
+ return 1;
+} # clean_up
+
+
+
+sub fatal_error {
+ my @error = @_;
+
+ foreach $_ (split("\n",join('',@error))) {
+ (!$SILENT) && print STDERR ("$PROGRAM (fatal): $_\n");
+ print LOG ("[$LOCALTIME] (fatal): $_\n");
+ }
+ syslog('crit', "fatal error at: ".localtime(time()));
+ clean_up();
+ die("[$LOCALTIME] $PROGRAM: fatal error at: ".localtime(time()) );
+}
+
+
+sub log_error {
+ my @error = @_;
+
+ foreach $_ (split("\n",join('',@error))) {
+ (!$SILENT) && print STDERR ("$PROGRAM (warn): $_\n");
+ print LOG ("[$LOCALTIME] (warn): $_\n");
+ }
+
+}
+
+
+sub info_error {
+ my @error = @_;
+
+ foreach $_ (split("\n",join('',@error))) {
+ (!$SILENT) && print STDERR ("$PROGRAM (info): $_\n");
+ print LOG ("[$LOCALTIME] (info): $_\n");
+ }
+
+}
+
+
+sub which_binary_package_path {
+
+# this line will depend on the 'rpmfilename: ' in the rpmrc file in
+# the future we will need to try 'noos' as well as noarch, it is not
+# implemented in our RPM version.
+
+ my ($name, $version, $release) = @_;
+
+ foreach $dir ( split(':', $SEARCH_PATH) ) {
+ foreach $arch ($ARCH, 'noarch', '') {
+ foreach $os ($OS, 'noos', '') {
+
+ my $filename = '';
+ my $install_script_filename = '';
+
+ $filename = eval "return \"$BINARY_PACKAGE_FILE_PAT\";";
+ $install_script_filename = $filename;
+ $install_script_filename =~ s/^$dir/\$REPOSITORY/;
+ ( -f $filename ) && ( -s $filename ) && ( -r $filename )
+ && return ($filename, $install_script_filename);
+ ;
+ }
+ }
+ }
+ return ;
+}
+
+
+sub which_source_package_path {
+
+# Each binary rpm package encodes the name of the source file which it
+# came from. This is important since some sources generate several
+# binary packages (emacs, vim, perl), given one of those packages it
+# would be hard to find the source file name just doing regular
+# expressions on the name. We extract this information using an rpm
+# query.
+
+ my ($name, $version, $release) = @_;
+
+ $binary_package_file = (which_binary_package_path(@_))[0];
+
+ $binary_package_file || return ;
+
+ # this command would be better
+ # rpm -qp --queryformat '[%{SOURCERPM}\n]'
+
+ my ($wait_status, $log_out, $log_err) =
+ system3('cmd_vec' => [$SYS_CMDS{'rpm'}, '-qip', $binary_package_file],);
+
+ my ($source_rpm_file) = grep (/Source RPM: /, split(/\n+/, $log_out ));
+
+ ( $source_rpm_file =~ m/Source RPM:\s([-.\w]+)/ ) ||
+ return ;
+
+ $source_rpm_file = $1;
+
+ foreach $dir ( split(':', $SEARCH_PATH) ) {
+ my $filename = '';
+
+ $filename = eval "return \"$SOURCE_PACKAGE_FILE_PAT\";";
+ ( -f $filename ) && ( -s $filename ) && ( -r $filename )
+ && return $filename;
+ }
+
+ return ;
+}
+
+
+sub parse_fqn {
+
+ # This is difficult to parse since some hyphens are significant and
+ # others are not, some packages have alphabetic characters in the
+ # version number.
+
+ # Also remember that the format of the file is dependent on how RPM
+ # is configured so this may not be portable to all RPM users.
+ my ($fqn, $error_context) = @_;
+
+ (!("$fqn" =~ m/^$PACKAGE_PAT$/)) &&
+ die("package name '$fqn' is not in a valid format, $error_context");
+
+ return ($1, $2, $3);
+}
+
+
+
+sub system3 {
+
+# Lanuch a new child and wait for it to die. This is like a call to
+# system but we get the stdout and stderr in addition to $?.
+
+# call the function like this
+
+# my ($wait_status, $log_out, $log_err) =
+# open3(
+# 'cmd_vec' => [],
+# 'stdin_str' => '',
+# 'log_cmds'=> '';
+# 'ingore_error' => ''
+# );
+
+# cmd_vec is a command to run in execv format. It is a list not a
+# string since we want the safe version of exec
+
+# stdin_str is a string to pass on the standard in to the child program.
+
+# If log_cmds is set then the command will be sent to syslog and the
+# log file. All output from the command is also sent to the log file.
+
+# open3 signals all errors through a die so will I. If the command
+# exits with nonzero wait_status then system3 calls die. This feature
+# can be turned of fby setting ignore_errors.
+
+# the system3 function returns:
+
+# wait_status: the wait_status of the child process
+
+# log_out: the stdout that the child process wrote.
+
+# log_err: the stderr the child process wrote.
+
+ my (%args) = @_;
+
+ my ($log_cmds, $ignore_error, $cmd_ref, $stdin) = @_;
+
+# if ( ! ( (-x $args{'cmd_vec'}->[0]) && (-f $args{'cmd_vec'}->[0]) ) ) {
+# die ("Command not exectuable: '$args{'cmd_vec'}->[0]',\n");
+# }
+
+ my $info ="executing: '@{ $args{'cmd_vec'} }',\n";
+
+ if ($args{'log_cmds'} || ($VERBOSE) ) {
+ warn($info);
+ }
+
+ # start the process
+
+ my $fh_in = gensym();
+ my $fh_out = gensym();
+ my $fh_err = gensym();
+
+ ($fh_in && $fh_out && $fh_err) ||
+ die ("Could not create new symbol, 'gensym()' object.\n");
+
+ my $child_pid = IPC::Open3::open3(
+ $fh_in,
+ $fh_out,
+ $fh_err,
+ @{$args{'cmd_vec'}}
+ );
+
+ # this check should be redundant but better safe then sorry
+
+ ($child_pid) ||
+ die ("Open3() did not start: '@{$cmd}'. $!\n");
+
+ if ($args{'stdin_str'}) {
+
+ # we should not have a deadlock with this syswrite since this
+ # process writes and then the child reads. It is hard to
+ # imagine how this could fail and the machine still be in a
+ # reasonable shape.
+
+ my $write_len = length($args{'stdin_str'})+1;
+ my $rc = syswrite ($fh_in,
+ $args{'stdin_str'}."\n", $write_len);
+
+ (defined ($rc) && ( $rc == $write_len ) ) ||
+ die("Syswrite to child stdin failed. ".
+ "Could not write: '$write_len' ".
+ "only wrote: '$rc' characters. ".
+ "Trying to write to stdin: '$stdin'. ".
+ ": $!\n");
+ }
+
+
+ close($fh_in) ||
+ die("Could not close child stdin: $!\n");
+
+ main::nonblock($fh_out);
+ main::nonblock($fh_err);
+
+ my $log_out = undef;
+ my $log_err = undef;
+
+ my $reaped_pid = -1;
+ my $wait_status = 0;
+
+ # wait for child to die, but keep clearing out stdout and stderr
+ # buffers for process so we do not deadlock.
+
+ # WE seem to be loosing childrens signals occasionally, so actively
+ # check if the child is alive.
+
+ while ($reaped_pid != $child_pid) {
+
+ sleep(1);
+
+ $reaped_pid = waitpid(-1, &WNOHANG | POSIX::WUNTRACED);
+
+ if ($reaped_pid == $child_pid) {
+
+ ($wait_status = $?);
+
+ # child signaled but did not exit
+ # set to the same pid as 'no child waiting'
+
+ (WIFSTOPPED($wait_status)) &&
+ ($reaped_pid = -1);
+ }
+
+ my $data_out = '';
+ my $data_err = '';
+ my $rc = '';
+
+ # do the reading after reaping so we are sure that we exit the
+ # loop only after draining the sockets.
+
+ # I do not think we need to log $rc errors as they happen
+ # frequently and nothing seems wrong:
+ # Resource temporarily unavailable file_handle
+
+ do {
+ $rc = sysread($fh_out, $data_out, POSIX::BUFSIZ, 0);
+ $log_out .= $data_out;
+ } until ($rc <= 0);
+
+ do {
+ $rc = sysread($fh_err, $data_err, POSIX::BUFSIZ, 0);
+ $log_err .= $data_err;
+ } until ($rc <= 0);
+
+ ($data_err) && warn($data_err);
+
+ } # while pid
+
+ # the reads are at the bottom of the loop so we do not need to do
+ # any more reading of the filehandles.
+
+ close($fh_out) ||
+ &$log_error("Could not close child stdout: $!\n");
+
+ close($fh_err) ||
+ &$log_error("Could not close child stderr: $!\n");
+
+ my @info = (
+ "command results: \n",
+ " wait_status: $wait_status\n",
+ " stdout: '\n",
+ # turn string into a list and indent each element
+ (map {" $_\n"} (split /\n+/, $log_out)),
+ " stdout: '\n",
+ " stderr: '\n",
+ # turn string into a list and indent each element
+ (map {" $_\n"} (split /\n+/, $log_err)),
+ " stderr: '\n",
+ );
+
+ if ( (!$args{'ignore_error'}) && ($wait_status) ) {
+ print "\n\n";
+ die("Cmd exited with error:\n",
+ "\t@{$args{'cmd_vec'}}\n",
+ @info);
+ }
+
+ if ( ($VERBOSE) ||
+ ( ($args{'log_cmds'}) &&
+ ($wait_status || $log_out || $log_err) ) ) {
+ warn(@info);
+ }
+
+ return ($wait_status, $log_out, $log_err);
+} # system3
+
+
+sub get_rpm_info {
+ my (@rpm_args) = @_;
+
+ update_time();
+ my (@rpm_info) = '';
+
+ my ($wait_status, $log_out, $log_err) =
+ system3('cmd_vec' => [$SYS_CMDS{'rpm'}, '-qa'],);
+
+ (@rpm_info) = split(/\n+/, $log_out);
+
+ %INSTALLED_BY_NAME=();
+ my $lineno =0;
+ foreach $fqn (@rpm_info) {
+ $lineno++;
+ chomp $fqn;
+ my ($pkg) = new_rpm_package($fqn, "System Info lineno: $lineno");
+ push @{ $INSTALLED_BY_NAME{$pkg->{'name'}} }, $pkg;
+ }
+
+ return ;
+}
+
+
+sub remove_extra_packages {
+
+ # arguments are not used but allowed for symetry with other
+ # functions
+
+ my(@pkg_list) = @_;
+
+ get_rpm_info();
+
+ # Remove packages installed on the machine but not not in the
+ # manifest. This is important as we sometimes change the package
+ # names while upgrading them and if we did not remove all packages
+ # which are not listed these packages would remain.
+
+ # We also need to remove old versions of just upgraded packages.
+ # Currently we have a problem, some old packages are not being
+ # removed when we do an rpm update. Since we are currently only
+ # installing one version of each package, remove all other versions
+ # then what was required.
+
+
+ # We would like to remove all packages in reverse topological order.
+ # I have no way of finding out what that order is, so I use a single
+ # command which removes all pacakges. RPM will figure out the
+ # correct order at run time. This will cause us to reach the
+ # command line limit if the list of packages to remove is large
+ # enough.
+
+ my @extra_packages = ();
+
+ foreach $pkgname ( keys %INSTALLED_BY_NAME ) {
+ foreach $pkg (@{ $INSTALLED_BY_NAME{$pkgname} }) {
+
+ ($LISTED_BY_FQN{$pkg->{'fqn'}}) && next;
+
+ push @extra_packages, $pkg->{'fqn'};
+ }
+ }
+
+ if (@extra_packages) {
+ my ($wait_status, $log_out, $log_err) =
+ system3(
+ 'cmd_vec' => [$SYS_CMDS{'rpm'}, '-e', @RPM_ARGS,
+ @extra_packages],
+ 'log_cmds'=> 1,
+ );
+ }
+
+ return ;
+}
+
+
+# update the installation with packages
+
+sub update_packages {
+ my(@pkg_list) = @_;
+
+ get_rpm_info();
+
+
+ # first just test and see if this upgrade could work.
+ # this may blowup some OS maximal argument size limit
+
+# my ($wait_status, $log_out, $log_err) =
+# system3(
+# 'cmd_vec' => [$SYS_CMDS{'rpm'}, "--test", '-U',
+# @RPM_ARGS,
+# @upgrade_list],
+# 'log_cmds'=> 1,
+# );
+
+
+ foreach $pkg (@pkg_list) {
+
+ (is_installed($pkg)) && next;
+
+ my ($wait_status, $log_out, $log_err) =
+ system3(
+ 'cmd_vec' => [
+ $SYS_CMDS{'rpm'}, '-U',
+ @{ $pkg->{'rpm_flags'} }, @RPM_ARGS,
+ $pkg->{'rpm_file'} ],
+ 'log_cmds'=> 1,
+ );
+ } # each $fqn
+
+ return ;
+} # update
+
+
+
+# rollback the previous update installation
+
+sub rollback_packages {
+ my(@pkg_list) = @_;
+
+ get_rpm_info();
+
+
+ # first just test and see if this upgrade could work.
+ # this may blowup some OS maximal argument size limit
+
+# my ($wait_status, $log_out, $log_err) =
+# system3(
+# 'cmd_vec' => [$SYS_CMDS{'rpm'}, "--test", '-U',
+# @RPM_ARGS,
+# @upgrade_list],
+# 'log_cmds'=> 1,
+# );
+
+
+ foreach $pkg (reverse @pkg_list) {
+
+ (is_installed($pkg)) && next;
+
+ my ($wait_status, $log_out, $log_err) =
+ system3(
+ 'cmd_vec' => [
+ $SYS_CMDS{'rpm'}, '-U', '--oldpackage',
+ @{ $pkg->{'rpm_flags'} }, @RPM_ARGS,
+ $pkg->{'rpm_file'}
+ ],
+ 'log_cmds'=> 1,
+ );
+
+ } # each $fqn
+
+ return ;
+} # rollback
+
+
+# force ALL the packages to be reinstalled
+
+sub force_packages {
+ my(@pkg_list) = @_;
+
+ # force all the packages in the list to be reinstalled
+
+ # first just test and see if this upgrade could work.
+ # this may blowup some maximal argument size
+
+# my ($wait_status, $log_out, $log_err) =
+# system3(
+# 'cmd_vec' => [$SYS_CMDS{'rpm'}, "--test", '-U', '--force',
+# @pkg_list],
+# 'log_cmds'=> 1,
+# );
+
+
+ foreach $pkg (@pkg_list) {
+ my ($wait_status, $log_out, $log_err) =
+ system3(
+ 'cmd_vec' => [
+ $SYS_CMDS{'rpm'}, '-U', '--force', '--oldpackage',
+ @{ $pkg->{'rpm_flags'} }, @RPM_ARGS,
+ $pkg->{rpm_file}
+ ],
+ 'log_cmds'=> 1,
+ );
+ }
+
+ return ;
+} # force
+
+
+
+# check that the verify command exits without error.
+
+sub verify_packages {
+
+ my ($wait_status, $log_out, $log_err) =
+ system3(
+ 'cmd_vec' => [
+ $SYS_CMDS{'rpm'}, '-Va',
+ ],
+ 'log_cmds'=> 1,
+ );
+
+ return ;
+} # verify
+
+
+
+sub create_scriptfile {
+ my(@pkg_list) = @_;
+
+ my $num_pkgs = scalar(@pkg_list);
+
+ if ($FORCE) {
+ @args = ('-U', '--force');
+ } elsif ($UPDATE) {
+ @args = ('-U', );
+ } else {
+ die("Scripts can only be created for --update or --force")
+ }
+
+ my $out = '';
+
+ $out =<<EOF
+
+# This file automatically generated by program: $0
+# version: $main::VERSION
+# on host: $main::HOSTNAME
+# localtime: $main::LOCALTIME
+#
+# This install file automatically installs
+# manifest file $MANIFEST_FILE
+
+ $SYS_CMDS{'rpm'} --rebuilddb
+
+EOF
+;
+
+ foreach $pkg (@pkg_list) {
+ my @cmd = (
+ $SYS_CMDS{'rpm'}, @args,
+ @{ $pkg->{'rpm_flags'} }, @RPM_ARGS,
+ $pkg->{install_script_file}
+ );
+
+ $out .=<<EOF;
+
+ @cmd
+ if [ \$\? \-ne 0 ]; then
+ echo \>\&2 "\$0: Error running: @cmd"
+ exit 1;
+ fi
+
+EOF
+
+ }
+
+
+ $out .=<<EOF;
+
+ # check that the install for accuracy
+
+ $SYS_CMDS{'rpm'} --rebuilddb;
+
+ $SYS_CMDS{'rpm'} -Va;
+ if [ \$\? \-ne 0 ]; then
+ echo \>\&2 "\$0: Error installing Packages";
+ echo \>\&2 "\$0: 'rpm -Va' reports errors";
+ exit 1;
+ fi
+
+ num_installed_pkgs=\` $SYS_CMDS{'rpm'} -qa | wc \-\l | sed "s/[^0-9]//g" \`;
+ if [ \$num_installed_pkgs \-ne $num_pkgs ]; then
+ echo \>\&2 "\$0: Error installing Packages";
+ echo \>\&2 "\$0: rpm -qa gives \$num_installed_pkgs packages installed";
+ echo \>\&2 "\$0: expected $num_pkgs installed";
+ exit 1;
+ fi
+
+ exit 0;
+
+EOF
+
+ return $out;
+} # create_script
+
+
+
+
+# check what running with --update would do. If I were to write a
+# check_rollback_packages the output would be similar but the packge
+# update order would be reversed.
+
+sub test_update {
+ my(@pkg_list) = @_;
+
+ get_rpm_info();
+
+ # find what we will upgrade
+
+ foreach $pkg (@pkg_list) {
+ is_installed($pkg) && next;
+ push @out, "out of sync, must update: $pkg->{'fqn'}\n";
+ } # each $fqn
+
+
+ # remove old versions of what we installed.
+
+ foreach $pkgname ( keys %INSTALLED_BY_NAME ) {
+ foreach $pkg (@{ $INSTALLED_BY_NAME{$pkgname} }) {
+
+ ($LISTED_BY_FQN{$pkg->{'fqn'}}) && next;
+
+ push @out, "out of sync, must delete: $pkg->{'fqn'}\n";
+ }
+ }
+
+ return @out;
+} # test_update
+
+
+
+sub nonblock {
+
+ # unbuffer a fh so we can select on it
+
+ my ($fh) = shift;
+ my $rc = '';
+ my $flags = '';
+
+ $flags = fcntl($fh, F_GETFL, 0) ||
+ fatal_error("Could not get flags of socket: $fh : $!\n");
+
+ $flags |= O_NONBLOCK;
+
+ $rc = fcntl($fh, F_SETFL, $flags) ||
+ fatal_error("Could not set flags of socket: $fh : $!\n");
+
+ return 1;
+}
+
+
+
+sub mkdir_R {
+# a recusive mkdir function
+
+ my ($dir, $mode) = @_;
+ my @dir = split('/', $dir);
+
+ foreach $i (0..$#dir) {
+
+ my ($dir) = join('/', @dir[0..$i]);
+ ($dir) || next;
+
+ (-d $dir) ||
+ mkdir($dir, $mode) ||
+ die("Could not mkdir: $dir, for writing: $!\n");
+ }
+
+ return ;
+}
+
+
+sub chk_system_config {
+ # refuse to start if the system is in a dangerous state
+
+
+ @problem = ();
+
+ # this is just a placeholder for now
+ # checks go here and failures add to @problem
+
+ return @problem;
+}
+
+
+# park a bunch of unused function here for future scripts
+
+
+sub run_local_rcscripts {
+ my @script_args = @_;
+
+ (-d $LOCAL_RC2_DIR) || return ;
+
+ my @rc_files = ();
+
+ opendir(DIR, "$LOCAL_RC2_DIR") ||
+ die("Could not opendir: '$LOCAL_RC2_DIR': $!\n");
+
+ @rc_files = grep(/^S/, readdir(DIR));
+
+ closedir(DIR) ||
+ die("Could not closedir : '$LOCAL_RC2_DIR': $!\n");
+
+ ( scalar(@rc_files) > 0 ) || return ;
+
+ if ($script_args[0] eq 'start') {
+ @rc_files = sort @rc_files;
+ }else{
+ @rc_files = reverse sort @rc_files;
+ }
+
+ foreach $script (@rc_file) {
+ my ($wait_status, $log_out, $log_err) =
+ system3(
+ 'cmd_vec' => ["$LOCAL_RC2_DIR/$script", @script_args],
+ 'log_cmds'=> 1,
+ );
+ }
+
+ return ;
+}
+
+
+sub update_package_list {
+
+ my $update_script = '';
+
+ # learn what updates we wish to make
+
+ {
+ open(FILELIST, "<$BUILD_FILE") ||
+ die("Could not open build file: '$BUILD_FILE': $!\n");
+
+ my $lineno = 0;
+ while ($fqn=<FILELIST>) {
+ $lineno++;
+ $fqn =~ s/\#.*$//;
+ $fqn =~ s/\s+//g;
+
+ # untaint the input. As a security precaution only allow a few
+ # "good characters" in the package name, or our eval of the
+ # update_script might do some really unexpected things.
+
+ if ($fqn =~ m/([-_.a-zA-Z0-9]+)/) {
+ my $pkg = new_rpm_package($1, "file: $BUILD_FILE lineno: $lineno");
+ $update_script .= "\$fqn =~ s/^$pkg->{'name'}-\\d.*\$/$pkg->{'fqn'}/;\n"
+ }
+ }
+
+ close(FILELIST) ||
+ die("Could not close build file: '$BUILD_FILE': $!\n");
+ }
+
+ # Perform the modifications to the file list
+
+ {
+
+# co -l $MANIFEST_FILE
+
+ rename($MANIFEST_FILE, $MANIFEST_FILE.".bak") ||
+ die("Could not rename ".
+ "file: $MANIFEST_FILE, ${PACKAGE_FILE}.bak: $!\n");
+
+ open(FILELIST_IN, "<${PACKAGE_FILE}.bak") ||
+ die("Could not open for writing ".
+ "packagefile: '${PACKAGE_FILE}.bak': $!\n");
+
+ open(FILELIST_OUT, ">$MANIFEST_FILE") ||
+ die("Could not read from packagefile: '${PACKAGE_FILE}.bak': $!\n");
+
+ while ($fqn=<FILELIST_IN>) {
+ eval $update_script;
+ print FILELIST_OUT $fqn;
+ }
+
+ close(FILELIST_OUT) ||
+ die("Could not close packagefile: '$MANIFEST_FILE': $!\n");
+
+ close(FILELIST_IN) ||
+ die("Could not close packagefile: '${PACKAGE_FILE}.bak': $!\n");
+
+# ci -u $MANIFEST_FILE
+
+ }
+
+ return ;
+}
+
+
+sub include_file {
+ my ($filename) = @_;
+ my (@inc) = ();
+ my $fh = gensym();
+
+ (-f "$INCLUDE_DIR/$filename") ||
+ die("include file: $INCLUDE_DIR/$filename, \n".
+ "found while expanding: $BUILD_FILE, does not exist.\n");
+
+ open($fh, ">$INCLUDE_DIR/$filename") ||
+ die("Could not open include file: '$INCLUDE_DIR/$filename': $!\n");
+
+ while (defined($line = <$fh>) ) {
+
+ if ($line =~ m/\w*\$([-_.a-zA-Z0-9]+)/) {
+ push @inc, include_file($line);
+ } else {
+ push @inc, $line;
+ }
+
+ }
+
+ close($fh) ||
+ die("Could not close include file: '$INCLUDE_DIR/$filename': $!\n");
+
+ return @inc;
+}
+
+
+sub expand_package_list {
+
+ my $update_script = '';
+
+ # learn what updates we wish to make
+
+ open(INFILE, "<$BUILD_FILE") ||
+ die("Could not open build file: '$BUILD_FILE': $!\n");
+
+ open(OUTFILE, ">$TMP_FILE") ||
+ die("Could not open tmp file: '$TMP_FILE': $!\n");
+
+ while ($line=<INFILE>) {
+
+ # untaint the input. As a security precaution only allow a few
+ # "good characters" in the package name.
+
+ if ($line =~ m/\w*\$([-_.a-zA-Z0-9]+)/) {
+ print include_file($1);
+ } else {
+ print $line;
+ }
+
+ }
+
+ close(INFILE) ||
+ die("Could not close build file: '$BUILD_FILE': $!\n");
+
+ close(OUTFILE) ||
+ die("Could not close tmp file: '$TMP_FILE': $!\n");
+
+ return ;
+}
+
+
+sub update_time {
+
+ $TIME = time();
+ $LOCALTIME = localtime($main::TIME);
+
+ return ;
+}
+
+
+
+sub set_static_vars {
+
+# This functions sets all the static variables which are often
+# configuration parameters. Since it only sets variables to static
+# quantites it can not fail at run time. Some of these variables are
+# adjusted by parse_args() but asside from that none of these
+# variables are ever written to. All global variables are defined here
+# so we have a list of them and a comment of what they are for.
+
+ @ORIG_ARGV = @ARGV;
+
+ $INCLUDE_DIR = "";
+ $TMP_FILE = "";
+
+ $LOG_FILE="/var/log/rpmsync/log";
+ $LOCK_FILE="/var/lock/rpmsync";
+ $MANIFEST_FILE="/usr/local/etc/rpmpkg.manifest";
+
+ $FTP_PATH='ftp://machine.iname.net/pub/redhat';
+ $SEARCH_PATH = (
+ # the old hard mounted master-mm package repository
+ '/net/master-mm/export/rpms/redhat'.
+
+ # the new auto mounted master-mm package repository
+ ':/network/master-mm.mail.com/export/rpms/redhat'.
+
+ # look in obvious places on the machine for packages
+ ':/tmp'.
+
+ ':/usr/local/src/redhat/noarch'.
+ ':/usr/local/src/redhat/sparc'.
+ ':/usr/local/src/redhat/i386'.
+
+ # for testing: this is how the current build machine
+ # is set up.
+
+ ':/data1/archive/redhat');
+
+ $VERSION = ( qw$Revision: 1.1 $ )[1];
+
+ $VERBOSE=0;
+ $SKIP_CHECK=0;
+
+ # The pattern for fqn. remember that the format of the file is
+ # dependent on how RPM is configured so this may not be portable to
+ # all RPM users.
+
+ $BINARY_PACKAGE_FILE_PAT = ('$dir/RPMS/$arch/'.
+ '$name-$version-$release.$os-$arch.rpm');
+ $SOURCE_PACKAGE_FILE_PAT = '$dir/SRPMS/$source_rpm_file';
+
+ # The pattern for parsing fqn into ($name, $version, $release).
+ # This is difficult to parse since some hyphens are significant and
+ # others are not, some packages have alphabetic characters in the
+ # version number.
+
+ $PACKAGE_PAT ='(.*)-([^-]+)-([^-]+)';
+
+ # set a known path
+
+ $ENV{'PATH'}= (
+ '/usr/bin'.
+ ':/data/gnu/bin'.
+ ':/data/local/bin'.
+ ':/data/devel/bin'.
+ ':/usr/local/bin'.
+ ':/bin'.
+ '');
+
+
+ # taint perl requires we clean up these bad environmental variables.
+
+ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+ %SYS_CMDS = (
+ 'hostname' => 'hostname',
+ 'rpm' => 'rpm',
+ 'uname' => 'uname',
+ );
+
+ $SIG{'CHLD'} = 'DEFAULT';
+
+ return ;
+}
+
+
+sub get_env {
+
+# this function sets variables similar to set_static variables. This
+# function may fail only if the OS is in a very strange state. after
+# we leave this function we should be all set up to give good error
+# handling, should things fail.
+
+ $| = 1;
+ $PROGRAM = basename($0);
+ $PID = $$;
+ $TIME = time();
+ $LOCALTIME = localtime($main::TIME);
+
+ $START_TIME = $TIME;
+ $UID = $<;
+
+ update_time();
+ my ($wait_status, $log_out, $log_err) =
+ system3('cmd_vec' => ['hostname'],);
+
+ $HOSTNAME = $log_out;
+ chomp $HOSTNAME;
+
+ my ($wait_status, $log_out, $log_err) =
+ system3('cmd_vec' => ['uname', '-a'],);
+ $uname = $log_out;
+
+ ( $uname =~ m/sparc/ ) && ( $ARCH="sparc");
+ ( $uname =~ m/i\d86/ ) && ( $ARCH="i386" );
+
+ $osname = $^O;
+ ( $osname =~ m/solaris/ ) && ( $OS="solaris2.6" );
+ ( $osname =~ m/linux/ ) && ( $OS="linux" );
+
+ return ;
+} # get_env
+
+
+sub parse_args {
+
+ Getopt::Long::config('require_order', 'auto_abbrev', 'ignore_case');
+
+ my ($help, $version, $force_and_verify);
+
+ %option_linkage = (
+ "version" => \$version,
+ "verbose" => \$VERBOSE,
+ "silent" => \$SILENT,
+ "help" => \$help,
+ "skip_check" => \$SKIP_CHECK,
+ "log_file" => \$LOG_FILE,
+ "manifest_file" => \$MANIFEST_FILE,
+ "update"=>\$UPDATE,
+ "force"=>\$FORCE,
+ "force_and_verify"=>\$force_and_verify,
+ "rollback"=>\$ROLLBACK,
+ "test"=>\$TEST,
+ "rpm_args" =>\@RPM_ARGS,
+ "script_file" =>\$SCRIPT_FILE,
+ );
+
+
+ GetOptions (\%option_linkage, qw(
+ silent! verbose! version! help! skip_check!
+ update! force! force_and_verify! rollback! test!
+ manifest_file=s script_file=s
+ log_file=s manifest_file=s
+ rpm_args=s@
+ )) ||
+ die("Illegal options in \@ARGV: '@ARGV',");
+
+ if ($force_and_verify) {
+ $FORCE = 1;
+ $VERIFY = 1;
+ }
+
+ if ($version) {
+ print "$0: Version: $VERSION\n";
+ exit 0;
+ }
+
+ if ($help) {
+ usage();
+ }
+
+ $Process::VERBOSE = $VERBOSE;
+
+ {
+
+ my $args=0;
+
+ ($UPDATE) &&
+ $args++;
+
+ ($FORCE) &&
+ $args++;
+
+ ($TEST) &&
+ $args++;
+
+ ($ROLLBACK) &&
+ $args++;
+
+ ($args == 0) &&
+ die("Must have: 'update', 'force', 'test', 'rollback', argument.\n");
+
+ ($args > 1) &&
+ die("Can not choice more then one: ".
+ "'update', 'force', 'test', 'rollback', arguments.\n");
+ }
+
+ return 1;
+} # parse_args
+
+
+sub set_logging {
+
+# setup the logging facilities to send errors to syslog/log file.
+
+# this needs to come after parse_args() so that we send usage and argv
+# errors to the stderr.
+
+ {
+ my $logopt = 'cons,ndelay';
+ my $facility = 'daemon';
+
+ # no need to test if this succeeds. It calls croak so we will
+ # die if there is a problem.
+
+ openlog($PROGRAM, $logopt, $facility);
+ }
+
+ $SIG{'__WARN__'} = \&log_error;
+ $SIG{'__DIE__'} = \&fatal_error;
+
+ my @sys_errors = chk_system_config();
+
+ if (@sys_errors) {
+ if ($SKIP_CHECK) {
+
+ # even though we are skipping the test put a record of the
+ # problems in the log
+
+ warn(
+ "Warning Error list:\n",
+ @sys_errors,
+ "End Warning Error list\n",
+ "These Errors would be fatal, ".
+ "if run without '--skip_check'\n"
+ );
+
+ } else {
+
+ # should not start with these problems
+
+ die("Fatal Error list:\n",
+ @sys_errors,
+ "End Fatal Error list\n");
+ }
+ }
+
+ if ($LOG_FILE) {
+ # redirect error log
+ mkdir_R(dirname($LOG_FILE), 0755);
+
+ open (LOG, ">>$LOG_FILE") ||
+ die("Could not open log_file: $LOG_FILE, ".
+ "for writing: $!\n");
+
+ print LOG "\n";
+ chmod 0744, $LOG_FILE;
+ LOG->autoflush(1);
+ }
+
+ STDERR->autoflush(1);
+
+}
+
+
+sub get_package_list {
+
+# load the $package_file into memory
+
+# this fucntion must follow get_env() since we need $skip_check to be
+# respected, if set.
+
+ my ($package_file) = @_;
+ my @pkg_list = ();
+
+ (%LISTED_BY_FQN) = ();
+
+ my %package_count = ();
+ open(FILELIST, "<$package_file") ||
+ die("Could not open packagefile: '$package_file': $!\n");
+
+ my $fqn;
+ my $lineno = 0;
+
+ while ($fqn=<FILELIST>) {
+ $lineno++;
+ my $new_package = '';
+ my $pkg_flags = '';
+
+ chomp $fqn;
+ $fqn =~ s/\#.*$//;
+ if ($fqn =~ s/\s+(.*)$// ) {
+ $pkg_flags = $1;
+ }
+ ($fqn) || next;
+
+ $new_package = new_rpm_package($fqn, "file: BUILD_FILE lineno: $lineno");
+ ($pkg_flags) &&
+ ($new_package->{'rpm_flags'} = [ split(/\s+/, $pkg_flags) ] );
+ $package_count{ $new_package->{'name'} }++;
+ $LISTED_BY_FQN{$new_package->{'fqn'}} = 1;
+ push @pkg_list, $new_package ;
+ }
+
+ close(FILELIST) ||
+ die("Could not close packagefile: '$package_file': $!\n");
+
+ foreach $pkg_name (sort keys %package_count) {
+ ($package_count{ $pkg_name } > 1) &&
+ die("Package: $pkg_name is listed ".
+ "$package_count{ $pkg_name } times ".
+ "in file: $package_file\n");
+ }
+
+ return (@pkg_list);
+}
+
+
+sub get_package_hash {
+
+# load the $package_file into memory
+
+# this fucntion must follow get_env() since we need $skip_check to be
+# respected, if set.
+
+ my $package_file = @_;
+ my $pkg_hash = ();
+
+ open(FILELIST, "<$package_file") ||
+ die("Could not open packagefile: '$package_file': $!\n");
+ my $lineno = 0;
+
+ while ($fqn=<FILELIST>) {
+ $lineno++;
+ $fqn =~ s/\#.*$//;
+ $fqn =~ s/\s+//g;
+ chomp $fqn;
+ ($fqn) || next;
+
+ my ($pkg) = new_rpm_package($fqn, "file: $package_file lineno: $lineno");
+ push @{ $pkg_hash{$pkg->{'name'}} }, $pkg;
+ }
+
+
+ close(FILELIST) ||
+ die("Could not close packagefile: '$package_file': $!\n");
+
+ return ($pkg_hash);
+}
+
+
+
+sub pkg_diff {
+
+ $hash0=get_package_hash($file0);
+ $hash1=get_package_hash($file1);
+
+ my ($pkg_out, $file_out);
+ my @warnings = ();
+ my %seen = ();
+
+ foreach $pkg_name ( keys %{$hash0}, keys %{$hash1} ) {
+
+ $seen{$pkg_name} && next;
+ $seen{$pkg_name} = 1;
+ if (
+ ( scalar($hash0->{$pkg_name}) > 1) ||
+ ( scalar($hash1->{$pkg_name} > 1 ) )
+ ) {
+ push @warnings, $pkg_name;
+ }
+
+ if ( ($hash0->{$pkg_name}) &&
+ (!($hash1->{$pkg_name}) ) ) {
+ $pkg_out .= "missing $hash0->{$pkg_name}->{'fqn'}\n";
+ next;
+ } elsif ( (!($hash0->{$pkg_name})) &&
+ ($hash1->{$pkg_name}) ) {
+ $pkg_out .= "added $hash1->{$pkg_name}->{'fqn'}\n";
+ next;
+ } else {
+
+ my ($wait_status, $log_out, $log_err) =
+ system3('cmd_vec' => [
+ 'rpmdiff',
+ ($hash0->{$pkg_name}->{'name'}),
+ ($hash1->{$pkg_name}->{'name'}),
+ ],);
+ $file_out .= $log_out;
+ }
+
+ } # each $pkg_name
+
+ print ("Package Differences:\n\n".
+ sort( split(/\n+/, $pkg_out) ).
+ "\n\nFile Differences:\n\n".
+ sort( split(/\n+/, $file_out) ) );
+
+ if (@warnings) {
+ print STDERR ("The following packages have more then one version\n".
+ " mentioned in the pkglist: ".
+ join(", ", @warnings)."\n".
+ "The diff algorithm assumes only single versions\n".
+ "in pkglist file.\n");
+ }
+
+ return ;
+}
+
+
+
+# -----------------------main--------------------------
+
+{
+ set_static_vars();
+ get_env();
+
+ parse_args();
+ set_logging();
+ @MANIFEST = get_package_list($MANIFEST_FILE);
+
+ # Learn the state of the machine and ensure that we have the srpms
+ # and rpms for this state. This must be done after parsing the
+ # arguments since we may have set '--skip_check'
+
+ get_rpm_info('-qa');
+
+ info_error("starting argv: '@ORIG_ARGV' \n");
+ syslog('info', "starting argv: '@ORIG_ARGV' \n");
+
+ my ($exit_with_error) = 0;
+
+ my ($wait_status, $log_out, $log_err) = ();
+
+ ($UID == 0 ) &&
+ ( ($wait_status, $log_out, $log_err) =
+ system3('cmd_vec' => [$SYS_CMDS{'rpm'}, '--rebuilddb'],));
+
+ if ($TEST) {
+
+ my (@todo) = test_update(@MANIFEST);
+
+ if (@todo) {
+ warn(@todo);
+ $exit_with_error = 1;
+ }
+
+ } elsif ($SCRIPT_FILE) {
+
+ open(SCRIPT_FILE, ">$SCRIPT_FILE") ||
+ die("Could not write to file: $SCRIPT_FILE. $!\n");
+
+ my $script = create_scriptfile(@MANIFEST);
+ print SCRIPT_FILE $script;
+
+ close(SCRIPT_FILE) ||
+ die("Could not close file: $SCRIPT_FILE. $!\n");
+
+ } else {
+
+ # eventually there will be a installer id who will run this code but
+ # for now rpm must be run as root.
+
+ ($UID == 0 ) ||
+ die("Must run this program as root\n");
+
+ ($FORCE) &&
+ force_packages(@MANIFEST);
+
+ ($UPDATE) &&
+ update_packages(@MANIFEST);
+
+ ($ROLLBACK) &&
+ rollback_packages(@MANIFEST);
+
+ remove_extra_packages(@MANIFEST);
+
+ my ($wait_status, $log_out, $log_err) =
+ system3('cmd_vec' => [$SYS_CMDS{'rpm'}, '--rebuilddb'],);
+
+ my @problems = test_update(@MANIFEST);
+
+ (@problems) && die("@problems");
+
+ ($VERIFY) &&
+ verify_packages(@MANIFEST);
+ }
+
+ info_error("finished argv: '@ORIG_ARGV' \n");
+ syslog('info', "finished argv: '@ORIG_ARGV' \n");
+
+ clean_up();
+
+ ($exit_with_error) &&
+ exit 9;
+
+ exit 0;
+}
+
diff --git a/scripts/sql.prov b/scripts/sql.prov
new file mode 100755
index 000000000..5d2b31860
--- /dev/null
+++ b/scripts/sql.prov
@@ -0,0 +1,115 @@
+#!/usr/bin/perl
+
+# RPM and it's source code are covered under two separate licenses.
+
+# The entire code base may be distributed under the terms of the GNU
+# General Public License (GPL), which appears immediately below.
+# Alternatively, all of the source code in the lib subdirectory of the
+# RPM source code distribution as well as any code derived from that
+# code may instead be distributed under the GNU Library General Public
+# License (LGPL), at the choice of the distributor. The complete text
+# of the LGPL appears at the bottom of this file.
+
+# This alternatively is allowed to enable applications to be linked
+# against the RPM library (commonly called librpm) without forcing
+# such applications to be distributed under the GPL.
+
+# Any questions regarding the licensing of RPM should be addressed to
+# marc@redhat.com and ewt@redhat.com.
+
+
+# sql.prov - a simple script to print the proper name for sql from
+# both the sepecification and body files.
+
+
+# by Ken Estes Mail.com kestes@staff.mail.com
+
+if ("@ARGV") {
+ foreach (@ARGV) {
+ process_file($_);
+ }
+} else {
+
+ # notice we are passed a list of filenames NOT as common in unix the
+ # contents of the file.
+
+ foreach (<>) {
+ process_file($_);
+ }
+}
+
+
+
+foreach $module (sort keys %require) {
+ print "sql($module)\n";
+}
+
+exit 0;
+
+
+
+sub process_file {
+
+ my ($filename) = @_;
+ chomp $filename;
+
+ open(FILE, "<$filename")||
+ die("$0: Could not open file: '$filename' : $!\n");
+
+ my ($package, $version) = ();
+
+ my (@file) = <FILE>;
+
+ my ($file) = "@file";
+
+ close(FILE)||
+ die("$0: Could not close file: '$file' : $!\n");
+
+ # skip the comments
+
+ $file =~ s!/\*(.*?)\*/!!gs;
+ $file =~ s!\s*--(.*?)\n!\n!gm;
+
+ @file = split(/\n/, $file);
+
+ foreach (@file) {
+
+ # remove strings
+
+ s!\'[^\']*\'!!g;
+
+
+ # not everyone puts the package name of the file as the first
+ # package name so we report all namespaces as if they were
+ # provided packages (really ugly).
+
+ if (m/\bpackage\s+(body\s*)?(\S+)\s+[ia]s/i) {
+ $package=$2;
+ $package=lc($package);
+ $require{$package}=1;
+ }
+
+ if (m/((procedure)|(function))\s+(\S+)\s*\(/i) {
+ my $func = $4;
+ $func = lc($func);
+ if ($package) {
+ $require{"$package.$func"}=1;
+ } else {
+ $require{$func}=1;
+ }
+ }
+
+ # Each keyword can appear multiple times. Don't
+ # bother with datastructures to store these strings,
+ # if we need to print it print it now.
+
+ if ( m/^\s*\$RPM_Provides\s*:=\s*["'](.*)['"]/i) {
+ foreach $_ (spit(/\s+/, $1)) {
+ print "$_\n";
+ }
+ }
+
+ }
+
+ return ;
+}
diff --git a/scripts/sql.req b/scripts/sql.req
new file mode 100755
index 000000000..24fa97278
--- /dev/null
+++ b/scripts/sql.req
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+
+# RPM and it's source code are covered under two separate licenses.
+
+# The entire code base may be distributed under the terms of the GNU
+# General Public License (GPL), which appears immediately below.
+# Alternatively, all of the source code in the lib subdirectory of the
+# RPM source code distribution as well as any code derived from that
+# code may instead be distributed under the GNU Library General Public
+# License (LGPL), at the choice of the distributor. The complete text
+# of the LGPL appears at the bottom of this file.
+
+# This alternatively is allowed to enable applications to be linked
+# against the RPM library (commonly called librpm) without forcing
+# such applications to be distributed under the GPL.
+
+# Any questions regarding the licensing of RPM should be addressed to
+# marc@redhat.com and ewt@redhat.com.
+
+
+# sql.req - a simple script to print the uses of sql functions.
+
+
+# by Ken Estes Mail.com kestes@staff.mail.com
+
+if ("@ARGV") {
+ foreach (@ARGV) {
+ process_file($_);
+ }
+} else {
+
+ # notice we are passed a list of filenames NOT as common in unix the
+ # contents of the file.
+
+ foreach (<>) {
+ process_file($_);
+ }
+}
+
+
+
+foreach $module (sort keys %require) {
+ print "sql($module)\n";
+}
+
+exit 0;
+
+
+
+sub process_file {
+
+ my ($filename) = @_;
+ chomp $filename;
+
+ open(FILE, "<$filename")||
+ die("$0: Could not open file: '$filename' : $!\n");
+
+ my ($package, $version) = ();
+
+ my (@file) = <FILE>;
+
+ my ($file) = "@file";
+
+ close(FILE)||
+ die("$0: Could not close file: '$file' : $!\n");
+
+ # skip the comments
+
+ # Suck the whole file in to make removing /* */ (multiple lines
+ # comments) comments easier
+
+ $file =~ s!/\*(.*?)\*/!!gs;
+ $file =~ s!^\s*--(.*?)\n!\n!gm;
+
+ @file = split(/\n/, $file);
+
+ foreach (@file) {
+
+ # remove strings
+
+ s!\'[^\']*\'!!g;
+
+
+ # we are interested in function names which have a dot in them and
+ # are followed by an open parenthesis
+
+ foreach ( m/([a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)\s*\(/ ) {
+ my $func = $_;
+ $func=lc($func);
+ $func =~ m/\.\./ &&
+ next;
+ $require{$func}=1;
+ }
+
+ # Each keyword can appear multiple times. Don't
+ # bother with datastructures to store these strings,
+ # if we need to print it print it now.
+
+ if ( m/^\s*\$RPM_Provides\s*:=\s*["'](.*)['"]/i) {
+ foreach $_ (spit(/\s+/, $1)) {
+ print "$_\n";
+ }
+ }
+
+ }
+
+ return ;
+}
diff --git a/scripts/tcl.req b/scripts/tcl.req
new file mode 100644
index 000000000..43c5920c9
--- /dev/null
+++ b/scripts/tcl.req
@@ -0,0 +1,101 @@
+#!/usr/bin/perl
+
+# tcl.req - a simple makedepends like script for tcl.
+
+# I plan to rewrite this in C so that perl is not required by RPM at
+# build time.
+
+# by Ken Estes Mail.com kestes@staff.mail.com
+
+use File::Basename;
+
+if ("@ARGV") {
+ foreach (@ARGV) {
+ process_file($_);
+ }
+} else {
+
+ # notice we are passed a list of filenames NOT as common in unix the
+ # contents of the file.
+
+ foreach (<>) {
+ process_file($_);
+ }
+}
+
+
+foreach $module (sort keys %require) {
+ print "tcl($module)\n";
+}
+
+exit 0;
+
+
+
+sub process_file {
+
+ my ($file) = @_;
+ chomp $file;
+
+ open(FILE, "<$file")||
+ die("$0: Could not open file: '$file' : $!\n");
+
+ while (<FILE>) {
+
+ # Each keyword can appear multiple times. Don't
+ # bother with datastructures to store these strings,
+ # if we need to print it print it now.
+
+ if ( m/^\s*\$RPM_Requires\s*=\s*["'](.*)['"]/i) {
+ foreach $_ (spit(/\s+/, $1)) {
+ print "$_\n";
+ }
+ }
+
+ s/\#.*//;
+
+ # Each keyword can appear multiple times. Don't
+ # bother with datastructures to store these strings,
+ # if we need to print it print it now.
+
+ if ( m/^\s*\$RPM_Requires\s*=\s*["'](.*)['"]/i) {
+ foreach $_ (spit(/\s+/, $1))
+ print "$_\n";
+ }
+
+
+# we wish to capture these source statements:
+
+# source "$PATH/lib/util.tcl"
+# source "comconf.tcl"
+# if {[catch {source $env(CONTROL_PANEL_LIB_DIR)/bindings.tcl}] != 0} {
+
+ # quick check to see if the complex regexps could possibly match.
+ # This should speed things up.
+
+ (m/source/) || next;
+
+ # note we include parethesis and '$' and '\' in the pattern
+
+ if (
+ (m!source\s+([\'\"])?([0-9A-Za-z/._\-\\\(\)\$]+)!)
+ ) {
+
+ my ($module) = $2;
+
+ # If there is some interpolation of variables,
+ # see if taking the basename will give us the filename.
+
+ ($module =~ m/\$/) &&
+ ($module = basename($module));
+
+ ($module =~ m/\$/) ||
+ ($require{$module}=1);
+ }
+ }
+
+ close(FILE)||
+ die("$0: Could not close file: '$file' : $!\n");
+
+ return ;
+}
diff --git a/scripts/u_pkg.sh b/scripts/u_pkg.sh
index 86ff951bf..7e5687b51 100755
--- a/scripts/u_pkg.sh
+++ b/scripts/u_pkg.sh
@@ -2,6 +2,12 @@
# a universal interface to Unix OS package managment systems
+# This script is not finished. It is a bunch of ideas for creating a
+# universal package manager using the OS package manager. I wish to
+# only use tools which are installed in the OS by default and be
+# portable to all OS package managers.
+
+
PATH="/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb:/usr/bsd:$PATH"
export PATH
@@ -17,6 +23,10 @@ fi
#
# Set OS dependent defaults
#
+
+# note: that the "package name" which are returned by this script
+# should always include the version and release number.
+
case $osname in
Linux)
check_all_packages='rpm -Va'
diff --git a/scripts/vpkg-provides.sh b/scripts/vpkg-provides.sh
index fc13ed865..b0fbf18f1 100755
--- a/scripts/vpkg-provides.sh
+++ b/scripts/vpkg-provides.sh
@@ -6,14 +6,23 @@
#
# This file is distributed under the terms of the GNU General Public License
#
+
# vpkg-provides.sh is part of RPM, the Red Hat Package Manager.
-# vpkg-provides.sh searches a list of directories (based on what OS it's
-# being executed on) for shared libraries and interpreters that have been
-# installed by some packaging system other than RPM. It then generates a
-# spec file that can be used to build a "virtual package" that provides all
-# of these things without actually installing any files. This makes it much
-# easier to use RPM on non-Linux systems.
-#
+
+# vpkg-provides.sh searches a list of directories (based on what OS
+# it's being executed on) for shared libraries and interpreter files
+# that have been installed by some packaging system other than RPM.
+# It then generates a spec file that can be used to build a "virtual
+# package" that provides all of these things without actually
+# installing any files. The spec file in effect tells rpm what it
+# needs to know about operating system files which are not under rpm
+# control. This makes it much easier to use RPM on non-Linux systems.
+
+# By default the script also generates a %verifyscript (with hard
+# coded $shlib_dirs, $ignore_dirs values) which will check that the
+# checksum of each file in the directories searched has not changed
+# since the package was built.
+
# Comments: This script is a quick hack. A better solution is to use the
# vendor's package management commands to actually query what's installed, and
# build one or more spec files based on that. This is something
@@ -21,6 +30,8 @@
# first effort was great, so I didn't want to wait until the better solution
# was done.
+# The complete specfile will be sent to stdout.
+
# you will need to create a spec_header for the virtual package. This
# header will provide such specfile information as:
#
@@ -33,16 +44,19 @@
# Source:
-usage= "usage: $0 [--spec_header '/path/to/os-base-header.spec'] \n"
-usage= "$usage\t[--find_provides '/path/to/find-provides']\n"
-usage= "$usage\t[--shlib_dirs 'dirs:which:contain:shared:libs']\n"
-usage= "$usage\t[--ignore_dirs 'egrep|pattern|of|paths|to|ignore']\n"
+# most of the command line arguments have defaults
+
+usage="usage: $0 --spec_header '/path/to/os-base-header.spec' \n"
+usage="$usage\t[--find_provides '/path/to/find-provides']\n"
+usage="$usage\t[--shlib_dirs 'dirs:which:contain:shared:libs']\n"
+usage="$usage\t[--ignore_dirs 'egrep|pattern|of|paths|to|ignore']\n"
# these two should be unnessary as the regular dependency analysis
# should take care of interpreters as well as shared libraries.
-usage= "$usage\t[--interp_dirs 'dirs:which:contain:interpreters']\n"
-usage= "$usage\t[--interps 'files:to:assume:are:installed']\n"
+usage="$usage\t[--interp_dirs 'dirs:which:contain:interpreters']\n"
+usage="$usage\t[--interps 'files:to:assume:are:installed']\n"
+usage="$usage\t[--no_verify]\n"
# this command may not be portable to all OS's, does something else
@@ -55,7 +69,7 @@ hostname=`uname -n`
# if some subdirectories of the system directories needs to be ignored
# (eg /usr/local is a subdirectory of /usr but should not be part of
-# the virtual package) then call this script with IGNORE_DIRS set to a
+# the virtual package) then call this script with ignore_dirs set to a
# vaild egrep pattern which discribes the directories to ignored.
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb:/usr/bsd
@@ -68,7 +82,11 @@ export PATH
spec_header='/usr/lib/rpm/os-base-header.spec';
interps="sh:csh:ksh:dtksh:wish:tclsh:perl:awk:gawk:nawk:oawk"
find_provides='/usr/lib/rpm/find-provides';
-ignore_dirs="."
+
+ # no file names begin with this character so it is a good default
+ # for dirs to ignore.
+
+ignore_dirs="@"
osname=`uname -s`
@@ -174,6 +192,9 @@ do
interps=$1
shift
;;
+ --no_verify)
+ no_verify=1
+ ;;
--help)
echo $usage
exit 0
@@ -223,7 +244,7 @@ fi
#
for d in `echo $shlib_dirs | sed -e 's/:/ /g'`
do
- find $d -type f -print 2>/dev/null | egrep -v \'$IGNORE_DIRS\' | $find_provides >> $provides_tmp
+ find $d -type f -print 2>/dev/null | egrep -v \'$ignore_dirs\' | $find_provides >> $provides_tmp
done
sum_tmp=/tmp/sum.$$
@@ -237,7 +258,7 @@ fi
#
for d in `echo $shlib_dirs | sed -e 's/:/ /g'`
do
- find $d -type f -print 2>/dev/null | egrep -v \'$IGNORE_DIRS\' | $sum_cmd >> $sum_tmp
+ find $d -type f -print 2>/dev/null | egrep -v \'$ignore_dirs\' | $sum_cmd >> $sum_tmp
done
@@ -247,17 +268,20 @@ done
cat $spec_header
#
-# Output the shared libraries
+# output the 'Provides: ' part of the spec file
#
{
+ #
+ # Output the shared libraries
+ #
for f in `cat $provides_tmp | sort -u`
do
echo "Provides: $f"
done
-#
-# Output the available shell interpreters
-#
+ #
+ # Output the available shell interpreters
+ #
for d in `echo $interp_dirs | sed -e 's/:/ /g'`
do
for f in `echo $interps | sed -e 's/:/ /g'`
@@ -280,17 +304,27 @@ cat <<_EIEIO_
This is a virtual RPM package. It contains no actual files. It uses the
\`Provides' token from RPM 3.x and later to list many of the shared libraries
and interpreters that are part of the base operating system and associated
-subsets for $osname.
+OS packages for $osname.
This virtual package was constructed based on the vendor/system software
-installed on the $osname machine named $hostname, as of the date
-$date.
+installed on the '$osname' machine named '$hostname', as of the date
+'$date'.
+
+Input to the script:
+
+ spec_header=$spec_header
+ ignore_dirs=$ignore_dirs
+ find_provides=$find_provides
+ shlib_dirs=$shlib_dirs
+ interp_dirs=$interp_dirs
+ interps=$interps
_EIEIO_
#
# Output the build sections of the spec file
#
+
echo '%prep'
echo '# nothing to do'
echo '%build'
@@ -300,8 +334,10 @@ echo '# nothing to do'
echo '%clean'
echo '# nothing to do'
+if [ -z "${no_verify}" ]; then
+
#
-# Output the verify section of the spec file
+# Output the optional verify section of the spec file
#
cat <<_EIEIO_
@@ -325,7 +361,7 @@ fi
for d in `echo $shlib_dirs | sed -e 's/:/ /g'`
do
- find \$d -type f -print 2>/dev/null | egrep -v \'$IGNORE_DIRS\' | $sum_cmd >> \$sum_current_tmp
+ find \$d -type f -print 2>/dev/null | egrep -v \'$ignore_dirs\' | $sum_cmd >> \$sum_current_tmp
done
cat >\$sum_package_tmp <<_EOF_
@@ -342,16 +378,21 @@ _EOF_
cmp \$sum_package_tmp \$sum_current_tmp
-if [ $? -ne 0 ]; then
- echo "Differences found by: cmp \$sum_package_tmp \$sum_current_tmp"
+if [ \$? -ne 0 ]; then
+ echo"Differences found by: cmp \$sum_package_tmp \$sum_current_tmp"
exit \$?
fi
_EIEIO_
+# end optional verify section
+fi
+
#
# Output the files section of the spec file
#
echo '%files'
echo '# no files in a virtual package'
+
+exit 0
diff --git a/scripts/vpkg-provides2.sh b/scripts/vpkg-provides2.sh
index 86e39f805..dcbfcc509 100755
--- a/scripts/vpkg-provides2.sh
+++ b/scripts/vpkg-provides2.sh
@@ -1,5 +1,10 @@
#!/bin/sh
+# This script is not finished. It is a bunch of ideas for using the
+# OS package manager to create a spec file of virtual dependencies for
+# each OS package. I wish to only use tools which are installed in
+# the OS by default.
+
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb:/usr/bsd
export PATH