diff options
Diffstat (limited to 'scripts/rpmsync')
-rw-r--r-- | scripts/rpmsync | 1625 |
1 files changed, 0 insertions, 1625 deletions
diff --git a/scripts/rpmsync b/scripts/rpmsync deleted file mode 100644 index 528c15eb9..000000000 --- a/scripts/rpmsync +++ /dev/null @@ -1,1625 +0,0 @@ -#!/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; -} - |