package SunOS;

# SunOS.pm - these are operating system specific routines for SunOS
#
# 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 Jeff Putsch <putsch@unitrode.com>
#	Based upon code by Bruce W. Mohler
# 
# Contributed source:
#	Copyright (c) 1999, 2000 Martin Andrews
#	Copyright (c) 2000 Gustavo Chaves
#	Copyright (c) 1999, 2000 Frank Crawford
#	Copyright (c) 1999, 2000 Paul Farrall
#	Copyright (c) 1999, 2000 Kent Gordon
#	Copyright (c) 1999, 2000 Alek O. Komarnitsky
#	Copyright (c) 1999, 2000 Keong Lim
#	Copyright (c) 1999, 2000 Bruce W. Mohler
#	Copyright (c) 2000 Ken Monville
#	Copyright (c) 1999, 2000 Uwe Willenbacher
# 
# ==============================================================
# 
# $Header: /export/home/mohlerb/syssumm/remote/RCS/SunOS.pm,v 1.46 2000/05/21 23:28:22 mohlerb Exp mohlerb $
# 
# $Revision: 1.46 $
#
# $Log: SunOS.pm,v $
# Revision 1.46  2000/05/21 23:28:22  mohlerb
# changes from Gustavo Chaves including:
# One of my SPARCServer-1000 has some network interfaces disabled
# and the the ifconfig command complained in the STDERR.  I made
# it throw it to /dev/null instead.
#
# Revision 1.45  2000/05/11 22:39:44  mohlerb
# changes from Bruce Mohler including:
# added code to determine whether the Veritas Volume Manager is
# running if it is installed on the system
#
# Revision 1.44  2000/05/05 22:16:50  mohlerb
# changes from Bruce Mohler including:
# in ReturnDisksSunOS, for $DeviceModels that contain angle brackets
# and multiple contiguous whitespace, remove them
#
# Revision 1.43  2000/05/03 21:51:01  mohlerb
# changes from Bruce Mohler including:
# refined ReturnTapesSunOS so that it would handle the condition
# of no tape being in the drive (at which point it *doesn't*
# return the tape drive model)
#
# Revision 1.42  2000/05/03 21:22:25  mohlerb
# changes from Bruce Mohler including:
# replaced dmesg dependent version of ReturnTapesSunOS
# with one that actually returns tapes drives
#
# Revision 1.41  2000/05/03 20:37:02  mohlerb
# changes from Bruce Mohler including:
# changed the device pattern to identify a cd-rom drive from
# "\dt6s" to "\dt[26]d"; note that the Ultra 5 tends to have
# the cd-rom on device 2 and the trailing "d" was, I think,
# a typo
#
# Revision 1.40  2000/04/18 03:23:30  mohlerb
# changes from Ken Monville including:
# detect an atm network device; had to change the way the MAC address
# was discovered because arp would hang trying to "resolve" the MAC
# for the atm interfaces
#
# Revision 1.39  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.38  2000/04/05 14:40:45  mohlerb
# changes from Frank Crawford including:
# the mod to SunOS.pl to use ReadSunDisklabel.pl; the old code
# was left in (commented out) so that we can easily swap between
# the new and old in case there are problems
#
# Revision 1.37  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.36  2000/02/07 02:23:22  mohlerb
# changes from Frank Crawford including:
# changes to fix the problem with disk models; also fixed the
# error checking a bit and converted it to sysopen and sysread
#
# Revision 1.35  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.34  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.33  2000/01/13 21:38:58  mohlerb
# changes from Jeff Putsch including:
# Keep interface name in SunOS::AddNetInfo from getting chopped;
# Make device speed detection work (if there was a problem
# running ndd, this section did not work correctly)
#
# Revision 1.32  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.31  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.30  1999/12/30 18:39:58  mohlerb
# changes from Kent Gordon including:
# comments about further changes required for Solaris on Intel
# added just above ReturnDisksSunOS function; corresponding
# C source file added to remote directory as Read Sun Disk label.c
#
# Revision 1.29  1999/11/03 20:00:39  mohlerb
# changes from Paul Farrall including:
# removed slice number from device name in ReturnDisksSunOS
#
# Revision 1.28  1999/11/03 19:47:57  mohlerb
# changes from Kent Gordon including:
# reworked ReturnDisksSunOS function to not depend on dmesg
# but to iterate across /dev/rdsk/c*t*d*s2 devices and return
# the disk label information; [from Bruce Mohler] fixed typo in
# RunningLpsched function: s/BSDLp/BSDLpr/
#
# Revision 1.27  1999/11/02 23:29:29  mohlerb
# changes from Bruce Mohler including:
# abstracted Lpsched routines into 3 routines moved to common.pm
# including ReturnRunningSysvLp, ReturnRunningBSDLpr, and
# ReturnRunningLPRng, then inserted code in each of the OS
# modules (except AIX.pm) to call the appropriate routines
# and concatenate the results into a single coherent response
#
# Revision 1.26  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.25  1999/10/30 02:44:30  mohlerb
# changes from Bruce Mohler including:
# added linkspeed to NetworkInterface routine
#
# Revision 1.24  1999/10/27 20:03:38  mohlerb
# changes from Alek O. Komarnitsky including:
# added ReturnFstab() to common.pm and stubs as appropriate
#
# Revision 1.23  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.22  1999/10/26 21:49:28  mohlerb
# Changes from Alek O. Komarnitsky including:
# filled in ReturnEepromSunOS() function
#
# Revision 1.21  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.20  1999/10/24 00:29:37  mohlerb
# changes from Alek O. Komarnitsky including:
# work in ReturnDisksSunOS() in order to recognize local and
# Veritas disks but bypass /proc and loopback file systems;
# change to default "" mode for device c0t6d0s0 to be "Probably
# a CD-ROM"
#
# Revision 1.19  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.18  1999/08/05 21:33:05  mohlerb
# changes from Bruce Mohler including:
# fed $DefaultRouter to IPtoFQDN before returning
#
# Revision 1.17  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.16  1999/07/02 21:21:15  mohlerb
# changes from Bruce Mohler including:
# Perl complained about the UnameCommand in syssumm.pl not being
# used because it couldn't see (yet) the references down in the
# OS modules.  Since AIX doesn't have the uname command in the
# same place as the other OSs (so far), I moved the UnameCommand
# local to each OS module.
#
# Revision 1.15  1999/07/02 16:22:36  mohlerb
# changes from Frank Crawford including:
# Added "MB" to end of disk size field.
#
# Revision 1.14  1999/06/17 17:29:50  mohlerb
# changes from Frank Crawford including:
# Moved ReturnDomainNameSunOS to common.pm.
# Fixed up ReturnTapesSunOS to find tape drives.
# Bullet-proofed ReturnDisksSunOS when some data missing.
# Reworked the routines finding NICs to handle more types and
# be more general.
# Fixed extra linebreak in mount point listing.
#
# Revision 1.13  1999/06/15 14:04:42  mohlerb
# changes from Frank Crawford including:
# fix for ReturnRunningSamba function: added $InetdConfPath
#
# Revision 1.12  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.11  1999/06/11 17:47:21  mohlerb
# changes from Bruce Mohler including:
# factored ReturnRunningSamba routine into common.pm
#
# Revision 1.10  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.9  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.8  1999/06/10 20:01:23  mohlerb
# changes from Frank Crawford including:
# Changed %Routines to use both ReturnNodeName and ReturnRunAsRoot in common.pm
# (included in a change in syssumm.pl).
# Changed ReturnProcessorsSunOS to return a list rather than a string.
# Changed ReturnDomainNameSunOS to be the same as Linux.pm & IRIX.pm.
# Changed ReturnRunningSambaSunOS to new version (from AIX.pm).
# Fixed ReturnRemoteFileSystemsSunOS to not return anything if no remote
# mounts defined in $EtcFstabPath (previously return any automounts).
# Changed coversion to MB to division by 1024 rather than 1000.
# Extracted some pattern into variables for pattern matches.
# Deleted some unused variables and functions.
# Fixed some prototyping errors.
#
# Revision 1.7  1999/06/07 20:02:13  mohlerb
# changes from Bruce Mohler including:
# provided for condition in ReturnPatchesSunOS patches where
# no patches have been installed
#
# Revision 1.6  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.5  1999/06/04 18:51:51  mohlerb
# changes from Bruce Mohler including:
# Added code for "RunAsRoot" functionality.
#
# Revision 1.4  1999/06/03 20:37:10  mohlerb
# changes from Jeff Putsch including:
# Updated AddNetInfo to take an interface name and return device
# and hardware type information. For example it now returns a
# string that looks like this:
#
#   le0 type=ether device="Am7990 (LANCE)" macaddr=8:0:20:87:21:3a
#     ip=10.32.32.100 netmask=255.255.224.0 broadcast=10.32.63.255
#
# I've broken that across two lines for readability.
#
# Revision 1.3  1999/05/27 14:09:57  mohlerb
# changes from Keong Lim including:
# added Header RCS tag above Revision tag
#
# Revision 1.2  1999/05/25 19:39:57  mohlerb
# changes from Bruce Mohler including:
# Diked out "FauxNameServer" routine in common which was cute
# but silly and non-portable and replaced it with call to
# gethostbyaddr via IPtoFQDN routine in common.pm
#
# Revision 1.1  1999/05/25 18:43:32  mohlerb
# Initial revision
#

