/***************************************************************************
 *   Property rights (C) 2004-2006 by EVER Sp. z o.o.                      *
 *                                                                         *
 *   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.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

extern "C" {
#include <pthread.h>
}
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include "monitor.h"
#include "map.h"
#include "upsints.h"
#include "glfunc.h"
#include "shmdefs.h"

LPFNIOCTL	g_pDrvfn=NULL;		// driver function pointer
sflow_ctl	g_afctl;		// main application control struct (contains flags)
sapp_config	g_appcfg;		// application configuration structure
suser_config	g_usrcfg;		// user configuration structure
CLog		g_evtlog;
CConf		*g_pCfgMain=NULL, *g_pCfgUser=NULL, *g_pCfgDev=NULL;
CShMem		*g_pShmMain=NULL, *g_pShmUser=NULL;
pthread_t	g_pthMonitor, g_pthAction, g_pthNetMgmt, g_pthShutdown;

const int g_iShmSizeConst = PS_SHM_SIZE;

bool g_bUpsShutdownNow = false;		// flag to perform ups shutdown
bool g_bUpsStateReadingFlag = false;	// flag identifying that data is being read from UPS
bool g_bShutdownTmActive = false;	// shutdown thread is active
bool g_bNetShutdownTmActive = false;	// network shutdown thread is active
bool g_bDrvInitialized = false;		// is driver initialized?
bool g_bUpsOperationFlag = false;	// if any operation on UPS is currently performed
bool g_bJustStarted = true;
long g_lSystemShutdownTimeout = 0;	// time to system shutdown. valid only in battery mode

#undef ISSETAPPFLAG
#undef SETAPPFLAG
#undef CLRAPPFLAG
#define ISSETAPPFLAG(f)		(flag_check(g_appcfg.ulAppFlag, f))
#define SETAPPFLAG(f)		(flag_set(&g_appcfg.ulAppFlag, f))
#define CLRAPPFLAG(f)		(flag_clear(&g_appcfg.ulAppFlag, f))

#define GETAPPFLOWCTL		(g_pShmMain->read(&g_afctl, AFLOWCTL_OFFSET, sizeof(sflow_ctl), true))
#define UPDATEAPPFLOWCTL	(g_pShmMain->write(&g_afctl, AFLOWCTL_OFFSET, sizeof(sflow_ctl), true))
#define GETAPPCFG			(g_pShmMain->read(&g_appcfg, APPCFG_OFFSET, sizeof(sapp_config), true))
#define UPDATEAPPCFG		(g_pShmMain->write(&g_appcfg, APPCFG_OFFSET, sizeof(sapp_config), true))
#define GETUSRCFG			(g_pShmMain->read(&g_usrcfg, USRCFG_OFFSET, sizeof(suser_config), true))
#define UPDATEUSRCFG		(g_pShmMain->write(&g_usrcfg, USRCFG_OFFSET, sizeof(suser_config), true))


/*
 * returns pointer to specified ups interface function
 */
LPFNIOCTL get_ups_interface(char *name)
{
	int ic=0;
	if (name==NULL)
		return NULL;
	while(ups_interface[ic].ups_name!=0) {
		if (strcasecmp(ups_interface[ic].ups_name, name)==0) {
			return (LPFNIOCTL)(ups_interface[ic].fnctl);
		}
		ic++;
	}
	return NULL;
}

/*
 * Gather all information from
 */
int getAllUpsData()
{
#undef SUPPORTED
#define SUPPORTED(d)	(flag_check(g_appcfg.supsi.ulMask, d))

	unsigned long ulBuff;
	int iReturn;
	if (g_pDrvfn==NULL)
		return 0;

	// Connection test
	iReturn=g_pDrvfn(IOCTL_TESTUPSLINK2, NULL, NULL);
	if (iReturn!=IOCTL_ERROR_CMD_NOTSUPPORTED)
	{
		if (iReturn!=IOCTL_ERROR_SUCCESS)
		{
			g_appcfg.iErrorCounter++;
			return 0;
		}
	}

	// UPS shutdown
	if (g_afctl.bUpsShutdownNow)
	{
		iReturn=g_pDrvfn(IOCTL_SHUTDOWNUPS, NULL, NULL);
		if (iReturn!=IOCTL_ERROR_SUCCESS)
		{
			g_evtlog.new_msg(msg_error, TXT_ERR_UPSSHUTDOWN, iReturn);
			GETAPPFLOWCTL;
			g_afctl.bUpsShutdownNow=false;
			UPDATEAPPFLOWCTL;
			g_appcfg.iErrorCounter++;
		}
		else
		{
			g_evtlog.new_msg(msg_info, TXT_INF_UPSSHUTDOWN);
			sleep(1);
			GETAPPFLOWCTL;
			g_afctl.bUpsShutdownNow=false;
			UPDATEAPPFLOWCTL;
			return -1;	// now leave, because we don't want to get Low Battery status
		}
	}
	// update configuration
	if (g_afctl.bConfigUpdate)
	{
		iReturn=g_pDrvfn(IOCTL_UPDATECONFIG, NULL, NULL);
		if (iReturn!=IOCTL_ERROR_SUCCESS)
		{
			iReturn=g_pDrvfn(IOCTL_GET_ERRORNO, NULL, NULL);
			g_evtlog.new_msg(msg_error, TXT_ERR_UPDATECONFIG, iReturn);
			g_appcfg.iErrorCounter++;
			return 0;
		}
		else
		{
			g_evtlog.new_msg(msg_info, TXT_INF_UPDATECONFIG, iReturn);
			GETAPPFLOWCTL;
			g_afctl.bConfigUpdate=false;
			UPDATEAPPFLOWCTL;
		}
	}
	if (SUPPORTED(UI_UPS_STATE))
	{
		GETAPPFLOWCTL;
		g_afctl.bUpsStateReading=true;
		UPDATEAPPFLOWCTL;
		unsigned long ulBuff=sizeof(g_appcfg.supsi.ulUpsStateMask);
		iReturn=g_pDrvfn(IOCTL_GET_UPSSTATESMASK, 
			(void *)&g_appcfg.supsi.ulUpsStateMask, (int *)&ulBuff);
		if (iReturn!=IOCTL_ERROR_SUCCESS)
		{
#ifdef _DEBUGMODE			
			g_evtlog.new_msg(msg_error, TXT_ERR_GETUPSSTATE, iReturn);
#endif//_DEBUGMODE
			g_appcfg.iErrorCounter++;
			GETAPPFLOWCTL;
			g_afctl.bUpsStateReading=false;
			UPDATEAPPFLOWCTL;
			return 0;
		}
		else
		{
			unsigned long ulBuff=sizeof(g_appcfg.supsi.ulUpsState);
			iReturn=g_pDrvfn(IOCTL_GET_UPSSTATE, 
				(void *)&g_appcfg.supsi.ulUpsState, (int *)&ulBuff);
			if (iReturn!=IOCTL_ERROR_SUCCESS)
			{
#ifdef _DEBUGMODE			
				g_evtlog.new_msg(msg_error, TXT_ERR_GETUPSSTATE, iReturn);
#endif//_DEBUGMODE
				g_appcfg.iErrorCounter++;
				GETAPPFLOWCTL;
				g_afctl.bUpsStateReading=false;
				UPDATEAPPFLOWCTL;
				return 0;
			}
		}
		GETAPPFLOWCTL;
		g_bUpsStateReadingFlag=false;
		UPDATEAPPFLOWCTL;
	}
	if (SUPPORTED(UI_PARAMETERS))
	{
		unsigned long ulBuff = sizeof(g_appcfg.supsi.sUpsParameters);
		iReturn = g_pDrvfn(IOCTL_GET_UPSPARAMS, 
			(void*)&g_appcfg.supsi.sUpsParameters, (int*)&ulBuff);
		if (iReturn!=IOCTL_ERROR_SUCCESS)
		{	
#ifdef _DEBUGMODE
			g_evtlog.new_msg(msg_error, TXT_ERR_GETUPSPARAMS, iReturn);
#endif//_DEBUGMODE
			g_appcfg.iErrorCounter++;
			return 0;
		}
	}
	if (SUPPORTED(UI_SETUP_PARAMETERS))
	{
		unsigned long ulBuff=sizeof(g_appcfg.supsi.sUpsSetupParameters);
		iReturn=g_pDrvfn(IOCTL_GET_UPSSETUPPARAMS, 
			(void *)&g_appcfg.supsi.sUpsSetupParameters, (int *)&ulBuff);
		if (iReturn!=IOCTL_ERROR_SUCCESS)
		{
#ifdef _DEBUGMODE
			g_evtlog.new_msg(msg_error, TXT_ERR_GETUPSSETUPPARAMS, iReturn);
#endif//_DEBUGMODE
			g_appcfg.iErrorCounter++;
			return 0;
		}
	}
	return 1;
}

