package BSD;

# BSD.pm - these are operating system specific routines for BSD
#
# 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 Frank Crawford
#	Based upon code by Bruce W. Mohler
# 
# Contributed source:
#	Copyright (c) 2000 Martin Andrews
#	Copyright (c) 2000 Bruce W. Mohler
# 
# ==============================================================
# 
# $Header: /export/home/mohlerb/syssumm/remote/RCS/BSD.pm,v 1.8 2000/04/10 17:27:14 mohlerb Exp mohlerb $
# 
# $Revision: 1.8 $
#
# $Log: BSD.pm,v $
# Revision 1.8  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.7  2000/04/06 14:07:11  mohlerb
# changes from Frank Crawford including:
# a fix for BSD.pm, to cater for the fact that "/etc/netstart"
# is a shell script, and hence an executable, but also contains
# the necessary data as shell variables; also had to fix the
# test for a router, if it came back with no match, and exclude
# the "sw" network interfaces (they are for swIPE, i.e. some form
# of IPSec pseudo-device)
#
# Revision 1.6  2000/04/04 02:36:00  mohlerb
# changes from Frank Crawford including:
# fix typo in ReturnSwapBSD function
#
# Revision 1.5  2000/03/02 22:03:03  mohlerb
# changes from Frank Crawford including:
# deleting the use of syslog and getting the swap function to work
#
# Revision 1.4  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.3  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.2  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.1  2000/01/12 18:08:20  mohlerb
# Initial revision
#

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(
	&InitializeBSD
);
# 
# 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 InitializeBSD()
$OSVersion = "";
$OSVersionMajor = "";
$OSVersionMinor = "";

# where commands are located			standard args
# CUSTOMIZE
$AmdCommand = "/usr/sbin/amd";
$DfCommand = "/bin/df";				$LocalFileSystems = "-t ufs";
						$NFSFileSystems = "-t nfs";
$DiskinfoCommand = "";				# filled in below
$DomainnameCommand = "/sbin/domainname";
$HostIdCommand = "/usr/bin/hostid";
$IfconfigCommand = "/sbin/ifconfig";		$IfconfigArgs = "-a";
$IOStatCommand = "/usr/sbin/iostat";
$MountdCommand = "/sbin/mountd";
$NetstatCommand = "/usr/bin/netstat";		$InterfaceArgs = "-i";
$NfsdCommand = "/sbin/nfsd";
$PsCommand = "/bin/ps";				$PsArgs = "-alx";
$SendmailCommand = "/usr/sbin/sendmail";
$SwapCommand = "/usr/sbin/pstat";		$SwapArgs = "-s";
$SysCtlCommand = "/sbin/sysctl";		$SysCtlLicenseUsers = "-n kern.license_users";
						$SysCtlHardware = "-a hw";
$UnameCommand = "/usr/bin/uname";

# where files are located
# CUSTOMIZE
$ExportsPath = "/etc/exports";
$InetdConfPath = "/etc/inetd.conf";
$NameserverConfPath = "/etc/named.conf";
$NetConfPath = "/etc/netstart";			$DefaultRouterTag = "defroute";
$PatchLogPath = "/var/db/install.log";
$PatchSysLogPath = "/sys/PATCHLOG";
$PrintcapPath = "/etc/printcap";
$ResolvConfPath = "/etc/resolv.conf";

$SubroutineSuffix = "";

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

$NameServerPattern = "named";
$NfsdCommandPattern = "nfsd";
$SysCtlMaxMemPattern = "hw.physmem";
$SysCtlNCpuPattern = "hw.ncpu";
$SysCtlMachinePattern = "hw.machine";
$SysCtlModelPattern = "hw.model";


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

	# initialize $SubroutineSuffix...
	WhatVersionOfBSD();

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

	# Set the blocksize for future disk functions (e.g. df).
	$ENV{'BLOCKSIZE'} = '1024';

