package AIX;

# AIX.pm - these are operating system specific routines for AIXv4
#
# Rules for these subroutines:
# 
#	1.  These subroutines can be called in any order except
#	    for Initialize${OSName}.  That will always be called
#	    before any others.
#	2.  These subroutines can be called in any order...
# 
# ==============================================================
# 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.
# 
# Original source:
#	Copyright (c) 1999, 2000 Brett M Hogden
#	Based upon code by Bruce W. Mohler
# 
# Contributed source:
#	Copyright (c) 2000 Martin Andrews
#	Copyright (c) 2000 Gustavo Chaves
#	Copyright (c) 1999, 2000 Frank Crawford
#	Copyright (c) 1999, 2000 Brett M Hogden
#	Copyright (c) 1999, 2000 Alek O. Komarnitsky
#	Copyright (c) 1999, 2000 Bruce W. Mohler
#	Copyright (c) 1999, 2000 Keong Lim
#	Copyright (c) 2000 Jens Persson
#	Copyright (c) 1999, 2000 Uwe Willenbacher
# 
# ==============================================================
#
# $Header: /export/home/mohlerb/syssumm/remote/RCS/AIX.pm,v 1.30 2000/06/09 19:58:17 mohlerb Exp mohlerb $
# 
# $Revision: 1.30 $
#
# $Log: AIX.pm,v $
# Revision 1.30  2000/06/09 19:58:17  mohlerb
# changes from Jens Persson including:
# clean up of ModelInfo hash; fixed ReturnHostIdAIX function
#
# Revision 1.29  2000/06/06 21:49:36  mohlerb
# changes from Jens Persson including:
# added support for tapes
#
# Revision 1.28  2000/05/21 22:45:13  mohlerb
# changes from Gustavo Chaves including:
# Checking for undefined variables in two places (NIC card speed
# and whether a swap command exists)
#
# Revision 1.27  2000/05/05 22:38:22  mohlerb
# changes from Bruce Mohler including:
# fix problem in ReturnDefaultRouterAIX where $DefaultRouter
# was not being initialized after the information was obtained
#
# Revision 1.26  2000/04/10 17:27:14  mohlerb
# changes from Bruce Mohler including:
# replaced calls to ReturnArray with either ReturnArrayFile or
# ReturnArrayExec; also removed references to ReturnArrayGlob
#
# Revision 1.25  2000/02/09 23:44:18  mohlerb
# changes from Bruce Mohler including:
# cleaned up ReturnServices by moving function to common.pm
# and having it be driven by a @Services array in syssumm.pl;
# note also the option at the end of the Initialize<OS> function
# where you can add OS-specific services that you wish to have
# monitored
#
# Revision 1.24  2000/02/04 00:14:32  mohlerb
# changes from Bruce Mohler including:
# implemented ReturnServices to return all of the Running*
# functions as a multiple value field
#
# Revision 1.23  2000/02/03 23:22:02  mohlerb
# changes from Martin Andrews including:
# IRIX, Linux, and SunOS now report swap file sizes, stubs added
# to other OS modules
#
# Revision 1.22  2000/01/07 01:20:51  mohlerb
# changes from Bruce Mohler including:
# added code to ReturnRunningSendmail* to determine what version
# of sendmail is running from the sendmail.cf file
#
# Revision 1.21  2000/01/03 20:18:18  mohlerb
# changes from Bruce Mohler including:
# cleaned out references to $MailCommand to complete changes
# that Frank Crawford made to syssumm.pl and common.pm in
# order to use the Sendmail.pm module
#
# Revision 1.20  1999/11/02 22:15:20  mohlerb
# changes from Alek O. Komarnitsky including:
# support for local exclusions file to prevent one or more
# categories/sub-categories from processing without having
# to specify command line options or change Perl code
#
# Revision 1.19  1999/10/27 20:03:38  mohlerb
# changes from Alek O. Komarnitsky including:
# added ReturnFstab() to common.pm and stubs as appropriate
#
# Revision 1.18  1999/10/26 22:11:53  mohlerb
# Changes from Alek O. Komarnitsky including:
# added a stub routine for listing the file systems exported from
# the local machine
#
# Revision 1.17  1999/10/26 14:19:15  mohlerb
# Changes from Alek O. Komarnitsky including:
# added a stub routine for dumping the EEPROM for disaster
# recovery purposes
#
# Revision 1.16  1999/09/29 22:08:17  mohlerb
# changes from Uwe Willenbacher including:
# support for multiple StorageManagers; support for
# Veritas storage manager under SunOS
#
# Revision 1.15  1999/08/05 21:23:14  mohlerb
# changes from Bruce Mohler including:
# folded post processing after IPtoFQDN call into common.pm into
# actual IPtoFQDN call to expand IP address to fully qualified
# domain name (plus IP address) if possible
#
# Revision 1.14  1999/06/17 17:30:54  mohlerb
# changes from Frank Crawford including:
# Moved ReturnDomainNameSunOS to common.pm.
#
# Revision 1.13  1999/06/16 15:47:09  mohlerb
# changes from Brett M Hogden including:
# added parentheses to calls to _aix_get_disk_info and dyked
# forward reference; set ReturnGraphicalUserInterface to
# return "CDE"; added full paths to all command calls;
# fixed input argument to ReturnRunningNISAIX call
#
# Revision 1.12  1999/06/15 14:47:35  mohlerb
# changes from Brett M Hogden including:
# change to format of output line from ReturnProcessorsAIX;
# added NIS domainname output if NIS is running;
# added ReturnIPAddressAIX and ReturnSubnetMaskAIX functions;
# added fix to ReturnRunningNISAIX function call
#
# Revision 1.11  1999/06/15 14:04:42  mohlerb
# changes from Frank Crawford including:
# fix for ReturnRunningSamba function: added $InetdConfPath
#
# Revision 1.10  1999/06/11 18:33:14  mohlerb
# changes from Bruce Mohler including:
# put in "stub" for adding domainname to NIS field if NIS running
#
# Revision 1.9  1999/06/11 17:47:21  mohlerb
# changes from Bruce Mohler including:
# factored ReturnRunningSamba routine into common.pm
#
# Revision 1.8  1999/06/10 22:50:38  mohlerb
# changes from Bruce Mohler including:
# modified unified call to Net::Domain::Hostname to determine
# hostname of local system as it calls a CPAN module and we
# want to avoid this for purposes of portability; instead we
# call Sys::Hostname which is part of the core Perl 5 distribution
#
# Revision 1.7  1999/06/10 21:58:01  mohlerb
# changes from Bruce Mohler including:
# standardized derivation of NodeName across OS modules:
# it is defined near the beginning of the Initialize${OSName}
# and stored in $main::NodeName.  The ReturnNodeNameField
# merely returns $main::NodeName.
#
# Revision 1.6  1999/06/10 21:47:13  mohlerb
# changes from Bruce Mohler including:
# standardized reference to $MailCommand in initialization function
#
# Revision 1.5  1999/06/10 21:21:20  mohlerb
# changes from Brett M Hogden including:
# now sets the $RunAsRoot variable where appropriate
# returns the processor list as an array rather than a scalar
#
# Revision 1.4  1999/06/07 18:56:13  mohlerb
# change from Bruce Mohler including:
# common ReturnRunAsRoot relocated from individual OS modules
# to common.pm; RunAsRoot variable relocated to syssumm.pl
# (referenced as $main::RunAsRoot); this suggestion was really
# Frank Crawford's...
#
# Revision 1.3  1999/06/04 18:51:51  mohlerb
# changes from Bruce Mohler including:
# Added code for "RunAsRoot" functionality.
#
# Revision 1.2  1999/05/27 14:09:57  mohlerb
# changes from Keong Lim including:
# added Header RCS tag above Revision tag
#
# Revision 1.1  1999/05/26 20:38:29  mohlerb
# Initial revision
#