int checkUpsStates()
{
#undef SUPPORTED
#undef NOTPASSED
#undef PASSED
#undef ISSET
#undef SETACT
#undef SETPASS
#undef ALLOK
#undef RESETIFNOMORESET
#undef IFNOMORESET
#define SUPPORTED(d)	(flag_check(g_appcfg.supsi.ulUpsStateMask, d))
#define NOTPASSED(d)	(!flag_check(g_appcfg.ulOpFlag, d))
#define PASSED(d)		(flag_check(g_appcfg.ulOpFlag, d))
#define ISSET(d)		(flag_check(g_appcfg.supsi.ulUpsState, d))
#define SETACT(d)		(flag_set(&g_appcfg.ulActFlag, d))
#define SETPASS(d)	(flag_set(&g_appcfg.ulOpFlag, d))
#define CLEARACT(d)	(flag_clear(&g_appcfg.ulActFlag, d))
#define ALLOK(d1, d2, d3)	(SUPPORTED(d1) && NOTPASSED(d2) && ISSET(d3))
#define RESETIFNOMORESET(d1, d2, d3) {														\
	if (SUPPORTED(d1) && PASSED(d2) && !ISSET(d3)) 										\
		flag_clear(&g_appcfg.ulOpFlag, d2); }
#define IFNOMORESET(d1, d2, d3) (SUPPORTED(d1) && PASSED(d2) && !ISSET(d3))

	// Online work 
	if (ALLOK(US_POWERON, OF_POWERON_PASS, US_POWERON)) {
		flag_clear(&g_appcfg.ulOpFlag, 
			OF_POWERFAIL_PASS | OF_STANDBY_PASS | OF_WATCH_PASS | OF_BATTERYLOW_PASS | OF_FAILURE_PASS);
		SETPASS(OF_POWERON_PASS);
		SETACT(ACF_POWERON);
		g_evtlog.new_msg(msg_info, TXT_INF_POWERON);
		/* wylaczamy flage aby przerwac ewentualny shutdown */
		GETAPPFLOWCTL;
		g_afctl.bShutdownProcActive=g_afctl.bNetShutdownProcActive=false;
		UPDATEAPPFLOWCTL;
	}
	// Battery work
	if (ALLOK(US_POWERFAIL, OF_POWERFAIL_PASS, US_POWERFAIL)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_POWERON_PASS | OF_STANDBY_PASS | OF_WATCH_PASS | 
			OF_AVRUP_PASS | OF_AVRLOWER_PASS | OF_FAILURE_PASS);
		SETPASS(OF_POWERFAIL_PASS);
		SETACT(ACF_POWERFAIL);
		g_evtlog.new_msg(msg_info, TXT_INF_POWERFAIL);
	}
	// Standby mode 
	if (ALLOK(US_STANDBY, OF_STANDBY_PASS, US_STANDBY)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_POWERON_PASS | OF_POWERFAIL_PASS | OF_WATCH_PASS | 
			OF_BATTERYLOW_PASS | OF_AVRLOWER_PASS | OF_AVRLOWER_PASS | OF_FAILURE_PASS);
		SETPASS(OF_STANDBY_PASS);
		SETACT(ACF_STANDBY);
		g_evtlog.new_msg(msg_info, TXT_INF_STANDBY);
	}
	// Battery low
	if (ALLOK(US_BATTERYLOW, OF_BATTERYLOW_PASS, US_BATTERYLOW)) {
		SETPASS(OF_BATTERYLOW_PASS);
		SETACT(ACF_BATTERYLOW);
		g_evtlog.new_msg(msg_info, TXT_INF_BATTERYLOW);
	}
	// AVR low
	RESETIFNOMORESET(US_AVRLOWER, OF_AVRLOWER_PASS, US_AVRLOWER);
	if (ALLOK(US_AVRLOWER, OF_AVRLOWER_PASS, US_AVRLOWER)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_AVRUP_PASS);
		SETPASS(OF_AVRLOWER_PASS);
		SETACT(ACF_AVRLOWER);
		g_evtlog.new_msg(msg_info, TXT_INF_AVRLOWER);
	}
	// AVR up
	RESETIFNOMORESET(US_AVRUP, OF_AVRUP_PASS, US_AVRUP);
	if (ALLOK(US_AVRUP, OF_AVRUP_PASS, US_AVRUP)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_AVRLOWER_PASS);
		SETPASS(OF_AVRUP_PASS);
		SETACT(ACF_AVRUP);
		g_evtlog.new_msg(msg_info, TXT_INF_AVRUP);
	}
	// Bd moduw
	RESETIFNOMORESET(US_BADEXTERNALMODULES, OF_BADEXTERNALMODULES_PASS, US_BADEXTERNALMODULES);
	if (ALLOK(US_BADEXTERNALMODULES, OF_BADEXTERNALMODULES_PASS, US_BADEXTERNALMODULES)) {
		SETPASS(OF_BADEXTERNALMODULES_PASS);
		SETACT(ACF_BADEXTERNALMODULES);
		g_evtlog.new_msg(msg_info, TXT_INF_BADEXTERNALMODULES);
	}
	// Internal error
	RESETIFNOMORESET(US_INTERNALERROR, OF_INTERNALERROR_PASS, US_INTERNALERROR);
	if (ALLOK(US_INTERNALERROR, OF_INTERNALERROR_PASS, US_INTERNALERROR)) {
		SETPASS(OF_INTERNALERROR_PASS);
		SETACT(ACF_INTERNALERROR);
		g_evtlog.new_msg(msg_info, TXT_INF_INTERNALERROR);
	}
	// Inverter failure
	RESETIFNOMORESET(US_INVERTERFAILURE, OF_INVERTERFAILURE_PASS, US_INVERTERFAILURE);
	if (ALLOK(US_INVERTERFAILURE, OF_INVERTERFAILURE_PASS, US_INVERTERFAILURE)) {
		SETPASS(OF_INVERTERFAILURE_PASS);
		SETACT(ACF_INVERTERFAILURE);
		g_evtlog.new_msg(msg_info, TXT_INF_INVERTERFAILURE);
	}
	// BYPASS
	if (ALLOK(US_BYPASS, OF_BYPASS_PASS, US_BYPASS)) {
		SETPASS(OF_BYPASS_PASS);
		SETACT(ACF_BYPASS);
		g_evtlog.new_msg(msg_info, TXT_INF_BYPASS);
	}
	// OVERLOAD
	RESETIFNOMORESET(US_OVERLOAD, OF_OVERLOAD_PASS, US_OVERLOAD);
	if (ALLOK(US_OVERLOAD, OF_OVERLOAD_PASS, US_OVERLOAD)) {
		SETPASS(OF_OVERLOAD_PASS);
		SETACT(ACF_OVERLOAD);
		g_evtlog.new_msg(msg_info, TXT_INF_OVERLOAD);
	}
	// SHORT
	RESETIFNOMORESET(US_SHORT, OF_SHORT_PASS, US_SHORT);
	if (ALLOK(US_SHORT, OF_SHORT_PASS, US_SHORT)) {
		SETPASS(OF_SHORT_PASS);
		SETACT(ACF_SHORT);
		g_evtlog.new_msg(msg_info, TXT_INF_SHORT);
	}
	// TEMPERATURETOOHIGH
	RESETIFNOMORESET(US_TEMPERATURETOOHIGH, OF_TEMPERATURETOOHIGH_PASS, US_TEMPERATURETOOHIGH);
	if (ALLOK(US_TEMPERATURETOOHIGH, OF_TEMPERATURETOOHIGH_PASS, US_TEMPERATURETOOHIGH)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_TEMPERATURETOOLOW_PASS);
		SETPASS(OF_TEMPERATURETOOHIGH_PASS);
		SETACT(ACF_TEMPERATURETOOHIGH);
		g_evtlog.new_msg(msg_info, TXT_INF_TEMPERATURETOOHIGH);
	}
	// TEMPERATURETOOLOW
	RESETIFNOMORESET(US_TEMPERATURETOOLOW, OF_TEMPERATURETOOLOW_PASS, US_TEMPERATURETOOLOW);
	if (ALLOK(US_TEMPERATURETOOLOW, OF_TEMPERATURETOOLOW_PASS, US_TEMPERATURETOOLOW)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_TEMPERATURETOOHIGH_PASS);
		SETPASS(OF_TEMPERATURETOOLOW_PASS);
		SETACT(ACF_TEMPERATURETOOLOW);
		g_evtlog.new_msg(msg_info, TXT_INF_TEMPERATURETOOLOW);
	}
	// Watch mode 
	if (ALLOK(US_WATCH, OF_WATCH_PASS, US_WATCH)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_POWERON_PASS | OF_POWERFAIL_PASS | OF_STANDBY_PASS);
		SETPASS(OF_WATCH_PASS);
		SETACT(ACF_WATCH);
		g_evtlog.new_msg(msg_info, TXT_INF_WATCH);
	}
	// if battery depleted then show a message 
	if (ALLOK(US_BATTERYDEPLETED, OF_BATTERYDEPLETED_PASS, US_BATTERYDEPLETED)) {
		SETPASS(OF_BATTERYDEPLETED_PASS);
		SETACT(ACF_BATTERYDEPLETED);
		g_evtlog.new_msg(msg_info, TXT_INF_BATTERYDEPLETEDSH);
		console_report(TXT_INF_BATTERYDEPLETEDSH);
	}
	// RETROACTIVEPOWER
	RESETIFNOMORESET(US_RETROACTIVEPOWER, OF_RETROACTIVEPOWER_PASS, US_RETROACTIVEPOWER);
	if (ALLOK(US_RETROACTIVEPOWER, OF_RETROACTIVEPOWER_PASS, US_RETROACTIVEPOWER)) {
		SETPASS(OF_RETROACTIVEPOWER_PASS);
		SETACT(ACF_RETROACTIVEPOWER);
		g_evtlog.new_msg(msg_warning, TXT_INF_RETROACTIVEPOWER);
	}
	// FAILURE mode
	if (ALLOK(US_FAILURE, OF_FAILURE_PASS, US_FAILURE)) {
		flag_clear(&g_appcfg.ulOpFlag, OF_POWERON_PASS | OF_POWERFAIL_PASS | OF_WATCH_PASS | 
			OF_BATTERYLOW_PASS | OF_AVRLOWER_PASS | OF_AVRLOWER_PASS | OF_STANDBY_PASS);
		SETPASS(OF_FAILURE_PASS);
		SETACT(ACF_FAILURE);
		g_evtlog.new_msg(msg_warning, TXT_INF_FAILURE);
	}

	return 1;
}


