sysstatemgmt/systemstarter/src/multiplewait2.cpp
changeset 0 4e1aa6a622a0
equal deleted inserted replaced
-1:000000000000 0:4e1aa6a622a0
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include <startupproperties.h>
       
    17 
       
    18 #include "multiplewait2.h"
       
    19 #include "appstarter2.h"
       
    20 #include "sysmonclisess.h"
       
    21 #include "restartsys.h"
       
    22 
       
    23 #include "SysStartDebug.h"
       
    24 #include "sysstartpanic.h"
       
    25 
       
    26 
       
    27 // 
       
    28 // Standard Symbian factory functions 
       
    29 // 
       
    30 
       
    31 CMultipleWait* CMultipleWait::NewL(CommandList& aDeferredList, CStartupUtilProvider& aProvider)
       
    32 	{
       
    33 	CMultipleWait* self = NewLC(aDeferredList, aProvider);
       
    34 	CleanupStack::Pop(self);
       
    35 	return self;
       
    36 	}
       
    37 
       
    38 CMultipleWait* CMultipleWait::NewLC(CommandList& aDeferredList, CStartupUtilProvider& aProvider)
       
    39 	{
       
    40 	CMultipleWait* self = new (ELeave) CMultipleWait(aProvider);	
       
    41 	CleanupStack::PushL(self);	 
       
    42 	self->ConstructL(aDeferredList);
       
    43 	return self;
       
    44 	}
       
    45 
       
    46 CMultipleWait::CMultipleWait(CStartupUtilProvider& aProvider)
       
    47 	:iProvider(aProvider)
       
    48 	{
       
    49 	}
       
    50 	
       
    51 void CMultipleWait::ConstructL(CommandList& aDeferredList)
       
    52 	{
       
    53 	TInt size = aDeferredList.Count();
       
    54 
       
    55 	TInt i=0;
       
    56 	for (; i<size; i++)
       
    57 		{
       
    58 		iDeferredList.AppendL(aDeferredList[i]);
       
    59 		}
       
    60 	}
       
    61 
       
    62 CMultipleWait::~CMultipleWait()
       
    63 	{ 
       
    64   	iDeferredList.Reset();
       
    65 	}
       
    66 
       
    67 // 
       
    68 // Public member functions
       
    69 //
       
    70 void CMultipleWait::SetFailOnError(TBool aFail)
       
    71 	{
       
    72 	iFailOnError = aFail;
       
    73 	}
       
    74 
       
    75 void CMultipleWait::SetTimeout(TInt aTimeout)
       
    76 	{
       
    77 	iTimeout = aTimeout;
       
    78 	}
       
    79 	
       
    80 /** The timeout value indicates when to kill a command 
       
    81 that's taking too long to initialize.
       
    82 A value of zero means "do not time this command out,
       
    83 wait indefinitely".
       
    84 */
       
    85 TInt CMultipleWait::Timeout() const
       
    86 	{
       
    87 	return iTimeout;
       
    88 	}
       
    89 
       
    90 /** Whether startup should fail or continue if a command 
       
    91 doesn't succeed
       
    92 */
       
    93 TBool CMultipleWait::FailOnError() const
       
    94 	{
       
    95 	return iFailOnError;
       
    96 	}
       
    97 
       
    98 
       
    99 
       
   100 /** Implementation of MStartupCommand interface.
       
   101 
       
   102 @see MStartupCommand.
       
   103 */
       
   104 void CMultipleWait::Execute(TRequestStatus& aStatus)
       
   105 	{
       
   106 	aStatus = KRequestPending;
       
   107  	 	
       
   108   	// Start timer going if required. A timer value of 0 means that no timer
       
   109   	// will be started and we simply wait until all commands have completed.
       
   110   	TRequestStatus timerStatus = KRequestPending; 
       
   111   	RTimer timer;
       
   112   	timer.CreateLocal();
       
   113     if (iTimeout != 0)
       
   114   		{
       
   115 		timer.After(timerStatus, TTimeIntervalMicroSeconds32(iTimeout*1000));
       
   116 		}
       
   117 
       
   118 	// The MULTIPLE_WAIT command in the SSC can specify either
       
   119 	// fail_on_error == EIgnoreCommandFailure(0) - if a command fails just 
       
   120 	// carry on with the next one.
       
   121 	// fail_on_error == EPanicOnCommandFailure(1) - Restart system as soon as a command fails
       
   122 	// or the timer expires.
       
   123  
       
   124 	if (iFailOnError == EPanicOnCommandFailure)
       
   125 	 	{
       
   126 	 	CheckCommandsForRestart(timerStatus);
       
   127 	 	}
       
   128 	else // iFailOnError == EIgnoreCommandFailure
       
   129 		{
       
   130 		CheckCommandsAndIgnore(timerStatus);
       
   131 		}
       
   132 
       
   133   
       
   134  	// Cancel the timer if it has been started and is still running. 			
       
   135  	if ((iTimeout != 0) && (timerStatus == KRequestPending))
       
   136   		{  	
       
   137  		timer.Cancel();
       
   138 		User::WaitForRequest(timerStatus);  
       
   139 		}
       
   140 	timer.Close();  
       
   141  
       
   142 	// signal KErrNone to caller because all process fail have be ignored by now or the system restarted
       
   143 	TRequestStatus* requestStatus = &aStatus;
       
   144 	User::RequestComplete(requestStatus, KErrNone);
       
   145 	}
       
   146 
       
   147 
       
   148 /** This is the helper function to check all the deferred commands when fail_on_error is EIgnoreCommandFailure.
       
   149     In this case, if the process is finished before RTimer expire (indicated by aTimerStatus), 
       
   150     the process can be added to System Monitor if it was started sucessfully.
       
   151     If the process was not started succfully, we ignore the process failure.
       
   152     
       
   153     @param aTimerStatus This is the TReauestStatus of RTimer that this MULTIPLE_WAIT command is waiting on
       
   154 */
       
   155 void CMultipleWait::CheckCommandsAndIgnore(TRequestStatus& aTimerStatus)
       
   156 	{
       
   157 	TBool timerExpired = EFalse;
       
   158 	
       
   159 	// Each deferred command will have been invoked and stored in a deferred 
       
   160  	// list. Need to process each command object in the list checking if each has
       
   161  	// been initialized i.e. rendezvoused.
       
   162  	TInt i = 0;
       
   163 	for (; i < iDeferredList.Count(); i++)
       
   164 		{
       
   165 		// Get the next command 
       
   166  		CAppStarter* commandPtr = static_cast<CAppStarter*>(iDeferredList [i]);  
       
   167   	 		
       
   168 #ifdef _DEBUG  	 		
       
   169  		TParsePtrC parser(commandPtr->AppInfo().FileName());
       
   170 		TPtrC commandName = parser.Name();
       
   171 		DEBUGPRINT2(_L("SysStart: checking status of deferred command %S"), &commandName);
       
   172 #endif			
       
   173 			
       
   174 		// Some deferred commands may have failed in creation. So don't wait for
       
   175  		// them to rendezvous.
       
   176 		if (commandPtr->Process().Handle() > 0)
       
   177 			{
       
   178 			
       
   179  			// Once a timer expires all waiting for commands stops.
       
   180  			// The status of any commands not yet processed in the list is checked.
       
   181  			// Any that have not yet completed successfully are killed. 
       
   182  			if (!timerExpired)
       
   183  				{
       
   184  				TRequestStatus& commandStatus = commandPtr->CommandStatus();
       
   185  				User::WaitForRequest(aTimerStatus, commandStatus);
       
   186  				if (aTimerStatus != KRequestPending) 
       
   187  					{
       
   188  					timerExpired = ETrue;
       
   189  					if (commandStatus != KErrNone)
       
   190 						{	 
       
   191 						// Process timed out before reaching a rendezvous point. Kill
       
   192 						// the process and set the timerExpired variable so that no 
       
   193 						// other commands are waited for.
       
   194 						DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());
       
   195 						commandPtr->Process().Kill(KErrTimedOut);
       
   196 						User::WaitForRequest(commandStatus);
       
   197 						}
       
   198 					else 
       
   199 						{
       
   200 						User::WaitForRequest(commandStatus);
       
   201 						DEBUGPRINT2(_L("SysStart: successful rendezvous of deferred command %S"), &commandName);		
       
   202 						TRAPD(err, RegisterMonitoringL(*commandPtr));
       
   203 						// if monitoring failed, kill the process because start and monitor is expected to succeed or fail as one action
       
   204 						if (err != KErrNone)
       
   205 							{
       
   206 							DEBUGPRINT3(_L("SysStart: %S failed to register for monitor, err is %d "), &commandName, err);
       
   207 							commandPtr->Process().Kill(err);
       
   208 							}
       
   209 						}
       
   210  					}
       
   211 				}
       
   212 			else // timer has expired, just do clean up.
       
   213 				{
       
   214 				// If the command has not already rendezvoused successfully then 
       
   215 				// kill the process	
       
   216 				TRequestStatus& commandStatus = commandPtr->CommandStatus();
       
   217 				if (commandStatus != KErrNone)  
       
   218 					{	
       
   219 					DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());	
       
   220 					commandPtr->Process().Kill(KErrTimedOut);
       
   221 					User::WaitForRequest(commandStatus);
       
   222 					}
       
   223 				else
       
   224 					{
       
   225 					User::WaitForRequest(commandStatus);
       
   226 					DEBUGPRINT2(_L("SysStart: successful rendezvous of deferred command %S"), &commandName);		
       
   227 					TRAPD(err, RegisterMonitoringL(*commandPtr));
       
   228 					// if monitoring failed, kill the process because start and monitor is expected to succeed or fail as one action
       
   229 					if (err != KErrNone)
       
   230 						{
       
   231 						DEBUGPRINT3(_L("SysStart: %S failed to register for monitor, err is %d "), &commandName, err);
       
   232 						commandPtr->Process().Kill(err);
       
   233 						}
       
   234 					}
       
   235 				}
       
   236 			}
       
   237 		else
       
   238 			{
       
   239 			DEBUGPRINT2(_L("SysStart: command %S not created so no rendezvous check"), &commandName);		
       
   240 			}
       
   241  		 }
       
   242 	}
       
   243 
       
   244 /** This is the helper function to check all the deferred commands when fail_on_error is EPanicOnCommandFailure.
       
   245     In this case, if the process failed for any reason (either before or after RTime expire), the system
       
   246     will be restarted base on the process/application starting command.
       
   247     Otherwise, the process will be added to System Monitor for monitoring if required.
       
   248     
       
   249     @param aTimerStatus This is the TReauestStatus of RTimer that this MULTIPLE_WAIT command is waiting on
       
   250 */
       
   251 void CMultipleWait::CheckCommandsForRestart(TRequestStatus& aTimerStatus)
       
   252 	{
       
   253  	// Each deferred command will have been invoked and stored in a deferred 
       
   254  	// list. Need to process each command in the list checking if each has
       
   255  	// been initialized i.e. rendezvoused.
       
   256  	TInt i = 0;
       
   257 	for (; i < iDeferredList.Count(); i++)
       
   258 		{
       
   259 		// Get the next command 
       
   260  		CAppStarter* commandPtr = static_cast<CAppStarter*>(iDeferredList[i]); 
       
   261  		
       
   262 #ifdef _DEBUG  	 		
       
   263  		TParsePtrC parser(commandPtr->AppInfo().FileName());
       
   264 		TPtrC commandName = parser.Name();
       
   265 		DEBUGPRINT2(_L("SysStart: checking status of deferred command %S"), &commandName);
       
   266 #endif			
       
   267 				
       
   268  		if (commandPtr->Process().Handle() > 0)
       
   269  			{			
       
   270  			// Wait for either the command to complete the rendezvous or the timer
       
   271  			// to expire. (If no timer has been started this code will just wait for the 
       
   272  			// command to complete).
       
   273  			TRequestStatus& commandStatus = commandPtr->CommandStatus();
       
   274  			User::WaitForRequest(aTimerStatus, commandStatus);
       
   275  			
       
   276  			if ((aTimerStatus != KRequestPending) && (commandStatus != KErrNone))
       
   277 				{ 
       
   278 				// Command has timed out. fail_on_error is set to EPanicOnCommandFailure
       
   279 				// so restart system immediately.
       
   280 				DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());		
       
   281   	 			RestartSystem(commandPtr->AppInfo());
       
   282 				}
       
   283 			else
       
   284 				{
       
   285 			 	// Command has completed. Check that the command has completed successfully. 
       
   286 				// If there is an error restart system immediately.
       
   287 				if (commandStatus != KErrNone)
       
   288 					{
       
   289 					DEBUGPRINT3(_L("SysStart: status of deferred command %S, is %d "), &commandName, commandStatus.Int());		
       
   290 	  	 			RestartSystem(commandPtr->AppInfo());
       
   291 					}
       
   292 				else
       
   293 					{
       
   294 					DEBUGPRINT2(_L("SysStart: successful rendezvous of deferred command %S "), &commandName);
       
   295 					TRAPD(err, RegisterMonitoringL(*commandPtr));
       
   296 
       
   297 					// if monitoring failed, restart system because start and monitor is expected to succeed or fail as one action
       
   298 					if (err != KErrNone)
       
   299 						{
       
   300 						DEBUGPRINT3(_L("SysStart: %S failed to register for monitor, err is %d "), &commandName, err);
       
   301 						RestartSystem(commandPtr->AppInfo());
       
   302 						}
       
   303 					}
       
   304 				}
       
   305  			}
       
   306  		else
       
   307  			{
       
   308  			// the command already failed in creation, just restart system
       
   309  			DEBUGPRINT2(_L("SysStart: command %S not created so no rendezvous check"), &commandName);
       
   310  			RestartSystem(commandPtr->AppInfo());
       
   311  			}
       
   312 
       
   313 		}
       
   314 	}
       
   315 
       
   316 
       
   317 /** Helper function to restart system according to the process/application start command properties
       
   318 	@param aStartupProperties The startup properties for restarting system
       
   319 */
       
   320 void CMultipleWait::RestartSystem(const CStartupProperties& aStartupProperties)
       
   321 	{
       
   322 	TInt err = KErrNone;
       
   323 	if (aStartupProperties.RecoveryMethod() == ERestartOSWithMode)
       
   324 		{
       
   325 		err = RestartSys::RestartSystem(aStartupProperties.RestartMode());
       
   326 		}
       
   327 	else
       
   328 		{
       
   329 		err = RestartSys::RestartSystem();
       
   330 		}
       
   331 
       
   332 	if (KErrNone != err)
       
   333 		{
       
   334  		DEBUGPRINT2(_L("SysStart: RestartSystem error = %d"), err);
       
   335 		PanicNow(KPanicMultipleWait, ERestartSystemCallFailed);
       
   336 		}
       
   337 
       
   338 	User::After(5000000); // required by RestartSys API, see comments in RestartSys::RestartSystem()
       
   339 	}
       
   340 
       
   341 /** Helper function to register for monitoring after a process has been started successfully
       
   342 	@param aStartCommand The command that has been started successfully
       
   343 */
       
   344 void CMultipleWait::RegisterMonitoringL(CAppStarter& aStartCommand)
       
   345 	{
       
   346 	if (aStartCommand.AppInfo().Monitored())
       
   347 		{
       
   348 		// When a deferred wait for start process needs to be monitord, the start method needs to 
       
   349 		// be changed to EFireAndForget because it cannot be deferred when it die after being monitored
       
   350 		aStartCommand.AppInfo().SetStartMethod(EFireAndForget);
       
   351 		
       
   352 		iProvider.SysMonSessionL().MonitorL(aStartCommand.AppInfo(), aStartCommand.Process());
       
   353 		}
       
   354 	}
       
   355 
       
   356 void CMultipleWait::Release() 
       
   357 	{
       
   358 	delete this;
       
   359 	}