use Sys::Hostname;
use Time::Local;
use common;

require Exporter;
@ISA = qw(Exporter);
###########################################################################
# the following sub-routines (&) are variables ($) will be 
# visible in a program using this module...
@EXPORT = qw(
	&InitializeSunOS
);
# 
# 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 InitializeSunOS()
$OSVersion = "";
$OSVersionMajor = "";
$OSVersionMinor = "";

# where commands are located			standard args
# CUSTOMIZE
$ArpCommand = "/usr/sbin/arp";
$AutomountCommand = "/usr/lib/autofs/automountd";
				$AutomountPattern = "(\/automount|\/autofsd)";

$DfCommand = "/bin/df";				$LocalFileSystems = "-kl";
						$LoopbackFileSystems = "-k -Flofs";
						$NFSFileSystems = "-k -F nfs";
$DmesgCommand = "/bin/dmesg";			$DmesgArgs = "3>/dev/null";
$EepromCommand = "/usr/sbin/eeprom";
$HostIdCommand = "/usr/bin/hostid";
$IfconfigCommand = "/sbin/ifconfig";
$LpcCommand = "/usr/sbin/lpc";			$LpcArgs = "status";
$LpDaemonPattern = "(\/lpsched|\/lpd)";
$MountdCommand = "/usr/lib/nfs/mountd";
$MtCommand = "/usr/bin/mt";
$NameServerPattern = "/usr/sbin/(in\.)?named";
$NddCommand = "/usr/sbin/ndd";
$NetstatCommand = "/usr/bin/netstat";		$NetstatRouteArgs = "-rn";
$NfsdCommand = "/lib/nfs/nfsd";
$NISplusServerCommand = "/usr/sbin/rpc.nisd";	$NISplusYPcompatArgs = "-Y";
$NISServerCommand = "/usr/lib/netsvc/yp/ypserv"; 
$NISClientCommand = "/usr/lib/netsvc/yp/ypbind";
$PkginfoCommand = "/bin/pkginfo";		$PkginfoArgs = "-i";
$PrtconfCommand = "/usr/sbin/prtconf";		$PrtconfArgs = "-v";
$PsCommand = "/usr/bin/ps";			$PsArgs = "-aef";
$PsrinfoCommand = "/usr/sbin/psrinfo";		$PsrinfoArgs = "-v";
# SysSumm Helper program.  Found in the syssumm directory.
$ReadDiskLabelCommand = "ReadSunDisklabel.pl";	$ReadDiskLabelArgs = "-p";
$SendmailCommand = "/usr/lib/sendmail";
$ShowrevCommand = "/bin/showrev";		$ShowrevPatchArgs = "-p";
$SwapCommand = "/usr/sbin/swap -l";
$UnameCommand = "/bin/uname";

# where files are located
# CUSTOMIZE
$ExportsPath = "/etc/dfs/dfstab";
$EtcFstabPath = "/etc/vfstab";
$InetdConfPath = "/etc/inetd.conf";
$NsSwitchConfPath = "/etc/nsswitch.conf";
$ResolvConfPath = "/etc/resolv.conf";
$SendmailConfPath = '/etc/mail/sendmail.cf';
# $SyslogConfPath = "/etc/syslog.conf";


# device path patterns - filled in by InitializeSunOS()
$DiskNamePattern = "";
$TapeNamePattern = "";