/*
 * Check if Ups is currently connected.
 */
int testUpsLink(void)
{
	if (g_pDrvfn(IOCTL_TESTUPSLINK, NULL, NULL) != IOCTL_ERROR_SUCCESS)
		return 0;
	else
		return 1;
}

/*
 * Main monitoring routine
 */
void monitor(void *lpv)
{
	int iErrorCounterShd=0;
	int iGaudRet=0;

	g_appcfg.iErrorCounter = 0;
	g_afctl.iMonitorStatus = STATUS_ACTIVE;
	while(g_afctl.bMonitorActive)
	{
		sleep(INT_MONITOR_INTERVAL);
		
		iGaudRet=getAllUpsData();
		if (iGaudRet==1) {
			if (g_appcfg.iErrorCounter > 0)
				g_appcfg.iErrorCounter--;
		}
		GETAPPFLOWCTL;
		if (!g_afctl.bMonitorActive) {
			continue;
		}
	
		checkUpsStates();

		UPDATEAPPCFG;
		GETAPPFLOWCTL;

		if (g_appcfg.iErrorCounter >= INT_MAX_COMMERRORS) {
			g_evtlog.new_msg(msg_critical, TXT_ERR_TOOMANYCOMMERRORS, 0x01201);
			g_appcfg.iErrorCounter = 0;
/*
			error_report(TXT_ERR_TOOMANYCOMMERRORS, 0x01201);
			break;
*/
		}
	}
	g_afctl.iMonitorStatus = STATUS_HASSTOPPED;	
}

void *actions(void *lpv)
{
#undef ISSET
#undef SETACT
#undef UNSETACT
#define ISSET(c, d)	(flag_check(c, d))
#define SETACT(d)		(flag_set(&g_appcfg.ulActFlag, d))
#define UNSETACT(d)	(flag_clear(&g_appcfg.ulActFlag, d))
	
	// tablica identyfikatorow zdarzen dla akcji 
	unsigned long ulIds[ INT_MAX_PROGRAMEVENTS ] = {
		ACF_POWERON,
		ACF_POWERFAIL,
		ACF_STANDBY,
		ACF_OVERLOAD,
		ACF_AVRUP,
		ACF_AVRLOWER,
		ACF_BATTERYLOW,
		ACF_BATTERYDEPLETED,
		ACF_BYPASS,
		ACF_WATCH,
		ACF_SHORT,
		ACF_INTERNALERROR,
		ACF_INVERTERFAILURE,
		ACF_TEMPERATURETOOHIGH,
		ACF_TEMPERATURETOOLOW,
		ACF_BADEXTERNALMODULES,
		ACF_RETROACTIVEPOWER,
		ACF_BATTERYCHARGING,
		ACF_FAILURE
	};
	unsigned long ulActFlagTmp;
	
	sleep(2);

	g_afctl.iActionStatus = STATUS_ACTIVE;
	while(g_afctl.bActionActive) 
	{
		//console_report("actions loop...\n");
		
		sleep(INT_ACTION_INTERVAL);

		ulActFlagTmp=g_appcfg.ulActFlag;
		
		// testujemy flagi akcji do wykonania zdarzen
		for (int ie=0; ie<INT_MAX_PROGRAMEVENTS; ie++) {
			if (!ISSET(ulActFlagTmp, ulIds[ ie ]))
				continue;
			
			UNSETACT(ulIds[ ie ]);
			//console_report("checking for configured actions...\n");
			for (int ic=0; ic<g_usrcfg.iPeCount; ic++) {
				if (!ISSET(ulIds[ ie ], g_usrcfg.spe[ic].ulId))
					continue;
				
				if (g_usrcfg.spe[ic].bEnabled) {
					executeActions(ic);
				}
			}
			//console_report("checking for configured actions done!\n");
		}
	}
	
	g_afctl.iActionStatus = STATUS_HASSTOPPED;
	pthread_exit(NULL);
}


// Function name	: executeActions
// Description		: Wykonuje reakcje na zdarzenia
// Return type		: void 
// Argument         : int iEventIdx
void executeActions(int iEventIdx)
{
	int iReturn, ilc;

	if (!g_usrcfg.spe[ iEventIdx ].bNetwork)
	{
		if (g_usrcfg.spe[iEventIdx].sa.smsg.bEnabled)
		{
#ifdef _DEBUGMODE
			console_report("Action: Message\n");
#endif//_DEBUGMODE
			if (!wall_msg(g_usrcfg.spe[iEventIdx].sa.smsg.szContent)) {
				g_evtlog.new_msg(msg_error, TXT_ERR_FAILTOMESSAGE, errno);
			}
		}
		if (g_usrcfg.spe[iEventIdx].sa.scmd.bEnabled)
		{
#ifdef _DEBUGMODE			
			console_report("Action: Execute\n");
#endif//_DEBUGMODE
			if (system(g_usrcfg.spe[iEventIdx].sa.scmd.szPath)==-1) {
				g_evtlog.new_msg(msg_error, TXT_ERR_FAILTOEXECUTE, -1);
			}
		}
		if (g_usrcfg.spe[iEventIdx].sa.sem.bEnabled)
		{
#ifdef _DEBUGMODE			
			console_report("Action: Email\n");
#endif//_DEBUGMODE
			CEmail ceml;
			if (ceml.Init(g_usrcfg.szSMTPServer, "", "", "")==0) 
			{
				ceml.SetAuthorization(g_usrcfg.szSMTPAuth_Name, g_usrcfg.szSMTPAuth_Password);
				ceml.SetRecipient(g_usrcfg.spe[iEventIdx].sa.sem.szRecipient);
				ceml.SetSender(g_usrcfg.szSenderAddress, g_usrcfg.spe[iEventIdx].sa.sem.szSender);
				if (ceml.SendMessage(g_usrcfg.spe[iEventIdx].sa.sem.szSubject, 
					g_usrcfg.spe[iEventIdx].sa.sem.szContent, 
					strlen(g_usrcfg.spe[iEventIdx].sa.sem.szContent))==0) 
				{
					g_evtlog.new_msg(msg_info, TXT_INF_MAILSENDTO, 
						g_usrcfg.spe[iEventIdx].sa.sem.szRecipient);
				}
				else
				{
					char szErrBuff[256] = "";
					ceml.GetLastErrorString(szErrBuff, sizeof(szErrBuff));
					g_evtlog.new_msg(msg_error, TXT_ERR_FAILTOSENDMAILTO, 
						g_usrcfg.spe[iEventIdx].sa.sem.szRecipient, szErrBuff);
				}
			} else
				g_evtlog.new_msg(msg_error, TXT_ERR_FAILTOINITMAILENGINE);
		}
		if (g_usrcfg.spe[iEventIdx].sa.ssd.bEnabled)
		{
#ifdef _DEBUGMODE			
			console_report("Action: Shutdown\n");
#endif//_DEBUGMODE
			// cancel all pending shutdown threads
			GETAPPFLOWCTL;
			g_afctl.bShutdownProcActive=false;
			UPDATEAPPFLOWCTL;
			sleep(1);
			
			// initialize new shutdown thread
			GETAPPFLOWCTL;
			g_afctl.bShutdownProcActive=true;
			UPDATEAPPFLOWCTL;
			
			if (g_usrcfg.spe[iEventIdx].sa.ssd.ulTimeoutSec==0)
			{
				if (g_usrcfg.ulUpsShutdown)
				{
					GETAPPFLOWCTL;
					g_afctl.bUpsOperationActive=true;
					UPDATEAPPFLOWCTL;
#ifdef _DEBUGMODE
					console_report("UPS shutdown!\n");
#endif//_DEBUGMODE
					GETAPPFLOWCTL;
					g_afctl.bUpsShutdownNow = true;
					UPDATEAPPFLOWCTL;
					for (ilc=0; ilc<30; ilc++) {
						if (!g_afctl.bUpsShutdownNow)
							break;
						else {
							usleep(500000);
						}
					}
#ifdef _DEBUGMODE
					console_report("UPS shutdown -> done\n!");
#endif//_DEBUGMODE
				}
				GETAPPFLOWCTL;
				g_afctl.bUpsOperationActive=false;
				UPDATEAPPFLOWCTL;
				if (g_afctl.bShutdownProcActive) {
					switch(g_usrcfg.spe[iEventIdx].sa.ssd.esm)
					{
					case sm_normal:
						g_evtlog.new_msg(msg_info, TXT_INF_SHUTDOWN_NORMAL);
						shutdown_system(sm_normal);
						break;
					
					case sm_reboot:
						g_evtlog.new_msg(msg_info, TXT_INF_SHUTDOWN_REBOOT);
						shutdown_system(sm_reboot);
						break;
					
					default:
						g_evtlog.new_msg(msg_warning, TXT_ERR_UNKNOWNSHUTDOWNCONFIG);
						shutdown_system(sm_normal);
						break;
					}
				}//g_bShutdownTmActive
			}
			else
			{
				int icpid;
				ssdownhlp sbuff;
				sbuff.ulTriggerFlag=g_usrcfg.spe[ iEventIdx ].ulId;
				sbuff.lpssd = &g_usrcfg.spe[ iEventIdx ].sa.ssd;
				 
				if (pthread_create(&g_pthShutdown, NULL, shutdown, &sbuff) > 0) {
					error_report(TXT_ERR_CREATESHUTDOWNPROC);
					g_evtlog.new_msg(msg_error, TXT_ERR_CREATESHUTDOWNPROC);
				}
			}

		}
	}
	else	// obsluga sieciowych polecen
	{
		sendToNetClients(g_usrcfg.spe[iEventIdx]);
	}
}

