sl@0: // Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: // DBMS server implementation
sl@0: // 
sl@0: //
sl@0: 
sl@0: #include "SD_STD.H"
sl@0: #include "Sd_PlatDep.h"
sl@0: 
sl@0: using namespace DBSC;
sl@0: 
sl@0: //CDbmsActiveScheduler class just exposes the access to 
sl@0: //CActiveScheduler::Level() method, which is needed to 
sl@0: //make a decision when to call CDbmsActiveScheduler::Stop().
sl@0: class CDbmsActiveScheduler : public CActiveScheduler
sl@0: 	{
sl@0: public:
sl@0: 	inline TInt Level() const
sl@0: 		{
sl@0: 		return CActiveScheduler::Level();
sl@0: 		}
sl@0: 	};
sl@0: 
sl@0: ///////////////////////
sl@0: // Class CDbsServer
sl@0: inline CDbsServer::CDbsServer() :
sl@0: 	CServer2(0, ESharableSessions), 
sl@0: 	iSources(iCache),
sl@0: 	iDbPropsFactory(iFs),
sl@0: 	iDriveSpaceCol(iFs)
sl@0: 	{
sl@0: 	DbgPrint1(_L("###CDbsServer::CDbsServer(), Server ProcID=%d\n"), RDbProcess().Id());
sl@0: 	}
sl@0: 
sl@0: //"iCache.Hold(this,KDbsExitDelay)" statement will put CDbsServer instance in the cache, 
sl@0: //which ensures that CDbsServer instance will be automatically destroyed if nobody
sl@0: //uses it next KDbsExitDelay microseconds.
sl@0: CDbsServer* CDbsServer::NewL()
sl@0: 	{
sl@0: 	DbgPrint1(_L("###CDbsServer::NewL(), Server ProcId=%d\n"), RDbProcess().Id());
sl@0: 	CDbsServer* self= new (ELeave) CDbsServer;
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL();
sl@0: 	CleanupStack::Pop(self);
sl@0: 	self->iCache.Hold(self, KDbsExitDelay);		// the last thing we do here
sl@0: 	//Intentional behaviour, resources freed after timeout only
sl@0: 	//coverity[use_after_free]
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // 2nd phase construction - ensure the timer and server objects are running
sl@0: //
sl@0: void CDbsServer::ConstructL()
sl@0: 	{
sl@0: 	DbgPrint1(_L("###CDbsServer::ConstructL(), Server ProcId=%d\n"), RDbProcess().Id());
sl@0: 	__LEAVE_IF_ERROR(Dll::SetTls(this));
sl@0: 	StartL(KDbsServerName);
sl@0: 	__LEAVE_IF_ERROR(iFs.Connect());
sl@0: 	iDbPropsFactory.OpenL();
sl@0: 	//EDriveZ - Symbian OS ROM drive !?!?
sl@0: 	iDbPropsFactory.GetPrivatePathL(EDriveZ, iFileName);
sl@0: 	iPolicyProxy = CPolicyProxy::NewL(iFs,iFileName);
sl@0: 	User::LeaveIfError(iCache.Open(ECacheSize, ETrue));	// create a timed cache
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // Invoked via cache entry expiry (or cleanup)
sl@0: //
sl@0: CDbsServer::~CDbsServer()
sl@0: 	{
sl@0: 	DbgPrint1(_L("###CDbsServer::~CDbsServer(), Server ProcId=%d\n"), RDbProcess().Id());
sl@0: 	iDriveSpaceCol.Close();
sl@0: 	delete iPolicyProxy;
sl@0: 	iDbPropsFactory.Close();
sl@0: 	iCache.Close();
sl@0: 	iSources.Close();
sl@0: 	iFs.Close();
sl@0: 	//Stop the scheduler if the nesting level > 0.
sl@0: 	//Sometime it happens that more than one thread tries to run the server and you will have
sl@0: 	//CDbmsActiveScheduler::Install() called multiple times. But you have to stop the 
sl@0: 	//scheduler only once!
sl@0: 	CDbmsActiveScheduler* scheduler = static_cast <CDbmsActiveScheduler*> (CActiveScheduler::Current());
sl@0: 	if(scheduler->Level() > 0)
sl@0: 		{
sl@0: 		DbgPrint2(_L("###CDbsServer::~CDbsServer(), stop the scheduler, Server ProcId=%d, scheduler=%x\n"), RDbProcess().Id(), (TInt)scheduler);
sl@0: 		CDbmsActiveScheduler::Stop();
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // Creates a new client session. This should really check the version number.
sl@0: //
sl@0: CSession2* CDbsServer::NewSessionL(const TVersion& aVersion,const RMessage2&) const
sl@0: 	{
sl@0: 	if(!User::QueryVersionSupported(RDbs::Version(), aVersion))
sl@0: 		{
sl@0: 		__LEAVE(KErrNotSupported);
sl@0: 		}
sl@0: 	CSession2* session = new (ELeave) CDbsSession;
sl@0: 	iCache.Release(*this);
sl@0: 	return session;
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // Returns the instance of the server
sl@0: //
sl@0: CDbsServer* CDbsServer::Instance()
sl@0: 	{
sl@0: 	return (CDbsServer*)Dll::Tls();
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // initiates exit if the last client is closing
sl@0: //
sl@0: void CDbsServer::RemoveSession()
sl@0: 	{
sl@0: 	iSessionIter.SetToFirst();
sl@0: 	iSessionIter++;
sl@0: 	if(iSessionIter++ == 0)
sl@0: 		{
sl@0: 		iCache.Hold(this, KDbsExitDelay);
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // Performs all server initialisation, in particular creation of the
sl@0: // scheduler and server.
sl@0: // Noting has to be leaved in the cleanup stack!
sl@0: // Both: DBMS server and the active scheduler will be destroyed
sl@0: // later. The active scheduler - in CDbsServer destructor.
sl@0: // CDbsServer instance - by iCache instance (RDbCache).
sl@0: static void CreateServerL()
sl@0: 	{
sl@0: 	DbgPrint1(_L("###DBMS-CreateServerL(), Server ProcId=%d\n"), RDbProcess().Id());
sl@0: 	// naming the server thread after the server helps to debug panics
sl@0: 	User::LeaveIfError(User::RenameThread(KDbsServerName));
sl@0: 	// ensure the server thread has a handle on EDBMS.DLL
sl@0: 	LoadDbmsLibraryL();
sl@0: 
sl@0: 	// create and install the active scheduler we need
sl@0: 	CDbmsActiveScheduler* scheduler = new (ELeave) CDbmsActiveScheduler;
sl@0: 	CleanupStack::PushL(scheduler);
sl@0: 	CDbmsActiveScheduler::Install(scheduler);
sl@0: 
sl@0: 	// create the server
sl@0: 	(void)CDbsServer::NewL();
sl@0: 
sl@0: 	CleanupStack::Pop(scheduler);
sl@0: 
sl@0: 	// Initialisation complete, now signal the client
sl@0: 	RDbProcess::Rendezvous(KErrNone);
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // Performs all server initialisation, in particular creation of the
sl@0: // scheduler and server and then run the scheduler
sl@0: //
sl@0: static void RunServerL()
sl@0: 	{
sl@0: 	DbgPrint1(_L("###DBMS-RunServerL(), Server ProcId=%d\n"), RDbProcess().Id());
sl@0: 	::CreateServerL();
sl@0: 	//After successfull creation CreateServerL() call leaves the active scheduler instance
sl@0: 	//and the DBMS server instance in the heap - they will be destroyed later.
sl@0: 	__UHEAP_MARK;
sl@0: 	//Start the scheduler. The execution control is transferred to the curent
sl@0: 	//active scheduler. The statement after "CDbmsActiveScheduler::Start();" will
sl@0: 	//be reached after the stop of active scheduler.
sl@0: 	CDbmsActiveScheduler::Start();
sl@0: 	__UHEAP_MARKEND;
sl@0: 	}
sl@0: 
sl@0: //
sl@0: // Server process entry-point
sl@0: //
sl@0: EXPORT_C TInt Dbs::Run()
sl@0: 	{
sl@0: 	CTrapCleanup* cleanup = CTrapCleanup::New();
sl@0: 	TInt err = KErrNoMemory;
sl@0: 	if(cleanup)
sl@0: 		{
sl@0: 		TRAP(err, ::RunServerL());
sl@0: 		delete cleanup;
sl@0: 		}
sl@0: 	DbgPrint2(_L("###Dbs::Run(), end, Server ProcId=%d, err=%d\n"), RDbProcess().Id(), err);
sl@0: 	return err;
sl@0: 	}
sl@0: 	
sl@0: ///////////////////////
sl@0: // Client called code
sl@0: 
sl@0: //
sl@0: // Starts the DBMS server
sl@0: //
sl@0: TInt Dbs::Start()
sl@0: 	{
sl@0: 	DbgPrint1(_L("#-#Dbs::Start(), Client ProcId=%d\n"), RDbProcess().Id());
sl@0: 	RDbProcess server;
sl@0: 	TInt err = ::CreateDbmsProcess(server);
sl@0: 	if(err != KErrNone)
sl@0: 		{
sl@0: 		DbgPrint2(_L("#-#Dbs::Start(), CreateDbmsProcess, Client ProcId=%d, err=%d\n"), RDbProcess().Id(), err);
sl@0: 		return err;
sl@0: 		}
sl@0: 
sl@0: 	TRequestStatus stat;
sl@0: 	server.Rendezvous(stat);
sl@0: 	if(stat != KRequestPending)
sl@0: 		{
sl@0: 		server.Kill(0);		// abort startup
sl@0: 		}
sl@0: 	else
sl@0: 		{
sl@0: 		server.Resume();	// logon OK - start the server
sl@0: 		}
sl@0: 	User::WaitForRequest(stat);		// wait for start or death
sl@0: 
sl@0: 	// we can't use the 'exit reason' if the server panicked as this
sl@0: 	// is the panic 'reason' and may be '0' which cannot be distinguished
sl@0: 	// from KErrNone
sl@0: 	err = (server.ExitType() == EExitPanic) ? KErrGeneral : stat.Int();
sl@0: 	server.Close();
sl@0: 	DbgPrint2(_L("#-#Dbs::Start(), end, Client ProcId=%d, err=%d\n"), RDbProcess().Id(), err);
sl@0: 	return err;
sl@0: 	}
sl@0: