#!/usr/bin/perl -Tw # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # bos720 src/bos/usr/sbin/install/suma/lib/SUMA/FixInventory.pm 1.4 # # Licensed Materials - Property of IBM # # Restricted Materials of IBM # # COPYRIGHT International Business Machines Corp. 2004 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG package SUMA::FixInventory; # code starts after '=cut' =head1 NAME SUMA::FixInventory - A module to display a system's fix inventory information in multiple formats. =head1 SYNOPSIS use SUMA::FixInventory qw/get_inv_sys /; # # Fix inventory # $format="instfix"; $instfix_flags="Tci"; $clients="localhost"; ($fix_list_fh,$pid)=get_inv_sys($format,$instfix_flags,"localhost"); # ($fix_list_fh,$pid)=get_inv_sys("localhost"); print (<$fix_list_fh>); close $fix_list_fh; waitpid($pid,0); my $rc=$? >> 8; # exit $rc; # or $fix_list_fh=get_inv_sys($format,$instfix_flags,@clients); # $fix_list_fh=get_inv_sys(@clients); print (<$fix_list_fh>); close $fix_list_fh; # exit 0; =cut use strict; use Getopt::Std; use File::Spec; use HTML::Entities qw/encode_entities/; # SUMA modules use lib qw(/usr/suma/lib); use SUMA::GConfig; use SUMA::Util qw(ckPrivate writeXml); use SUMA::Messenger qw( MSG_CANT_FORK MSG_GENERIC MSG_NOT_MASTER MSG_NOT_ROOT MSG_NO_HOSTNAME ); require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw( get_inv_sys ); # Default Values my $user=""; my $nimMaster=0; # Whether this is a nim master my $hostname="localhost"; # Globals my $format; my $instfix_flags; my @properties; my $xml_arrayRef; my @INSTFIX; # Error Codes my $ecFailure=1; # General error including usage my $ecPartialFailure=2; # Some clients returned information others did not =head1 METHODS =over 4 =item get_inv_sys CLIENT_STR =item get_inv_sys FORMAT_STR, FLAGS_STR, CLIENT_STR Outputs the software inventory of a system. The CLIENT_STR must be "localhost". FORMAT_STR is the format the output should be in; this is either "instfix" or "xml". FLAGS_STR is the instfix flags to pass to the instfix command. The FLAGS_STR's value is ignored if the format is xml, though a value must still be passed in. In the case where only CLIENT_STR is passed in then the output will be the same as if FLAGS_STR="Tci" and FORMAT_STR="instfix". The value returned is either a file handle to the output or an array holding the file handle and then the pid of the process writing the output. The pid is returned so you may get a return code (see synopsis for example). The type of return value is determined by looking at what you are expecting as a return value either a scalar or an array. =cut sub get_inv_sys { my @clients; my $client; my $writer; my $reader; if (scalar(@_) == 1) { $format="lslpp"; $instfix_flags="Tci"; } else { $format=shift; if (! defined($format)) {$format="instfix";} $instfix_flags=shift; if (! defined($instfix_flags)) {$instfix_flags="Tci";} } @clients=@_; if (! defined($clients[0])) {@clients=("localhost")} if ((scalar(@clients) > 1) && ($clients[0] ne "localhost")) {exit $ecFailure;} # Get current user $user=`/usr/bin/whoami`; chomp($user); # Check for nim master system("/usr/bin/grep NIM_CONFIGURATION=master /etc/niminfo > /dev/null 2>&1 "); if ($? == 0) {$nimMaster=1;} # Get hostname $hostname=`/usr/bin/hostname`; if (($? != 0) && ($format eq "xml")) { mesg(LVL_ERROR, MSG_NO_HOSTNAME); exit $ecFailure; } chomp($hostname); my $returnCode=0; # set return code my($wroteXmlHead)=0; # Pipe the content out. Caller deals with it. pipe($reader, $writer); if(my $pid = fork) { # Parent close $writer; if (wantarray()) { return ($reader,$pid); } else { return $reader; } } else { # Child - write content into the pipe and then exit. unless(defined($pid)) { mesg(LVL_ERROR, MSG_CANT_FORK); mesg(LVL_DEBUG, MSG_GENERIC, "Fork failed piping lslpp content out of " . (caller(0))[3]); return undef; } close $reader; foreach $client (@clients) { $client=_clean($client); my $rc=_getFixInfo($client); if (($rc == 1) && ($returnCode == 0)) {$returnCode=$ecFailure;} if (($rc == 0) && ($returnCode == $ecFailure)) {$returnCode=$ecPartialFailure;} # Output Client Information if ($rc == 0) { GET_CASE1: { ($format eq "instfix") && do { _writeInstfix($writer); last GET_CASE1; }; ($format eq "xml") && do { if(! $wroteXmlHead) { _writeXmlHead($writer); $wroteXmlHead=1; } my $xml_arrayRef=_createXmlStructure($client); writeXml($writer,$xml_arrayRef, 2); last GET_CASE1; }; } # end GET_CASE1 } # end if $rc == 0 } # end foreach $client # Write the end text if the output was in xml. if (($format eq "xml") && ($returnCode != 1)) {_writeXmlTail($writer);} close $writer; exit($returnCode); } } # end get_inv_sys # Function: _getFixInfo(client_str) # Purpose: To acquire the instfix information needed for fix inventory. # Side effects: Variable INSTFIX contains the output from the # instfix commands. # Returns: 0 success, $ecFailure sub _getFixInfo { ckPrivate(); my $client=shift; if (! defined($client)) {return $ecFailure}; $instfix_flags=_clean($instfix_flags); if (! defined($instfix_flags)) {return $ecFailure}; if (($client eq "localhost") || ($client eq $hostname)) { mesg(LVL_DEBUG, MSG_GENERIC, "Getting instfix output for the localhost."); @INSTFIX=`/usr/sbin/instfix -$instfix_flags 2>&1 `; if ( $? != 0) {return $ecFailure;} } else { mesg(LVL_DEBUG, MSG_GENERIC, "Getting instfix output for nim client: ",$client,"."); if ($user ne "root") { mesg(LVL_ERROR, MSG_NOT_ROOT); return $ecFailure; } if (! $nimMaster) { mesg(LVL_ERROR, MSG_NOT_MASTER); return $ecFailure; } if ($format eq "xml") { $instfix_flags="Tci"; } @INSTFIX=`/usr/sbin/nim -o instfix -a instfix_flags=$instfix_flags $client 2>&1 `; if ( $? != 0) {return $ecFailure;} } if ($format eq "xml") { @INSTFIX=map {$_->[0]} # does a sort of instfix lines by the 2nd field (fileset) sort {$a->[1] cmp $b->[1]} map { [$_, (split /:/)[1] ]} @INSTFIX; } return 0 } # end _getFixInfo # Function: _writeInstfix(writer_fh) # Purpose: To write the contents of the variable INSTFIX to the filehandle passed in. # Side effects: Writes to stream. # Returns: 0 success sub _writeInstfix { ckPrivate(); my $writer=shift; print ($writer @INSTFIX); } # end _writeInstfix # Function: _writeXmlHead(writer_fh) # Purpose: To write the xml header and the first two tags to the filehandle passed in. # Side effects: Writes to filehandle. # Returns: 0 success sub _writeXmlHead { ckPrivate(); my $writer=shift; print($writer " \n"); print($writer "\n"); print($writer " \n"); print($writer " \n\n"); } # end _writeXmlHead # Function: _createXmlStructure(client_str) # Purpose: To create the array reference that will hold the filled in xml structure # for the xml document that is created from reading the INSTFIX variable. # Side effects: - # Returns: The tagArrayRef for an xml structure for the client passed in. # See SUMA::Util->writeXml for more details on structure. sub _createXmlStructure { ckPrivate(); my $client=shift; my $clientHostname=$client; my $clientOSLevel; if ($clientHostname eq "localhost") # localhost { $client=$hostname; $clientHostname=$hostname; $clientOSLevel=`/usr/bin/oslevel`; chomp($clientOSLevel); } else # nim client { $clientHostname=(`/usr/sbin/lsnim -Za if1 $client`)[1]; chomp($clientHostname); $clientHostname=(split(":",$clientHostname))[2]; $clientOSLevel=""; } my($keyword,$fileset,$requiredLevel,$installedLevel,$status,$abstract,$extras); my $last_fileset=""; my $filesets_array=[]; my ($fixProp_array,$filesetProp_array,$child_array,$fixes_array); my ($child_tagArrayRef,$property_tagArrayRef,$fixes_tagArrayRef); foreach my $line (@INSTFIX) { chomp($line); $line=encode_entities($line); # html encode entries # field:1 2 3 4 5 6 ($keyword,$fileset,$requiredLevel,$installedLevel,$status,$abstract, # field: 7+ $extras)=split(/:/,$line); $filesetProp_array=[]; if ($fileset ne $last_fileset) { # PCP $child_tagArrayRef=['Value',$fileset,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','PCP',$child_array, 'name', '"PCP"', 'displayName', '"Component"']; push(@{$filesetProp_array},$property_tagArrayRef); # PVK $child_tagArrayRef=['Value',$installedLevel,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','PVK',$child_array, 'name', '"PVK"', 'displayName', '"Version"']; push(@{$filesetProp_array},$property_tagArrayRef); # Use the V in VRMF of bos.rte for oslevel if it is a client if (($clientOSLevel eq "") && ($fileset eq "bos.rte")) { $clientOSLevel=substr($installedLevel,0,1); $clientOSLevel.=".0.0.0"; } $fixes_array=[]; } $fixProp_array=[]; # FFI $child_tagArrayRef=['Value',$keyword,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','FFI',$child_array, 'name', '"FFI"', 'displayName', '"Fix Id"']; push(@{$fixProp_array},$property_tagArrayRef); # DS $child_tagArrayRef=['Value',$abstract,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','DS',$child_array, 'name', '"DS"', 'displayName', '"Description"']; push(@{$fixProp_array},$property_tagArrayRef); # PVK $child_tagArrayRef=['Value',$requiredLevel,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','PVK',$child_array, 'name', '"PVK"', 'displayName', '"Version"']; push(@{$fixProp_array},$property_tagArrayRef); # FSC $child_tagArrayRef=['Value',$status,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','FSC',$child_array, 'name', '"FSC"', 'displayName', '"Fix State Code"']; push(@{$fixProp_array},$property_tagArrayRef); my $uniqueID=$fileset . "-" . $keyword; my $fix_tagArrayRef=['Resource',$fileset,$fixProp_array, 'displayName', '"'.$fileset.'"', 'uniqueId', '"'.$uniqueID.'"']; push(@{$fixes_array},$fix_tagArrayRef); if ($fileset ne $last_fileset) { $fixes_tagArrayRef=['ResourceList','',$fixes_array, 'displayName', '"Fixes"']; push(@{$filesetProp_array},$fixes_tagArrayRef); my $fileset_tagArrayRef=['Resource',$fileset,$filesetProp_array, 'displayName', '"'.$fileset.'"', 'uniqueId', '"'.$fileset.'"']; push(@{$filesets_array},$fileset_tagArrayRef); } $last_fileset=$fileset; } # end foreach $line my $filesets_tagArrayRef=['ResourceList','',$filesets_array, 'displayName', '"Components"']; my $sysProp_array=[]; # HN $child_tagArrayRef=['Value',$clientHostname,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','HN',$child_array, 'name', '"HN"', 'displayName', '"HostName"']; push(@{$sysProp_array},$property_tagArrayRef); # OS my $OSLevel="AIX ".$clientOSLevel; $child_tagArrayRef=['Value',$OSLevel,"", 'type', '"string"']; $child_array=[$child_tagArrayRef]; $property_tagArrayRef=['Property','OS',$child_array, 'name', '"OS"', 'displayName', '"Operating System"']; push(@{$sysProp_array},$property_tagArrayRef); push(@{$sysProp_array},$filesets_tagArrayRef); my $client_tagArrayRef=['Resource',$client,$sysProp_array, 'displayName', '"'.$client.'"', 'uniqueId', '"'.$client.'"']; my $xml_arrayRef=$client_tagArrayRef; return $xml_arrayRef; } # end _createXmlStructure # Function: _writeXmlTail(writer_fh) # Purpose: To write the last two end tags to the filehandle passed in. # Side effects: Writes to filehandle. # Returns: 0 success sub _writeXmlTail { ckPrivate(); my $writer=shift; print($writer " \n"); print($writer " \n"); } # end _writeXmlTail # Function: _clean(variable_str) # Purpose: To clean the taint from a variable passed in. # A clean variable in this function will contain only # alphanumerics, underscore, hyphen, at-sign, or a period. # Side effects: - # Returns: The variable passed in untainted if it was clean, or # "" if it was not a valid value. sub _clean { ckPrivate(); my $value=shift; if ($value =~ /^([-\@\w.]+)$/) { $value=$1; } else { mesg(LVL_DEBUG, MSG_GENERIC, "Invalid value: $value"); $value=""; } } # end _clean 1;