void *shutdown(void/*ssdownhlp*/ *lpsc)
{
#undef ISSET
#define ISSET(c, d)	(flag_check(c, d))

	char szShutdownText[512];
	int ilc;
	unsigned long ulTriggerId = ((ssdownhlp *)lpsc)->ulTriggerFlag;
	lpsshutdown lpss = ((ssdownhlp *)lpsc)->lpssd;
	unsigned long ulTimeOut=time(NULL) + lpss->ulTimeoutSec;

	if (lpss->ulTimeoutSec <= 300)
		sprintf(szShutdownText, TXT_EVENT_PREPARETOSHUTDOWN_SEC, lpss->ulTimeoutSec);
	else if ((lpss->ulTimeoutSec > 300) && (lpss->ulTimeoutSec<1800))
		sprintf(szShutdownText, TXT_EVENT_PREPARETOSHUTDOWN_MIN, lpss->ulTimeoutSec / 60);
	else
		sprintf(szShutdownText, TXT_EVENT_PREPARETOSHUTDOWN_HRS, lpss->ulTimeoutSec / 3600);

	wall_msg(szShutdownText);

	while(g_afctl.bShutdownProcActive) 
	{
	    GETAPPFLOWCTL;
	    g_afctl.lShutdownTimeout = ulTimeOut - time(NULL);
	    UPDATEAPPFLOWCTL;
		if (time(NULL) < ulTimeOut) {
			usleep(500000);
		} else {
			if (g_usrcfg.ulUpsShutdown)
			{
				GETAPPFLOWCTL;
				g_afctl.bUpsOperationActive=true;
				UPDATEAPPFLOWCTL;
#ifdef _DEBUG
				console_report("UPS shutdown!\n");
#endif//_DEBUG
					GETAPPFLOWCTL;
					g_afctl.bUpsShutdownNow=true;
					UPDATEAPPFLOWCTL;
					for (ilc=0; ilc<30; ilc++) {
						if (g_afctl.bUpsShutdownNow==false)
							break;
						else
							sleep(1);
					}
#ifdef _DEBUG
					console_report("UPS shutdown -> OK!\n");
#endif//_DEBUG
			}
			GETAPPFLOWCTL;
			g_afctl.bUpsOperationActive=false;
			UPDATEAPPFLOWCTL;

			if (g_afctl.bShutdownProcActive) {
				switch(lpss->esm)
				{
				case sm_normal:
					g_evtlog.new_msg(msg_info, TXT_INF_SHUTDOWN_NORMAL);
					shutdown_system(sm_normal);
					break;
				
				case sm_reboot:
					g_evtlog.new_msg(msg_info, TXT_INF_SHUTDOWN_REBOOT);
					shutdown_system(sm_reboot);
					break;
				
				default:
					g_evtlog.new_msg(msg_warning, TXT_ERR_UNKNOWNSHUTDOWNCONFIG);
					shutdown_system(sm_normal);
					break;
				}
			}//g_bShutdownTmActive
			GETAPPFLOWCTL;
			g_afctl.bShutdownProcActive=false;
			UPDATEAPPFLOWCTL;
			return NULL;
		}
	}
	if (!g_afctl.bShutdownProcActive) {
		wall_msg(TXT_EVENT_PREPARETOSHUTDOWNABORT);
	}
	pthread_exit(NULL);
}

int shutdown_system(int imode)
{
#ifdef _DEBUGMODE
	wall_msg("SHUTDOWN HAS BEEN STARTED!!!!\n");
#else
	switch(imode)
	{
	default:
	case sm_normal:
		wall_msg(TXT_INF_SHUTDOWN_NORMAL);
		g_evtlog.new_msg(msg_info, TXT_INF_SHUTDOWN_NORMAL);
		system(g_usrcfg.szShutdownCmd);
		break;
		
	case sm_reboot:
		wall_msg(TXT_INF_SHUTDOWN_REBOOT);
		g_evtlog.new_msg(msg_info, TXT_INF_SHUTDOWN_REBOOT);
		system("reboot");
		break;
	}

	g_afctl.bActionActive=g_afctl.bMonitorActive=g_afctl.bNetMgmtActive=false;
	sleep(2);
	terminate(0);
	
#endif
	return 1;
}

// Function name	: sendToNetClients
// Description		: sends info to the network clients
int sendToNetClients(sprogramevent spe)
{
	u_short usPort=12321;
	char szNetBuff[2048], szNetBuff1[2048], szNetBuff2[2048];
	struct hostent *lphentry;
	struct sockaddr_in sai;
	int iLen, iRet;
	int fNetSock;
	unsigned long ulAddr;
	bool bDomainName;
	pthread_t pthNet;

	if (!spe.bNetwork)
		return 0;	// hmmm...cos nie tak???

	if (spe.sa.ssd.ulTimeoutSec > 0) {
		if (spe.sa.ssd.bEnabled) {
			g_bNetShutdownTmActive=true;
			pthread_create(&pthNet, NULL, netShutdownThread, &spe);
		}
	}
	for (int i=0; i<spe.iNetCompsIdsCnt; i++)
	{
		bDomainName=isalpha(*g_usrcfg.snc[ spe.ulNetCompIds[ i ] ].szComputer);
		ulAddr=inet_addr(g_usrcfg.snc[ spe.ulNetCompIds[ i ] ].szComputer);

		if (bDomainName)
			lphentry=gethostbyname(g_usrcfg.snc[ spe.ulNetCompIds[ i ] ].szComputer);
		else
			lphentry=gethostbyaddr((const char *)&ulAddr, 4, AF_INET);

		if (lphentry==NULL)
			continue;
		
		sai.sin_family=AF_INET;
		sai.sin_addr=*((in_addr*)*lphentry->h_addr_list);
		sai.sin_port=htons(usPort);
		
		fNetSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
		if (fNetSock==-1) {
			g_evtlog.new_msg(msg_error, TXT_ERR_CREATESOCKET, errno);
			return -1;
		}
		bzero(szNetBuff, sizeof(szNetBuff));
		sprintf(szNetBuff, APP_NET_MAGIC, APP_NET_VERSION_MAJOR, APP_NET_VERSION_MINOR);

		/* komunikat */
		if (spe.sa.smsg.bEnabled) {
			bzero(szNetBuff1, sizeof(szNetBuff));
			bzero(szNetBuff2, sizeof(szNetBuff));
			strcpy(szNetBuff1, szNetBuff);
			iLen=strlen(szNetBuff);
			sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_SHOWMESSAGE);
			iLen += APP_NET_CMDFMT_LEN;
			strcat(szNetBuff2, spe.sa.smsg.szContent);
			iLen += strlen(spe.sa.smsg.szContent);
			szNetBuff2[ iLen++ ] = '\0'; szNetBuff2[ iLen++ ] = '\0';

			strcat(szNetBuff1, szNetBuff2);
			iRet=sendto(fNetSock, szNetBuff1, iLen, 0, (sockaddr *)&sai, sizeof(sockaddr));
			if (iRet==-1) {
				g_evtlog.new_msg(msg_error, TXT_ERR_SENDDATATOCLIENT, g_usrcfg.snc[ spe.ulNetCompIds[ i ] ].szComputer, errno);
			}
		} 
		/* polecenie systemowe */
		if (spe.sa.scmd.bEnabled) {
			bzero(szNetBuff1, sizeof(szNetBuff));
			bzero(szNetBuff2, sizeof(szNetBuff));
			strcpy(szNetBuff1, szNetBuff);
			iLen=strlen(szNetBuff);
			sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_EXECUTE);
			iLen += APP_NET_CMDFMT_LEN;
			
			strcat(szNetBuff2, spe.sa.scmd.szPath);
			iLen += strlen(spe.sa.scmd.szPath);
			strcat(szNetBuff2, "\t");
			iLen++;
			strcat(szNetBuff2, spe.sa.scmd.szDirectory);
			iLen += strlen(spe.sa.scmd.szDirectory);
			strcat(szNetBuff2, "\t");
			iLen++;
			strcat(szNetBuff2, spe.sa.scmd.szParams);
			iLen += strlen(spe.sa.scmd.szParams);
			szNetBuff2[ iLen++ ] = '\0';
			szNetBuff2[ iLen++ ] = '\0';

			strcat(szNetBuff1, szNetBuff2);
			iRet=sendto(fNetSock, szNetBuff1, iLen, 0, (sockaddr *)&sai, sizeof(sockaddr));
			if (iRet==-1) {
				g_evtlog.new_msg(msg_error, TXT_ERR_SENDDATATOCLIENT, g_usrcfg.snc[ spe.ulNetCompIds[ i ] ].szComputer, errno);
			}
		}
		// shutdown
		if (spe.sa.ssd.bEnabled && !spe.sa.ssd.ulTimeoutSec) {
			bzero(szNetBuff1, sizeof(szNetBuff));
			bzero(szNetBuff2, sizeof(szNetBuff));
			strcpy(szNetBuff1, szNetBuff);
			iLen=strlen(szNetBuff);
			switch(spe.sa.ssd.esm) {
			case sm_normal:
				sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_DOWN);
				break;
			case sm_forced:
				sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_DOWNFORCE);
				break;
			case sm_reboot:
				sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_DOWNREBOOT);
				break;
			case sm_hibernate:
				sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_HIBERNATE);
				break;
			default:
				continue;
			}
			iLen += APP_NET_CMDFMT_LEN;
			strcat(szNetBuff1, szNetBuff2);
			iRet=sendto(fNetSock, szNetBuff1, iLen, 0, (sockaddr *)&sai, sizeof(sockaddr));
			if (iRet==-1) {
				g_evtlog.new_msg(msg_error, TXT_ERR_SENDDATATOCLIENT, g_usrcfg.snc[ spe.ulNetCompIds[ i ] ].szComputer, errno);
			}
		}
		shutdown(fNetSock, SHUT_RDWR);
		close(fNetSock);
	}
	return 1;
}