# 	# fill in different file and command paths

	# plug in appropriate subroutines
 	$main::Routines{General}{Vendor} = \&ReturnVendorBSD;
 	$main::Routines{General}{Model} = \&ReturnModelBSD;
 	$main::Routines{General}{HostId} = \&ReturnHostIdBSD;

 	$main::Routines{Hardware}{Processors} = \&ReturnProcessorsBSD;
 	$main::Routines{Hardware}{Memory} = \&ReturnMemoryBSD;
 	$main::Routines{Hardware}{Disks} = \&ReturnDisksBSD;
 	$main::Routines{Hardware}{Tapes} = \&ReturnTapesBSD;
 	$main::Routines{Hardware}{StorageManager} = \&ReturnStorageManagerBSD;
 	$main::Routines{Hardware}{Eeprom} = \&ReturnEepromBSD;

 	$main::Routines{Network}{DomainName} = \&ReturnDomainNameBSD;
 	$main::Routines{Network}{DefaultRouter} = \&ReturnDefaultRouterBSD;
 	$main::Routines{Network}{NameServer} = \&ReturnNameServerBSD;
 	$main::Routines{Network}{IPAddress} = \&ReturnIPAddressBSD;
 	$main::Routines{Network}{SubnetMask} = \&ReturnSubnetMaskBSD;
 	$main::Routines{Network}{NetworkInterfaces} = \&ReturnNetworkInterfacesBSD;

 	$main::Routines{Software}{OsName} = \&ReturnOsNameBSD;
 	$main::Routines{Software}{OsVersion} = \&ReturnOsVersionBSD;
 	$main::Routines{Software}{NbrOfLicenses} = \&ReturnNbrOfLicensesBSD;
 	$main::Routines{Software}{Patches} = \&ReturnPatchesBSD;
 	$main::Routines{Software}{Printers} = \&ReturnPrintersBSD;
 	$main::Routines{Software}{Services} = \&ReturnServices;
 	$main::Routines{Software}{IPAddressResolution} = \&ReturnIPAddressResolutionBSD;
 	$main::Routines{Software}{GraphicalUserInterface} = \&ReturnGraphicalUserInterfaceBSD;
 	$main::Routines{Software}{FontServer} = \&ReturnFontServerBSD;
 	$main::Routines{Software}{Fstab} = \&ReturnFstab;
 	$main::Routines{Software}{LocalFileSystems} = \&ReturnLocalFileSystemsBSD;
 	$main::Routines{Software}{RemoteFileSystems} = \&ReturnRemoteFileSystemsBSD;
 	$main::Routines{Software}{ExportedFileSystems} = \&ReturnExportedFileSystemsBSD;
	$main::Routines{Software}{Swap} = \&ReturnSwapBSD;

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

	RemoveLocalExclusions();

	return;
}


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


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

	$OSVersion = `$UnameCommand -r`;
	chomp($OSVersion);

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

	$SubroutineSuffix = $OSVersionMajor;

	return;
}


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

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

 	my $Vendor = `$UnameCommand -v`;
 	chomp($Vendor);
	$Vendor = (split(' ', $Vendor))[0];

	return ( $Vendor );
}


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

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

	return ( $Model );
}


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

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

	return ( $IdNbr );
}


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

# Expand this list.
%ProcessorTypes = ( 'i386' => 'Intel' );

sub ReturnProcessorsBSD ($)
{	my $Subcategory = shift;
	my @Processors = ( );
	# XXX Could get this from dmesg.
	my $ProcessorSpeed = "speed unknown";

 	my $NumProcessors = SearchFileForPattern("$SysCtlCommand $SysCtlHardware", $SysCtlNCpuPattern, 2);
	my $ProcessorType = SearchFileForPattern("$SysCtlCommand $SysCtlHardware", $SysCtlMachinePattern, 2);
	my $ProcessorModel = SearchFileForPattern("$SysCtlCommand $SysCtlHardware", $SysCtlModelPattern, 2);

	$ProcessorType = $ProcessorTypes{$ProcessorType}
		if defined($ProcessorTypes{$ProcessorType});
	for $cnt (0 .. ($NumProcessors - 1)) {
		push @Processors, "processor $cnt $ProcessorType $ProcessorModel $ProcessorSpeed";
	}

	my @Response = ForceMultipleResponses(\@Processors);

	return @Response;
}


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

