sysstatemgmt/systemstatemgr/ss/src/fireandforget.cpp
changeset 0 4e1aa6a622a0
child 76 cb32bcc88bad
equal deleted inserted replaced
-1:000000000000 0:4e1aa6a622a0
       
     1 // Copyright (c) 2007-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 "fireandforget.h"
       
    17 #include "timeoutwaiter.h"
       
    18 #include "ssmdebug.h"
       
    19 
       
    20 _LIT(KApStartDLL, "apstart.dll");
       
    21 _LIT( KSysMonProxyDLL, "loadsysmon.dll" );
       
    22 typedef CApaStarter* (*TFuncNewL)( void );
       
    23 typedef MSsmLoadSysMon* (*TFuncCreateL)( void );
       
    24 
       
    25 void CFireAndForget::FireD()
       
    26 	{
       
    27 	SetActive();
       
    28 
       
    29 	TRequestStatus* pTrs = &iStatus;
       
    30 	User::RequestComplete( pTrs, KErrNone );
       
    31 	}
       
    32 
       
    33 /**
       
    34  Start the app or process and delete oneself.
       
    35  */
       
    36 void CFireAndForget::RunL()
       
    37 	{
       
    38 	// create timer only if timeout > KSsmStartSafeNoTimeout and iTimeoutWaiter is NULL
       
    39 	const TInt32 timeout = iStartupProperties->Timeout();
       
    40 	if( timeout > KSsmStartSafeNoTimeout && !iTimeoutWaiter)
       
    41 		{
       
    42 		iTimeoutWaiter = CTimeoutWaiter::NewL( timeout, *this );
       
    43 		}
       
    44 
       
    45 	switch( iState )
       
    46 		{
       
    47 		case EFAndFIdle:
       
    48 			{
       
    49 			iState = EFAndFStateRetrying;
       
    50 			InvokeL();
       
    51 			}
       
    52 			break;
       
    53 
       
    54 		case EFAndFStateRetrying:
       
    55 			{
       
    56 			CleanupOrRetryL();
       
    57 			}
       
    58 			break;
       
    59 
       
    60 		default:
       
    61 			User::Leave( KErrUnknown );
       
    62 			break;
       
    63 		}
       
    64 	}
       
    65 
       
    66 TBool CFireAndForget::GoodStart()
       
    67 	{
       
    68 	TBool goodStart( ETrue );
       
    69 	TInt err = KErrNone;
       
    70 
       
    71 	if(iStatus == KErrAlreadyExists && (iStartupProperties->IsMonitoringRequired()))
       
    72 		{
       
    73 		// This is done only when trying to start an already running server(process), as when we try to start an application 
       
    74 		// which is already running, it doesn't return KErrAlreadyExists.  It would be started as another instance of same application.
       
    75 		TFullName searchTerm(iStartupProperties->FileName());
       
    76 		_LIT(KSearchAny, "*");
       
    77 		searchTerm += KSearchAny;
       
    78  	    TFindProcess find(searchTerm);
       
    79 		TFullName name;
       
    80 		err = find.Next(name);
       
    81 		if(err == KErrNone)
       
    82 			{
       
    83 			err = iProcess.Open(find);
       
    84 			DEBUGPRINT2A("*** CFireAndForget - Tried to Open process which is already running with %d", err);
       
    85 			}
       
    86 		}
       
    87 	iStatus = ( iStatus == KErrAlreadyExists ) ? KErrNone : iStatus.Int(); // In the case of pre-existing servers.
       
    88 
       
    89 	// Relevant if StartApp
       
    90 	err = GetProcessHandle(iProcess, iThreadId);
       
    91 	if( iStatus.Int() != KErrNone || err != KErrNone )
       
    92 		{
       
    93 		goodStart = EFalse;
       
    94 
       
    95 		if(KErrNone == err && iProcess.Handle() && (EExitPending == iProcess.ExitType()))
       
    96 			{
       
    97 			TPtrC fileName = iStartupProperties->FileName();
       
    98 			DEBUGPRINT3A("*** CFireAndForget - terminating with err: %d for app/process %S", iStatus.Int(), &fileName);
       
    99 			iProcess.Terminate( iStatus.Int() );
       
   100 			iProcess.Close();
       
   101 			}
       
   102 		}	
       
   103 	else if(KErrNone == err && iProcess.Handle() && (EExitPanic == iProcess.ExitType()))		 
       
   104 		{
       
   105 		goodStart = EFalse;
       
   106 		//We can't use the 'exit reason' if the process has panicked as this is the 
       
   107 		//panic 'reason' and may be '0' which cannot be distinguished from KErrNone
       
   108 		iStatus = KErrGeneral;
       
   109 		}
       
   110 
       
   111 	return goodStart;
       
   112 	}
       
   113 
       
   114 TInt CFireAndForget::GetProcessHandle( RProcess& aProcess, const TThreadId& aThreadId )
       
   115 	{
       
   116 	TInt err = KErrNone;
       
   117 	if( ESsmCmdStartApp == iStartupProperties->CommandType() )
       
   118 		{
       
   119 		RThread thread;
       
   120 		err = thread.Open(aThreadId);
       
   121 		if( KErrNone == err )
       
   122 			{
       
   123 			err = thread.Process(aProcess);
       
   124 			}
       
   125 		thread.Close();
       
   126 		}
       
   127 	return err;
       
   128 	}
       
   129 
       
   130 void CFireAndForget::CleanupOrRetryL()
       
   131 	{
       
   132 	if( GoodStart() )
       
   133 		{
       
   134 		// Rendezvous succeeded.
       
   135 		// Monitor the process/app if needed
       
   136 		StartMonitorIfRequiredL();
       
   137 		delete this;
       
   138 		}
       
   139 	else
       
   140 		{		
       
   141 		if( --iRetries < 0)
       
   142 			{
       
   143 			// We have not succeeded and retries, if any, have been exhausted.
       
   144 			TPtrC fileName = iStartupProperties->FileName();
       
   145 			DEBUGPRINT2(_L("*** CFireAndForget - retries exhausted for app/process %S"), &fileName);
       
   146 			delete this;
       
   147 			}
       
   148 		else
       
   149 			{
       
   150 			InvokeL();
       
   151 			}
       
   152 		}
       
   153 	}
       
   154 
       
   155 void CFireAndForget::InvokeL()
       
   156 	{
       
   157 	TPtrC fileName = iStartupProperties->FileName();
       
   158 	DEBUGPRINT3(_L("*** CFireAndForget::InvokeL() - iRetries is %d for app/process %S"), iRetries, &fileName);
       
   159 
       
   160 	switch( iStartupProperties->CommandType() )
       
   161 		{
       
   162 		case ESsmCmdStartApp:
       
   163 			{
       
   164 			if( !iApaStarter )
       
   165 				{
       
   166 				User::Leave( KErrNotSupported );
       
   167 				}
       
   168 			SetActive();
       
   169 			TRAPD( err, iApaStarter->StartAppL( iStartupProperties->FileName(), 
       
   170 									iStartupProperties->Args(), 
       
   171 									iStartupProperties->Viewless(), 
       
   172 									iStartupProperties->StartInBackground(), iThreadId, iStatus ));
       
   173 			if( KErrNone != err )
       
   174 				{
       
   175 				// In the case of error here, we know that Apparc has not called Rendezvous()
       
   176 				TRequestStatus* trs = &iStatus;
       
   177 				User::RequestComplete( trs, err );
       
   178 				}
       
   179 			}
       
   180 			break;
       
   181 
       
   182 		case ESsmCmdStartProcess:
       
   183 			{
       
   184 			SetActive(); 
       
   185 			TInt err = iProcess.Create( iStartupProperties->FileName(), iStartupProperties->Args() );
       
   186 			if( KErrNone != err )
       
   187 				{
       
   188 				// In case of error here, copmplete self with err.
       
   189 				TRequestStatus* trs = &iStatus;
       
   190 				User::RequestComplete( trs, err );
       
   191 				return;
       
   192 				}
       
   193 			iProcess.Rendezvous( iStatus );
       
   194 			// Rendezvous() can complete with KErrNoMemory, hence:-
       
   195 			if( iStatus == KRequestPending )
       
   196 				{
       
   197 				iProcess.Resume();
       
   198 				}
       
   199 			}
       
   200 			break;
       
   201 	
       
   202 		default:
       
   203 			User::Leave( KErrArgument );
       
   204 		}
       
   205 
       
   206 	if( iTimeoutWaiter && !iTimeoutWaiter->IsActive())
       
   207 		{
       
   208 		iTimeoutWaiter->ActuateTimer();
       
   209 		}
       
   210 	}
       
   211 
       
   212 void CFireAndForget::DoCancel()
       
   213 	{
       
   214 	if( iTimeoutWaiter )
       
   215 		{
       
   216 		iTimeoutWaiter->Cancel();
       
   217 		}
       
   218 	// Relevant if StartApp
       
   219 	 TInt err = GetProcessHandle(iProcess, iThreadId);
       
   220 	if( KErrNone == err )
       
   221 		{
       
   222 		err = iProcess.RendezvousCancel( iStatus );
       
   223 		}	
       
   224 	}
       
   225 
       
   226 TInt CFireAndForget::RunError( TInt aError )
       
   227 	{
       
   228 	DEBUGPRINT2A("*** CFireAndForget::RunError error %d", aError);
       
   229 
       
   230 	delete this;
       
   231 	
       
   232 	// We've dealt with the error here, so return KErrNone always
       
   233 	aError = KErrNone;
       
   234 	return aError;
       
   235 	}
       
   236 
       
   237 CFireAndForget* CFireAndForget::NewL( const CSsmStartupProperties& aStartupProperties )
       
   238 	{
       
   239 	CFireAndForget* self = NewLC( aStartupProperties );
       
   240 	CleanupStack::Pop();
       
   241 	return self;
       
   242 	}
       
   243 
       
   244 CFireAndForget* CFireAndForget::NewLC( const CSsmStartupProperties& aStartupProperties  )
       
   245 	{
       
   246 	CFireAndForget* self = new(ELeave) CFireAndForget();
       
   247 	CleanupStack::PushL( self );
       
   248 	self->ConstructL( aStartupProperties );
       
   249 	return self;
       
   250 	}
       
   251 
       
   252 void CFireAndForget::ConstructL( const CSsmStartupProperties& aStartupProperties )
       
   253 	{
       
   254 	if ( aStartupProperties.FileName() == KNullDesC ) 
       
   255 		{
       
   256 		User::Leave(KErrArgument);
       
   257 		}
       
   258 	iStartupProperties = CSsmStartupProperties::NewL( aStartupProperties );
       
   259 	
       
   260 	// We need to decrement the retries in case of application\process failure and since we need
       
   261 	// the retries for monitoring it is stored as a separate member variable
       
   262 	iRetries = aStartupProperties.Retries();
       
   263 	// create iApaStarter, we are not using apaStarter of SS as 
       
   264 	// this object will be running even after SS is finished.
       
   265 	LoadApStartLibL();
       
   266 	}
       
   267 
       
   268 CFireAndForget::CFireAndForget()
       
   269 	: CActive( EPriorityStandard ),
       
   270 	iThreadId( KSsmStartSafeNullThreadId )
       
   271 	{
       
   272 	CActiveScheduler::Add( this );
       
   273 	}
       
   274 
       
   275 CFireAndForget::~CFireAndForget()
       
   276 	{
       
   277 	Cancel();
       
   278 
       
   279 	delete iTimeoutWaiter;
       
   280 	delete iApaStarter;
       
   281 	iApStartLib.Close();
       
   282 	iProcess.Close();
       
   283 	iSysMonProxyLib.Close();
       
   284 	delete iStartupProperties;
       
   285 	}
       
   286 
       
   287 /**
       
   288  From MTimeoutWaiterNotification
       
   289 */
       
   290 void CFireAndForget::NotifyTimeout()
       
   291 	{
       
   292 	// Relevant if StartApp
       
   293 	TInt err = GetProcessHandle(iProcess, iThreadId);
       
   294 	if( KErrNone == err )
       
   295 		{
       
   296 		TPtrC fileName = iStartupProperties->FileName();
       
   297 		DEBUGPRINT2(_L("*** CFireAndForget - timeout exhausted for app/process %S"), &fileName);
       
   298 		iProcess.Terminate( KErrTimedOut ); // should invoke RunL once more.
       
   299 		delete this;
       
   300 		}
       
   301 	}
       
   302 
       
   303 /**
       
   304 Load the library. Locate and call the ordinal corresponding to CApStart::NewL().
       
   305 
       
   306 Note: We do not leave in the case of being unable to load the libray, but assume Apparc is not present.
       
   307       iApaStarter is checked for NULL before use passim and appropriate error code supplied if it is. 
       
   308       The return code is derived from the loader-server (Base) and not closely specified in RLibrary docs or code.
       
   309 */
       
   310 void CFireAndForget::LoadApStartLibL()
       
   311 	{
       
   312 	 if( KErrNone != iApStartLib.Load(KApStartDLL) )
       
   313 	 	{
       
   314 	 	return;	
       
   315 	 	}
       
   316 
       
   317 	TFuncNewL apStartNewL = reinterpret_cast<TFuncNewL>( iApStartLib.Lookup(1) );
       
   318 	iApaStarter = apStartNewL();
       
   319 	}
       
   320 
       
   321 /**
       
   322 If Monitoring is required for the started process/app, load SysMonCli and
       
   323 launch the monitor
       
   324 */
       
   325 void CFireAndForget::StartMonitorIfRequiredL()
       
   326 	{
       
   327 	if (iStartupProperties->IsMonitoringRequired())
       
   328 		{
       
   329 		// Load the dll interfacing between us and the System Monitor component, if installed.
       
   330 		MSsmLoadSysMon* sysMonCli = NULL;
       
   331 		if( KErrNone != iSysMonProxyLib.Load(KSysMonProxyDLL) )
       
   332 		 	{
       
   333 		 	return;	
       
   334 		 	}
       
   335 		
       
   336 		TFuncCreateL sysMonProxyCreateL = reinterpret_cast<TFuncCreateL>( iSysMonProxyLib.Lookup(1) );
       
   337 		sysMonCli = sysMonProxyCreateL();
       
   338 		CleanupStack::PushL(sysMonCli);
       
   339 		sysMonCli->OpenL();
       
   340 		TRAPD( err, sysMonCli->MonitorL( *iStartupProperties, iProcess ) );
       
   341 		sysMonCli->Close();
       
   342 		CleanupStack::PopAndDestroy();
       
   343 		if( KErrNone != err )
       
   344 			{
       
   345 			iProcess.Terminate( err );
       
   346 			User::Leave( err );
       
   347 			}
       
   348 		}
       
   349 	}