// Function name	: NetShutdownThread
void *netShutdownThread(void *lpv)
{
#undef ISSET
#define ISSET(c, d)	(TestBit(c, d))

	lpsprogramevent lpspe = (lpsprogramevent)lpv;
	
	u_short usPort=12321;
	char szNetBuff[2048], szNetBuff1[2048], szNetBuff2[2048];
	struct hostent *lphentry;
	struct sockaddr_in sai;
	int iLen, iRet;
	int fNetSock;
	unsigned long ulAddr;
	bool bDomainName;
	unsigned long ulTimeOut=time(NULL) + lpspe->sa.ssd.ulTimeoutSec;

	while(g_afctl.bShutdownProcActive) 
	{
		GETAPPFLOWCTL;
		if (time(NULL) < ulTimeOut) {
			//if (!ISSET(sac.ulOpFlag, dwTriggerId))
			//	g_bNetShutdownTmActive=FALSE;
			usleep(500000);
		}
		else
		{
			for (int i=0; i<lpspe->iNetCompsIdsCnt; i++)
			{
				bDomainName=isalpha(*g_usrcfg.snc[ lpspe->ulNetCompIds[ i ] ].szComputer);
				ulAddr=inet_addr(g_usrcfg.snc[ lpspe->ulNetCompIds[ i ] ].szComputer);

				if (bDomainName)
					lphentry=gethostbyname(g_usrcfg.snc[ lpspe->ulNetCompIds[ i ] ].szComputer);
				else
					lphentry=gethostbyaddr((const char *)&ulAddr, 4, AF_INET);

				if (lphentry==NULL)
					continue;
				
				sai.sin_family=AF_INET;
				sai.sin_addr=*((in_addr *)*lphentry->h_addr_list);
				sai.sin_port=htons(usPort);
				fNetSock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
				if (fNetSock==-1) {
					g_evtlog.new_msg(msg_error, TXT_ERR_CREATESOCKET, errno);
					continue;
				}
				sprintf(szNetBuff, APP_NET_MAGIC, APP_NET_VERSION_MAJOR, APP_NET_VERSION_MINOR);
				iLen=strlen(szNetBuff);
				// shutdown
				if (lpspe->sa.ssd.bEnabled)
				{
					strcpy(szNetBuff1, szNetBuff);
					iLen=strlen(szNetBuff);
					switch(lpspe->sa.ssd.esm) {
					case sm_normal:
						sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_DOWN);
						break;
					case sm_forced:
						sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_DOWNFORCE);
						break;
					case sm_reboot:
						sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_DOWNREBOOT);
						break;
					case sm_hibernate:
						sprintf(szNetBuff2, APP_NET_CMDFMT, APP_NET_MAGIC01, APP_NET_MAGIC02, APP_NET_CMD_HIBERNATE);
						break;
					default:
						continue;
					}
					iLen += APP_NET_CMDFMT_LEN;
					strcat(szNetBuff1, szNetBuff2);
					iRet=sendto(fNetSock, szNetBuff1, iLen, 0, (sockaddr *)&sai, sizeof(sockaddr));
					if (iRet==-1) {
						g_evtlog.new_msg(msg_error, TXT_ERR_SENDDATATOCLIENT, g_usrcfg.snc[ lpspe->ulNetCompIds[ i ] ].szComputer, errno);
						close(fNetSock);
					}
					shutdown(fNetSock, SHUT_RDWR);
					close(fNetSock);
				}
			}
			break;
		}
	}
}

// Funkcja odbiera dane z sieci i/lub czeka na nie
int netMgmtRecvData(int sock, struct sockaddr_in *bsai, char *buff, int isize, int itimeout)
{
	int i = 0, nd = 0;
	fd_set frd;
	struct timeval tv;
	u_long ulfArg = 0;

	if (buff==NULL || isize<0 || itimeout<0)
		return -1;

	FD_ZERO(&frd);
	FD_SET(sock, &frd);
	nd = max(nd, sock);
	tv.tv_sec = itimeout;
	tv.tv_usec = 0;
	
	i = select(nd+1, &frd, NULL, NULL, &tv);
	if (i == -1)	// disconnection
		return -2;
	if (i == 0)				// timeout
		return 0;
	i = ioctl(sock, FIONREAD, &ulfArg);
	if (i == -1)
		return -3;
	if (ulfArg > (unsigned long)isize)
		ulfArg = (unsigned long)isize;
	memset(buff, 0, isize);
	socklen_t length = sizeof(sockaddr_in);
	i = recvfrom(sock, buff, ulfArg, 0, (struct sockaddr*)bsai, &length);
	if (i != -1)
		return ulfArg;
	else
		return -4;
}

int checkForConnection(int sock, int itimeoutms)
{
	int i = 0, nd = 0;
	fd_set frd;
	struct timeval tv;

	if (itimeoutms<0)
		return -1;

	FD_ZERO(&frd);
	FD_SET(sock, &frd);
	nd = max(nd, sock);
	tv.tv_sec = 0;
	tv.tv_usec = itimeoutms*1000;
	
	i = select(nd+1, &frd, NULL, NULL, &tv);
	if (i == -1)	// disconnection
		return -2;
	if (i == 0)		// timeout
		return 0;
	else
		return 1;
}