use Socket;
use Sys::Hostname;
use common;

require Exporter;
@ISA = qw(Exporter);
###########################################################################
# the following sub-routines (&) are variables ($) will be 
# visible in a program using this module...
@EXPORT = qw(
	&InitializeAIX
);
# 
# Note that you do not need to export the Return* subroutine
# names since they are being called by reference.
# 
###########################################################################

###########################################################################
# Public Global Variables (must be declared above)
###########################################################################


###########################################################################
# Private Global Variables
###########################################################################


# parts of OS version - filled in by InitializeAIX()
$OSVersion = "";
$OSVersionMajor = "";
$OSVersionMinor = "";

# where commands are located			standard args
# CUSTOMIZE
$DfCommand        = '/usr/bin/df';
$GrepCommand      = '/usr/bin/grep';
$HostidCommand    = '/usr/sbin/hostid';
$LsallqCommand    = '/usr/bin/lsallq';
$LsattrCommand    = '/usr/sbin/lsattr';
$LscfgCommand     = '/usr/sbin/lscfg';
$LsdevCommand     = '/usr/sbin/lsdev';
$LslicenseCommand = '/usr/bin/lslicense';
$LssrcCommand     = '/usr/bin/lssrc';
$NamerslvCommand  = '/usr/sbin/namerslv';
$NetstatCommand   = '/usr/sbin/netstat';
$OslevelCommand   = '/usr/bin/oslevel';
$PsCommand        = '/usr/bin/ps';		$PsArgs = '-e';
$SsaxlateCommand  = '/usr/sbin/ssaxlate';
$SwapCommand = "";
$UnameCommand     = '/usr/bin/uname';

