## ## Copyright (C) Centeris Corporation 2004-2007 ## Copyright (C) Likewise Software 2007-2008 ## All rights reserved. ## ## 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, see . ## # # Copyright Centeris Corporation. All rights reserved. # package Centeris; use strict; use warnings; BEGIN { use Exporter (); our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); # # Version for version checking # $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = qw(&GetOsType &GetDistroType &GetDistroVersion &ReadFile &WriteFile &ReplaceFile &ReplaceSymlink &GetFileParseMessage &QuickBaseName &CheckTestPrefix &CreateGlobalOpts &CompareLines); %EXPORT_TAGS = ( ); # # Exported package globals and optionally exported functions # @EXPORT_OK = qw(); } our @EXPORT_OK; sub GetOsType(;$) { my $testPrefix = shift || ''; my $result = undef; if ( $testPrefix and ( -f "$testPrefix/ostype" ) ) { my $lines = ReadFile("$testPrefix/ostype"); $result = $lines->[0]; } else { my @lines = `uname`; $result = $lines[0]; } chomp($result); return $result; } sub GetDistroInfo(;$) { my $testPrefix = shift || ''; my $osType = GetOsType($testPrefix); my $distro = undef; my $version = undef; # # If using osdistro/osver, both must be present # my $distroFile = "$testPrefix/osdistro"; my $verFile = "$testPrefix/osver"; if ( $testPrefix and ( ( -f $distroFile ) or ( -f $verFile ) ) ) { my $lines; $lines = ReadFile($distroFile); $distro = $lines->[0]; chomp($distro); $lines = ReadFile($verFile); $version = $lines->[0]; chomp($version); } elsif ( $osType eq 'AIX' ) { $distro = 'aix'; my @lines; @lines = `uname -v`; my $versionPart = $lines[0]; chomp($versionPart); @lines = `uname -r`; my $releasePart = $lines[0]; chomp($releasePart); $version = "$versionPart.$releasePart"; } elsif ( $osType eq 'SunOS' ) { $distro = 'solaris'; my @lines = `uname -r`; $version = $lines[0]; chomp($version); } elsif ( $osType eq 'Darwin' ) { $distro = 'darwin'; my @lines = `uname -r`; $version = $lines[0]; chomp($version); $version =~ s/^8.(\d+).(\d+)$/8.x.x/; } elsif ( $osType eq 'HP-UX' ) { $distro = 'hpux'; my @lines = `uname -r`; $version = $lines[0]; chomp($version); $version =~ s/(\D+)//; } elsif ( $osType eq 'Linux' ) { my $RELEASE_INFO = { order => [ qw( rhel redhat fedora centos suse opensuse sles sled ubuntu debian ) ], files => { rhel => '/etc/redhat-release', redhat => '/etc/redhat-release', fedora => '/etc/redhat-release', centos => '/etc/redhat-release', suse => '/etc/SuSE-release', opensuse => '/etc/SuSE-release', sles => '/etc/SuSE-release', sled => '/etc/SuSE-release', ubuntu => '/etc/lsb-release', debian => '/etc/debian_version', }, }; foreach my $distroType (@{$RELEASE_INFO->{order}}) { my $relFile = $testPrefix.$RELEASE_INFO->{files}->{$distroType}; next if not -r $relFile; my $text = join('', @{ReadFile($relFile)}); if ($distroType eq "rhel") { # The format of the line is something like: # Red Hat Enterprise Linux ES release 4 (Nahant Update 1) # Red Hat Advanced Server release 2.1AS (Pensacola) # In addition, Oracle Linux reports itself as: # Enterprise Linux Enterprise Linux AS release 4 (October Update 5) if ($text =~ /^\s*((Red Hat)|(Enterprise Linux)) ((Enterprise Linux \S+)|(Linux (Advanced|Enterprise) Server)) release (\S+)/) { $distro = $distroType; $version = $8; $version =~ s/(AS)|(ES)$//; $version =~ s/^(\d+)\.\d+$/$1/; last; } } elsif ($distroType eq "redhat") { # The format of the line is something like: # Red Hat Linux release 7.3 (Valhala) if ($text =~ /^\s*Red Hat Linux release (\d+(\.\d+)?)/) { $distro = $distroType; $version = $1; last; } } elsif ($distroType eq "fedora") { # The format of the line is something like: # Fedora Core release 4 (Stentz) if ($text =~ /^\s*Fedora (Core )?release (\S+)/) { $distro = $distroType; $version = $2; last; } } elsif ($distroType eq "centos") { # The format of the line is something like: # CentOS release 4.x (Final) if ($text =~ /^\s*CentOS release (\S+)/) { $distro = $distroType; $version = $1; $version =~ s/^(\d+)\.\d+$/$1/; last; } } elsif ($distroType eq "suse") { if ($text =~ /^\s*SUSE LINUX (\d+\.\d+)\s+/i) { $distro = $distroType; $version = $1; last; } } elsif ($distroType eq "opensuse") { if ($text =~ /^\s*openSUSE (\d+\.\d+)\s+/i) { $distro = $distroType; $version = $1; last; } } elsif ($distroType eq "sles") { if ($text =~ /^\s*SUSE LINUX Enterprise Server (\d+)\s+/i) { $distro = $distroType; $version = $1; last; } } elsif ($distroType eq "sled") { if ($text =~ /^\s*SUSE LINUX Enterprise Desktop (\d+)\s+/i) { $distro = $distroType; $version = $1; last; } } elsif ($distroType eq "ubuntu") { # The file will have lines that include: # DISTRIB_ID=Ubuntu # DISTRIB_RELEASE=6.06 if ($text=~ /^\s*DISTRIB_ID\s*=\s*Ubuntu\s*$/m) { $distro = $distroType; } if ($text=~ /^\s*DISTRIB_RELEASE\s*=\s*(\S+)\s*$/m) { $version = $1; } last if (defined($distro) || defined($version)); } elsif ($distroType eq "debian") { my $lsb_release = $RELEASE_INFO->{files}->{ubuntu}; # # Debian and Ubuntu both have /etc/debian_version, # but only Ubuntu has an /etc/lsb-release # if ( -r $lsb_release ) { die "Unexpetected file: $lsb_release" } $distro = $distroType; # The format of the entire file is a single line like: # 3.1 # and nothing else, so that is the version $version = $text; chomp($version); last; } if (defined($distro) || defined($version)) { die "Should have bailed: [$distro, $version]\n"; } } if (not defined($distro)) { # Support a catch-all for Linux distros: $distro = "other"; $version = "0"; } } if (not defined($distro)) { die "Unable to determine distribution type for OS \"$osType\""; } if (not defined($version)) { die "Unable to determine version for OS \"$osType\" and distro \"$distro\""; } return { os => $osType, distro => $distro, version => $version }; } sub GetDistroType(;$) { my $testPrefix = shift || ''; return GetDistroInfo($testPrefix)->{distro}; } sub GetDistroVersion(;$) { my $testPrefix = shift || ''; return GetDistroInfo($testPrefix)->{version}; } sub ReadFile($) { my $file = shift || die; my $lines = []; my $line; open(FILE, "<$file") || die "Unable to open $file"; while ( $line = ) { push(@$lines, $line); } close(FILE); die "Empty file: $file" if not $lines or not @$lines; return $lines; } sub WriteFile($$) { my $file = shift || die; my $lines = shift || die; open(FILE, ">$file") || die "Unable to open $file"; foreach my $line (@$lines) { chomp($line); print FILE "$line\n"; } close(FILE); } sub ReplaceFileInternal($$) { my $file = shift || die; my $newfile = shift || die; my $orig = "$file.lwidentity.orig"; my $backup = "$file.lwidentity.bak"; if ( ! -e $orig ) { $backup = $orig; } if ( -e $file or -l $file ) { rename($file, $backup) || die "Unable to rename $file to $backup\n"; } rename($newfile, $file) || die "Unable to rename $newfile to $file\n"; } sub ReplaceFile($$) { my $file = shift || die; my $lines = shift || die; # # TODO # # - Perhaps write out new file to /tmp. # - What if backup file is already there? # my $newfile = "$file.new"; WriteFile( $newfile, $lines ); ReplaceFileInternal($file, $newfile); } sub ReplaceSymlink($$) { my $link = shift || die; my $target = shift || die; if ( -e $link and -l $link ) { my $currentTarget = readlink($link); if ( $currentTarget eq $target ) { return 0; } } my $newlink = "$link.new"; symlink($target, $newlink) || die "Unable to create symlink $newlink -> $target\n"; ReplaceFileInternal($link, $newlink); return 1; } sub GetFileParseMessage($;$$$) { my $message = shift || die; my $file = shift; my $lineNumber = shift; my $line = shift; if ( $file ) { $message .= " in file \"$file\""; } if ( $lineNumber ) { $message .= " at line $lineNumber"; } $message .= "\n"; if ( defined($line) ) { chomp($line); $message .= " line contains \"$line\"\n"; } return $message; } sub QuickBaseName($) # this is just so we do not have to depend on File::basename { my $path = shift || die; return ( reverse(split('/', $path)) )[0]; } sub CheckTestPrefix($) { my $prefix = shift || ''; if ( $prefix ) { if ( not -d $prefix ) { die "Invalid test mode directory specified: \"$prefix\"\n"; } print "Using test mode with prefix=\"$prefix\"\n"; } return $prefix; } sub CreateGlobalOpts($) { my $opts = shift || die; return { debug => $opts->{debug} ? 1 : 0, test => $opts->{test} ? 1 : 0 }; } sub CompareLines($$) { my $lines1 = shift || die; my $lines2 = shift || die; if ( $#{@$lines1} != $#{@$lines2} ) { return 1; } my $last_index = $#{@$lines1}; for (my $i = 0; $i <= $last_index; $i++) { my $line1 = $lines1->[$i]; my $line2 = $lines2->[$i]; chomp($line1); chomp($line2); if ( not ( $line1 eq $line2 ) ) { return 1; } } return 0; } 1;