// Network management thread
// 12.2005 - a new option should be added to the main app config
void *netManagement(void *lpv)
{
	struct sockaddr_in saiServer, saiClient;
	int sNetClient, sNetServer;
	short sPortNo = 12322;
	int iRet = 0;
	char szBuffer[1024] = "";
	
	sleep(6);

	g_afctl.iActionStatus = STATUS_ACTIVE;
	
	if (!g_afctl.bNetMgmtActive)
		return NULL;

	memset(&saiServer, 0, sizeof(struct sockaddr_in));
	memset(&saiClient, 0, sizeof(struct sockaddr_in));
	saiServer.sin_family = AF_INET;
	saiServer.sin_addr.s_addr = INADDR_ANY;
	saiServer.sin_port = htons(sPortNo);

	sNetServer = socket(AF_INET, SOCK_STREAM, 0);
	if (sNetServer == -1) {
		g_evtlog.new_msg(msg_error, "%s (%d)", TXT_ERR_CREATESOCKET, errno);
		return NULL;
	}
	fcntl(sNetServer, F_SETFL, O_NONBLOCK);
	int yes = 1;
	if (setsockopt(sNetServer, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)) < 0) {
		g_evtlog.new_msg(msg_error, "%s (%d)", TXT_ERR_CREATESOCKET, errno);
		close(sNetServer);
		return NULL;
	}
	if (bind(sNetServer, (struct sockaddr*)&saiServer, sizeof(saiServer)) == -1) {
		g_evtlog.new_msg(msg_error, "%s (%d)", TXT_ERR_BINDFAIL, errno);
		close(sNetServer);
		return NULL;
	}
	if (listen(sNetServer, 1) == -1) {
		g_evtlog.new_msg(msg_error, "%s (%d)", TXT_ERR_LISTENFAIL, errno);
		close(sNetServer);
		return NULL;
	}
	while(g_afctl.bNetMgmtActive)
	{
		sleep(1);
		GETAPPFLOWCTL;
		//UPDATEAPPFLOWCTL;
		
/*		console_report("ENTERED NETMGMT LOOP...\n");
		console_report("int size is: %ld\n", sizeof(int));
		console_report("enum size is: %ld\n", sizeof(shm_actions));
		console_report("unsigned long size is: %ld\n", sizeof(unsigned long));
		console_report("long size is: %ld\n", sizeof(long));
		console_report("unsigned int size is: %ld\n", sizeof(unsigned int));
		console_report("__int64 size is: %ld\n", sizeof(__int64));
		console_report("char size is: %ld\n", sizeof(char));
		console_report("sups_characteristic size is: %ld\n", sizeof(sups_characteristic));
		console_report("sups_parameters size is: %ld\n", sizeof(sups_parameters));
		console_report("sups_setup_parameters size is: %ld\n", sizeof(sups_setup_parameters));
*/
		do {
			if (checkForConnection(sNetServer, 250) > 0) {
				//console_report("INCOMING CONNECTION...\n");
				break;
			}
			//console_report(".");
		} while (g_afctl.bNetMgmtActive);
		
		memset(&saiClient, 0, sizeof(struct sockaddr_in));
		socklen_t iLen = sizeof(saiClient);
		sNetClient = accept(sNetServer, (struct sockaddr*)&saiClient, &iLen);
		//console_report("AN ACCEPT HAS RETURNED!\n");
		if ((sNetClient < 0) || (errno==EAGAIN)) {
			continue;
		}
		// connected
		if (g_afctl.bNetMgmtActive) {
			//memset(&saiClient, 0, sizeof(struct sockaddr_in));
			//console_report("WAITING FOR ACK...\n");
			iRet = netMgmtRecvData(sNetClient, &saiClient, szBuffer, sizeof(szBuffer), 10);
			if (iRet > 0 && !strcmp(APP_NMS_CMD_ACKNOWLEDGE, szBuffer)) {
				// send acknowledge
				//console_report("SENDING ACK...\n");
				send(sNetClient, APP_NMS_CMD_ACKNOWLEDGE, strlen(APP_NMS_CMD_ACKNOWLEDGE), 0);
				// continue with approval
			} else {
				//console_report("ACK ERROR!(%ld, Buffer[%s])\n", iRet, szBuffer);
				continue;
			}

			// clear the buffer
			memset(szBuffer, 0, sizeof(szBuffer));
			//console_report("WAITING FOR COMMAND...\n");
			iRet = netMgmtRecvData(sNetClient, &saiClient, szBuffer, sizeof(szBuffer), 10);
			if (iRet <= 0) {
				//console_report("COMMAND ERROR!\n");
				shutdown(sNetClient, SHUT_RDWR);
				close(sNetClient);
				continue;
			}

			// Returns nothing
			if (!strcmp(APP_NMS_CMD_THANKYOU, szBuffer)) {
				// do nothing. Thank You.
				//console_report("THANK YOU!\n");
			}

			// Returns main data
			if (!strcmp(APP_NMS_CMD_GETAPPDATA, szBuffer)) {
				//console_report("GET APP DATA!\n");
				GETAPPCFG;
				// continue with approval and return the data
				shm_exchange sse;
				
				memcpy(&sse.suc, &g_appcfg.supsi.sUpsCharacteristic, sizeof(sse.suc));
				memcpy(&sse.sup, &g_appcfg.supsi.sUpsParameters, sizeof(sse.sup));
				memcpy(&sse.susp, &g_appcfg.supsi.sUpsSetupParameters, sizeof(sse.susp));
				sse.ulMask = g_appcfg.supsi.ulMask;
				sse.ulUpsStateMask = g_appcfg.supsi.ulUpsStateMask;
				sse.ulUpsState = g_appcfg.supsi.ulUpsState;

				//console_report("SENDING NMS DATA OF %ld SIZE.\n", SHMEM_DATAEXCH_SIZE);
				// send the data
				if (send(sNetClient, (const char*)&sse, SHMEM_DATAEXCH_SIZE, 0) != SHMEM_DATAEXCH_SIZE)
				{
					//console_report("ERROR SENDING NMS DATA!\n");
					//error
				}
			}
		}
		//console_report("CLIENT SOCKET SHUTDOWN!\n");
		shutdown(sNetClient, SHUT_RDWR);
		close(sNetClient);
	}
	shutdown(sNetServer, SHUT_RDWR);
	close(sNetServer);

	// ustaw flage na TRUE aby oznajmić o wyjciu z pętli
	g_afctl.bNetMgmtActive = true;

	g_afctl.iActionStatus = STATUS_HASSTOPPED;
	pthread_exit(NULL);
	
	return NULL;
}

/* read main configuration */
int update_main_config()
{
	char *fp=get_config_filepath(APP_FILE_MAINCONFIG);
	if (fp==NULL) {
		error_report("%s (%s)", TXT_ERR_MEMALLOC, strerror(errno));
		return -1;
	}
	g_pCfgMain=new CConf;
	if (g_pCfgMain->init(fp)!=CONF_SUCCESS) {
		free(fp);
		g_evtlog.new_msg(msg_error, "%s (%s)", TXT_ERR_READAPPCONFIG, g_pCfgMain->geterrordesc());
		error_report("%s (%s)", TXT_ERR_READAPPCONFIG, g_pCfgMain->geterrordesc());
		if (g_pCfgMain!=NULL)
			delete g_pCfgMain;
		return -1;
	} else {
		free(fp);
		g_pCfgMain->parse_config();
	}
	
	if (compile_main_config_map(*g_pCfgMain)==-1) {
		g_evtlog.new_msg(msg_error, "%s", TXT_ERR_READAPPCONFIG);
		error_report("%s", TXT_ERR_READAPPCONFIG);
		if (g_pCfgMain!=NULL)
			delete g_pCfgMain;
		return -1;
	} else {
		if (g_pCfgMain!=NULL)
			delete g_pCfgMain;
	}
	
	return 0;	// success
}

/* read user configuration */
int update_user_config()
{
	if (g_pCfgUser!=NULL)
		delete g_pCfgUser;
	
	char *fp=get_config_filepath(str_ext_config);
	if (fp==NULL) {
		error_report("%s (%s)", TXT_ERR_MEMALLOC, strerror(errno));
		return -1;
	}
	g_pCfgUser=new CConf;
	if (g_pCfgUser->init(fp)!=CONF_SUCCESS) {
		free(fp);
		g_evtlog.new_msg(msg_error, "%s (%s)", TXT_ERR_READUSERCONFIG, g_pCfgUser->geterrordesc());
		error_report("%s (%s)", TXT_ERR_READUSERCONFIG, g_pCfgUser->geterrordesc());
		delete g_pCfgUser;
		return -1;
	} else
		g_pCfgUser->parse_config();
		
	free(fp);

	if (compile_user_config_map(*g_pCfgUser)==-1) {
		g_evtlog.new_msg(msg_error, "%s", TXT_ERR_READUSERCONFIG);
		error_report("%s", TXT_ERR_READUSERCONFIG);
		delete g_pCfgUser;
		return -1;
	}
	
	//
	// user actions config
	//
	fp=get_config_filepath(g_appcfg.sdrvcfg.szCfgFileName);
	if (fp==NULL) {
		error_report("%s (%s)", TXT_ERR_MEMALLOC, strerror(errno));
		delete g_pCfgUser;
		return -1;
	}
	CConf *pCfgEv=new CConf;
	if (pCfgEv->init(fp)!=CONF_SUCCESS) {
		free(fp);
		g_evtlog.new_msg(msg_error, "%s", TXT_ERR_READUSERCONFIG);
		error_report("%s", TXT_ERR_READUSERCONFIG);
		delete pCfgEv;
		delete g_pCfgUser;
		return -1;
	} else {
		free(fp);
		pCfgEv->parse_config();
	}
	
	const int iNoOfItems=3;
	const char szCfgItems[iNoOfItems][128] = { "de%02d.id\0", "de%02d.name\0", "de%02d.action.index\0" };
	char szBuf[128] = "", *s;
	int iMaxItems=INT_MAX_PROGRAMEVENTS, iEventIdx=0;
	
	for (int iItem=0; iItem<iMaxItems; iItem++) {
		for (int i=0; i<iNoOfItems; i++) {
			sprintf(szBuf, szCfgItems[i], iItem);
			s=pCfgEv->getcfgitemvalue((char *)szBuf);
			if (s!=NULL && s!="") {
				switch(i) {
				case 0:
					g_usrcfg.spe[iEventIdx].ulId=strtoul(s, NULL, 16); 
					break;
				case 1:
					strcpy(g_usrcfg.spe[iEventIdx].szTitle, s); 
					break;
				case 2: {
						int iActIdx=atol(s);
						if (read_action_config(g_pCfgUser, iActIdx, &g_usrcfg.spe[iEventIdx])==0) {	// on success
							g_usrcfg.spe[iEventIdx].bEnabled=true;
							iEventIdx++;
						} else
							g_usrcfg.spe[iEventIdx].bEnabled=false;
					}
					break;
				}// switch(i)
			}// if (s!=NULL && s!="")
/*			if (i<2) {
				if (s==NULL || s=="")
					break;
				if (g_usrcfg.spe[iEventIdx].ulId==0) {
					g_usrcfg.spe[iEventIdx].bEnabled=false;
					break;
				}
			} else if (i==2 && s!=NULL && s!="")
				iEventIdx++;
*/
		}
	}
	g_usrcfg.iPeCount=iEventIdx;
 	delete pCfgEv;

	//
	// user network clients config
	//	
	fp=get_config_filepath(APP_FILE_NETCONFIG);
	if (fp==NULL) {
		error_report("%s (%s)", TXT_ERR_MEMALLOC, strerror(errno));
		delete g_pCfgUser;
		return -1;
	}
	CConf *pCfgNet=new CConf;
	if (pCfgNet->init(fp)!=CONF_SUCCESS) {
		free(fp);
		g_evtlog.new_msg(msg_error, "%s", TXT_ERR_READUSERCONFIG);
		error_report("%s", TXT_ERR_READUSERCONFIG);
		delete pCfgNet;
		delete g_pCfgUser;
		return -1;
	} else {
		free(fp);
		pCfgNet->parse_config();
	}
	//
	// network computers
	//
	g_usrcfg.iNcCount=0;
	for (int iItem=0; iItem<INT_MAX_NETCOMPUTERS; iItem++) {
		sprintf(szBuf, "nc%02d.name", iItem);
		s=pCfgNet->getcfgitemvalue((char *)szBuf);
		if (s!=NULL && strlen(s)) {
			strcpy(g_usrcfg.snc[g_usrcfg.iNcCount].szComputer, s);
			g_usrcfg.snc[g_usrcfg.iNcCount++].bEnabled=true;
		} else {
			continue;
		}
	}
	
 	delete pCfgNet;
	delete g_pCfgUser;
	
	return 0;	// success
}