# where files are located
# CUSTOMIZE
$InetdConfPath       = '/etc/inetd.conf';
$ResolvConfPath      = '/etc/resolv.conf';
$SendmailConfPath    = '/etc/sendmail.cf';

$NisState            = '';

#
# model information (based on cpu/machine id)
#
# XXX this list was gotten from IBM in 1998 or thereabouts -- probably
#     needs an update
#
%ModelInfo = (
	'02' => '7015-930',
	'10' => '7013-530/7016-720/7016-730',
	'11' => '7013-540',
	'14' => '7013-540',
	'18' => '7013-530H',
	'1C' => '7013-550',
	'20' => '7015-930',
	'2E' => '7015-950',
	'30' => '7013-520/7018-74X',
	'31' => '7012-320',
	'34' => '7013-520H',
	'35' => '7012-320H',
	'37' => '7012-340',
	'38' => '7012-350',
	'41' => '7011-220',
	'42' => '7006-41X',
	'43' => '7008-M20/7008-M20A',
	'45' => '7011-220',
	'46' => '7011-250',
	'47' => '7011-230',
	'48' => '7009-C10',
	'49' => '7011-25F',
	'4C' => '7248-43P/7026-H50/7025-F50/7043-140/7043-240',
	'4D' => '7020-40P',
	'57' => '7012-390/7030-3BT',
	'58' => '7012-380/7030-3AT',
	'59' => '7012-39H/7012-3CT',
	'5C' => '7013-560',
	'63' => '7015-97x',
	'64' => '7015-98x',
	'66' => '7013-58x',
	'67' => '7013-570/7013-77x/7015-R10/7018-77x',
	'70' => '7013-590',
	'71' => '7013-58H',
	'72' => '7013-59H/7015-R20',
	'75' => '7012-37x',
	'76' => '7012-36x',
	'77' => '7012-350/7012-355/7013-55x',
	'79' => '7013-591/7015-R21',
	'80' => '7015-990',
	'82' => '7015-R24',
	'89' => '7013-595',
	'90' => '7009-C20',
	'91' => '7006-42x',
	'A0' => '7013-J30',
	'A1' => '7013-J40',
	'A3' => '7015-R30',
	'A4' => '7013-R40',
	'A6' => '7012-G30',
	'A7' => '7013-G40',
	'C0' => '7024-E20/7024-E30',
	'C4' => '7025-F30',
	'F0' => '7007-N40'
);


###########################################################################
# Subroutines
###########################################################################

# 
# This subroutine is called before any others.  It's
# reason for existence is to plug in the references to
# subroutines below to actually generate the information.
# 

