sysstatemgmt/systemstarter/src/multiplewait2.cpp
changeset 0 4e1aa6a622a0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sysstatemgmt/systemstarter/src/multiplewait2.cpp	Tue Feb 02 00:53:00 2010 +0200
@@ -0,0 +1,359 @@
+// 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 <startupproperties.h>
+
+#include "multiplewait2.h"
+#include "appstarter2.h"
+#include "sysmonclisess.h"
+#include "restartsys.h"
+
+#include "SysStartDebug.h"
+#include "sysstartpanic.h"
+
+
+// 
+// Standard Symbian factory functions 
+// 
+
+CMultipleWait* CMultipleWait::NewL(CommandList& aDeferredList, CStartupUtilProvider& aProvider)
+	{
+	CMultipleWait* self = NewLC(aDeferredList, aProvider);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CMultipleWait* CMultipleWait::NewLC(CommandList& aDeferredList, CStartupUtilProvider& aProvider)
+	{
+	CMultipleWait* self = new (ELeave) CMultipleWait(aProvider);	
+	CleanupStack::PushL(self);	 
+	self->ConstructL(aDeferredList);
+	return self;
+	}
+
+CMultipleWait::CMultipleWait(CStartupUtilProvider& aProvider)
+	:iProvider(aProvider)
+	{
+	}
+	
+void CMultipleWait::ConstructL(CommandList& aDeferredList)
+	{
+	TInt size = aDeferredList.Count();
+
+	TInt i=0;
+	for (; i<size; i++)
+		{
+		iDeferredList.AppendL(aDeferredList[i]);
+		}
+	}
+
+CMultipleWait::~CMultipleWait()
+	{ 
+  	iDeferredList.Reset();
+	}
+
+// 
+// Public member functions
+//
+void CMultipleWait::SetFailOnError(TBool aFail)
+	{
+	iFailOnError = aFail;
+	}
+
+void CMultipleWait::SetTimeout(TInt aTimeout)
+	{
+	iTimeout = aTimeout;
+	}
+	
+/** The timeout value indicates when to kill a command 
+that's taking too long to initialize.
+A value of zero means "do not time this command out,
+wait indefinitely".
+*/
+TInt CMultipleWait::Timeout() const
+	{
+	return iTimeout;
+	}
+
+/** Whether startup should fail or continue if a command 
+doesn't succeed
+*/
+TBool CMultipleWait::FailOnError() const
+	{
+	return iFailOnError;
+	}
+
+
+
+/** Implementation of MStartupCommand interface.
+
+@see MStartupCommand.
+*/
+void CMultipleWait::Execute(TRequestStatus& aStatus)
+	{
+	aStatus = KRequestPending;
+ 	 	
+  	// Start timer going if required. A timer value of 0 means that no timer
+  	// will be started and we simply wait until all commands have completed.
+  	TRequestStatus timerStatus = KRequestPending; 
+  	RTimer timer;
+  	timer.CreateLocal();
+    if (iTimeout != 0)
+  		{
+		timer.After(timerStatus, TTimeIntervalMicroSeconds32(iTimeout*1000));
+		}
+
+	// The MULTIPLE_WAIT command in the SSC can specify either
+	// fail_on_error == EIgnoreCommandFailure(0) - if a command fails just 
+	// carry on with the next one.
+	// fail_on_error == EPanicOnCommandFailure(1) - Restart system as soon as a command fails
+	// or the timer expires.
+ 
+	if (iFailOnError == EPanicOnCommandFailure)
+	 	{
+	 	CheckCommandsForRestart(timerStatus);
+	 	}
+	else // iFailOnError == EIgnoreCommandFailure
+		{
+		CheckCommandsAndIgnore(timerStatus);
+		}
+
+  
+ 	// Cancel the timer if it has been started and is still running. 			
+ 	if ((iTimeout != 0) && (timerStatus == KRequestPending))
+  		{  	
+ 		timer.Cancel();
+		User::WaitForRequest(timerStatus);  
+		}
+	timer.Close();  
+ 
+	// signal KErrNone to caller because all process fail have be ignored by now or the system restarted
+	TRequestStatus* requestStatus = &aStatus;
+	User::RequestComplete(requestStatus, KErrNone);
+	}
+
+
+/** This is the helper function to check all the deferred commands when fail_on_error is EIgnoreCommandFailure.
+    In this case, if the process is finished before RTimer expire (indicated by aTimerStatus), 
+    the process can be added to System Monitor if it was started sucessfully.
+    If the process was not started succfully, we ignore the process failure.
+    
+    @param aTimerStatus This is the TReauestStatus of RTimer that this MULTIPLE_WAIT command is waiting on
+*/
+void CMultipleWait::CheckCommandsAndIgnore(TRequestStatus& aTimerStatus)
+	{
+	TBool timerExpired = EFalse;
+	
+	// Each deferred command will have been invoked and stored in a deferred 
+ 	// list. Need to process each command object in the list checking if each has
+ 	// been initialized i.e. rendezvoused.
+ 	TInt i = 0;
+	for (; i < iDeferredList.Count(); i++)
+		{
+		// Get the next command 
+ 		CAppStarter* commandPtr = static_cast<CAppStarter*>(iDeferredList [i]);  
+  	 		
+#ifdef _DEBUG  	 		
+ 		TParsePtrC parser(commandPtr->AppInfo().FileName());
+		TPtrC commandName = parser.Name();
+		DEBUGPRINT2(_L("SysStart: checking status of deferred command %S"), &commandName);
+#endif			
+			
+		// Some deferred commands may have failed in creation. So don't wait for
+ 		// them to rendezvous.
+		if (commandPtr->Process().Handle() > 0)
+			{
+			
+ 			// Once a timer expires all waiting for commands stops.
+ 			// The status of any commands not yet processed in the list is checked.
+ 			// Any that have not yet completed successfully are killed. 
+ 			if (!timerExpired)
+ 				{
+ 				TRequestStatus& commandStatus = commandPtr->CommandStatus();
+ 				User::WaitForRequest(aTimerStatus, commandStatus);
+ 				if (aTimerStatus != KRequestPending) 
+ 					{
+ 					timerExpired = ETrue;
+ 					if (commandStatus != KErrNone)
+						{	 
+						// Process timed out before reaching a rendezvous point. Kill
+						// the process and set the timerExpired variable so that no 
+						// other commands are waited for.
+						DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());
+						commandPtr->Process().Kill(KErrTimedOut);
+						User::WaitForRequest(commandStatus);
+						}
+					else 
+						{
+						User::WaitForRequest(commandStatus);
+						DEBUGPRINT2(_L("SysStart: successful rendezvous of deferred command %S"), &commandName);		
+						TRAPD(err, RegisterMonitoringL(*commandPtr));
+						// if monitoring failed, kill the process because start and monitor is expected to succeed or fail as one action
+						if (err != KErrNone)
+							{
+							DEBUGPRINT3(_L("SysStart: %S failed to register for monitor, err is %d "), &commandName, err);
+							commandPtr->Process().Kill(err);
+							}
+						}
+ 					}
+				}
+			else // timer has expired, just do clean up.
+				{
+				// If the command has not already rendezvoused successfully then 
+				// kill the process	
+				TRequestStatus& commandStatus = commandPtr->CommandStatus();
+				if (commandStatus != KErrNone)  
+					{	
+					DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());	
+					commandPtr->Process().Kill(KErrTimedOut);
+					User::WaitForRequest(commandStatus);
+					}
+				else
+					{
+					User::WaitForRequest(commandStatus);
+					DEBUGPRINT2(_L("SysStart: successful rendezvous of deferred command %S"), &commandName);		
+					TRAPD(err, RegisterMonitoringL(*commandPtr));
+					// if monitoring failed, kill the process because start and monitor is expected to succeed or fail as one action
+					if (err != KErrNone)
+						{
+						DEBUGPRINT3(_L("SysStart: %S failed to register for monitor, err is %d "), &commandName, err);
+						commandPtr->Process().Kill(err);
+						}
+					}
+				}
+			}
+		else
+			{
+			DEBUGPRINT2(_L("SysStart: command %S not created so no rendezvous check"), &commandName);		
+			}
+ 		 }
+	}
+
+/** This is the helper function to check all the deferred commands when fail_on_error is EPanicOnCommandFailure.
+    In this case, if the process failed for any reason (either before or after RTime expire), the system
+    will be restarted base on the process/application starting command.
+    Otherwise, the process will be added to System Monitor for monitoring if required.
+    
+    @param aTimerStatus This is the TReauestStatus of RTimer that this MULTIPLE_WAIT command is waiting on
+*/
+void CMultipleWait::CheckCommandsForRestart(TRequestStatus& aTimerStatus)
+	{
+ 	// Each deferred command will have been invoked and stored in a deferred 
+ 	// list. Need to process each command in the list checking if each has
+ 	// been initialized i.e. rendezvoused.
+ 	TInt i = 0;
+	for (; i < iDeferredList.Count(); i++)
+		{
+		// Get the next command 
+ 		CAppStarter* commandPtr = static_cast<CAppStarter*>(iDeferredList[i]); 
+ 		
+#ifdef _DEBUG  	 		
+ 		TParsePtrC parser(commandPtr->AppInfo().FileName());
+		TPtrC commandName = parser.Name();
+		DEBUGPRINT2(_L("SysStart: checking status of deferred command %S"), &commandName);
+#endif			
+				
+ 		if (commandPtr->Process().Handle() > 0)
+ 			{			
+ 			// Wait for either the command to complete the rendezvous or the timer
+ 			// to expire. (If no timer has been started this code will just wait for the 
+ 			// command to complete).
+ 			TRequestStatus& commandStatus = commandPtr->CommandStatus();
+ 			User::WaitForRequest(aTimerStatus, commandStatus);
+ 			
+ 			if ((aTimerStatus != KRequestPending) && (commandStatus != KErrNone))
+				{ 
+				// Command has timed out. fail_on_error is set to EPanicOnCommandFailure
+				// so restart system immediately.
+				DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());		
+  	 			RestartSystem(commandPtr->AppInfo());
+				}
+			else
+				{
+			 	// Command has completed. Check that the command has completed successfully. 
+				// If there is an error restart system immediately.
+				if (commandStatus != KErrNone)
+					{
+					DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());		
+	  	 			RestartSystem(commandPtr->AppInfo());
+					}
+				else
+					{
+					DEBUGPRINT2(_L("SysStart: successful rendezvous of deferred command %S "), &commandName);
+					TRAPD(err, RegisterMonitoringL(*commandPtr));
+
+					// if monitoring failed, restart system because start and monitor is expected to succeed or fail as one action
+					if (err != KErrNone)
+						{
+						DEBUGPRINT3(_L("SysStart: %S failed to register for monitor, err is %d "), &commandName, err);
+						RestartSystem(commandPtr->AppInfo());
+						}
+					}
+				}
+ 			}
+ 		else
+ 			{
+ 			// the command already failed in creation, just restart system
+ 			DEBUGPRINT2(_L("SysStart: command %S not created so no rendezvous check"), &commandName);
+ 			RestartSystem(commandPtr->AppInfo());
+ 			}
+
+		}
+	}
+
+
+/** Helper function to restart system according to the process/application start command properties
+	@param aStartupProperties The startup properties for restarting system
+*/
+void CMultipleWait::RestartSystem(const CStartupProperties& aStartupProperties)
+	{
+	TInt err = KErrNone;
+	if (aStartupProperties.RecoveryMethod() == ERestartOSWithMode)
+		{
+		err = RestartSys::RestartSystem(aStartupProperties.RestartMode());
+		}
+	else
+		{
+		err = RestartSys::RestartSystem();
+		}
+
+	if (KErrNone != err)
+		{
+ 		DEBUGPRINT2(_L("SysStart: RestartSystem error = %d"), err);
+		PanicNow(KPanicMultipleWait, ERestartSystemCallFailed);
+		}
+
+	User::After(5000000); // required by RestartSys API, see comments in RestartSys::RestartSystem()
+	}
+
+/** Helper function to register for monitoring after a process has been started successfully
+	@param aStartCommand The command that has been started successfully
+*/
+void CMultipleWait::RegisterMonitoringL(CAppStarter& aStartCommand)
+	{
+	if (aStartCommand.AppInfo().Monitored())
+		{
+		// When a deferred wait for start process needs to be monitord, the start method needs to 
+		// be changed to EFireAndForget because it cannot be deferred when it die after being monitored
+		aStartCommand.AppInfo().SetStartMethod(EFireAndForget);
+		
+		iProvider.SysMonSessionL().MonitorL(aStartCommand.AppInfo(), aStartCommand.Process());
+		}
+	}
+
+void CMultipleWait::Release() 
+	{
+	delete this;
+	}