int read_action_config(CConf *pCfg, int iIndex, sprogramevent *lpspe)
{
	const int iNoOfItems=16;
	const char szCfgItems[iNoOfItems][128] = { 
		"ua%02d.alert.enable\0", "ua%02d.alert.text\0", 
		"ua%02d.command.enable\0", "ua%02d.command.path\0", "ua%02d.command.directory\0", "ua%02d.command.params\0",
		"ua%02d.email.enable\0", "ua%02d.email.sender\0", "ua%02d.email.recipient\0", "ua%02d.email.subject\0", "ua%02d.email.content\0",
		"ua%02d.shutdown.enable\0", "ua%02d.shutdown.reboot\0", "ua%02d.shutdown.timeout\0", 
		"ua%02d.network.enable\0", "ua%02d.network.clients\0" };
	char szBuf[128], *s;
	
	for (int i=0; i<iNoOfItems; i++) {
		sprintf(szBuf, szCfgItems[i], iIndex);
		s=pCfg->getcfgitemvalue((char *)szBuf);
		if (s!=NULL && strlen(s)) {
			switch(i) {
			case 0:
				lpspe->sa.smsg.bEnabled = (bool)atoi(s);
				break;
			case 1:
				strcpy(lpspe->sa.smsg.szContent, s);
				break;
			case 2:
				lpspe->sa.scmd.bEnabled = (bool)atoi(s);
				break;
			case 3:
				strcpy(lpspe->sa.scmd.szPath, s);
				break;
			case 4:
				strcpy(lpspe->sa.scmd.szDirectory, s);
				break;
			case 5:
				strcpy(lpspe->sa.scmd.szParams, s);
				break;
			case 6:
				lpspe->sa.sem.bEnabled = (bool)atoi(s);
				break;
			case 7:
				strcpy(lpspe->sa.sem.szSender, s);
				break;
			case 8:
				strcpy(lpspe->sa.sem.szRecipient, s);
				break;
			case 9:
				strcpy(lpspe->sa.sem.szSubject, s);
				break;
			case 10:
				strcpy(lpspe->sa.sem.szContent, s);
				break;
			case 11:
				lpspe->sa.ssd.bEnabled = (bool)atoi(s);
				break;
			case 12:
				if ((bool)atoi(s))
					lpspe->sa.ssd.esm=sm_reboot;
				else
					lpspe->sa.ssd.esm=sm_normal;
				break;
			case 13:
				lpspe->sa.ssd.ulTimeoutSec = (unsigned long)strtoul(s, NULL, 10);
				break;
			case 14:
				lpspe->bNetwork = (bool)atoi(s);
				break;
			case 15:
				{
					lpspe->iNetCompsIdsCnt=0;
					char *tok=strtok(s, ",;");
					for (int icl=0; icl<INT_MAX_NETCOMPUTERS; icl++) {
						if (tok!=NULL && tok!="")
							lpspe->ulNetCompIds[lpspe->iNetCompsIdsCnt++]=atoi(tok);
						else
							break;
						tok=strtok(NULL, ",;");
					}
				}
				break;			
			}
		} else
			continue;
	}
	return 0;	// success
}

void print_device_parameters(lpsapp_config lpsac, lpsflow_ctl lpsfc)
{
	char szBatteryState[5][64]={
		TXT_PARAM_BS_CHARGING,TXT_PARAM_BS_FULL,TXT_PARAM_BS_DISCHARGING,
		TXT_PARAM_BS_DISCHARGED,TXT_PARAM_BS_DEPLETED};
	unsigned int *uiValTmp, *uiDivTmp;
	char szTmpVal[256];
	
	lpsparams_binder lpspb = new sparams_binder[INT_MAX_PROGRAMPARAMS];
	if (lpspb == NULL) {
		console_report("%s (%s)\n", TXT_ERR_MEMALLOC, strerror(errno));
		return;
	}
	
	printf("===========================================================\n");
	/* status */
	printf(TXT_DESCPARAM_CURRENTSTATE);
	size_t slength = strlen(TXT_DESCPARAM_CURRENTSTATE);
	for (int ispc=0; ispc<36-slength; ispc++) printf(" ");
	if (getUpsStateString(szTmpVal, lpsac->supsi.ulUpsStateMask, lpsac->supsi.ulUpsState, 0)==1) {
		printf(": %s\n", szTmpVal);
	} else {
		printf(": %s\n", TXT_EVENT_UNKNOWNSTATE);
	}
	/* alarm */
	printf(TXT_DESCPARAM_CURRENTALARM);
	slength = strlen(TXT_DESCPARAM_CURRENTALARM);
	for (int ispc=0; ispc<36-slength; ispc++) printf(" ");
	if (getUpsStateString(szTmpVal, lpsac->supsi.ulUpsStateMask, lpsac->supsi.ulUpsState, 1)==1) {
		printf(": %s\n", szTmpVal);
	} else {
		printf(": - \n");
	}
	if (lpsfc->bShutdownProcActive) {
	    /* shutdown timeout */
	    printf(TXT_DESCPARAM_SHUTDOWNSTATE);
	    slength = strlen(TXT_DESCPARAM_SHUTDOWNSTATE);
	    for (int ispc=0; ispc<36-slength; ispc++) printf(" ");
	    printf(": %ld sec\n", lpsfc->lShutdownTimeout);
	}
	if (fill_params_struct(lpspb, &lpsac->supsi.sUpsParameters) < INT_MAX_PROGRAMPARAMS)
		return;
	printf("-----------------------------------------------------------\n");
	for (int i = 0; i < INT_MAX_PROGRAMPARAMS; i++) {
		if (!flag_check(lpsac->supsi.sUpsParameters.iMask, lpspb[i].iId))
			continue;
		if (lpspb[i].bIntegral) {
			uiValTmp = (unsigned int *)(lpspb[i].lpVarPtr);
			uiDivTmp = (unsigned int *)(lpspb[i].lpDivPtr);
			if (uiDivTmp != NULL)
				if ((*uiDivTmp != 1) && (*uiDivTmp != -1))
					sprintf(szTmpVal, "%.1f", ((float)(*uiValTmp) / (float)(*uiDivTmp)));
				else
					sprintf(szTmpVal, "%.0f", ((float)(*uiValTmp) / (float)(*uiDivTmp)));
			else
				if (lpspb[i].iId == UP_BATTERYSTATE)
					sprintf(szTmpVal, "%s", (char *)szBatteryState[(*uiValTmp)]);
				else
					sprintf(szTmpVal, "%d", *uiValTmp);
		} else {
			if (lpspb[i].lpVarPtr!=NULL)
				sprintf(szTmpVal, "%s", (char *)lpspb[i].lpVarPtr);
		}
		printf("%s", lpspb[i].szName);
		if (strlen(lpspb[i].szName)<36)
			for (int ispc=0; ispc<36-strlen(lpspb[i].szName); ispc++) 
				printf(" ");
		printf(": %s ", szTmpVal);
		if (lpspb[i].bIntegral && lpspb[i].iId != UP_BATTERYSTATE)
			printf("[%s]\n", lpspb[i].szDesc);
		else
			printf("\n");
	}
	delete [] lpspb;
	printf("===========================================================\n");
}

