1626 lines
38 KiB
Perl
1626 lines
38 KiB
Perl
#!/usr/bin/perl
|
|
|
|
|
|
# rpmsync - written by Ken Estes kestes@staff.mail.com
|
|
|
|
# $Revision: 1.2 $
|
|
# $Date: 2001/09/15 13:49:39 $
|
|
# $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.2 $ )[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;
|
|
}
|
|
|