# The following list was found by examining the man pages for Solaris 2.6
# Note: the response is "$Device|$Type" where $Type defaults to "ether"
%IfList = (
	'ba' =>         'SUNWatm, Classical IP interface',
	'be' =>		'BigMAC Fast Ethernet',
	'cnft' =>	'Compaq NIC',
	'dnet' =>	'DEC 21040, 21041, 21140',
	'eepro' =>	'Intel EtherExpress-Pro',
	'el' =>		'3COM 3C503',
	'elink' =>	'3COM 3C507',
	'elx' =>	'3COM EtherLink III',
	'hme' =>	'SUNW,hme Fast-Ethernet',
	'ie' =>		'Intel 82586',
	'iee' =>	'Intel EtherExpress 16',
	'ieef' =>	'Intel EtherExpress Flash32/82596',
	'iprb' =>	'Intel 82557 (D100)-controlled Network Cards',
	'le' =>		'Am7990 (LANCE)',
	'nee' =>	'Novell NE3200',
	'nei' =>	'Novell NE2000, NE2000plus',
	'nf' =>		'Network Peripherals SBus FDDI|FDDI',
	'nfe' =>	'Compaq Netflex-2 Dualport and ENET/TR',
	'pcelx' =>	'3COM EtherLink III PCMCIA',
	'pcn' =>	'AMD PCnet',
	'pe' =>		'Xircom Pocket',
	'qe' =>		'QEC/MACE',
	'riles' =>	'Racal Interlan ES-3210',
	'smc' =>	'SMC 8003/8013/8216/8416',
	'smce' =>	'SMC 3032/EISA dual-channel',
	'smceu' =>	'SMC Elite32 Ultra (8232)',
	'smcf' =>	'SMC Ether100 (9232)',
	'tr' =>		'IBM 16/4 Token Ring Network Adapter|Token Ring',
);


###########################################################################
# 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 InitializeSunOS ()
{
	print "InitializeSunOS()...\n" if $main::Debug;

	# initialize $SubroutineSuffix...
	WhatVersionOfSunOS();

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

	# fill in device path patterns
	$DiskNamePattern = "/dev/rdsk/c%st%sd0";	# cont, addr
	$TapeNamePattern = "/dev/rmt/%sm";		# addr

	# plug in appropriate subroutines
	$main::Routines{General}{Vendor} = \&ReturnVendorSunOS;
	$main::Routines{General}{Model} = \&ReturnModelSunOS;
	$main::Routines{General}{HostId} = \&ReturnHostIdSunOS;

	$main::Routines{Hardware}{Processors} = \&ReturnProcessorsSunOS;
	$main::Routines{Hardware}{Memory} = \&ReturnMemorySunOS;
	$main::Routines{Hardware}{Disks} = \&ReturnDisksSunOS;
	$main::Routines{Hardware}{Tapes} = \&ReturnTapesSunOS;
	$main::Routines{Hardware}{StorageManager} = \&ReturnStorageManagerSunOS;
	$main::Routines{Hardware}{Eeprom} = \&ReturnEepromSunOS;

	$main::Routines{Network}{DomainName} = \&ReturnDomainNameSunOS;
 	$main::Routines{Network}{DefaultRouter} = \&ReturnDefaultRouterSunOS;
 	$main::Routines{Network}{NameServer} = \&ReturnNameServerSunOS;
 	$main::Routines{Network}{IPAddress} = \&ReturnIPAddressSunOS;
	$main::Routines{Network}{SubnetMask} = \&ReturnSubnetMaskSunOS;
 	$main::Routines{Network}{NetworkInterfaces} = \&ReturnNetworkInterfacesSunOS
;
 
	$main::Routines{Software}{OsName} = \&ReturnOsNameSunOS;
	$main::Routines{Software}{OsVersion} = \&ReturnOsVersionSunOS;
	$main::Routines{Software}{NbrOfLicenses} = \&ReturnNotApplicableField;
	$main::Routines{Software}{Patches} = \&ReturnPatchesSunOS;
	$main::Routines{Software}{Printers} = \&ReturnPrintersSunOS;
	$main::Routines{Software}{Services} = \&ReturnServices;
;
	$main::Routines{Software}{IPAddressResolution} = \&ReturnIPAddressResolutionSunOS;
	$main::Routines{Software}{GraphicalUserInterface} = \&ReturnGraphicalUserInterfaceSunOS;
	$main::Routines{Software}{FontServer} = \&ReturnFontServerSunOS;
	$main::Routines{Software}{Fstab} = \&ReturnFstab;
	$main::Routines{Software}{LocalFileSystems} = \&ReturnLocalFileSystemsSunOS;
	$main::Routines{Software}{RemoteFileSystems} = \&ReturnRemoteFileSystemsSunOS;
	$main::Routines{Software}{ExportedFileSystems} = \&ReturnExportedFileSystemsSunOS;
	$main::Routines{Software}{Swap} = \&ReturnSwapSunOS;

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

	RemoveLocalExclusions();

	return;
}


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


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

	$OSVersion = `$UnameCommand -r`;
	chomp($OSVersion);
	print "\tRunning version $OSVersion...\n" if $main::Debug;

	$OSVersion =~ /([\d]+\.[\d]+)(\.([\d]+))?/;
	$OSVersionMajor = $1;
	$OSVersionMinor = "";
	if ($3) {$OSVersionMinor = $3};
	print "\tMajor OS version is $OSVersionMajor...\n" if $main::Debug;
	print "\tMinor OS version is $OSVersionMinor...\n" if $main::Debug;

	return;
}


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

sub ReturnVendorSunOS ($)
{	my $Subcategory = shift;
	my $VendString;
	my $Line = 0;
	my @Prtconf;

	@Prtconf = ReturnArrayExec("$PrtconfCommand $PrtconfArgs");

	# CUSTOMIZE
	foreach (@Prtconf) {
		if ( /^$/ ) { $Line++; next; }
		if ( $Line ) { $VendString = $_; last; }
	}

	if ( $VendString =~ /Axil/ ) {
		return ("Axil");
	} elsif ( $VendString =~ /compsta/i ) {
		return ("Tatung");
	} elsif ( $VendString =~ /^SUNW,/ ) {
		return ("Sun");
	}
	
	return ("UNKNOWN");
}


sub ReturnModelSunOS ($)
{	my $Subcategory = shift;
	my @Prtconf;

	my $Model = `$UnameCommand -m`;
	chomp($Model);

	@Prtconf = ReturnArrayExec("$PrtconfCommand $PrtconfArgs");

	foreach (@Prtconf) {
		if ( /^$/ ) { $Line++; next; }
		if ( $Line ) { $Model = $_ . " ($Model)"; last; }
	}

	return ( $Model );
}


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

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

	return ( $IdNbr );
}


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