int service_start()
{
	CEmail ceml;
	unsigned long ulBuff;
	int icnt, iReturn=0;	// buffer for return values
	pid_t actpid;		// action process pid
	key_t smkey;
	
	signal(SIGSEGV, terminate);
	signal(SIGINT, terminate);
	signal(SIGQUIT, terminate);
	signal(SIGTERM, terminate);

	g_evtlog.open(APP_FILE_EVENTLOG, APP_PSCORE);
	
	if (update_main_config()!=0) {
		goto __cleanup__;
	}

	if ((g_pDrvfn=get_ups_interface(g_appcfg.sdrvcfg.szName))==NULL) {
		g_evtlog.new_msg(msg_error, "%s", TXT_ERR_DRIVERREAD);
		console_report("%s", TXT_ERR_DRIVERREAD);
		goto __cleanup__;
	}
	
	ulBuff=sizeof(g_appcfg.sdrvcfg);
	iReturn=g_pDrvfn(IOCTL_GET_DRIVER_INFO, (void *)&g_appcfg.sdrvcfg, (int *)&ulBuff);
	if (iReturn!=IOCTL_ERROR_SUCCESS) {
		g_evtlog.new_msg(msg_error, TXT_ERR_GETDRIVERINFO);
		console_report(TXT_ERR_GETDRIVERINFO"\n");
		goto __cleanup__;
	}
	
	g_evtlog.new_msg(msg_info, TXT_SERVICE_STARTING);
	
	/* driver init */
	ulBuff=2;	// force comm initialization, shutdown if error
	iReturn=g_pDrvfn(IOCTL_INIT, &ulBuff, NULL);
	if (iReturn==IOCTL_ERROR_NOTYETCONFIGURED) {
		iReturn=g_pDrvfn(IOCTL_AUTOCONFIGURE, NULL, NULL);
		if (iReturn!=IOCTL_ERROR_SUCCESS) {
			g_bDrvInitialized=false;
			iReturn=g_pDrvfn(IOCTL_GET_ERRORNO, NULL, NULL);
			g_evtlog.new_msg(msg_error, TXT_ERR_DRIVERNOTCONFIGURED, iReturn);
			console_report(TXT_ERR_DRIVERNOTCONFIGURED"\n", iReturn);
			goto __cleanup__;
		} else {
			iReturn=g_pDrvfn(IOCTL_UNINIT, NULL, NULL);
			iReturn=g_pDrvfn(IOCTL_INIT, NULL, NULL);
		}
	}
	if (iReturn!=IOCTL_ERROR_SUCCESS) {
		iReturn=g_pDrvfn(IOCTL_GET_ERRORNO, NULL, NULL);
		g_evtlog.new_msg(msg_error, TXT_ERR_DRIVERINITFAIL, -1, iReturn);
		console_report(TXT_ERR_DRIVERINITFAIL"\n", -1, iReturn);
		g_bDrvInitialized=false;
		iReturn=g_pDrvfn(IOCTL_UNINIT, NULL, NULL);
		goto __cleanup__;
	} else
		g_bDrvInitialized=true;
	
	ulBuff=sizeof(g_appcfg.supsi.ulMask);
	iReturn=g_pDrvfn(IOCTL_GET_UPSINFOMASK, (void *)&g_appcfg.supsi.ulMask, (int *)&ulBuff);
	if (iReturn!=IOCTL_ERROR_SUCCESS) {
		iReturn=g_pDrvfn(IOCTL_GET_ERRORNO, NULL, NULL);
		g_evtlog.new_msg(msg_error, TXT_ERR_DRIVERINITFAIL, 0x01008, iReturn);
		console_report(TXT_ERR_DRIVERINITFAIL"\n", 0x01008, iReturn);
		goto __cleanup__;
	}
	
	if (update_main_config()!=0) {
		goto __cleanup__;
	}
	/* update user configuration */
	if (update_user_config()!=0) {
		goto __cleanup__;
	}
	
	g_appcfg.ulActFlag=0;
	g_appcfg.ulOpFlag=0;
	g_appcfg.iErrorCounter=0;
	
	/* start */
	g_afctl.bMonitorActive = true;
	g_afctl.bActionActive = true;
	g_afctl.bNetMgmtActive = true;
	g_afctl.iMonitorStatus = STATUS_INACTIVE;
	g_afctl.iActionStatus = STATUS_INACTIVE;
	g_afctl.iNetMgmtStatus = STATUS_INACTIVE;
	
	create_pid_file(APP_PSCORE);
	
	// create dummy shared mem - applicable always when system has no 
	// shared memory segments initialized, because shmem with id equal to 0 is created 
	// which is, in some way failing to work correctly
	{
		CShMem *pcDummy = new CShMem;
		if (pcDummy!=NULL) {
			if (pcDummy->create(0,1)!=SHM_FAILURE) {
				sleep(1);
				pcDummy->close();
			}
			delete pcDummy;
		}
	}
		
	// the real chared memory segment
	g_pShmMain=new CShMem;
	if (g_pShmMain->createkey(SHM_PATHNAME, SHM_PROJID)!=SHM_SUCCESS) {
		console_report("%s (%s)\n",TXT_ERR_SHAREMEMCREATE, strerror(errno));
		goto __cleanup__;
	} else {
		//console_report("SHM: %s : %c\n", SHM_PATHNAME, SHM_PROJID);
		if (g_pShmMain->create(g_iShmSizeConst)!=SHM_SUCCESS) {
			console_report("%s (%s)\n",TXT_ERR_SHAREMEMCREATE, strerror(errno));
			if (g_pShmMain->open(g_iShmSizeConst)!=SHM_SUCCESS) {
				goto __cleanup__;
			}
		}
		//console_report("SHM Created: shmid=%d; sem_id=%d\n", g_pShmMain->shm_id, g_pShmMain->sem_id);
	}
	UPDATEAPPCFG;
	UPDATEUSRCFG;
	UPDATEAPPFLOWCTL;
	
	iReturn=g_pDrvfn(IOCTL_TESTUPSLINK, NULL, NULL);
	if (iReturn!=IOCTL_ERROR_SUCCESS) {
		iReturn=g_pDrvfn(IOCTL_GET_ERRORNO, NULL, NULL);
		g_evtlog.new_msg(msg_error, TXT_ERR_UPSLINKFAIL, 0x01009, iReturn);
		console_report("%s", TXT_ERR_UPSLINKFAILSH);
		goto __cleanup__;
	} else {
		g_evtlog.new_msg(msg_info, TXT_ERR_UPSLINKOK, 0x0100A);
	}
	// Test if ups state monitoring is available and if not, just end the game :P
	if (!flag_check(g_appcfg.supsi.ulMask, UI_UPS_STATE)) {
		g_evtlog.new_msg(msg_critical, TXT_ERR_NOSTATEMONITORING, 0x0100B);
		goto __cleanup__;
	}

	if (flag_check(g_appcfg.supsi.ulMask, UI_CHARACTERISTICS)) {
		ulBuff=sizeof(g_appcfg.supsi.sUpsCharacteristic);
		iReturn=g_pDrvfn(IOCTL_GET_UPSCHARACTERISTIC, 
			(void *)&g_appcfg.supsi.sUpsCharacteristic, (int *)&ulBuff);
		if (iReturn!=IOCTL_ERROR_SUCCESS)
			g_evtlog.new_msg(msg_error, TXT_ERR_GETUPSPARAMS, 0x0100C);
	}
	
	// first contact
	getAllUpsData();
	checkUpsStates();

	if (!bQuietInit)
	    print_device_parameters(&g_appcfg, &g_afctl);
	
	// create some forms of life
	
	// action thread
	pthread_create(&g_pthAction, NULL, actions, (void*)(icnt=0));
	// net management thread
	//pthread_create(&g_pthNetMgmt, NULL, netManagement, NULL);
	// monitor loop
	monitor(NULL);

	// get rid of lives
	stop_monitoring();

__cleanup__:
	
	if (g_bDrvInitialized) {
		iReturn=g_pDrvfn(IOCTL_UNINIT, NULL, NULL);
		if (iReturn != IOCTL_ERROR_SUCCESS)
			g_evtlog.new_msg(msg_error, TXT_ERR_DRIVERUNINITFAIL, 0x01021);
		else
		    g_bDrvInitialized = false;
	}
	terminate(-1);
	
	return 0;
}

void stop_monitoring()
{
	const int timeout_sec = 10;
	
	if (g_afctl.iActionStatus!=STATUS_INACTIVE) {
		/* wait for action thread */
		if (g_afctl.bActionActive)
			g_afctl.bActionActive=false;
		int icnt=timeout_sec;
		while(g_afctl.iActionStatus!=STATUS_HASSTOPPED) {
			sleep(1);
			if ((icnt--)<=0)
				break;
		}
	}
	
	if (g_afctl.iMonitorStatus!=STATUS_INACTIVE) {
		/* wait for monitor */
		if (g_afctl.bMonitorActive)
			g_afctl.bMonitorActive=false;
		int icnt=timeout_sec;
		while(g_afctl.iMonitorStatus!=STATUS_HASSTOPPED) {
			sleep(1);
			if ((icnt--)<=0)
				break;
		}
	}
	
	if (g_afctl.iNetMgmtStatus!=STATUS_INACTIVE) {
		/* wait for net management thread */
		if (g_afctl.iNetMgmtStatus)
			g_afctl.iNetMgmtStatus=false;
		int icnt=timeout_sec;
		while(g_afctl.iNetMgmtStatus!=STATUS_HASSTOPPED) {
			sleep(1);
			if ((icnt--)<=0)
				break;
		}
	}
	
	if (g_bDrvInitialized) {
		int iReturn=g_pDrvfn(IOCTL_UNINIT, NULL, NULL);
		if (iReturn != IOCTL_ERROR_SUCCESS)
			g_evtlog.new_msg(msg_error, TXT_ERR_DRIVERUNINITFAIL, 0x01021);
		else
		    g_bDrvInitialized = false;
	}
}

void terminate(int iCode)
{
	rem_pid_file(APP_PSCORE);
	
	if (iCode!=-1)
		stop_monitoring();
	
	if (g_pShmMain!=NULL) {
		g_pShmMain->close();
		delete g_pShmMain;
	}
	if (iCode>-1) {
		g_evtlog.new_msg(msg_info, TXT_ERR_TERMINATION, iCode);
	}
	if (iCode>=-1) {
		g_evtlog.new_msg(msg_info, TXT_ERR_SERVICESTOPPED);
		console_report("\n%s\n", TXT_ERR_SERVICESTOPPED);
	}
	g_evtlog.close();
	if (iCode>-1) {
		exit(0);
	}
}

