#! /usr/local/bin/perl # #! /usr/gnu/bin/perl # Mimic v1.4.2 # pek@pdc.kth.se 20001127 # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Directories and configuration files $root_prefix="/"; $conf_file="$root_prefix/etc/mimic.local.conf"; $inst_root=""; $default_root="/afs/pdc.kth.se/system/dist/sgi/6.3"; $global_db="mimic.global.db"; # The path is gotten from the local conf file $logpath="/tmp"; # Save log and instfile here $iconf_file="$logpath/mimic.inst"; # inst conf file $rootpath="/"; # Install into this tree $afsdirconf_file="$root_prefix/etc/mimic.afsdirs.conf"; $ALLOWDOWNGRADE=0; $FAKE=0; $FORCE=0; $REPAIR=0; $ISKIP=0; $NOEXCL=0; $VERBOSE=0; $VERIFY=0; $VVERBOSE=0; $REMOVE=0; $NOGCONF=0; $NOCLOBBER=0; $current_idir="_BOTTOM"; # Getting subsystems from this directory $current_define="_BOTTOM"; $fromnone=""; # If only removals are performed, this will be -f none (to inst) %definehash={}; %selecthash={}; %installedhash={}; # Already installed stuff %installpathhash={}; # Paths to install from @afsdirlist=(); @excludelist=(); @includelist=(); %brokenhash={}; # This is a hash keyed on the name (and path) of broken files # and contains a list of product names (with paths) that the # file is broken in $version="Mimic v1.4.2 20001127\n"; $usage="mimic.pl \n\t -version print version\n\t" . " -f fake installation (will write selection file)\n\t" . " -a append to logfile (don't clobber)\n\t" . " -d allow downgrades\n\t" . " -r enable removes, installed products not in mimic.global.conf will be removed\n\t" . " -ne no exclusions\n\t" . " -rc reads the directories to skip from \n\t" . " -lp sets the logpath to \n\t" . " -F force selection of all products\n\t" . " -il inverse link skip, only install files in afs\n\t" . " -re forces install of all files in the selected products\n\t" . " -c gets the global configuration from instead of the default\n\t" . " -R installs with as root path\n\t" . " -V verbose mode\n\t" . " -VV very verbose mode\n"; # Predeclarations sub link_check; sub verbose { if ($VERBOSE) { print $_[0]; } } sub vverbose { if ($VVERBOSE) { print $_[0]; } } sub parse_options { while ($#ARGV >= 0) { $str = shift @ARGV; if ($str eq "-f") { # Fake installation verbose "Faking installation\n"; $FAKE=1; } elsif ($str eq "-R") { $rootpath = shift @ARGV; if (!$rootpath) { die "Must set path with -R switch.\n"; } verbose "Root path set to $rootpath\n"; } elsif ($str eq "-a") { $NOCLOBBER = 1; } elsif ($str eq "-c") { # Get global conf from named file $NOGCONF = 1; $str = shift @ARGV; verbose "Getting global conf from $str\n"; if (! -e $str) { print "Global configuration file $str does not exist.\n"; exit; } $global_db = $str; } elsif ($str eq "-F") { # Force selection of all products verbose "Forcing complete selection\n"; $FORCE=1; } elsif ($str eq "-r") { # Enable removes verbose "Removes enabled\n"; $REMOVE=1; } elsif ($str eq "-re") { # Repair selected products verbose "Repair enabled\n"; $REPAIR=1; } elsif ($str eq "-il") { # Only install files in afs verbose "Inverse linkskip\n"; $ISKIP=1; } elsif ($str eq "-ne") { # No exclusions, install everything verbose "Skipping exclusions\n"; $NOEXCL=1; } elsif ($str eq "-lp") { # Set log path $str = shift @ARGV; verbose "Setting logpath to $str\n"; $logpath = $str; } elsif ($str eq "-rc") { # Get exclusions from named file $str = shift @ARGV; verbose "Getting read-only directory listing from $str\n"; if (! -e $str) { print "Could not open read-only directory listing file $str\n"; exit; } $afsdirconf_file = $str; } elsif ($str eq "-d") { # Allow downgrades verbose "Allowing downgrades\n"; $ALLOWDOWNGRADES = 1; } elsif ($str eq "-V") { # Verbose verbose "Running in verbose mode\n"; $VERBOSE=1; } elsif ($str eq "-VV") { # Very verbose print "Running in very verbose mode\n"; $VERBOSE=1; $VVERBOSE=1; } elsif ($str eq "-version") { print $version; exit; } else { print $usage; exit; } } } sub read_local_conf { open (LOCAL_CONF, $conf_file) or die "Could not open $conf_file\n"; foreach $str () { # Strip comments $str =~ /\s*#.*|\n/; if ($`) { $tmp = $`; # Get the path to the global mimic db if ($tmp =~ /^%db_dir\s+(.*)$/) { if (!$NOGCONF) { $global_db = $1 . '/' . $global_db; $global_db_path = $1; } } # Store defines in local_defines elsif ($tmp =~ /^%define\s+(.*)$/) { $definehash{$1} = {}; push @local_defines, $1; } # Get the install root (if defined here) elsif ($tmp =~ /^%inst_root\s+(.*)$/) { $inst_root = $tmp; } # Get files to exclude from updates/replacements elsif ($tmp =~ /^%exclude\s+(.*)$/) { push @excludelist, $1; } else { print "Parse error in $conf_file :\nDoes not understand $tmp\n"; } } } verbose "Active defines @local_defines\n"; close LOCAL_CONF; unless (open (AFS_CONF, $afsdirconf_file)) { print "Could not open $afsdirconf_file, " . "no directories will be excluded.\n"; return; } foreach $str () { # Strip comments $str =~ /\s*#.*|\n/; if ($`) { $tmp = $`; # Get the afs-paths if ($tmp =~ /^(.*)/) { push @afsdirlist, $1; } } } close AFS_CONF; } sub read_global_conf { local *CONF_FILE; $conf_file = $_[0]; $definehash{_BOTTOM} = 1; $definehash{INSTALL_TOKEN} = 1; open (CONF_FILE, $conf_file) or die "Could not open $conf_file\n"; verbose "Opened global conf $conf_file\n"; foreach $str () { # Strip comments $str =~ /\s*#.*|\n/; if ($`) { $tmp = $`; # Get the path to the following subsystems if ($tmp =~ /^%inst_path\s+(\/.*)$/ && $definehash{$current_define}) { verbose "Adding path $1\n"; if (-e $1) { $current_idir = $1; $installpathhash{$current_idir} = 1; } else { die "Invalid installation search path $1\n"; } } # Incomplete path elsif ($tmp =~ /^%inst_path\s+(.*)$/ && $definehash{$current_define}) { verbose "Adding path $1\n"; if (-e "$inst_root/$1") { $current_idir = $inst_root . "/" . $1; $installpathhash{$current_idir} = 1; } else { verbose "Failed to find path $1, would become $inst_root/$1\n"; die "Invalid installation search path $1\n"; } } # Get the install root (if defined here) elsif ($tmp =~ /^%inst_root\s+(.*)$/ && $inst_root eq "" && $definehash{$current_define}) { $inst_root = $1; verbose "Setting the inst_root to $inst_root\n"; } # Push define on stack elsif ($tmp =~ /^%ifdef\s+(.*)$/) { verbose "Pushing define $current_define on define stack.\n"; push @define_stack, $current_define; $current_define = $1; } elsif ($tmp =~ /^%ifndef\s+(.*)$/) { push @define_stack, $current_define; if (not $definehash{$1}) { $current_define = "INSTALL_TOKEN"; } else { $current_define = "NOINSTALL_TOKEN"; } } elsif ($tmp =~ /^%endif(.*)$/) { # Sanity check if ($current_define eq "_BOTTOM") { die "mimic internal error : Tried to read past bottom of define-stack.\n"; } if ($current_define) { verbose "Finished reading distributions for define $current_define\n"; } verbose "Popping define $current_define from define stack.\n"; $current_define = pop @define_stack; } elsif ($tmp =~ /^%include\s+(.*)$/ && $definehash{$current_define}) { $filename = $1; $filename = "$global_db_path/$filename"; if (-e $filename) { verbose "Including file $filename in global conf.\n"; read_global_conf($filename); } } # Read distribution includes for the active define else { if ($definehash{$current_define}) { ($op, $subsys, $ver) = $tmp =~ /^(.+?)\s+(.+?)\s+(\d*)\s*$/; if ($op && $subsys) { $tmp = "$op $subsys"; if (! $ver) { $ver = 0; } # $selecthash{$subsys} = ($ver, $op); $selecthash{$subsys}[0] = $ver; $selecthash{$subsys}[1] = $op; } vverbose "Saved $ver $op on key $subsys\n"; } } } } if ($inst_root eq "") { $inst_root = $default_root; } close CONF_FILE; } sub get_versions { @tmp = split /\n/, `/usr/sbin/versions -nr $rootpath`; # Remove versions header (4 lines) splice (@tmp, 0, 4); # Extract the subsystem name and version for (@tmp) { unless (($_ =~ /\n/) || ! $_) { ($name, $ver) = $_ =~ /.\s+(.*?)\s+(\d+).*$/; $installedhash{$name} = $ver; } } } sub prune { # Find already installed subsystems and remove from selection foreach $subsys (keys %installedhash) { # If the subsys is selected, has the same or older version, # and is marked for installation, prune it if ($selecthash{$subsys} && ! $FORCE && $selecthash{$subsys}[0] <= $installedhash{$subsys} && $selecthash{$subsys}[1] eq "i") { delete $selecthash{$subsys}; } } } sub build_inst_call { if ($VERBOSE || $VVERBOSE) { if ($NOCLOBBER) { $tee = "| tee -a"; } else { $tee = "| tee"; } } else { if ($NOCLOBBER) { $tee = ">>"; } else { $tee = ">"; } } $inst_call = "/usr/sbin/inst $fromnone -F $iconf_file -a -u upgrade_and_new -M -r $rootpath $tee /tmp/mimic.log\n"; } # Writes the current selection to an inst-digestible file sub write_inst_conf { open (INST_CONF, ">$iconf_file") or die "Could not create $iconf_file\n"; print INST_CONF "set abort_on_error off\n"; print INST_CONF "set autodeselect on\n"; print INST_CONF "set autoselect off\n"; print INST_CONF "set delay_idb_read on\n"; print INST_CONF "set delay_conflicts on\n"; print INST_CONF "set rulesoverride true\n"; if ($REPAIR) { print INST_CONF "set neweroverride on\n"; } print INST_CONF "set install_identical_files false\n"; # Put in all directories to install from foreach $dir (keys %installpathhash) { print INST_CONF "from $dir\n"; } if (! keys %installpathhash) { print "No install paths found\n"; $fromnone = "-f none"; } # Write files to be excluded from the installation if (@afsdirlist && !$ISKIP && !$NOEXCL) { $str = "set exclusions "; # First exclude the known afs-dirs foreach $afspath (@afsdirlist) { vverbose "Adding $afspath to exclusions\n"; $str = $str . "$afspath|"; } if ($#excludelist >= 0) { # Then exclude specific files foreach $destpath (@excludelist) { vverbose "Adding $destpath to exclusions\n"; $str = $str . "$destpath|"; } } chop $str; print INST_CONF "$str\n"; } # Write installations foreach $subsys (keys %selecthash) { $ver = $selecthash{$subsys}[0]; $op = $selecthash{$subsys}[1]; vverbose "Writing $op $subsys $ver to install file\n"; print INST_CONF "$op\t$subsys\t$ver\n"; } if (! $ALLOWDOWNGRADES) { print INST_CONF "k downgrade\n"; } close INST_CONF; } &parse_options; &read_local_conf; read_global_conf ($global_db); &get_versions; # Open logfile if (open (LOGFILE, ">>$logpath/mimic.log")) { if ($FAKE) { print LOGFILE "-----\n" . (localtime() . " (fake run)\n"); } else { print LOGFILE "-----\n" . (localtime() . "\n"); } } else { print "Could not open logfile.\n"; } if (!$FORCE) { &prune; } &write_inst_conf; &build_inst_call; verbose "-----\n"; if ($FAKE) { print $inst_call; die "Fake run complete.\n"; } verbose "Starting installation.\n"; print LOGFILE "Starting installation.\n"; close LOGFILE; `$inst_call`; verbose "Done. Log in $logpath/mimic.log\n"; exit;