sub InitializeAIX ()
{
	print "InitializeAIX()...\n" if $main::Debug;

	# initialize $SubroutineSuffix...
	WhatVersionOfAIX();

	# we need the nodename for later on...
	$main::NodeName = hostname();
	print "\tNodeName is $main::NodeName\n" if $main::Debug;

	# plug in appropriate subroutines
	$main::Routines{General}{Vendor} = \&ReturnVendorAIX;
	$main::Routines{General}{Model} = \&ReturnModelAIX;
	$main::Routines{General}{HostId} = \&ReturnHostIdAIX;

	$main::Routines{Hardware}{Processors} = \&ReturnProcessorsAIX;
	$main::Routines{Hardware}{Memory} = \&ReturnMemoryAIX;
	$main::Routines{Hardware}{Disks} = \&ReturnDisksAIX;
	$main::Routines{Hardware}{Tapes} = \&ReturnTapesAIX;
	$main::Routines{Hardware}{StorageManager} = \&ReturnStorageManagerAIX;
	$main::Routines{Hardware}{Eeprom} = \&ReturnEepromAIX;

	$main::Routines{Network}{DomainName} = \&ReturnDomainNameAIX;
	$main::Routines{Network}{DefaultRouter} = \&ReturnDefaultRouterAIX;
	$main::Routines{Network}{NameServer} = \&ReturnNameServerAIX;
	$main::Routines{Network}{IPAddress} = \&ReturnIPAddressAIX;
	$main::Routines{Network}{SubnetMask} = \&ReturnSubnetMaskAIX;
	$main::Routines{Network}{NetworkInterfaces} = \&ReturnNetworkInterfacesAIX;

	$main::Routines{Software}{OsName} = \&ReturnOsNameAIX;
	$main::Routines{Software}{OsVersion} = \&ReturnOsVersionAIX;
	$main::Routines{Software}{NbrOfLicenses} = \&ReturnNbrOfLicensesAIX;
	# apparently the AIX patch process is "obscure"...
	$main::Routines{Software}{ReturnPatches} = \&ReturnUnknownField;
	$main::Routines{Software}{Printers} = \&ReturnPrintersAIX;
	$main::Routines{Software}{Services} = \&ReturnServices;
	$main::Routines{Software}{IPAddressResolution} = \&ReturnIPAddressResolutionAIX;
	$main::Routines{Software}{GraphicalUserInterface} = \&ReturnGraphicalUserInterfaceAIX;
	$main::Routines{Software}{ReturnFontServer} = \&ReturnUnknownField;
	$main::Routines{Software}{Fstab} = \&ReturnFstab;
	$main::Routines{Software}{LocalFileSystems} = \&ReturnLocalFileSystemsAIX;
	$main::Routines{Software}{RemoteFileSystems} = \&ReturnRemoteFileSystemsAIX;
	$main::Routines{Software}{ExportedFileSystems} = \&ReturnExportedFileSystemsAIX;
	$main::Routines{Software}{Swap} = \&ReturnSwapAIX;

	# if you have OS-specific services, add them here
	# push @main::Services, "Local-Service";

	RemoveLocalExclusions();

	return;
}


###########################################################################
# Global
###########################################################################

sub WhatVersionOfAIX
{
	print "WhatVersionOfAIX()...\n" if $main::Debug;

	$OSVersion = `$OslevelCommand`;
	chomp($OSVersion);

	print "\tRunning version $OSVersion...\n" if $main::Debug;

	( $OSVersionMajor, $OSVersionMinor ) = (split(/\./, $OSVersion))[0,1];
	print "\tMajor OS version is $OSVersionMajor...\n" if $main::Debug;
	print "\tMinor OS version is $OSVersionMinor...\n" if $main::Debug;

	if ($OSVersionMajor ne '4') {
		die "This module only supports AIX v4.x\n";
	}

	return;
}


###########################################################################
# General
###########################################################################

sub ReturnVendorAIX ($)
{	my $Subcategory = shift;

	return ( 'IBM' );
}


sub ReturnModelAIX ($)
{	my $Subcategory = shift;

	(my $mach_id = `$UnameCommand -m`) =~ s#.+(..)..\s*$#$1#o;
	return ( exists $ModelInfo{$mach_id} 
		? $ModelInfo{$mach_id} 
		: 'Unknown' );
}


