loggingservices/rfilelogger/Logger/Src/Server.Cpp
changeset 0 08ec8eefde2f
child 11 667e88a979d7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loggingservices/rfilelogger/Logger/Src/Server.Cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,541 @@
+// Copyright (c) 2002-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:
+// Main log server engine.
+// Process log requests from multiple clients simultaneously.
+// 
+//
+
+/**
+ @file server.cpp
+*/
+#include "server.h"
+
+CLogServer* CLogServer::NewL()
+/**
+ * @return - Instance of the log server
+ */
+	{
+	CLogServer * server = new (ELeave) CLogServer();
+	CleanupStack::PushL(server);
+	server->ConstructL();
+	// CServer base class call
+	server->StartL(KFileLogrerServerName);
+	CleanupStack::Pop(server);
+	return server;
+	}
+
+void CLogServer::ConstructL()
+/**
+ * Second phase construction
+ */
+	{
+	User::LeaveIfError(Fs().Connect());
+	}
+
+
+CLogServer::CLogServer() : CServer2(EPriorityStandard,ESharableSessions)
+/**
+ * Constructor
+ */
+	{
+	}
+
+CLogServer::~CLogServer()
+/**
+ * Destructor
+ */
+	{
+	// Close the array of control structures
+	LogControl().Close();
+	Fs().Close();
+	}
+
+
+CSession2* CLogServer::NewSessionL(const TVersion& /*aVersion*/,const RMessage2& /*aMessage*/) const
+/**
+ * @param RMessage - RMessage for the session open
+ */
+	{
+	// Just create the session
+	CLogSession* session = new (ELeave) CLogSession();
+	return session;
+	}
+
+void CLogServer::ControlComplete(CLogFileControl& aControl)
+/**
+ * @param aControl - Logfile control class reference
+ *
+ * Checks to see if this control session can be removed 
+ */
+	{
+	// Check session count and the data buffers on the queue 
+	if(aControl.SessionCount() || !aControl.QueueEmpty())
+		return;
+
+	// There are no subsessions mapped to the logfile control class and
+	// no data buffers on its write queue
+	// Loop through the server's control array and remove it then delete it
+	TInt i;
+	for(i=0;i<LogControl().Count();i++)
+		{
+		if(&aControl == LogControl()[i])
+			{
+			// Done
+			LogControl().Remove(i);
+			delete &aControl;
+			break;
+			}
+		}
+	// If it's the last one then exit the server
+	if(!LogControl().Count())
+		CActiveScheduler::Stop();
+	}
+
+
+///////
+
+CLogSession::CLogSession()
+/**
+ * Constructor
+ */
+	{
+	}
+
+CLogSession::~CLogSession()
+/**
+ * Destructor
+ */
+	{
+	// Check for null
+	// Session close without a createlog call leaves iControl null
+	if(!iControl)
+		return;
+	// A logfile control structure can have multiple server sessions
+	// decrement its server session count
+	iControl->RemoveSession();
+	CLogServer* p=(CLogServer*) Server();
+	// Shuts Down the server if this is the last open session
+	p->ControlComplete(*iControl);
+	}
+
+void CLogSession::ServiceL(const RMessage2& aMessage)
+/**
+ * @param aMessage - Function and data for the session
+ */
+	{
+	switch(aMessage.Function())
+		{
+		// API CreateLog() call
+		case RFileFlogger::ECreateLog :
+			{
+			// Sanity check to make sure it's not been called multiple times
+			if(iControl)
+				{
+				aMessage.Complete(KErrInUse);
+				break;
+				}
+			// Get the filepath
+			// size policed on the client side
+			TBuf<KMaxLoggerFilePath> logFilePath;
+			// Read it
+			aMessage.ReadL(0,logFilePath);
+			// Get the log mode in the second argument
+			RFileFlogger::TLogMode logMode;
+			logMode = (RFileFlogger::TLogMode)aMessage.Int1();
+			// Get a pointer to the parent server
+			CLogServer* server=(CLogServer*) Server();
+			// For compare's convert the whole path to lower case
+			logFilePath.LowerCase();
+			// Get rid of leading and trailing spaces.
+			logFilePath.Trim();
+						
+			// Loop the through the server's logfile control class list
+			// to see if there's a match.
+			TInt i;
+			for(i=0;i<server->LogControl().Count();i++)
+				{
+				if(server->LogControl()[i]->LogFile() == logFilePath)
+					// This file's already in open so we don't have to open it
+					break;
+				}
+			TInt err = KErrNone;
+			// Check the count
+			if(i < server->LogControl().Count())
+				// Map this session to an existing logfile control class in the list
+				iControl = server->LogControl()[i];
+			else
+				{
+				// Create a new logfile control class
+				// creates/opens the logfile
+				TRAP(err,iControl = CLogFileControl::NewL(*server,logFilePath,logMode));
+				if(!err)
+					{  // nancy
+					// find out the type of output format and assign to this new created control
+					TInt error = logFilePath.Find(_L(".xml"));
+					if(error==KErrNotFound) iControl->SetLogType(CLogFileControl::ETxt);
+					else iControl->SetLogType(CLogFileControl::EXml);					
+					// end nancy
+					// Append it to the logfile control class list
+					server->LogControl().Append(iControl);					
+					}
+				}
+			if(!err)
+				// Increment its session count
+				iControl->AddSession();
+			aMessage.Complete(err);
+			}
+			break;
+		// One of the API write calls
+		case RFileFlogger::EWriteLog :
+			{
+			// Sanity check
+			if(!iControl)
+				{
+				aMessage.Complete(KErrNotFound);
+				break;
+				}
+			// Data can be any size
+			// Get the length from second argument
+			TInt bufferLength = aMessage.Int1();
+			// Get a heap buffer of the right size
+			HBufC8* buffer = HBufC8::NewLC(bufferLength);
+			TPtr8 ptr(buffer->Des());
+			// read the data
+			aMessage.ReadL(0,ptr);			
+			// Get a buffer control class contructed with the heap data
+			// takes ownership
+			CLogBuffer* logBuffer = new (ELeave) CLogBuffer(*buffer);
+			CleanupStack::Pop(buffer);
+			// Add it to the logfile control class buffer queue
+			iControl->AddLogBuffer(*logBuffer);
+			if(!iControl->IsActive())
+				// AO is idle, kick into life
+				iControl->Kick();
+			aMessage.Complete(KErrNone);
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+
+///////
+
+CLogFileControl* CLogFileControl::NewL(CLogServer& aParent, const TDesC& aLogFilePath,RFileFlogger::TLogMode aMode)
+/**
+ * @param aParent - Server reference for callback
+ * @param aLogFilePath - Full path and filename of the logfile
+ * @param aMode - Overwrite or Append
+ *
+ * First phase construction for logfile control class
+ */
+	{
+	CLogFileControl* self = new (ELeave) CLogFileControl(aParent,aLogFilePath);
+	CleanupStack::PushL(self);
+	self->ConstructL(aMode);
+	CleanupStack::Pop();
+	return self;
+	}
+
+CLogFileControl::CLogFileControl(CLogServer& aParent,const TDesC& aLogFilePath) :
+	iParent(aParent),
+	iLogFileName(aLogFilePath),
+	iTransmitted(EFalse)
+/**
+ * @param aParent - Server reference for callback
+ * @param aLogFilePath - Full path and filename of the logfile
+ *
+ * Constructor - Safe initialisation
+ */
+	{
+	iQueue.SetOffset(CLogBuffer::LinkOffset());
+	}
+
+void CLogFileControl::ConstructL(RFileFlogger::TLogMode aMode)
+/**
+ * @param aMode - Overwrite or Append
+ *
+ * Second phase construction - Create or open the logfile
+ */
+	{
+	if(aMode == RFileFlogger::ELogModeOverWrite)
+		// In overwrite mode replace
+		User::LeaveIfError(iLogFile.Replace(iParent.Fs(),iLogFileName,EFileWrite));
+	else
+		{
+		// For append try open then replace
+		TInt err = iLogFile.Open(iParent.Fs(),iLogFileName,EFileWrite);
+		if(err != KErrNone)
+			// Bomb out if replace fails
+			User::LeaveIfError(iLogFile.Replace(iParent.Fs(),iLogFileName,EFileWrite));
+		else
+			{
+			// Open worked. Position at EOF
+			TInt pos;
+			iLogFile.Size(pos);
+			User::LeaveIfError(iLogFile.Seek(ESeekEnd,pos));
+			}
+		}
+	}
+
+CLogFileControl::~CLogFileControl()
+/**
+ * Destructor
+ * The server maintains a list of these classes and will not destruct one if there
+ * is data on its queue
+ * Destructor just closes the file handle.
+ */
+	{
+	iLogFile.Close();
+	}
+
+void CLogFileControl::RunL()
+/**
+ * Main File writing pump
+ * Called on write completion or Kick() by the session that accepts the data
+ */
+	{
+#if (defined _DEBUG)
+	_LIT(KPanic,"LogEng RunL()");
+#endif
+	__ASSERT_DEBUG(iStatus.Int() == KErrNone,User::Panic(KPanic,iStatus.Int()));
+	// Check to see if this is the result of write completion
+	if(iTransmitted)
+		{
+		// Write completed
+		// Remove the buffer at the head of the queue and free it
+		CLogBuffer* buffer = iQueue.First();
+		iQueue.Remove(*buffer);
+		delete buffer;
+		}
+	// Check to see if there's more on the queue
+	if(!iQueue.IsEmpty())
+		{
+		// There is so write the head of the queue
+		CLogBuffer* buffer = iQueue.First();	
+		SetActive();
+		// Set the flag to say we've transmitted
+		iTransmitted = ETrue;
+
+//  ------------------------------------
+		if(iLogFormat==ETxt)  WriteTxt(buffer->Buf());
+		else WriteXml(buffer->Buf());
+		} 
+	else
+		{
+		// Nothing on the queue
+		iTransmitted = EFalse;
+		// Call into the server to check if this resource can be freed
+		iParent.ControlComplete(*this);
+		}
+	}
+
+void CLogFileControl::WriteXml(const TDesC8 &aDes)
+/**
+ * @param aDes - send a aDes string in xml format to a log file
+ */
+	{
+/*--------- Maintaince Warning:  -----------------------------------
+******* the fomat of below is sensible from client original format.  
+******* Any change should match actual string formated from client. 
+******* Double check the code on client side 
+
+* The current assumtion of format:
+* First string values are seperated by sign " - " and extra pair of fields are
+* seperated from main log message  by long unusual string "LogFieldsRequiredBeingAddedToAboveLogMessage"
+* The \t used to seperate field name and field value and \r\n used to seperate
+* each other from those pairs of field
+* \t\t\t\t\t\t is used to end of whole string
+--------------------------------------------------------------------------------*/
+		_LIT8(KxmlHeader,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n<LOGFILE>");
+		//The order of variables:
+		// time 		- aTime
+		// Severity 	- aSeverity
+		// Thread 		- aThread
+		// Filename 	- aFilename
+		// Linenumber 	- aLinenumber
+		// Text 		- aText
+		
+		// Start of string retrive/deformating 
+		HBufC8* pBuf1 = HBufC8::NewL(KMaxLoggerLineLength*2); //(aDes.Length()+200);
+		if(!pBuf1)
+			{
+			return; // no memory 
+			}
+		TPtr8 aPtr(pBuf1->Des());  // used for searching
+		TPtr8 alogbuf(pBuf1->Des());  //used for final log
+		aPtr.Append(aDes);
+		TPtrC8 SearchBuf;
+		TInt aCount[8]; 
+		aCount[0]=0;
+		TInt posI(0);
+
+// retrive log message:
+		// Retrive common part of log message:
+		for(TInt i = 1; i<6; i++)
+			{
+			SearchBuf.Set(aPtr.Mid(posI));
+			aCount[i]=SearchBuf.Find(KSeperation8)+posI;
+			posI=aCount[i]+3;
+			if(aCount[i]<aCount[i-1])	return; // wrong format string from client
+			}
+		// seperating common log message and extra log fields will be easy for future maintaince.
+		TLogField8* alogField = new TLogField8[6];  // the common part of log message for both 
+								  					// with and without extra log fields
+		if(!alogField) return; // no memory
+
+		TLogField8* extralogField=NULL; // only applied to extra log fields
+
+		TInt alength=0;  // a length of array of extra log fields
+
+		alogField[0].iLogTag8.Copy(_L8("TIME"));
+		alogField[1].iLogTag8.Copy(_L8("SEVERITY"));
+		alogField[2].iLogTag8.Copy(_L8("THREAD"));
+		alogField[3].iLogTag8.Copy(_L8("FILENAME"));
+		alogField[4].iLogTag8.Copy(_L8("LINENUMBER"));
+		alogField[5].iLogTag8.Copy(_L8("TEXT"));
+			
+		alogField[0].iLogValue8.Copy(aPtr.Mid(aCount[0],aCount[1]-aCount[0]));
+		for(TInt i=1; i<5; i++)
+			{
+			alogField[i].iLogValue8.Copy(aPtr.Mid(aCount[i]+3,aCount[i+1]-aCount[i]-3));				
+			}
+
+		SearchBuf.Set(aPtr.Mid(posI));
+		aCount[6]=SearchBuf.Find(_L8("LogFieldsRequiredBeingAddedToAboveLogMessage"))+posI; 
+		if(aCount[6]<posI)  // no addtional fields. Find return value is KErrNotFound or >0
+			{
+			alogField[5].iLogValue8.Copy(aPtr.Mid(aCount[5]+3,aDes.Length()-aCount[5]-5));
+			}
+		else
+		{
+			alogField[5].iLogValue8.Copy(aPtr.Mid(aCount[5]+3,aCount[6]-aCount[5]-5));
+			posI=aCount[6]+45;  //45 is from the length of long string and a tab
+			SearchBuf.Set(aPtr.Mid(posI));
+			aCount[7]=SearchBuf.Find(_L8("\r\n"))+posI;
+			TLex8 lex(aPtr.Mid(posI,aCount[7]-posI));  // get the length
+			TInt err=lex.Val(alength); 
+			if (err)	alength=0; // ignor the extra log fields. Let the log go
+
+			// Retrive the extra log fields
+			extralogField = new TLogField8[alength];
+			if(!extralogField) return; // no memory
+			for(TInt i=0; i<alength; i++)
+				{
+				aCount[6]=aCount[7]+2;
+				SearchBuf.Set(aPtr.Mid(aCount[6]));
+				aCount[7]=SearchBuf.Find(_L8("\t"))+aCount[6];
+				extralogField[i].iLogTag8.Copy(aPtr.Mid(aCount[6],aCount[7]-aCount[6]));
+				aCount[6]=aCount[7]+1;
+				SearchBuf.Set(aPtr.Mid(aCount[6]));
+				aCount[7]=SearchBuf.Find(_L8("\r\n"))+aCount[6];
+				extralogField[i].iLogValue8.Copy(aPtr.Mid(aCount[6],aCount[7]-aCount[6]));
+				}
+		}
+			
+			// Start to organize an XML format: 			
+			TInt afileSize;
+			_LIT(KLogMutex, "LoggingServerMutex");
+			RMutex mutex;
+			TInt r = mutex.CreateGlobal(KLogMutex);
+			if(r==KErrAlreadyExists)
+				r = mutex.OpenGlobal(KLogMutex);  
+			
+			if(!r)	mutex.Wait();  // if still failed, let logging go 
+									//without bother the mutex
+			  
+			iLogFile.Size(afileSize);
+			if(afileSize<12) // 12 is from charters of "\r\n</LOGFILE>" 
+						//It shoud happened once at the beginning of the file
+						// such as overwrite mode
+				{
+				afileSize=12; // used for lock position
+    			alogbuf.Copy(KxmlHeader);
+				}
+			alogbuf.Append(_L8("\r\n<MESSAGE>\r\n"));
+			for(TInt i=0; i<6; i++)
+				{
+				alogbuf.Append(_L8("  <"));
+				alogbuf.Append(alogField[i].iLogTag8);
+				alogbuf.Append(_L8(">"));
+				alogbuf.Append(alogField[i].iLogValue8);
+				alogbuf.Append(_L8("</"));
+				alogbuf.Append(alogField[i].iLogTag8);
+				alogbuf.Append(_L8(">\r\n"));				
+				}
+			for(TInt i=0; i<alength; i++)  
+				{
+				alogbuf.Append(_L8("  <"));
+				alogbuf.Append(extralogField[i].iLogTag8);
+				alogbuf.Append(_L8(">"));
+				alogbuf.Append(extralogField[i].iLogValue8);
+				alogbuf.Append(_L8("</"));
+				alogbuf.Append(extralogField[i].iLogTag8);
+				alogbuf.Append(_L8(">\r\n"));				
+				}
+			
+    		alogbuf.Append(_L8("</MESSAGE>"));
+    		alogbuf.Append(_L8("\r\n</LOGFILE>"));
+    		
+    		iLogFile.Write(afileSize-12, alogbuf,iStatus);
+    										
+			
+			if(!r)	
+				{
+				mutex.Signal();
+				mutex.Close();				
+				} 
+				
+		if(extralogField) delete[] extralogField;
+		delete[] alogField;	
+		delete pBuf1;
+
+	}
+
+void CLogFileControl::WriteTxt(const TDesC8 &aDes)
+/**
+ * @param aDes - send a aDes string in xml format to a log file
+ */
+	{
+/*--------- Maintaince Warning for aLogBuffer -----------------------------------
+******* the fomat of below is sensible from client original format.  
+******* Any change should match actual string formated from client. 
+******* Double check the code on client side 
+
+* The current assumtion of format:
+* First string values are seperated by sign " - " and extra pair of fields are
+* seperated from main log message  by long unusual string "LogFieldsRequiredBeingAddedToAboveLogMessage"
+* The \t used to seperate field name and field value and \r\n used to seperate
+* each other from those pairs of field
+--------------------------------------------------------------------------------*/
+	iLogFile.Write(aDes,iStatus);
+	}
+///////
+CLogBuffer::CLogBuffer(HBufC8& aLogBuffer) : iLogBuffer(aLogBuffer)
+/**
+ * @param aLogBuffer - Heap descriptor. This class takes ownership
+ * Constructor
+ */
+	{
+	}
+
+CLogBuffer::~CLogBuffer()
+/**
+ * Destructor
+ * This class owns a heap buffer so just free it
+ */
+	{
+	delete &iLogBuffer;
+	}