#include "SystemStatus.h"

#define SystemStatusConfigurationFile "systemStatus.cfg"

SystemStatus::SystemStatus()
{
	// Load the configuration
	loadConfig();

	// Open the system log
	openlog("SystemStatus", LOG_PID, 0);

}

SystemStatus::~SystemStatus()
{
	// Clean up after ourselves.
	closelog();
}

Boolean SystemStatus::loadConfig()
{
	char fullConfigFile[256];
	long nodes, period;
	Attributes attributes( "SystemStatusConfiguration" );

	strcpy( fullConfigFile, System::configurationFile(SystemStatusConfigurationFile));

	dprintf("SystemStatus::loadConfig() - Loading configuration from file: %s.", fullConfigFile);

	// Add the attributes
	 attributes.add( new BooleanAttribute("debug",
					       "Debugging output",
					       &debug,
					       0 ) );
	attributes.add( new IntegerAttribute("period",
		"The sampling period (in seconds) of the system status tool.",
		&period, 5) );

	attributes.add( new IntegerAttribute("numNodes",
		"Number of nodes on which to check status (maximum 10).",
		&nodes, 2) );

	attributes.add( new IntegerAttribute("maxProcesses",
		"Maximum number of processes to allow.",
		&_maxProcesses, 400) );

	attributes.add( new IntegerAttribute("minFreeMemory",
		"Minimum amount (in MB) of free memory required.",
		&_minFreeMemoryMB, 12) );

	attributes.add( new IntegerAttribute("minFreeStorage",
		"Minimum amount (in MB) of free hard drive space required.",
		&_minFreeStorageMB, 1000) );

	attributes.add( new BooleanAttribute("shutdownOnError",
		"Should the SystemStatus checker reboot the system when an error is detected?",
		&_shutdownOnError, True) );

	// Now parse the file...
	try {
		dprintf("SystemStatus::loadConfig() - Parsing configuration file.");
		AttributeParser::parse(fullConfigFile, &attributes);
		dprintf("SystemStatus::loadConfig() - Parsed configuration file sucessfully.");
		_samplePeriod = period;
		if ( nodes > 10 )
		{
			Syslog::write("SystemStatus::loadConfig() - Cannot check on more than 10 nodes. Using numNodes = 10.");
			_numNodes = 10;
		}
		else
		{
			_numNodes = nodes;
		}
	}
	catch(...)
	{
		Syslog::write("SystemStatus::loadConfig() - Error parsing configuration file, using defaults.");
		_samplePeriod = 5;
		_numNodes = 2;
		_maxProcesses = 400;
		_minFreeMemoryMB = 12;
		_minFreeStorageMB = 1000;
		_shutdownOnError = True;
	}

	Syslog::write("SystemStatus::loadConfig() - Sample period: %d seconds.", _samplePeriod);
	Syslog::write("SystemStatus::loadConfig() - Checking on %d nodes.", _numNodes);
	Syslog::write("SystemStatus::loadConfig() - Maximum number of processes: %d", _maxProcesses);
	Syslog::write("SystemStatus::loadConfig() - Minimum free memory: %d MB", _minFreeMemoryMB);
	Syslog::write("SystemStatus::loadConfig() - Minimum free hard drive space: %d MB", _minFreeStorageMB);
	Syslog::write("SystemStatus::loadConfig() - Shutdown system on error: %s", _shutdownOnError ? "Yes." : "No.");

	return True;
}

void SystemStatus::run()
{
	while(1)
	{
		if ( checkSystemStatus() )
		{
			// Something's wrong...
			printSystemStatus();
			handleError();
		}
		else
		{
			// Everything's ok - print status message.
			printSystemStatus();
		}
		dprintf("Sleeping for %d seconds.", _samplePeriod);
		if ( sleep(_samplePeriod) )
			dprintf("Why didn't I sleep? Error number %d.", errno);
		else
			dprintf("I think I slept.");
	}
}

int SystemStatus::checkSystemStatus(struct status *stat)
{
	// Get the system status from system calls...
	getSystemStatus();

	// If they want the values in a structure, copy them in...
	if ( stat != NULL )
	{
		memcpy(stat, (void const *)&_status, sizeof( struct SystemStatus ) );
	}

	// Now figure out if the values are within bounds.
	return getOverallStatus();
}

void SystemStatus::printSystemStatus()
{
    char mesg[256];
    
    // Put the output message into a string
    sprintf(mesg, "SystemStatus - System is %s", getOverallStatus() ? "NOT ok!" : "OK." );
    
    // Print to the screen & vehicle log.
    dwrite(1)("%s", mesg);
    
    // Now print to the system log.
    ::syslog(LOG_INFO, "%s", mesg);
    
    for ( int nodeCount = 0; nodeCount < _numNodes; nodeCount++ )
	{
	    dwrite(1)("Node %d: Processes = %d; FreeMem = %d MB; FreeSpace = %d MB.",
		      nodeCount+1, _status.numProcesses[nodeCount],
		      _status.freeMemory[nodeCount],
		      _status.freeSpace[nodeCount] );
	}


}

void SystemStatus::getSystemStatus()
{
    // Here's where we make some system calls to find out the internal goings on of QNX
    
    // Jump on in - count processes (virtual & real) on each node.
    int node;
    
    for ( node = 1; node <= _numNodes; node ++ )
	{
		_status.numProcesses[ node - 1 ] = getProcessCount( node );
		_status.freeMemory[ node - 1 ] = getFreeMemory( node );
		_status.freeSpace[ node - 1 ] = getFreeSpace( node );
		_status.supervisorRunning = getSupervisorStatus();
	}

	_status.dataValid = True;
}

int SystemStatus::getOverallStatus()
{
	// Compare the values in _status to the acceptable range as dictated by
	// the config parameters.

	// If the status isn't valid, then we can't really make an educated decision about
	// the state of the system.
	if ( ! _status.dataValid )
		return SystemStatus::Ok;

	for ( int nodeCount=0; nodeCount < _numNodes; nodeCount++)
	{
		if ( _status.numProcesses[nodeCount] > _maxProcesses )
			return ( SystemStatus::MaxProcesses | ( nodeCount << 16) );

		if ( _status.freeMemory[nodeCount] < _minFreeMemoryMB )
			return ( SystemStatus::MinFreeMemory | ( nodeCount << 16) );

		if ( _status.freeSpace[nodeCount] < _minFreeStorageMB )
			return ( SystemStatus::MinFreeStorage | ( nodeCount << 16) );
	}

	return SystemStatus::Ok;
}

void SystemStatus::handleError()
{
	// So someone determined that they weren't happy with the state of the
	// system. What we do now is based on configurable parameters...

	if ( _shutdownOnError == False )
	{
		Syslog::write("SystemStatus::handleError() - Configuration file says not to actually shutdown.");
		return;
	}

	Syslog::write("SystemStatus::handleError() - Getting ready to do something about the problems...");
	Syslog::write("SystemStatus::handleError() - Waiting 5 seconds and checking again (just in case).");
	sleep(5);
	if ( checkSystemStatus() )
	{
		Syslog::write("SystemStatus::handleError() - The problem didn't go away. Bummer. Shutting down.");
		::syslog(LOG_ALERT, "SystemStatus::handleError() - Shutting down because of problem on node %d.",
			 ( ( getOverallStatus() & 0xFF0000 ) >> 16 ) + 1 );
		system("/bin/shutdown");
		exit(1);
	}
	else
	{
		Syslog::write("SystemStatus::handleError() - Looks like things are back to managable levels. Not shutting down.");
	}
}

int SystemStatus::getProcessCount(short node)
{
    struct _psinfo psData;
    pid_t vid, proc_pid;
    int processCount = 1, numProcesses = 0;
    Boolean supervisorDriverRunning = False;
    Boolean supervisorServerRunning = False;

    dprintf("SystemStatus::getProcessCount() - Checking processes on node %d.", node);

	if ( node != getnid() )
	{
		dprintf("SystemStatus::getProcessCount() - Attaching virtual circuit.");
		vid = qnx_vc_attach(node, PROC_PID, 1000, 0);
		proc_pid = vid;
	}
	else
	{
		vid = -1;
		proc_pid = PROC_PID;
	}

	while ( ( processCount = qnx_psinfo(proc_pid, processCount, &psData, 0, NULL) ) != -1 )
	{
		dprintf("Process Count = %d.", processCount );
		numProcesses++; processCount++;
	}

	if ( vid != -1 )
	{
		dprintf("SystemStatus::getProcessCount() - Detaching vircuit circuit.");
		qnx_vc_detach(vid);
	}

	return ( numProcesses );
}

Boolean SystemStatus::getSupervisorStatus()
{
    
    int node = 1;
    struct _psinfo psData;
    pid_t vid, proc_pid;
    int processCount = 1;
    Boolean supervisorDriverRunning = False;
    Boolean supervisorServerRunning = False;

    dprintf("SystemStatus::getProcessCount() - Checking processes on node %d.", node);

	if ( node != getnid() )
	{
		dprintf("SystemStatus::getProcessCount() - Attaching virtual circuit.");
		vid = qnx_vc_attach(node, PROC_PID, 1000, 0);
		proc_pid = vid;
	}
	else
	{
		vid = -1;
		proc_pid = PROC_PID;
	}

	while ( ( processCount = qnx_psinfo(proc_pid, processCount, &psData, 0, NULL) ) != -1 )
	{
		if ( ! stricmp( psData.un.proc.name, "supervisorServer" ) )
			supervisorServerRunning = True;

		if ( ! stricmp( psData.un.proc.name, "supervisorDriver" ) )
			supervisorDriverRunning = True;

		if ( supervisorServerRunning && supervisorDriverRunning )
			break;

		processCount++;
	}

	if ( vid != -1 )
	{
		dprintf("SystemStatus::getProcessCount() - Detaching vircuit circuit.");
		qnx_vc_detach(vid);
	}

	return ( supervisorServerRunning && supervisorDriverRunning );
}

int SystemStatus::getFreeMemory(short node)
{
    
    struct _osinfo osData;
    if ( qnx_osinfo( node, &osData ) == -1 )
	{
	    return -1;
	}
    else
	{
	    return ( osData.freememk / 1024 );
	}
    
}

int SystemStatus::getFreeSpace(short node)
{
	int fd;
	char disk[30];
	long free, total;

	sprintf(disk, "//%d", node);
	dprintf("SystemStatus::getFreeSpace() - Checking out disk '%s'.", disk);

	if ( ( fd = open(disk, O_RDONLY) ) == -1 )
	{
		dprintf("SystemStatus::getFreeSpace() - Could not open disk.");
		return -1;
	}

	if ( disk_space( fd, &free, &total ) == -1 )
	{
		dprintf("SystemStatus::getFreeSpace() - Could not get disk info.");
		close(fd);
		return -1;
	}
	else
	{
		close(fd);
		return ( free / 2000 );
	}
}
