systemhealthmanagement/systemhealthmgr/sysmonsrc/monitor.cpp
changeset 0 4e1aa6a622a0
child 21 ccb4f6b3db21
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/systemhealthmanagement/systemhealthmgr/sysmonsrc/monitor.cpp	Tue Feb 02 00:53:00 2010 +0200
@@ -0,0 +1,506 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include <e32svr.h>
+#include "monitor.h"
+#include "startsafe.h"
+#include "restartsys.h"
+#include "sysmonserver.h"
+#include "sysmoncliserv.h"
+#include "timerlist.h"
+#include "sysmon_patch.h"
+#include <startupproperties.h>
+
+#include "shmadebug.h"
+#include "shmapanic.h"
+#include <u32hal.h>
+
+const TInt CMonitor::iOffset = _FOFF(CMonitor, iSglQueLink);
+const TInt KDelayRequiredForRestartSys = 5000000;	 // required by RestartSys API, see comments in RestartSys::RestartSystem()
+
+#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+_LIT(KRestartSysProxyDLL, "restartsys.dll");
+_LIT(KCustomisedRestartSysProxyDLL, "custrestartsys.dll");
+typedef TInt (*TFuncRestartL)(void);
+typedef TInt (*TFuncRestartWithModeL)(TInt);
+#endif	//SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+
+TProcessId CMonitor::ProcessId() const
+	{
+	return iProcessId;
+	}	
+
+
+CMonitor* CMonitor::NewL(CSysMonServer& aServer, const TProcessId& aId, CStartupProperties* aStartupProperties, TBool aExecuteRecoveryMethodOnFailure)
+	{
+	ASSERT(aStartupProperties);
+	CMonitor *monitor = new(ELeave) CMonitor(aServer, aId);
+	CleanupStack::PushL(monitor);
+	monitor->ConstructL(aStartupProperties, aExecuteRecoveryMethodOnFailure);
+	CleanupStack::Pop(monitor);
+	return monitor;
+	}
+
+
+CMonitor::CMonitor(CSysMonServer& aServer, const TProcessId& aId)
+	:CActive(EPriorityHigh),
+	iSysMonServer(aServer),
+	iProcessId(aId),
+	iProcess(aId),
+	iLoadTime()
+	{
+	CActiveScheduler::Add(this);
+	}
+
+
+void CMonitor::ConstructL(CStartupProperties* aStartupProperties, TBool aExecuteRecoveryMethodOnFailure)
+	{
+	iLogonBackoffTimer = CLogonBackoffTimer::NewL( *this );
+	
+	User::LeaveIfError(iProcess.Open(iProcessId));
+	
+	// Don't support monitoring of system critical components as they will restart the device on failure anyway
+	User::TCritical critical = User::ProcessCritical(iProcess);
+	if (critical == User::ESystemCritical || critical == User::ESystemPermanent)
+		{
+		User::Leave(KErrNotSupported);
+		}
+	
+	//Make sure the process is still running
+	if((EExitPending == iProcess.ExitType()))
+		{
+		//Ensure the the filename in aStartupProperties is the same as in iProcess
+		//(not possible to read filename from a dead process).
+		TParse nameInProc, nameInProp;
+		nameInProc.SetNoWild(iProcess.FileName(),NULL,NULL);
+		nameInProp.SetNoWild(aStartupProperties->FileName(),NULL,NULL);
+		
+		if( nameInProc.Name().CompareF(nameInProp.Name()) )
+			{
+			User::Leave(KErrArgument);
+			}
+		}
+	else
+		{
+		//The process is already dead, either leave now or let this monitor recover the process
+		if(!aExecuteRecoveryMethodOnFailure)
+			User::Leave(KErrDied);
+		}	
+	
+	iReLaunchAttempts = 0;	// No. of re-launch attempts at the start of monitering would be zero.
+	iReLaunchIntervalTimer = CRelaunchIntervalTimer::NewL(*this);
+
+	// Can't leave after taking ownership of aStartupProperties 
+	iStartupProperties = aStartupProperties;
+	iLoadTime.UniversalTime();		
+	}
+
+
+CMonitor::~CMonitor()
+	{
+	delete iReLaunchIntervalTimer;
+	delete iLogonBackoffTimer;
+	
+	Cancel();
+	delete iStartupProperties;
+	iProcess.Close();
+#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+	iRestartSysLib.Close();
+#endif	//SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+	}
+
+
+void CMonitor::Start()
+	{
+	iLogonBackoffTimer->ProcessLogon();
+	}
+
+
+
+void CMonitor::DoCancel()
+	{
+#ifdef _DEBUG
+	TPtrC fileName = iStartupProperties->FileName();
+	DEBUGPRINT2(_L("SysMonMonitor: Monitor Cancelled for %S"), &(fileName));
+#endif
+
+	iProcess.LogonCancel(iStatus); // cancels monitoring
+	}
+
+
+void CMonitor::RestartProcessL()
+	{
+#ifdef _DEBUG
+	TPtrC fileName = iStartupProperties->FileName();
+#endif
+	DEBUGPRINT3(_L("SysMonMonitor: Going to restart %S, old process id=%u"), &fileName, iProcessId.Id());
+	
+	CStartSafe* startSafe = CStartSafe::NewL();	
+	CleanupStack::PushL(startSafe);
+	
+	// As the retries are now counted(after PREQ1871) over a period of time it does make sense for fire and forget processes to have a non-zero retries count.
+	__ASSERT_DEBUG((iStartupProperties->StartMethod() == EWaitForStart || iStartupProperties->StartMethod() == EFireAndForget), PanicNow(KPanicMonitor, EInvalidStartMethod));
+	
+	TInt propRetries = iStartupProperties->NoOfRetries();
+	TBool restoreRetries = EFalse;
+	
+	if(propRetries > 0)
+		{
+		// In the restart scenario we want StartSafe to make 'NoOfRetries' attempts
+		// rather than '1 + NoOfRetries' which it will otherwise do.
+		restoreRetries = ETrue;
+		iStartupProperties->SetNoOfRetries(--propRetries);	
+		}
+	
+	TInt retried = 0;
+	
+	// Attempt restart/s. Do not allow a leave until NoOfRetries has been restored.
+	TRAPD( err, startSafe->StartL(*iStartupProperties, iProcess, retried) );
+	
+	if(restoreRetries)
+		{
+		iStartupProperties->SetNoOfRetries(++propRetries);	
+		}
+		
+	User::LeaveIfError(err);
+	CleanupStack::PopAndDestroy(startSafe);	
+	DEBUGPRINT3(_L("SysMonMonitor: %S restarted, new iProcessId=%u. Logon to monitor again"), &fileName, iProcess.Id().Id());
+	
+	iProcessId = iProcess.Id();
+	iReLaunchAttempts++;	// Increment after each re-launch attempt.
+	if (!iReLaunchIntervalTimer->IsActive())
+		{
+		DEBUGPRINT3(_L("SysMonMonitor: ReLaunch Interval Timer is started for %S with processId %u"), &fileName, iProcessId.Id());
+		iReLaunchIntervalTimer->Start();
+		}
+	DEBUGPRINT3(_L("SysMonMonitor: ReLaunch Attempts for %S is %d"), &fileName, iReLaunchAttempts);
+	iLogonBackoffTimer->ProcessLogon();
+	}
+
+
+/**
+ This function is inherited from MLogonCallback and is called from CLogonBackoffTimer
+*/
+TInt CMonitor::DoProcessLogon()
+	{
+
+	iProcess.Logon( iStatus );
+	return ( (iStatus == KRequestPending) ? KErrNone : iStatus.Int() );
+	}
+
+
+
+/**
+ This function is inherited from MLogonCallback and is called from CLogonBackoffTimer
+*/
+void CMonitor::ActivateSelf()
+	{
+	
+	iLoadTime.UniversalTime();
+	SetActive();		
+	}
+	
+TInt CMonitor::Callback(TAny* aParent)
+	{		
+	CMonitor* monitor = reinterpret_cast<CMonitor*> (aParent);
+	DEBUGPRINT2(_L("SysMonMonitor: Finished waiting for throttle time, try to restart failed processId=%u"), monitor->iProcessId.Id());
+
+ 	 if (!monitor->HasExceededRateOfFailurePolicy())
+		{
+		TRAPD(err, monitor->RestartProcessL());
+		if (err != KErrNone)
+			{
+			// process failed to be started, cancel monitoring of this process
+			DEBUGPRINT2(_L("SysMonMonitor::RestartProcessL failed with err=%d, cancelling"), err);
+			monitor->CancelMonitor();
+			}
+		}
+	return KErrNone;
+	}
+	
+	
+/*
+CMonitor::RunL() gets called when a monitor process terminates.
+@panic ERestartSystemCallFailed if the RestartSystem call fails
+@panic ERestartSystemCallWithMode if the RestartSystem_with_mode call fails
+@panic EInvalidStartMethod if the Start mode is invalid
+*/
+void CMonitor::RunL()
+	{
+	DEBUGPRINT1(_L("SysMonMonitor: CMonitor::RunL() called"));
+	
+	iProcess.Close();	// closing the current handle
+	
+	TRecoveryMethod recoveryMethod = iStartupProperties->RecoveryMethod();
+	if (recoveryMethod == ECriticalNoRetries)
+		{
+#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+		// If the retry_failure_recovery_method is set to ECriticalNoRetries, then SysMon will restart
+		// the OS. It will not try to restart the process even if iStartupProperties->NoOfRetries() 
+		// is greater than zero.
+		RestartSysDll(EFalse);
+#else
+		TInt err = RestartSys::RestartSystem() ;	// restart the system
+
+		if (KErrNone != err)
+			{
+			DEBUGPRINT2(_L("Shma: RestartSystem error %d"), err);
+			PanicNow(KPanicMonitor, ERestartSystemCallFailed);
+			}
+		User::After(KDelayRequiredForRestartSys);
+#endif	//SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+		}
+	else
+		{
+		if (iStartupProperties->NoOfRetries() == 0)
+			{
+			// If NoOfRetries() == 0, execute recovery method immediately.
+			FailureRecoveryPolicy();
+			}
+		else
+			{
+			TTime curTime;
+			curTime.UniversalTime(); // current time, can be considered as the time of termination for the process
+			
+			TTime thresholdTime = (iLoadTime + TTimeIntervalMicroSeconds32(KWaitTime)); // time, till when no restart should take place
+			
+			if (curTime < thresholdTime)
+				{	
+				// Implies process terminated less than KWaitTime since the last launch of the process
+				// So to reduce Denial of Service we wait the remaining time of KWaitTime.
+	#ifdef _DEBUG
+				TPtrC fileName = iStartupProperties->FileName();
+				DEBUGPRINT2(_L("SysMonMonitor: Wait for throttle time before restarting process %S"), &(fileName));
+	#endif
+				iSysMonServer.TimerListL().AddL(thresholdTime, TCallBack(Callback, this));
+				}
+			else
+				{
+	  			 if (!HasExceededRateOfFailurePolicy())
+					RestartProcessL();
+				}
+			}
+		}
+	}
+
+
+void CMonitor::FailureRecoveryPolicy()
+	{
+	TRecoveryMethod recoveryMethod = iStartupProperties->RecoveryMethod();
+	DEBUGPRINT2(_L("SysMonMonitor: Process failed RecoveryMethod=%d"), recoveryMethod);
+	if (recoveryMethod == ERestartOS)
+		{
+#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+		RestartSysDll(EFalse);
+#else
+		TInt err = RestartSys::RestartSystem() ;	// restart the system
+
+		if (KErrNone != err)
+			{
+			DEBUGPRINT2(_L("Shma: RestartSystem error %d"), err);
+			PanicNow(KPanicMonitor, ERestartSystemCallFailed);
+			}
+
+		User::After(KDelayRequiredForRestartSys);
+#endif	//SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+		}			
+	else if (recoveryMethod == ERestartOSWithMode)
+		{
+#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+		RestartSysDll(ETrue, iStartupProperties->RestartMode());	// restart system in a mode
+#else
+		TInt err = RestartSys::RestartSystem(iStartupProperties->RestartMode()) ;	// restart system in a mode
+
+		if (KErrNone != err)
+			{
+			DEBUGPRINT2(_L("Shma: RestartSystem with mode error %d"), err);
+			PanicNow(KPanicMonitor, ERestartSystemCallWithMode);
+			}
+
+		User::After(KDelayRequiredForRestartSys);
+#endif	//SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+		}
+	else
+		{
+		// ignore on failure, cancel monitoring of this process
+		CancelMonitor();
+		}
+	}
+
+TBool CMonitor::HasExceededRateOfFailurePolicy()
+	{
+	if (iReLaunchAttempts > iStartupProperties->NoOfRetries())
+		{
+		// If the component's failure rate exceeds that set for the component, no new re-launch attempts will be 
+		// performed on the component and failure recovery policy will be enacted.
+		DEBUGPRINT1(_L("SysMonMonitor: Failure Recovery Policy is enacted as component's failure rate exceeded that set for the component"));
+		FailureRecoveryPolicy();
+		return ETrue;
+		}
+	else
+		return EFalse;
+	}
+
+TInt CMonitor::RunError(TInt aError)
+	{
+	DEBUGPRINT2(_L("SysMonMonitor: RunError called with error=%d, cancelling"), aError);
+	// process failed to be started, cancel monitoring of this process
+	CancelMonitor();
+	
+	// we are returning KErrNone, as the error returned by RunL() has been handled by cancelling the monitor.
+	aError = KErrNone;
+	return aError;
+	}
+
+void CMonitor::CancelMonitor()
+	{
+	DEBUGPRINT2(_L("SysMonMonitor: CMonitor cancelling monitor with iProcessId=%u"), iProcessId.Id());
+	iSysMonServer.CancelMonitor(iProcessId);
+	}
+
+void CMonitor::DecrementRelaunchAttempts()
+	{
+	 // Decrement the re-launch attempts at regular interval(interval defined by KIntervalForReLaunchRateOfFailure)
+	TInt relaunchAttempts = (iReLaunchAttempts) ? (--iReLaunchAttempts): 0;
+	TPtrC fileName = iStartupProperties->FileName();
+	DEBUGPRINT3(_L("SysMonMonitor: Decremented ReLaunch Attempts for %S to %d"), &fileName, relaunchAttempts);
+
+	if (!relaunchAttempts)
+		{
+		DEBUGPRINT2(_L("SysMonMonitor: Cancelled ReLaunch Interval Timer with processId=%u"), iProcessId.Id());
+		iReLaunchIntervalTimer->Cancel();
+		}
+	else
+		{
+		// Start the relaunch interval timer again if the relaunch attempts is greater than 0.
+		iReLaunchIntervalTimer->Start();
+		}
+	}
+
+#ifdef SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+void CMonitor::RestartSysDll(TBool aUseStartupMode, TInt aStartupMode)
+	{
+	TInt err = KErrNone;
+	if (!iRestartSysLoaded)
+		{
+		err = iRestartSysLib.Load(KCustomisedRestartSysProxyDLL);
+		// if customised restartsys dll is not provided, we try to load the default
+		// restartsys dll
+		if(KErrNotFound == err)
+			{
+			DEBUGPRINT1(_L("SysMonMonitor: Loading default restartsys.dll as custrestartsys.dll is not found"));
+			err = iRestartSysLib.Load(KRestartSysProxyDLL);
+			}
+		if(KErrNone != err)
+	 		{
+			// restartsys dll (customised or defaut) should be present for ERestartOS and ERestartOSWithMode 
+			// recovery policies we panic here if it is not present
+			DEBUGPRINT2(_L("CustRestartSys/ RestartSys failed to load - Error Code= %d"), err);
+	 		PanicNow(KPanicMonitor, ERestartSysLibNotPresent);
+	 		}
+		iRestartSysLoaded = ETrue;
+		}
+
+	if (aUseStartupMode)
+		{
+		TFuncRestartWithModeL restartSys = reinterpret_cast<TFuncRestartWithModeL>(iRestartSysLib.Lookup(1));
+		if (restartSys == NULL)
+			{
+			DEBUGPRINT1(_L("CStartSafe: Ordinal Lookup Error\n Expected Function prototype: RestartSystem(TInt aStartupMode)"));
+			User::Leave(KErrBadLibraryEntryPoint);
+			}
+		err = restartSys(aStartupMode);	// restart the system with the given startup mode
+		}
+	else
+		{
+		TFuncRestartL restartSys = reinterpret_cast<TFuncRestartL>(iRestartSysLib.Lookup(2));
+		if (restartSys == NULL)
+			{
+			DEBUGPRINT1(_L("CStartSafe: Ordinal Lookup Error\n Expected Function prototype: RestartSystem()"));
+			User::Leave(KErrBadLibraryEntryPoint);
+			}
+		err = restartSys();	// restart the system
+		}
+
+	if (KErrNone != err)
+		{
+		DEBUGPRINT2(_L("Shma: RestartSystem error %d"), err);
+		if (aStartupMode == 0)
+			{
+			PanicNow(KPanicMonitor, ERestartSystemCallFailed);
+			}
+		else
+			{
+			PanicNow(KPanicMonitor, ERestartSystemCallWithMode);
+			}
+		}
+
+	User::After(KDelayRequiredForRestartSys);
+	}
+#endif	//SYMBIAN_SSM_GRACEFUL_SHUTDOWN
+
+CRelaunchIntervalTimer* CRelaunchIntervalTimer::NewL(CMonitor& aMonitor)
+	{
+	CRelaunchIntervalTimer* self=new (ELeave) CRelaunchIntervalTimer(aMonitor);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CRelaunchIntervalTimer::~CRelaunchIntervalTimer()
+	{
+	Cancel();
+	}
+
+CRelaunchIntervalTimer::CRelaunchIntervalTimer(CMonitor& aMonitor)
+		: CTimer(EPriorityStandard), iMonitor(&aMonitor)
+	{
+	}
+
+void CRelaunchIntervalTimer::ConstructL()
+	{
+	CTimer::ConstructL();
+	CActiveScheduler::Add(this);
+	}
+
+void CRelaunchIntervalTimer::Start()
+	{
+	DEBUGPRINT1(_L("SysMonMonitor: Start the relaunch interval timer"));
+	TTime curTime;
+	curTime.UniversalTime();
+	
+	TInt intervalForReLaunchRateOfFailure = KIntervalForReLaunchRateOfFailure;
+#ifdef __WINS__
+// KIntervalForReLaunchRateOfFailure is a Rom patchable constant, so need an emulator equivalent
+// if WINS then read value from epoc.ini requires licencees to set property in epoc.ini. This value is taking in secs.
+// Usage: In epoc.ini patchdata_sysmon_exe_KIntervalForReLaunchRateOfFailure 25
+
+	TInt valueOfKIntervalForReLaunchRateOfFailure = 0;
+	if (UserSvr::HalFunction(EHalGroupEmulator,EEmulatorHalIntProperty,(TAny*)"patchdata_sysmon_exe_KIntervalForReLaunchRateOfFailure",&valueOfKIntervalForReLaunchRateOfFailure) == KErrNone)
+		{
+		intervalForReLaunchRateOfFailure = valueOfKIntervalForReLaunchRateOfFailure;
+		}
+#endif
+	
+	const TTime reLaunchIntervalTime = (curTime + TTimeIntervalSeconds(intervalForReLaunchRateOfFailure));
+	AtUTC(reLaunchIntervalTime);
+	}
+
+void CRelaunchIntervalTimer::RunL()
+	{
+	iMonitor->DecrementRelaunchAttempts();
+	}
+