sub ReturnHostIdAIX ($)
{	my $Subcategory = shift;

	my $IdNbr = `$HostidCommand`;
	chomp($IdNbr);

	return ( $IdNbr );
}


###########################################################################
# Hardware
###########################################################################

#
# XXX   what about L2 cache?  could get it, but where should it go?
#
sub ReturnProcessorsAIX ($)
{	my $Subcategory = shift;
	my @Response = ();

	foreach $Line ( `$LsdevCommand -Ct proc_rspc -S a -F name` ) {
		chomp($Line);
		if ( $Line =~ /^(proc\d+)$/o ) {
			my $ptype = `$LsattrCommand -EOl $1 -a type | $GrepCommand -v '^#'`;
			chomp($ptype);
			push @Response, "name=$1 type=$ptype";
			next;
		}
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnMemoryAIX ($)
{	my $Subcategory = shift;
	my $Memory = 0;

	# how much memory?
	foreach $Line ( `$LsdevCommand -Cc memory -S a -F name` ) {
		chomp($Line);
		if ( $Line =~ /^(mem\d+)$/o ) {
			chomp($msize = `$LsattrCommand -EOl $1 -a size | $GrepCommand -v '^#'`);
			$Memory += $msize;
			next;
		}
	}
	$Memory = "$Memory" . "MB";

	return ( $Memory );
}


#
# @@NB: this handles normal disks (hdiskN) and SSA disks, but does *not*
#       handle pdisks that are SSA RAID hot spares
#
#       SSA checking requires root access
#
sub ReturnDisksAIX ($)
{	my $Subcategory = shift;
	my @Response = ();
	my %ssa;

	if ($> == 0)
	{
		foreach my $p (`$LsdevCommand -Ct hdisk -sssar -F name`)
		{
			chomp $p;
			@{$ssa{$p}} = split(/\s+/, `$SsaxlateCommand -l $p`);
		}
	}
	else {
		$RunAsRoot = 1;
	}


	foreach my $p (sort `$LsdevCommand -Cc disk -F name`) {
		chomp $p;
		if (exists $ssa{$p}) {
			push @Response, 
				sprintf("%s: %s (%s)", $p, (_aix_get_disk_info($p))[0],
				join(',',@{$ssa{$p}}));
			foreach my $sp (sort @{$ssa{$p}}) {
				push @Response, 
					sprintf("%s: %s %s %s", $sp, _aix_get_disk_info($sp));
			}
		}
		else {
			push @Response, 
				sprintf("%s: %s %s %s", $p, _aix_get_disk_info($p));
		}
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnTapesAIX ($)
{	my $Subcategory = shift;
	my @Response = ();

	foreach my $p (sort `$LsdevCommand -Cc tape -F name`) {
		chomp $p;
		push @Response,
		sprintf("%s: %s %s %s", $p, _aix_get_disk_info($p));
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub _aix_get_disk_info ($)
{	my $disk = shift;

	my( $desc, $manuf, $model) = 
		('(Type Unknown)','(Manufacturer Unknown)','(Model Unknown)');

	foreach (`$LscfgCommand -vl $disk 2>&-`) {
		chomp;
		s/\s+$//o;
		next unless (/^\s+($disk)\s+\S+\s+(.+)*$/ || 
			/^\s+(Manufacturer)\.+(.+)$/o || 
			/^\s+Machine Type and (Model)\.+(.+)$/o);
		if ( $1 eq $disk ) {
			$desc  = $2;
		}
		elsif ( $1 eq 'Manufacturer' ) {
			$manuf = $2;
		}
		elsif ( $1 eq 'Model' ) {
			$model = $2;
		}
	}

	return ($desc,$manuf,$model);
}


sub ReturnStorageManagerAIX ($)
{	my $Subcategory = shift;
	my @Response = ();

	push @Response, "Logical Volume Manager (LVM)";

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnEepromAIX ($)
{	my $Subcategory = shift;
	my $Eeprom = "";

	# XXX - this is a stub routine to be filled in later...

	return ( $Eeprom );
}


###########################################################################
# Network
###########################################################################

sub ReturnDomainNameAIX ($)
{	my $Subcategory = shift;

	return ReturnDomainName($Subcategory, $ResolvConfPath);
}


#
# XXX should use lsattr for this, but what the hey...
#
sub ReturnDefaultRouterAIX ($)
{	my $Subcategory = shift;

	my $DefaultRouter = 'Unknown';
	foreach $Line ( `$NetstatCommand -rn` ) {
		chomp($Line);
		next unless $Line =~ /^default/o;
		my $dr_ip = (split(/\s+/,$Line))[1];
		$DefaultRouter = IPtoFQDN($dr_ip);
	}

	return ( $DefaultRouter );
}


sub ReturnNameServerAIX ($)
{	my $Subcategory = shift;
	my @Response = ( );

	foreach $Line ( `$NamerslvCommand -sZI` ) {
		next if $Line =~ /^#/o;
		chomp($Line);
		my $NameServerFQDN = IPtoFQDN($Line);
		if ( ! $NameServerFQDN ) {
			$AIX::NonStandardNameServer = 1;
		}
		push @Response, $NameServerFQDN;
		next;
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnIPAddressAIX ($)
{	my $Subcategory = shift;

	# Returns the IP address associated with "$main::nodename",
	# the "default" IP address.

	my @Addresses = (gethostbyname $main::NodeName)[4];
	$IPAddress = inet_ntoa($Addresses[0]);

	return ( $IPAddress );
}


sub ReturnSubnetMaskAIX ($)
{	my $Subcategory = shift;
	my $SubnetMask  = '';
	my $IPAddress   = ReturnIPAddressAIX(@_);

	# Returns netmask associated with the "default" IP address

	# find interface corresponding to "default" IP
	# and get netmask from it
	foreach $Line ( ReturnNetworkInterfacesAIX(@_) ) {
		next unless ( $Line =~ /ipaddr=$IPAddress\s/ );
		if ( $Line =~ /netmask=(\S+)\s/o ) {
			$SubnetMask = $1;
		}
	}

	return ( $SubnetMask );
}


sub ReturnNetworkInterfacesAIX ($)
{	my $Subcategory = shift;
	my @Response = ( );

	my($iface,$desc,$devtype,$macaddr,$speed,$ipaddr,$netmask,$broadcast);

	foreach $Line ( `$LsdevCommand -Cc if -S a -F name,description` ) {
		chomp($Line);
		next if $Line =~ /^#|^lo0/o;
		next unless $Line =~ /^(\D+\d+),(.+)$/o;
		$iface = $1;
		$desc = $2;

		foreach my $n ( `$NetstatCommand -v $iface` ) {
			chomp($n);
			if ($n =~ /^Device Type: (.+)$/o) {
				$devtype = $1;
			} elsif ($n =~ /^Hardware Address: (.+)$/o) {
				$macaddr = $1;
			} elsif ($n =~ /^Media Speed Running: (.+)$/o) {
				$speed = $1;
			}
		}

		foreach my $n ( `$LsattrCommand -EOl $iface -a netaddr,netmask,broadcast` ) {
			chomp($n);
			next if $n =~ /^#/o;
			($ipaddr,$netmask,$broadcast) = split(/:/,$n);
			if ($broadcast eq '') {
				($broadcast = $ipaddr) =~ s/^(.+)\.\d+$/$1.255/o;
			}
		}

		$iface .= " ipaddr=$ipaddr netmask=$netmask broadcast=$broadcast mac=$macaddr";
		if ( defined $speed ) { $iface .= " speed=\"$speed\""; }
		$iface .= " desc=\"$desc\" devtype=\"$devtype\"";
		push @Response, $iface;
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}



###########################################################################
# Software
###########################################################################

sub ReturnOsNameAIX ($)
{	my $Subcategory = shift;

	return ( 'AIX' );
}


sub ReturnOsVersionAIX ($)
{	my $Subcategory = shift;

	return ( "$OSVersion" );
}


#
# @@NB: requires root
#
sub ReturnNbrOfLicensesAIX ($)
{	my $Subcategory = shift;

	my $NbrOfLicenses = 'Unknown';
	if ($> == 0) {
		my $lic_fixed = 0;
		my $lic_float = 'off';

		foreach $Line ( `$LslicenseCommand -c 2>/dev/null` ) {
			chomp($Line);
			next if $Line =~ /^#/o;
			($lic_fixed,$lic_float) = split(/:/, $Line);
			last;
		}
		$NbrOfLicenses = "fixed=$lic_fixed, float=$lic_float";
	}
	else {
		$RunAsRoot = 1;
	}

	return ( $NbrOfLicenses );
}


# 
# Note that this subroutine has a side-effect of setting
# a global variable in main:: called EmailAvailable if
# sendmail is running...
# 

sub ReturnRunningSendmailAIX ($)
{	my $Subcategory = shift;

	my $Sendmail = 'Unknown';
	foreach $Line ( `$LssrcCommand -s sendmail` ) {
		chomp($Line);
		next unless $Line =~ /\s*sendmail.+(active|inoperative)$/o;
		$Sendmail = ( $1 eq 'active' ? 'Yes' : 'No' );
		last;
	}

	# what version of sendmail are we running?
	if ( ( $Sendmail eq 'Yes' ) and ( -r $SendmailConfPath ) ) {
		@SendmailConfig = ReturnArrayFile("$SendmailConfPath");
		foreach $Line (@SendmailConfig) {
			if ( $Line =~ /^DZ(\d+\.\d+\.\d+)/ ) {
				$Sendmail .= " (version $1)";
				last;
			}
		}
	}

	# side-effect
	$main::EmailAvailable = ( $Sendmail eq 'Yes' );

	return ( $Sendmail );
}


sub ReturnRunningNFSAIX ($)
{	my $Subcategory = shift;

	my $NFSState = 'Unknown';
	foreach $Line ( `$LssrcCommand -g nfs` ) {
		chomp($Line);
		next unless $Line =~ /^\s*(nfsd|biod).+(active|inoperative)$/o;
		if ($1 eq 'nfsd') { $NFSServer = ($2 eq 'active'); }
		if ($1 eq 'biod') { $NFSClient = ($2 eq 'active'); }
	}
	print "\tNFSServer is $NFSServer\n" if $main::Debug;
	print "\tNFSClient is $NFSClient\n" if $main::Debug;
	if ( $NFSServer and $NFSClient) {
		$NFSState = 'Yes, both server and client';
	}
	elsif ( $NFSServer ) {
		$NFSState = 'Yes, server only';
	}
	elsif ( $NFSClient ) {
		$NFSState = 'Yes, client only';
	}
	else {
		$NFSState = 'No';
	}

	return ( $NFSState );
}


sub ReturnRunningNISAIX ($)
{	my $Subcategory = shift;

	if ( $NisState eq '' ) {
		my $slave_p  = 0;
		my $client_p = 0;

		$NisState = 'No';

		foreach $Line ( `$LssrcCommand -g yp` ) {
			chomp($Line);
			next unless $Line =~ /^\s*(ypbind|ypserv).+(active|inoperative)$/o;
			if ($1 eq 'ypserv') { $slave_p  = ($2 eq 'active'); }
			if ($1 eq 'ypbind') { $client_p = ($2 eq 'active'); }
		}

		if ( $slave_p && $client_p )  {
			$NisState = 'Yes, both server and client';
		}
		elsif ( $slave_p )  {
			$NisState = 'Yes, server only';
		}
		elsif ( $client_p ) {
			$NisState = 'Yes, client only';
		}
	}

	if ($NisState =~ /^yes/oi) {
		my $Domainname = `domainname`;
		chomp($Domainname);
		$NisState .= ", domain is $Domainname";
	}

	return ( $NisState );
}


sub ReturnRunningAutomountAIX ($)
{	my $Subcategory = shift;

	my $Automount = 'No';
	foreach $Line ( `$LssrcCommand -s automountd` ) {
		chomp($Line);
		next unless $Line =~ /^\s*automountd.+(active|inoperative)$/o;
		$Automount = ($1 eq 'active' ? 'Yes' : 'No');
		last;
	}

	return ( $Automount );
}


sub ReturnRunningNameserverAIX ($)
{	my $Subcategory = shift;

	$Nameserver = 'Unknown';
	foreach $Line ( `$LssrcCommand -s named` ) {
		chomp($Line);
		next unless $Line =~ /^\s*named.+(active|inoperative)$/o;
		$Nameserver = ( $1 eq 'active' ? 'Yes' : 'No' );
		last;
	}
	return ( $Nameserver );
}


sub ReturnIPAddressResolutionAIX ($)
{	my $Subcategory = shift;

	my $IPAddressResolution = '/etc/hosts';
	if ( -f $ResolvConfPath ) {
		$IPAddressResolution = 'DNS';
	}
	elsif ( ReturnRunningNISAIX($Subcategory) ne 'No' ) {
		$IPAddressResolution = 'NIS';
	}

	return ( $IPAddressResolution );
}


sub ReturnRunningSambaAIX ($)
{	my $Subcategory = shift;

	return ReturnRunningSamba($Subcategory, $PsCommand, $PsArgs, $InetdConfPath);
}


sub ReturnGraphicalUserInterfaceAIX ($)
{	my $Subcategory = shift;

	return ( "CDE" );
}


sub ReturnLocalFileSystemsAIX ($)
{	my $Subcategory = shift;
	my @Response = ( );

	foreach $Line ( `$DfCommand -k` ) {
		chomp($Line);
		next unless $Line =~ m#^/#o;
		my @Fields = split(/\s+/, $Line);
		my $Size = sprintf "%.1fMB",	$Fields[1] / 1024;
		push @Response, "$Fields[6] mount=$Fields[0] size=$Size sat=$Fields[3]";
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnRemoteFileSystemsAIX ($)
{	my $Subcategory = shift;
	my @Response = ( );

	foreach $Line ( `$DfCommand -k` ) {
		chomp($Line);
		next unless $Line =~ m#^(\w+):(/\S+)\s+#o;
		my($RemoteSystem, $RemoteFileSystem) = ($1, $2);
		my @Fields = split(/\s+/, $Line);
		my $Size = sprintf "%.1fMB",	$Fields[1] / 1024;
		push @Response, "$Fields[6] remote=$RemoteSystem mount=$RemoteFileSystem size=$Size sat=$Fields[3]";
	}

	@Response = ForceMultipleResponses(\@Response);
	return @Response;
}


sub ReturnExportedFileSystemsAIX ($)
{	my $Subcategory = shift;
	my @Response = ( );

	# XXX - this is a stub routine to be filled in later...

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


#
# @@NB: only handles native AIX queue daemon -- support lprng sometime?
#
sub ReturnRunningLpschedAIX ($)
{	my $Subcategory = shift;

	my $Lpsched = 'Unknown';
	foreach $Line ( `$LssrcCommand -s qdaemon` ) {
		chomp($Line);
		next unless $Line =~ /^\s*qdaemon.+(active|inoperative)$/o;
		$Lpsched = ( $1 eq 'active' ? 'Yes' : 'No' );
		last;
	}
	return ( $Lpsched );
}


sub ReturnSwapAIX ($)
{	my $Subcategory = shift;

	#
	# XXX - This is a dummy routine
	# 
	# The value of $SwapCommand needs to be initialized above.
	# 

	if ( $SwapCommand ) {
		return ReturnSwap($Subcategory, "$SwapCommand");
	} else {
		return '(unknown)';
	}
}


sub ReturnPrintersAIX ($)
{	my $Subcategory = shift;
	my @Response = ( );

	foreach $Line ( `$LsallqCommand -c` ) {
		if ( $Line =~ /^([^:]+):/o ) {
			push @Response, $1;
		}
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnRunningAccountingAIX ($)
{	my $Subcategory = shift;

	# XXX Let's defer this for now

	return ( "Unknown" );
}


###########################################################################
# End of AIX.pm
###########################################################################

1;