sub ReturnProcessorsSunOS ($)
{	my $Subcategory = shift;
	my $Nbr = 0;
	my @Processors = ( );
	my @Psrinfo = ReturnArrayExec("$PsrinfoCommand $PsrinfoArgs");

	foreach $Line (@Psrinfo) {
		($Line =~ /^Status\s+of\s+processor\s+(\d+)/) && ($Nbr = $1);
		($Line =~ /(\S+)\s+processor\s+operates\s+at\s+(\d+)\s+(\w+)/)
			&& push(@Processors, "processor $Nbr $1 $2 $3");
	}

	my @Response = ForceMultipleResponses(\@Processors);

	return @Response;
}


sub ReturnMemorySunOS ($)
{	my $Subcategory = shift;
	my $Memory = "";
	my @Prtconf;

	@Prtconf = ReturnArrayExec("$PrtconfCommand $PrtconfArgs");

	foreach (@Prtconf) {
		if (/^Memory size:\s+(\d+)/) {
			$Memory = $1;
			last;
		}
	}

	if ( $Memory ) {
		return ( $Memory . "MB" );
	}
	else {
		return ( ReturnUnknownField($Subcategory) );
	}
}


# 
# Comments from Kent Gordon <kgordon@primeco.com>
# 
# I did a little more work on ReturnDisksSunOS().  I found out that
# Solaris Intel uses SUNOS_VTOC_16 disk labels.  This make the problem
# for Solaris Intel because the ascii message for the disk label is in
# the middle of a structure.  I could calculate the offset in the
# structure, but I am wary since I could see it changing when 64bit
# Solaris Intel arrives.  The most robust solution would be to compile a
# small helper application that uses the OS header to find the actual
# location.  The use of a small helper application would also limit the
# concerns about running as root since it could be setuid root or setgid
# sys without any other parts of the program having to be trusted.  Here
# are the possible approaches:
# 
# 	1) Do ReturnDisksSunOS inline.  This is easiest, but requires 
# 	   root or group sys for entire program.
# 	2) Do ReturnDisksSunOS with C helper application.  This is the 
# 	   safest approach.  It should work with new releases of Solaris 
# 	   and only a small piece of code needs to be trusted.
# 	3) Do ReturnDisksSunOS with perl helper application.  This has 
# 	   almost the same security as 2 (I expect for this simple 
# 	   program that setgid is slightly safer in C).  This has the 
# 	   advantage that a compiler is not needed to install syssumm.  
# 	   It would assume that the disk label structure will not be 
# 	   changing in new OS versions.  I expect that this is safe for 
# 	   SPARC (fields at the front of structures rarely move).
# 
# My suggestion would be to go with option 3.  I have included the C
# code needed to find the ascii part of the disk label.  This code is
# hacked from a FAQ on sunsolve.sun.com.
# 
# [Note that there is a corresponding C source file in the remote
# directory called Read Sun Disk label.c.  This was also included
# in the e-mail message from Kent.]
#
# From Frank Crawford (<frank@ansto.gov.au>):
#
# I've now written ReadSunDisklabel.pl as a Perl program which should
# work on both Sparc and Intel versions of Solaris (and SunOS?).  I've
# modified ReturnDisksSunOS to use it.  Note, this program will need to
# be setgid sys (or setuid root), to allow anyone to read the labels.
# 

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

	my %DeviceList = ( );
	my @DeviceList = grep(($_ =~ s/s\d+$//) && !$DeviceList{$_}++, glob("/dev/rdsk/c*t*d*s*"));
	print "\tDevice list is @DeviceList...\n" if $main::Debug;

	foreach $Device ( @DeviceList ) {
		my $DeviceModel = `$ReadDiskLabelCommand $ReadDiskLabelArgs $Device 2>&1`;
		chomp($DeviceModel);
		if ($? != 0 && $DeviceModel !~ m#^<.*>$#) {     # An error occurred
			$main::RunAsRoot = 1 if ( $DeviceModel =~ /permission denied/i );
			$DeviceModel = "";  # Lose the error message.
		}
		# on an Ultra 5 it looks like the CD-ROM is on device 2
		# the previous pattern in the next line was "\dt6s"
		if ( $Device =~ /\dt[26]d/ && $DeviceModel eq "" ) {
			$DeviceModel = "Probably a CD-ROM";
		}

		# further editing - lose angle brackets because they
		# cause HTML mis-rendering and lose extra spaces
		$DeviceModel =~ s/<//g;
		$DeviceModel =~ s/>//g;
		$DeviceModel =~ s/[\s]{2,}/ /g;

		print "\tDevice $Device returned \"$DeviceModel\"...\n" if $main::Debug;

		my $Response = "dev=$Device model=\"$DeviceModel\"";

		push @Response, $Response;
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


# The old direct read version.

# sub ReturnDisksSunOS ($)
# {	my $Subcategory = shift;
#	my @Response = ( );
#
#	my @DeviceList = glob "/dev/rdsk/c*t*d*s2";
#	print "\tDevice list is @DeviceList...\n" if $main::Debug;
#
#	foreach $Device ( @DeviceList ) {
#		my $DeviceModel = "";
#		if (sysopen(DEVICE, $Device, 0) &&
#			sysread(DEVICE, $DeviceModel, 512)) {
#			($DeviceModel) = unpack("A128", $DeviceModel);
#		}
#		close(DEVICE);
#
#		if ( $DeviceModel eq "" ) {
#			$DeviceModel = "Probably a CD-ROM";
#		}
#
#		#remove the s2
#		$Device =~ s/s2//;
#
#		print "\tDevice $Device returned $DeviceModel...\n" if $main::Debug;
#
#		my $Response = "dev=$Device model=\"$DeviceModel\"";
#
#		push @Response, $Response;
#	}
#
#	@Response = ForceMultipleResponses(\@Response);
#
#	return @Response;
# }


# 
# This is the old dmesg dependent version of ReturnDisksSunOS
# 

# sub ReturnDisksSunOS ($)
# {	my $Subcategory = shift;
# 	my @Response = ( );
#   
# 	my @DmesgText = ReturnArrayExec("$DmesgCommand $DmesgArgs");
# 	my @DiskDevList = ReturnArrayExec("/bin/ls -l /dev/rdsk");
# 	my $DeviceAddress = "";
# 	my $DeviceModel = "";
# 
# 	foreach $Line ( @DmesgText ) {
# 		# 
# 		# Look for /^(sd|ssd|dad)\d+\s+is/ to find disks.
# 		# This currently gets SCSI, Storage Array, and IDE disks on Ultras
# 		# Obviously this pattern needs to be expanded.
# 		# 
#  
# 		$#Response = -1 if ($Line =~ /SunOS/);
#  
# 		if ($Line =~ /^(sd|ssd|dad)\d+\s+is\s+(.*)/) {
# 			$DeviceName = $1;
# 			$DeviceAddress = $2;
# 			if (@DeviceAddress = grep /$DeviceAddress/, @DiskDevList) {
# 				$DeviceAddress = (split " ", $DeviceAddress[0])[8];
# 			}
# 			else {
# 				$DeviceAddress = "";
# 			}
# 			next;
# 		}
#  
# 		if (length($DeviceAddress) > 0) {
# 			if ($Line =~ /\s+<(.*)\s+cyl/) {
# 				$DeviceModel = $1;
# 			}
# 
# 			# 
# 			# If the model of the device is blank
# 			# and it's on SCSI address 6, then
# 			# assume that it's a CD-ROM...
# 			# 
# 			if ( $DeviceModel =" " and
# 			     $DeviceAddress eq "c\dt6d0s0" ) {
# 				$DeviceModel = "Probably a CD-ROM";
# 			}
# 
# 			print "\tfound disk $DeviceAddress $DeviceModel\n" if $main::Debug;
#  
# 			my $Response = "dev=$DeviceAddress model=\"$DeviceModel\"";
#  
# 			push @Response, $Response;
#  
# 			$DeviceAddress = "";
# 			$DeviceModel = "";
# 		}
# 	}
# 
# 	@Response = ForceMultipleResponses(\@Response);
# 
# 	return @Response;
# }


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

	my @AllTapeDevices = glob("/dev/rmt/*");
	my @DeviceList = ( );
	foreach $TapeDevice (@AllTapeDevices) {
		if ( $TapeDevice =~ /\d+$/ ) {
			push @DeviceList, $TapeDevice;
		}
	}

	foreach $Device ( @DeviceList ) {
		my @MtText = ReturnArrayExec("$MtCommand -f $Device status 2>&1");
		next if $MtText[0] =~ /No such device/;

		# format the response
		my $DeviceModel = "";
		if ( $MtText[0] =~ /no tape loaded/ ) {
			# no tape is in the drive or it's offline
			$DeviceModel = "unknown model (no tape loaded)";
		}
		else {
			# a tape is in the drive and the model is 
			# returned in the first line of mt output
			$MtText[0] =~ /^(.*):/;
			$DeviceModel = $1;
		}
		my $Response = "dev=$Device model=\"$DeviceModel\"";
		push @Response, $Response;
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


# 
# This is the old dmesg dependent version of ReturnTapesSunOS
# 

# sub ReturnTapesSunOS ($)
# {	my $Subcategory = shift;
# 	my @Response = ( );
# 
# 
# 	my @DmesgText = ReturnArrayExec("$DmesgCommand $DmesgArgs");
# 	my @DiskDevList = ReturnArrayExec("/bin/ls -l /dev/rmt");
# 	my @DeviceAddress = ();
# 	my @DeviceModel = ();
# 
# 	foreach $Line ( @DmesgText ) {
# 		# 
# 		# Look for /^(st)\d+\s+is/ to find tapes.
# 		# This currently gets SCSI tapes.
# 		# Obviously this pattern needs to be expanded.
# 		# 
# 
# 		$#DeviceAddress = -1 if ($Line =~ /SunOS/);
# 
# 		if ($Line =~ /^st(\d+)\s+is\s+(.*)/) {
# 			$DeviceAddress{$1} = $2;
# 
# 			my @DeviceList;
# 			if (@DeviceList = grep /$DeviceAddress{$1}/, @DiskDevList) {
# 				$DeviceAddress{$1} = (split " ", $DeviceList[0])[8];
# 			} else {
# 				$DeviceAddress{$1} = "";
# 			}
# 		} elsif ($Line =~ /^st(\d+):\s+<([^>]*)>/) {
# 			$DeviceModel{$1} = $2;
# 		}
# 	}
# 
# 	foreach $DeviceName (sort { $DeviceAddress{$a} <=> $DeviceAddress{$b} } keys(%DeviceAddress)) {
# 		print "\tfound tape $DeviceAddress{$DeviceName} $DeviceModel{$DeviceName}\n" if $main::Debug;
# 
# 		my $Response = "dev=$DeviceAddress{$DeviceName} model=\"$DeviceModel{$DeviceName}\"";
# 
# 		push @Response, $Response;
# 	}
# 
# 	@Response = ForceMultipleResponses(\@Response);
# 
# 	return @Response;
# }


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

	my @Pkginfo = ReturnArrayExec("$PkginfoCommand $PkginfoArgs");

 	if ( grep /\sSUNWmd\s/, @Pkginfo) {
		push @Response, "Solstice DiskSuite";
 	}

 	if ( grep /\sSUNWvxva\s/, @Pkginfo) {
		push @Response, "SPARCstorage Volume Manager";
 	}

	if ( grep /VRTSvxva/, @Pkginfo) {
		#
		# The following code (to determine whether Veritas
		# Volume Manager is running) is loosely derived from
		# SysAudit v1.2 last maintained by 
		# David J. Young  <davidy@Psa.pencom.com>
		# 
		my $IsRunning = " (not running)";
		my @RunningProcs = ReturnArrayExec("$PsCommand $PsArgs");
		foreach $Process (@RunningProcs) {
			if ( $Process =~ /vxconfigd/ ) {
				$IsRunning = " (running)";
			}
		}
		push @Response, "VERITAS Volume Manager $IsRunning";
	}

	if ( $#Response < 0 ) {
		push @Response, "none";
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

	my @EepromList = ReturnArrayExec("$EepromCommand");
	foreach $Line (@EepromList) {
		# it's a single value return, but let the embedded breaks
		# format it onto multiple lines
		$Eeprom = $Eeprom . "$Line<br> ";
	}

	return ( $Eeprom );
}


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

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

	return ReturnDomainName($Subcategory, $ResolvConfPath);
}


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

	$DefaultRouter = "UNKNOWN";

	my @routes = ReturnArrayExec("$NetstatCommand $NetstatRouteArgs");
	my @defrouters = grep /^(default|0.0.0.0)/, @routes;

# This needs to be checked out. Probably need to be smarter with more than one
# default router.

	if ( @defrouters ) {
		$DefaultRouter = (split(" ", $defrouters[0]))[1];
	}
	$DefaultRouter = IPtoFQDN($DefaultRouter);

	return ( $DefaultRouter );
}


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

	my @ResolvConfText = ReturnArrayFile($ResolvConfPath);
	if ( scalar(@ResolvConfText) < 1 ) {
		push @Response, "None";
	}
	else {
		foreach $Line (@ResolvConfText) {
			# find the first line with "nameserver" in it?
			if ( $Line =~ /^nameserver\s+([\d]+\.[\d]+\.[\d]+\.[\d]+)/ ) {
				my $NameServerIP = $1;
				# do we recognize this nameserver?
				my $NameServerFQDN = IPtoFQDN($NameServerIP);
				push @Response, $NameServerFQDN;
				next;
			}
		}
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnIPAddressSunOS ($)
{
	# Returns the IP address associated with "$main::nodename"
	# The "default" IP address. This subroutine will be used
	# if Bruce decides to implement this function in syssumm.pl

	print "\tgethostbyname($main::NodeName)\n" if $main::Debug;
	my @Addresses = (gethostbyname $main::NodeName)[4];
	my ($a, $b, $c, $d) = unpack('C4', $Addresses[0]);
	$IPAddress = "$a." . "$b." . "$c." . "$d" ;

	return ( $IPAddress );
}


sub ReturnSubnetMaskSunOS ($)
{	my $SubnetMask = "";
	my $IPAddress = ReturnIPAddressSunOS(@_);
	my @Interfaces = ReturnNetworkInterfacesSunOS(@_);

	# Returns netmask associated with the "default" IP address
	# This subroutine will be used if Bruce decides to implement
	# this function in syssumm.pl

	# find interface corresponding to "default" IP
	# and get netmask from it
	foreach $Line (@Interfaces) {
		next unless ( $Line =~ /$IPAddress/ );
		if ( $Line =~ /netmask=(\d+\.\d+\.\d+\.\d+)/ ) {
			$SubnetMask = $1;
		}
	}

	return ($SubnetMask);
}


sub ReturnNetworkInterfacesSunOS ($)
{	my $Subcategory = shift;
	my %Interfaces;
	my $Interface;
	my @Response = ( );

	# 
	# Gather network interface name from anywhere we can. Right
	# now this looks at prtconf, dmesg and then ifconfig...
	# 

	if ( -x $PrtconfCommand ) {
		my @Prtconf = ReturnArrayExec("$PrtconfCommand $PrtconfArgs");
		my $Iface;

		foreach $Line (@Prtconf) {
#			$Iface =~ s/SUNW,//;
			$Line =~ s/SUNW,//;
			next unless ( $Line =~ /\s+([^,]*),\s+instance\s+\#(\d+)/ )
				and defined($IfList{$1});
			print "\tLine is $Line...\n" if $main::Debug;
			$Iface = "$1$2";
			$Interfaces{$Iface} = 1 unless defined $Interfaces{$Iface};
		}
	}

	if ( -x $DmesgCommand ) {
		my @Dmesg = ReturnArrayExec("$DmesgCommand $DmesgArgs");
		my $Iface;
		my @Ifaces = ( );

		foreach $Line (@Dmesg) {
			$#Ifaces = -1 if ($Line =~ /SunOS/); # Handle reboots
			next unless ( $Line =~ /([^,]*)(\d+) at/ )
				and defined($IfList{$1});
			print "\tLine is $Line...\n" if $main::Debug;
			$Iface = "$1$2";
			$Iface =~ s/SUNW,//;
			push @Ifaces, $Iface;
		}

		foreach $Iface (@Ifaces) {
			$Interfaces{$Iface} = 1 unless defined $Interfaces{$Iface};
		}
	}

	if ( -x $IfconfigCommand ) {
		my @Ifconfig = ReturnArrayExec("$IfconfigCommand -a");

		foreach $Line (@Ifconfig) {
			next unless ( $Line =~ /^((\w+)\d+(:\d+)?)/ )
				and defined($IfList{$2});
			print "\tLine is $Line...\n" if $main::Debug;
			$Interfaces{$1} = 1 unless defined $Interfaces{$1};
		}
	}

	# 
	# Now that we've got interface lists from dmesg, prtconf and ifconfig
	# call AddNetinfo to get information on these interfaces
	# 

	foreach $Interface ( keys %Interfaces ) {
		my $Response = AddNetInfo($Interface);
		push @Response, $Response if ($Response);
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub AddNetInfo ($)
{	my $NetworkInterfaceName = shift;
	
	print "AddNetInfo($NetworkInterfaceName)...\n" if $main::Debug;
	
	# use ifconfig to get the rest of the information
	my @IfconfigText = ReturnArrayExec("$IfconfigCommand $NetworkInterfaceName 2>/dev/null");
	
	my $MACAddress = "";
	my $IPAddress = "";
	my $BroadcastMask = "";
	my $NetMask = "";
	# assume that there could be multiple fields on each line...
	foreach $Line (@IfconfigText) {
		# grab the IP address
		if ( $Line =~ /inet\s+(\d+\.\d+\.\d+\.\d+)/ ) {
			$IPAddress = $1;
		}
		# grab the broadcast mask
		if ( $Line =~ /broadcast\s+(\d+\.\d+\.\d+\.\d+)/ ) {
			$BroadcastMask = $1;
		}
		# grab the netmask
		if ( $Line =~ /netmask\s+([a-f0-9]{8})/ ) {
			my $netmask = $1;
			$NetMask = "";
			for ($i = 0; $i < 8; $i = $i + 2) {
				$NetMask = $NetMask . hex(substr($netmask,$i,2));
				if ($i < 6) { $NetMask = "$NetMask."; }
			}
		}
	
		#
		# get the MAC address
		# first, check to see if it was in the ifconfig
		# using arp can hang the script if using a ba network device
		#
		if ( $Line =~ /ether\s+(\w+:\w+:\w+:\w+:\w+:\w+)/ ) {
			$MACAddress = $1;
		}
	}

	#
	# second, only use arp to find the mac if is wasn't in the ifconfig
	# hopefully, this isn't on a ba interface
	#
	if ( ! defined($MACAddress) ) {
		my $arp = `$ArpCommand $IPAddress`;
		if ( $arp =~ /at\s+(\w+:\w+:\w+:\w+:\w+:\w+)/ ) {
			$MACAddress = $1;
		}
	}

	my $NICType = "ether";      # a reasonable default...
	my $NICDevice = "";
	my $NetworkInterfaceType = $NetworkInterfaceName;
	$NetworkInterfaceType =~ s/[\d:]+$//;
	if ( defined($IfList{$NetworkInterfaceType}) ) {
		$NICDevice = "\"$IfList{$NetworkInterfaceType}\"";
		if ($NICDevice =~ /^"(.+)\|(.+)"$/) {
			$NICDevice = "\"$1\"";
			$NICType = $2;
		}
	}
	else {
		$NICType = "unknown";
		$NICDevice = "unknown";
	}
	$NICType = "\"$NICType\"" if $NICType =~ /\s/;
	$MACAddress =~ s/\b(\d)\b/0$1/g;

	# find out what the link speed is
	# from slava@embedded.sun.mcst.ru (Vyacheslav Ponomarenko)
	# in comp.unix.solaris
	# strip the digit off of the instance of the device
	my $DriverName = $NetworkInterfaceName;
	$DriverName =~ s/[\d:]+$//;
	my $LinkSpeed = `$NddCommand /dev/$DriverName link_speed`;
	chomp $LinkSpeed;
	if ( length($LinkSpeed) == 1 ) {
		if ( $LinkSpeed == 0 ) {
			$LinkSpeed = "10Mbps";
		}
		elsif ( $LinkSpeed == 1 ) {
			$LinkSpeed = "100Mbps";
		}
	} else {
		$LinkSpeed = "Unknown";
	}

	my $NICDescription =
	"$NetworkInterfaceName type=$NICType linkspeed=$LinkSpeed device=$NICDevice " .
	"macaddr=$MACAddress " .
	(($IPAddress)
		? "ip=$IPAddress netmask=$NetMask broadcast=$BroadcastMask"
		: "UNCONFIGURED");

	return $NICDescription;
}


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

# 
# We're using alot of the same tests for existance
# that are used by the /etc/rc.d/init.d scripts
# 

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

	return ( "SunOS" );
}


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

	return ( "$OSVersion" );
}


sub ReturnPatchesSunOS ($)
{	my $Subcategory = shift;
	my %Patches;
	my %Packages;
	my @Response = ( );

	#
	# For those of us that live dangerously and don't necessarily
        # backout all patches...
        # This reports the largest version of an installed patch.

	my @Patchlist = ReturnArrayExec("$ShowrevCommand $ShowrevPatchArgs");

	# quick test for no patches installed...
	if ( $Patchlist[0] eq "No patches are installed" ) {
	  	push @Response, $Patchlist[0];
		@Response = ForceMultipleResponses(\@Response);
		return @Response;
	}

	foreach $Line (@Patchlist) {
		$Line =~ /\w+:\s+([0-9]+)-([0-9]+).*Packages:\s+(.*)/;
		my $Version = $1;
		my $Revision = $2;
		my $Packages = $3;

		if (!defined $Patches{$Version} || $Patches{$Version} < $Revision ) {
			$Patches{$Version} = $Revision;
			$Packages{$Version} = $Packages;
		}
	}

	foreach $Version (sort keys %Patches) {
	  push @Response, "$Version-$Patches{$Version}  $Packages{$Version}";
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnRunningLpschedSunOS ($)
{	my $Subcategory = shift;
	my @Responses = ( );

	my $Response = ReturnRunningSysvLp($Subcategory, $PsCommand, $PsArgs);
	push @Responses, $Response if $Response =~ /Yes/;
	$Response = ReturnRunningBSDLpr($Subcategory, $PsCommand, $PsArgs);
	push @Responses, $Response if $Response =~ /Yes/;
	$Response = ReturnRunningLPRng($Subcategory);
	push @Responses, $Response if $Response =~ /Yes/;

	# concatenate the final responses as necessary
	my $FinalResponse = "";
	foreach $Response (@Responses) {
		if ( $FinalResponse eq "" ) {
			$FinalResponse = $Response;
		}
		else {
			$FinalResponse = "$FinalResponse, $Response";
		}
	}

	return ($FinalResponse);
}


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

	my @LocalPrinters = ReturnArrayExec("$LpcCommand $LpcArgs");
	foreach $Line (@LocalPrinters) {
		if ( $Line =~ /(\w+):/ ) {
			push @Response, $1;
		}
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

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

	my $Sendmail = ( ( -f "$SendmailCommand" ) ? "Yes" : "No" );

	# if sendmail is available, check to see if it is running, or
        # runnable vi /etc/inetd.conf...
	my $SendmailRunning = 0;
	if ($Sendmail eq "Yes") {
		my @RunningProcs = ReturnArrayExec("$PsCommand $PsArgs");
		my @InetdConf = ReturnArrayFile("$InetdConfPath");

		foreach $Line (@RunningProcs) {
			if ( $Line =~ /$SendmailCommand/ ) {
				print "\t$Line...\n" if $main::Debug;
				$SendmailRunning = 1;
				last;
			}
		}
		# if we didn't find it in the ps output, then proceed to inetd.conf
		if ( ! $SendmailRunning ) {
			foreach $Line (@InetdConf) {
				if ( $Line =~ /$SendmailCommand/ ) {
					print "\t$Line...\n" if $main::Debug;
					$SendmailRunning = 1;
					last;
				}
			}
		}
	}
	if ( ! $SendmailRunning ) {
		$Sendmail = "No";
	}

	# 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 ReturnRunningNFSSunOS ($)
{	my $Subcategory = shift;

	my $NFSState = "";
	if ( -f $NfsdCommand and -f $ExportsPath ) {
		my @Exports = ReturnArrayFile("$ExportsPath");
		$NFSServer = grep /^\s*share/, @Exports;
	}
	print "\tNFSServer is $NFSServer\n" if $main::Debug;

	my $NFSClient = ( -f $MountdCommand );
	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 ReturnRunningAutomountSunOS ($)
{
  my $Subcategory = shift;
	
  my $Automount = ( ( -f "$AutomountCommand" ) ? "Yes" : "No" );
  my @RunningProcs = ReturnArrayExec("$PsCommand $PsArgs");

  $Automount = ( $Automount eq "Yes"
		 && ( grep {m,$AutomountPattern,} @RunningProcs))
    ? "Yes" : "No";

  return ( $Automount );
}


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

	my $NISState = "";
	my @NsSwitch = ReturnArrayFile("$NsSwitchConfPath");
	my @RunningProcs = ReturnArrayExec("$PsCommand $PsArgs");

	my $NISplusClient = grep /^[^#]+nisplus/, @NsSwitch;
	my $NISClient = grep /^[^#]+\snis[^p]/, @NsSwitch;

	my $NISplusServer = ( -f $NISplusServerCommand ) 
		&& grep {m,$NISplusServerCommand[\s\$],} @RunningProcs;
	my $YPcompat = $NISplusServer 
		&& grep /$NISplusYPcompatArgs/, 
		   grep {m,$NISplusServerCommand,} @RunningProcs;

	my $NISServer = ( -f $NISServerCommand )
		&& grep {m,$NISServerCommand,} @RunningProcs;
	$NISClient = $NISClient && ( -f $NISClientCommand )
		&& grep {m,$NISClientCommand,} @RunningProcs;

	if ( $NISplusServer and $YPcompat and $NISplusClient ) {
		$NISState = "Yes, NIS+, Server in YP compatible mode and client";
	} elsif ($NISplusServer and $NISplusClient ) {
		$NISState = "Yes, NIS+, server and client";
	} elsif ($NISplusServer ) {
		$NISState = "Yes, NIS+, server only";
	} elsif ($NISplusClient ) {
		$NISState = "Yes, NIS+, client only";
	} elsif ( $NISServer and $NISClient ) {
		$NISState = "Yes, NIS (YP), server and client";
	} elsif ( $NISServer ) {
		$NISState = "Yes, NIS (YP), server only";
	} elsif ( $NISClient ) {
		$NISState = "Yes, NIS (YP), client only";
	} else {
		$NISState = "No";
	}

	# XXX add NIS domainname to $NisState if NIS is running...

	return ( $NISState );
}


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

	my @RunningProcs = ReturnArrayExec("$PsCommand $PsArgs");

	my $Nameserver = (grep {m,$NameServerPattern,} @RunningProcs)
	  ? "Yes" : "No";

	return ( $Nameserver );
}


sub ReturnIPAddressResolutionSunOS ($)
{	my $Subcategory = shift;
	my $IPAddressResolution = "Unknown";

	if ( -f $ResolvConfPath ) {
		$IPAddressResolution = "DNS";
	}
	else {
		$IPAddressResolution = "/etc/hosts";
	}

	return ( $IPAddressResolution );
}


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

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


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

	# XXX Let's defer this for now

	return ( "X11R6" );
}


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

	# XXX Let's defer this for now

	return ( "No" );
}


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

	# 
	# We're going to ask the df command to return all "local"
	# file systems.  This is going to return *more* than we
	# really want.  We'll dyke out the "loopback" mounts (lofs)
	# and /proc.  We'll return what remains...
	# 
	# Thanks to Alek O. Komarnitsky for fixing this.
	# 

	my @DfText = ReturnArrayExec("$DfCommand $LocalFileSystems");
	my @DfLoopbacks = ReturnArrayExec("$DfCommand $LoopbackFileSystems");
	my $PreviousLine = "";
	foreach $Line (@DfText) {
		# bypass the header line
		next if $Line =~ /^Filesystem/;
		next if ( grep /$Line/, @DfLoopbacks );
		next if $Line =~ /^\/proc /;
		# partial line?
		if ( $PreviousLine ) {
			$Line = $PreviousLine . $Line;
			$PreviousLine = "";
		}
		# clean and split line
		$Line =~ s/[\s]+/ /g;
		my @DfFields = split(/ /, $Line);
		# partial line?
		if ( scalar(@DfFields) < 2 ) {
			$PreviousLine = $Line;
			next;
		}
		# bypass any cdrom drives or remote file systems
		next if $Line =~ /cdrom/;
		next if $Line =~ /^[\w]/;
		# calc size in MB
		my $Size = sprintf("%.1fMB", ( $DfFields[1] / 1024 ) );
		push @Response, "$DfFields[5] mount=$DfFields[0] size=$Size sat=$DfFields[4]";
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

	#
	# I really don't want to see automounted NFS file systems. What gets
	#  found depends on when this is run. So, read fstab file, the run df
	#  on the nfs file systems found there.
	#

	my @FstabText = grep /^[^#]+\snfs\s/, ReturnArrayFile("$EtcFstabPath");

	foreach $Line (@FstabText) {
		push @Filesystems, (split " ", $Line)[2];
	}			  

	if ( @Filesystems ) {
		my $dfComm = "$DfCommand $NFSFileSystems " . join " ", @Filesystems;

		my @DfText = `$dfComm`;
		my $PreviousLine = "";
		foreach $Line (@DfText) {
			# bypass the header line
			next if $Line =~ /^Filesystem/;
			# partial line?
			if ( $PreviousLine ) {
				$Line = $PreviousLine . $Line;
				$PreviousLine = "";
			}
			# clean and split line
			$Line =~ s/[\s]+/ /g;
			my @DfFields = split(/ /, $Line);
			# partial line?
			if ( scalar(@DfFields) < 2 ) {
				$PreviousLine = $Line;
				next;
			}
			# bypass any cdrom drives or remote file systems
			next if $Line =~ /cdrom/;
			next if $Line =~ /^\//;
			print "\t$Line\n" if $main::Debug;
			# calc size in MB
			my $Size = sprintf("%.1fMB", ( $DfFields[1] / 1024 ) );
			my ( $RemoteSystem, $RemoteFileSystem ) = split(':', $DfFields[0]);
			push @Response, "$DfFields[5] remote=$RemoteSystem mount=$RemoteFileSystem size=$Size sat=$DfFields[4]";
		}
	}
  
	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

	@Exportslist = ReturnArrayFile("$ExportsPath");
	foreach $Line (@Exportslist) {
		next if ($Line =~ /^\s*$/);
		next if ($Line =~ /^#/);
		push @Response, "$Line";
	}

	if ( scalar(@Response) < 1 ) {
		push @Response, "No exports - not an NFS Server";
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

	ReturnSwap($Subcategory, "$SwapCommand");
}


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

	# 
	# XXX - Accounting was not running on the system I was porting this on...
	# 

	# XXX Let's defer this for now

	return ( "Unknown" );
}


###########################################################################
# End of SunOS.pm
###########################################################################

1;