# 	# how much memory?
 	my $Memory = SearchFileForPattern("$SysCtlCommand $SysCtlHardware", $SysCtlMaxMemPattern, 2);	# In bytes
 	$Memory = int ( $Memory / (1024 * 1024) );
 	$Memory = "$Memory" . "MB";

	return ( $Memory );
}


%DiskTypes = (
	'sd'	=> 'SCSI Disk',
	'sr'	=> 'Removable (probably CD-ROM)',
	'wd'	=> 'ST-506/IDE Disk',
);

sub ReturnDisksBSD ($)
{	my $Subcategory = shift;
	my @Controllers = ( );
	my $ControllerNbr = -1;
	my $CurrentController = -1;
	my @Response = ( );

	# XXX - This is a very rough first cut, use dmesg/disksetup for more info.
	my @IOStatText = ReturnArrayExec($IOStatCommand);
	foreach $Device (split(/\s+/, $IOStatText[0])) {
		next unless $Device;
		next if $Device =~ /tty|%|cpu/;
		my $DiskType;
		($DiskType = $Device) =~ s/\d+$//;
		$DiskType = $DiskTypes{$DiskType}
			if defined($DiskTypes{$DiskType});
		push @Response, "$Device type=$DiskType";
	}
	
	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub ReturnTapesBSD ($)
{	my $Subcategory = shift;
	my @Controllers = ( );
	my $ControllerNbr = -1;
	my $CurrentController = -1;
	my @Response = ( );

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

	push @Response, "Not Applicable (yet)";

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

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

	return ( $Eeprom );
}


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

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

	return ReturnDomainName($Subcategory, $ResolvConfPath);
}


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

	my $DefaultRouter = SearchFileForTagValue($NetConfPath, $DefaultRouterTag, "File");
	if ($DefaultRouter eq $NotFound or $DefaultRouter eq "") {
		$DefaultRouter = "";
	}
	else {
		$DefaultRouter = IPtoFQDN($DefaultRouter);
	}

	return ( $DefaultRouter );
}


sub ReturnNameServerBSD ($)
{	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 ReturnIPAddressBSD ($)
{	my $Subcategory = shift;

 	# 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 
 	my @Addresses = (gethostbyname $main::NodeName)[4];         
 	my ($a, $b, $c, $d) = unpack('C4', $Addresses[0]);
 	$IPAddress = "$a." . "$b." . "$c." . "$d" ;

	return ( $IPAddress );
}


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

 	# Returns netmask associated with the "default" IP address
 	# This subroutine will be used if Bruce decides to implement
 	# this function in syssumm.pl
 	my $SubnetMask = "";       
 	my $IPAddress = ReturnIPAddressBSD($Subcategory);
 	my @Interfaces = ReturnNetworkInterfacesBSD($Subcategory);
 
 	# find interface corresponding to "default" IP
 	# and get netmask from it
 	foreach $Line (@Interfaces) {
 		next unless ( $Line =~ /$IPAddress/ ); 
 		if ( $Line =~ /netmask=(\S+)/ ) {      
 			$SubnetMask = $1;
 		}
 	}

	return ($SubnetMask);
}     


