--- /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();
+ }
+