sub ReturnNetworkInterfacesBSD ($)
{	my $Subcategory = shift;
	my @Response = ( );
	my %Seen = ( );			# keys are interface name, value is type

	print "ReturnNetworkInterfacesLinux()...\n" if $main::Debug;

	# search for interfaces in netstat output
	if ( -x $NetstatCommand ) {
		my @NetstatOutput = ReturnArrayExec("$NetstatCommand $InterfaceArgs");
		foreach $Line ( @NetstatOutput ) {
			next if ( $Line =~ /^(lo|ppp|sl|pif|sw)/ );	# skip loopback device
			next unless ( $Line =~ /^((\w+)\d)\*?\s/ );
			my $Iface = $1;                 # name of interface found
			my $IfaceDevice = $2;

			# don't add interface if it's already been
			# found somewhere else
			next if defined ( $Seen{$Iface} );

			$Seen{$Iface} = $IfaceDevice; 
		}
	}

	# search for interfaces in ifconfig output
	if ( -x $IfconfigCommand ) {
		my @IfconfigOutput = ReturnArrayExec("$IfconfigCommand $IfconfigArgs");
		foreach $Line ( @IfconfigOutput ) {
	
			next if ( $Line =~ /^(lo|ppp|sl|pif|sw)/ );	# skip loopback device

			next unless ( $Line =~ /^((\w+)\d):/ );

			my $Iface = $1;			# name of interface found
			my $IfaceDevice = $2;


			# don't add interface if it's already been
			# found somewhere else
			next if defined ( $Seen{$Iface} );
	
			$Seen{$Iface} = $IfaceDevice;
		}
	}

	# details on found interfaces
	foreach $Interface ( keys %Seen ) {
		my $Response = AddNetInfo($Interface, $Seen{$Interface});
		push @Response, $Response if ( $Response);
	}

	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub AddNetInfo ($$)
{	my $NetworkInterfaceName = shift;	# interface descriptor like 'eth0'
	my $NICDevice = shift;			# Specific device. Like: '3COM 3c905'

	print "AddNetInfo($NetworkInterfaceName, $NICDevice)...\n" if $main::Debug;

	# use ifconfig to get the rest of the information
	my @IfconfigText = ReturnArrayExec("$IfconfigCommand $NetworkInterfaceName");

	my $MACAddress = "";
	my $IPAddress = "";
	my $BroadcastMask = "";
	my $NetMask = "";
	my $NICType = "unknown";

	# assume that there could be multiple fields on each line...
	foreach $Line (@IfconfigText) {
		# pick up the hardware address
		if ( $Line =~ /link [^:]* (\w+:\w+:\w+:\w+:\w+:\w+)/ ) {
			$MACAddress = $1;
			$MACAddress =~ s/\b(\w)\b/0$1/g
		}
		# grab the IP address
		if ( $Line =~ /inet (\d+\.\d+\.\d+\.\d+)/ ) {
			$IPAddress = $1;
		}
		# grab the broadcast mask
		if ( $Line =~ /broadcast (\d+\.\d+\.\d+\.\d+)/ ) {
			$BroadcastMask = $1;
		}
		# grab the netmask
		if ( $Line =~ /netmask (\d+\.\d+\.\d+\.\d+)/ ) {
			$NetMask = $1;
		}
		# get device type
		if ( $Line =~ /link type (\S+) / ) {
			$NICType = $1;   
		}
		# defaults to "unknown" - see above
	}

	my $NICDescription = 
		"$NetworkInterfaceName type=$NICType device=$NICDevice macaddr=$MACAddress " .
		"ip=$IPAddress netmask=$NetMask broadcast=$BroadcastMask ";

	return $NICDescription;
}


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

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

 	my $OsName = `$UnameCommand -s`;
 	chomp($OsName);

	return ( $OsName );
}


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

	return ( "$OSVersion" );
}


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

 	my $NbrOfLicenses = 0;

	if (-x $SysCtlCommand) {
		$NbrOfLicenses = `$SysCtlCommand $SysCtlLicenseUsers`;
		chomp($NbrOfLicenses);
	}
	$NbrOfLicenses = "unlimited" unless $NbrOfLicenses;

	return ( $NbrOfLicenses );
}


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

	# This is probably very BSD/OS specific.
	my $Version;
	my %PatchList = ( );

	($Version = $OSVersion) =~ tr/.//d;
	my @PatchListText = ReturnArrayFile($PatchLogPath);
	push @PatchListText, ReturnArrayFile($PatchSysLogPath);

	foreach $Line (@PatchListText) {
		next unless $Line =~ /^patch (M$Version-\d+)\.(\S+) /;
		$PatchList{$1}->{$2}++;
	}
 
	my $PatchVersion;
	foreach $PatchVersion (sort keys %PatchList) {
		my $PatchDescription = join(',', sort keys %{$PatchList{$PatchVersion}});
		push @Response, "$PatchVersion desc=\"$PatchDescription\"";
	}

 	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


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

	# XXX comment out as necessary
	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 ReturnPrintersBSD ($)
{	my $Subcategory = shift;
	my @Response = ( );

	# 
	# This function assumes a fairly "standard" format
	# for the printcap file like:
	#	# a comment
	#	lp|dj:\
	#		:sd=/var/spool/lpd/dj:\
	#		:mx#0:\
	#		:lp=/dev/lp0:\
	#		:sh:
	# 
	# One field per line.
	# 

	my @Printcap = ReturnArrayFile("$PrintcapPath");
	my $Name = "";
	my $System = "";
	my $Queue = "";
	foreach $Line (@Printcap) {
		next if $Line =~ /^#/;		# bypass comments

		# clean up the line
		chomp($Line);
		$Line =~ s/^[\s]+//;		# no leading spaces
		$Line =~ s/\\$//;		# trailing backslash

		next if ( $Line eq "" );	# bypass blank lines

		print "$Line\n" if $Debug;

		# printer line
		if ( $Line =~ /^\w+/ ) {
			# if anything left from previous printer
			# push it onto @Response
			if ( $Name ) {
				my $Location = FormatPrinterLocation($System, $Queue);
				my $PrinterLine = "$Name prints to $Location";
				push @Response, $PrinterLine;
				$Name = "";
				$System = "";
				$Queue = "";
			}

			# extract the name(s) of the printer(s)
			$Line =~ /^(.*):/;
			$Name = $1;
			$Name =~ s/\|/,/g;

			next;
		}

		# bypass fields we know we don't care about
		next if ( $Line =~ /^:[a-z][a-z]#/ );
		next if ( $Line =~ /^:[a-z][a-z]:/ );

		# fields
		if ( $Line =~ /^:/ ) {
			# extract the field label
			$Line =~ /^:([a-z][a-z])=(.*):/;
			my $Label = $1;
			if ( $Label eq "lp" ) {		# local printer
				$Queue = "local";
			}
			elsif ( $Label eq "rm" ) {	# remote server
				$System = $2;
			}
			elsif ( $Label eq "rp" ) {	# remote printer
				$Queue = $2;
			}

			next;
		}

		# ignore anything else...
	}
	if ( $Name ) {
		my $Location = FormatPrinterLocation($System, $Queue);
		my $PrinterLine = "$Name prints to $Location";
		push @Response, $PrinterLine;
	}
	@Response = ForceMultipleResponses(\@Response);

	return @Response;
}


sub FormatPrinterLocation ($$)
{	my $System = shift;
	my $Queue = shift;

	if ( $System ) {			# remote print queue
		return "queue $Queue on $System";
	}
	else {					# local print queue
		return "$Queue queue";
	}
}


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

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

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

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

	return ( $Sendmail );
}


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

	my $NFSState = "";
	my $NFSServer = "";
	if ( -f $NfsdCommand and -f $ExportsPath ) {
		my @Exports = ReturnArrayFile("$ExportsPath");
		$NFSServer = grep(m:^/:, @Exports);
		# 
		# We can get here if nothing is exported
		# from this machine...  let's see if 
		# the server is running...
		# 
		if ( $NFSServer eq "" ) {
			print "\tNothing exported...\n" if $main::Debug;
			my @PsText = ReturnArrayExec("$PsCommand $PsArgs");
			print "\tSearching ps output for $NfsdCommandPattern...\n" if $main::Debug;
			foreach $Line (@PsText) {
				print "\t\t$Line\n" if $main::Debug;
				if ( $Line =~ /$NfsdCommandPattern/ ) {
					$NFSServer = "Yes";
					last;
				}
			}
		}
	}
	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 ReturnRunningAutomountBSD ($)
{	my $Subcategory = shift;

	my $Automount = ( ( -f "$AmdCommand" ) ? "Yes" : "No" );

	return ( $Automount );
}


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

	# XXX Lets defer this for now

	return ( "No" );
}


sub ReturnRunningNameserverBSD ($)
{	my $Subcategory = shift;
	my $NameserverString = "No";
	my @NameserverConfArray = ();
	my $Line = "";
	my $Zone = "";
	my $Type = "";
	my %ZoneHash;

	my @RunningProcs = ReturnArrayExec("$PsCommand $PsArgs");
	if (grep {m,$NameServerPattern,} @RunningProcs) {

		print("Found nameserver\n") if $main::Debug;

		$NameserverString = "Yes";
		# Now try to get some more info from nameserver conf file
		if ( -r "$NameserverConfPath" ) {

			print("Found $NameserverConfPath\n") if $main::Debug;

			@NameserverConfArray = ReturnArrayFile($NameserverConfPath);
			foreach $Line ( @NameserverConfArray ) {

				# named.conf stores relevent data like this
				#
				# //
				# // a caching only nameserver config
				# //
				# zone "." {
				#         type hint;
				#         file "named.ca";
				# };
				#

				next if $Line =~ /^\/\//;  # skip comment Lines

				# Look for zone and type info
				if ( $Line =~ /zone\s+\"(.+)\"/ ) {
					$Zone = $1;
				} elsif ( $Line =~ /type\s+(\w+)/ ) {
					$Type = $1;
				}

				# skip reverse lookup and first undefined entry
				if ( ($Zone =~ /in-addr\.arpa/)  || ($Zone eq "") ) {
					next;
				}

				# if zone is ., it's going to be a caching server?
				if ( $Zone eq "." ) {
					$Zone = "n/a";
				}

				# if type is hint, it's a caching server only
				if ( $Type eq "hint" ) {
					$Type = "Caching";
				}

				$ZoneHash{"$Zone"} = $Type;

			}

			# Append hash data to return string
			foreach $key ( keys %ZoneHash ) {
				$NameserverString .= ", Domain=$key, Type=$ZoneHash{$key}";
			}

		}
	}

	return ( $NameserverString );
}


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

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

	return ( $IPAddressResolution );
}


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

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


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

	# XXX Lets defer this for now

	return ( "X11R6" );
}


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

	# XXX Lets defer this for now

	return ( "No" );
}


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

 	my @DfText = ReturnArrayExec("$DfCommand $LocalFileSystems");
 
 	my $PreviousLine = "";
 	$Suffix = 0;
 	foreach $Line (@DfText) {
 
 		# bypass the header line
 		next if $Line =~ /^Filesystem/;
 		# bypass any cdrom drives or remote file systems
 		next if $Line =~ /cdrom/;
 		next if $Line =~ /^[\w]/;
 
 		# 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;
 			# go get the rest of it
 			next;
 		}
 
 		# 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 ReturnRemoteFileSystemsBSD ($)
{	my $Subcategory = shift;
	my @Response = ( );

 	my @DfText = ReturnArrayExec("$DfCommand $NFSFileSystems");
 
 	my $PreviousLine = "";
 	$Suffix = 0;
 	foreach $Line (@DfText) {
 
 		# bypass the header line
 		next if $Line =~ /^Filesystem/;
 		# bypass any cdrom drives or local file systems
 		next if $Line =~ /cdrom/;
 		next if $Line =~ /^\//;
 
 		# 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;
 			# go get the rest of it
 			next;
 		}
 
 		# calc size in MB
 		my $Size = sprintf("%.1f", ( $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 ReturnExportedFileSystemsBSD ($)
{	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 ReturnSwapBSD ($)
{	my $Subcategory = shift;
	my $Blocks = "Unknown";

	my @SwapText = ReturnArrayExec("$SwapCommand $SwapArgs");

	foreach $Line (@SwapText) {
		$Blocks = $1 if $Line =~ /allocated out of (\d+) /;
	}
	$Blocks = ((int ( $Blocks / 1024 )) . "MB") if $Blocks > 0;

	return $Blocks;
}


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

	# XXX Lets defer this for now

	return ( "Unknown" );
}


###########################################################################
# End of BSD.pm
###########################################################################

1;

