1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/loggingservices/rfilelogger/Logger/Src/Server.Cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,553 @@
1.4 +// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// Main log server engine.
1.18 +// Process log requests from multiple clients simultaneously.
1.19 +//
1.20 +//
1.21 +
1.22 +/**
1.23 + @file server.cpp
1.24 +*/
1.25 +#include "server.h"
1.26 +
1.27 +CLogServer* CLogServer::NewL()
1.28 +/**
1.29 + * @return - Instance of the log server
1.30 + */
1.31 + {
1.32 + CLogServer * server = new (ELeave) CLogServer();
1.33 + CleanupStack::PushL(server);
1.34 + server->ConstructL();
1.35 + // CServer base class call
1.36 + server->StartL(KFileLogrerServerName);
1.37 + CleanupStack::Pop(server);
1.38 + return server;
1.39 + }
1.40 +
1.41 +void CLogServer::ConstructL()
1.42 +/**
1.43 + * Second phase construction
1.44 + */
1.45 + {
1.46 + User::LeaveIfError(Fs().Connect());
1.47 + }
1.48 +
1.49 +
1.50 +CLogServer::CLogServer() : CServer2(EPriorityStandard,ESharableSessions)
1.51 +/**
1.52 + * Constructor
1.53 + */
1.54 + {
1.55 + }
1.56 +
1.57 +CLogServer::~CLogServer()
1.58 +/**
1.59 + * Destructor
1.60 + */
1.61 + {
1.62 + // Close the array of control structures
1.63 + LogControl().Close();
1.64 + Fs().Close();
1.65 + }
1.66 +
1.67 +
1.68 +CSession2* CLogServer::NewSessionL(const TVersion& /*aVersion*/,const RMessage2& /*aMessage*/) const
1.69 +/**
1.70 + * @param RMessage - RMessage for the session open
1.71 + */
1.72 + {
1.73 + // Just create the session
1.74 + CLogSession* session = new (ELeave) CLogSession();
1.75 + return session;
1.76 + }
1.77 +
1.78 +void CLogServer::ControlComplete(CLogFileControl& aControl)
1.79 +/**
1.80 + * @param aControl - Logfile control class reference
1.81 + *
1.82 + * Checks to see if this control session can be removed
1.83 + */
1.84 + {
1.85 + // Check session count and the data buffers on the queue
1.86 + if(aControl.SessionCount() || !aControl.QueueEmpty())
1.87 + return;
1.88 +
1.89 + // There are no subsessions mapped to the logfile control class and
1.90 + // no data buffers on its write queue
1.91 + // Loop through the server's control array and remove it then delete it
1.92 + TInt i;
1.93 + for(i=0;i<LogControl().Count();i++)
1.94 + {
1.95 + if(&aControl == LogControl()[i])
1.96 + {
1.97 + // Done
1.98 + LogControl().Remove(i);
1.99 + delete &aControl;
1.100 + break;
1.101 + }
1.102 + }
1.103 + // If it's the last one then exit the server
1.104 + if(!LogControl().Count())
1.105 + CActiveScheduler::Stop();
1.106 + }
1.107 +
1.108 +
1.109 +///////
1.110 +
1.111 +CLogSession::CLogSession()
1.112 +/**
1.113 + * Constructor
1.114 + */
1.115 + {
1.116 + }
1.117 +
1.118 +CLogSession::~CLogSession()
1.119 +/**
1.120 + * Destructor
1.121 + */
1.122 + {
1.123 + // Check for null
1.124 + // Session close without a createlog call leaves iControl null
1.125 + if(!iControl)
1.126 + return;
1.127 + // A logfile control structure can have multiple server sessions
1.128 + // decrement its server session count
1.129 + iControl->RemoveSession();
1.130 + CLogServer* p=(CLogServer*) Server();
1.131 + // Shuts Down the server if this is the last open session
1.132 + p->ControlComplete(*iControl);
1.133 + }
1.134 +
1.135 +void CLogSession::ServiceL(const RMessage2& aMessage)
1.136 +/**
1.137 + * @param aMessage - Function and data for the session
1.138 + */
1.139 + {
1.140 + switch(aMessage.Function())
1.141 + {
1.142 + // API CreateLog() call
1.143 + case RFileFlogger::ECreateLog :
1.144 + {
1.145 + // Sanity check to make sure it's not been called multiple times
1.146 + if(iControl)
1.147 + {
1.148 + aMessage.Complete(KErrInUse);
1.149 + break;
1.150 + }
1.151 + // Get the filepath
1.152 + // size policed on the client side
1.153 + TBuf<KMaxLoggerFilePath> logFilePath;
1.154 + // Read it
1.155 + aMessage.ReadL(0,logFilePath);
1.156 + // Get the log mode in the second argument
1.157 + RFileFlogger::TLogMode logMode;
1.158 + logMode = (RFileFlogger::TLogMode)aMessage.Int1();
1.159 + // Get a pointer to the parent server
1.160 + CLogServer* server=(CLogServer*) Server();
1.161 + // For compare's convert the whole path to lower case
1.162 + logFilePath.LowerCase();
1.163 + // Get rid of leading and trailing spaces.
1.164 + logFilePath.Trim();
1.165 +
1.166 + // Loop the through the server's logfile control class list
1.167 + // to see if there's a match.
1.168 + TInt i;
1.169 + for(i=0;i<server->LogControl().Count();i++)
1.170 + {
1.171 + if(server->LogControl()[i]->LogFile() == logFilePath)
1.172 + // This file's already in open so we don't have to open it
1.173 + break;
1.174 + }
1.175 + TInt err = KErrNone;
1.176 + // Check the count
1.177 + if(i < server->LogControl().Count())
1.178 + // Map this session to an existing logfile control class in the list
1.179 + iControl = server->LogControl()[i];
1.180 + else
1.181 + {
1.182 + // Create a new logfile control class
1.183 + // creates/opens the logfile
1.184 + TRAP(err,iControl = CLogFileControl::NewL(*server,logFilePath,logMode));
1.185 + if(!err)
1.186 + { // nancy
1.187 + // find out the type of output format and assign to this new created control
1.188 + TInt error = logFilePath.Find(_L(".xml"));
1.189 + if(error==KErrNotFound) iControl->SetLogType(CLogFileControl::ETxt);
1.190 + else iControl->SetLogType(CLogFileControl::EXml);
1.191 + // end nancy
1.192 + // Append it to the logfile control class list
1.193 + server->LogControl().Append(iControl);
1.194 + }
1.195 + }
1.196 + if(!err)
1.197 + // Increment its session count
1.198 + iControl->AddSession();
1.199 + aMessage.Complete(err);
1.200 + }
1.201 + break;
1.202 + // One of the API write calls
1.203 + case RFileFlogger::EWriteLog :
1.204 + {
1.205 + // Sanity check
1.206 + if(!iControl)
1.207 + {
1.208 + aMessage.Complete(KErrNotFound);
1.209 + break;
1.210 + }
1.211 + // Data can be any size
1.212 + // Get the length from second argument
1.213 + TInt bufferLength = aMessage.Int1();
1.214 + // Get a heap buffer of the right size
1.215 + HBufC8* buffer = HBufC8::NewLC(bufferLength);
1.216 + TPtr8 ptr(buffer->Des());
1.217 + // read the data
1.218 + aMessage.ReadL(0,ptr);
1.219 + // Get a buffer control class contructed with the heap data
1.220 + // takes ownership
1.221 + CLogBuffer* logBuffer = new (ELeave) CLogBuffer(*buffer);
1.222 + CleanupStack::Pop(buffer);
1.223 + // Add it to the logfile control class buffer queue
1.224 + iControl->AddLogBuffer(*logBuffer);
1.225 + if(!iControl->IsActive())
1.226 + // AO is idle, kick into life
1.227 + iControl->Kick();
1.228 + aMessage.Complete(KErrNone);
1.229 + }
1.230 + break;
1.231 +
1.232 + default:
1.233 + break;
1.234 + }
1.235 + }
1.236 +
1.237 +///////
1.238 +
1.239 +CLogFileControl* CLogFileControl::NewL(CLogServer& aParent, const TDesC& aLogFilePath,RFileFlogger::TLogMode aMode)
1.240 +/**
1.241 + * @param aParent - Server reference for callback
1.242 + * @param aLogFilePath - Full path and filename of the logfile
1.243 + * @param aMode - Overwrite or Append
1.244 + *
1.245 + * First phase construction for logfile control class
1.246 + */
1.247 + {
1.248 + CLogFileControl* self = new (ELeave) CLogFileControl(aParent,aLogFilePath);
1.249 + CleanupStack::PushL(self);
1.250 + self->ConstructL(aMode);
1.251 + CleanupStack::Pop();
1.252 + return self;
1.253 + }
1.254 +
1.255 +CLogFileControl::CLogFileControl(CLogServer& aParent,const TDesC& aLogFilePath) :
1.256 + iParent(aParent),
1.257 + iLogFileName(aLogFilePath),
1.258 + iTransmitted(EFalse)
1.259 +/**
1.260 + * @param aParent - Server reference for callback
1.261 + * @param aLogFilePath - Full path and filename of the logfile
1.262 + *
1.263 + * Constructor - Safe initialisation
1.264 + */
1.265 + {
1.266 + iQueue.SetOffset(CLogBuffer::LinkOffset());
1.267 + }
1.268 +
1.269 +void CLogFileControl::ConstructL(RFileFlogger::TLogMode aMode)
1.270 +/**
1.271 + * @param aMode - Overwrite or Append
1.272 + *
1.273 + * Second phase construction - Create or open the logfile
1.274 + */
1.275 + {
1.276 + if(aMode == RFileFlogger::ELogModeOverWrite)
1.277 + // In overwrite mode replace
1.278 + User::LeaveIfError(iLogFile.Replace(iParent.Fs(),iLogFileName,EFileWrite));
1.279 + else
1.280 + {
1.281 + // For append try open then replace
1.282 + TInt err = iLogFile.Open(iParent.Fs(),iLogFileName,EFileWrite);
1.283 + if(err != KErrNone)
1.284 + // Bomb out if replace fails
1.285 + User::LeaveIfError(iLogFile.Replace(iParent.Fs(),iLogFileName,EFileWrite));
1.286 + else
1.287 + {
1.288 + // Open worked. Position at EOF
1.289 + TInt pos;
1.290 + iLogFile.Size(pos);
1.291 + User::LeaveIfError(iLogFile.Seek(ESeekEnd,pos));
1.292 + }
1.293 + }
1.294 + }
1.295 +
1.296 +CLogFileControl::~CLogFileControl()
1.297 +/**
1.298 + * Destructor
1.299 + * The server maintains a list of these classes and will not destruct one if there
1.300 + * is data on its queue
1.301 + * Destructor just closes the file handle.
1.302 + */
1.303 + {
1.304 + iLogFile.Close();
1.305 + }
1.306 +
1.307 +void CLogFileControl::RunL()
1.308 +/**
1.309 + * Main File writing pump
1.310 + * Called on write completion or Kick() by the session that accepts the data
1.311 + */
1.312 + {
1.313 +#if (defined _DEBUG)
1.314 + _LIT(KPanic,"LogEng RunL()");
1.315 +#endif
1.316 + __ASSERT_DEBUG(iStatus.Int() == KErrNone,User::Panic(KPanic,iStatus.Int()));
1.317 + // Check to see if this is the result of write completion
1.318 + if(iTransmitted)
1.319 + {
1.320 + // Write completed
1.321 + // Remove the buffer at the head of the queue and free it
1.322 + CLogBuffer* buffer = iQueue.First();
1.323 + iQueue.Remove(*buffer);
1.324 + delete buffer;
1.325 + }
1.326 + // Check to see if there's more on the queue
1.327 + if(!iQueue.IsEmpty())
1.328 + {
1.329 + // There is so write the head of the queue
1.330 + CLogBuffer* buffer = iQueue.First();
1.331 + SetActive();
1.332 + // Set the flag to say we've transmitted
1.333 + iTransmitted = ETrue;
1.334 +
1.335 +// ------------------------------------
1.336 + if(iLogFormat==ETxt) WriteTxt(buffer->Buf());
1.337 + else WriteXml(buffer->Buf());
1.338 + }
1.339 + else
1.340 + {
1.341 + // Nothing on the queue
1.342 + iTransmitted = EFalse;
1.343 + // Call into the server to check if this resource can be freed
1.344 + iParent.ControlComplete(*this);
1.345 + }
1.346 + }
1.347 +
1.348 +void CLogFileControl::WriteXml(const TDesC8 &aDes)
1.349 +/**
1.350 + * @param aDes - send a aDes string in xml format to a log file
1.351 + */
1.352 + {
1.353 +/*--------- Maintaince Warning: -----------------------------------
1.354 +******* the fomat of below is sensible from client original format.
1.355 +******* Any change should match actual string formated from client.
1.356 +******* Double check the code on client side
1.357 +
1.358 +* The current assumtion of format:
1.359 +* First string values are seperated by sign " - " and extra pair of fields are
1.360 +* seperated from main log message by long unusual string "LogFieldsRequiredBeingAddedToAboveLogMessage"
1.361 +* The \t used to seperate field name and field value and \r\n used to seperate
1.362 +* each other from those pairs of field
1.363 +* \t\t\t\t\t\t is used to end of whole string
1.364 +--------------------------------------------------------------------------------*/
1.365 + _LIT8(KxmlHeader,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n<LOGFILE>");
1.366 + //The order of variables:
1.367 + // time - aTime
1.368 + // Severity - aSeverity
1.369 + // Thread - aThread
1.370 + // Filename - aFilename
1.371 + // Linenumber - aLinenumber
1.372 + // Text - aText
1.373 +
1.374 + // Start of string retrive/deformating
1.375 + HBufC8* pBuf1 = HBufC8::New(KMaxLoggerLineLength*2); //(aDes.Length()+200);
1.376 + if(!pBuf1)
1.377 + {
1.378 + return; // no memory
1.379 + }
1.380 + TPtr8 aPtr(pBuf1->Des()); // used for searching
1.381 + TPtr8 alogbuf(pBuf1->Des()); //used for final log
1.382 + aPtr.Append(aDes);
1.383 + TPtrC8 SearchBuf;
1.384 + TInt aCount[8];
1.385 + aCount[0]=0;
1.386 + TInt posI(0);
1.387 +
1.388 + // retrive log message:
1.389 + // Retrive common part of log message:
1.390 + TInt i=0;
1.391 + for(i=1; i<6; i++)
1.392 + {
1.393 + SearchBuf.Set(aPtr.Mid(posI));
1.394 + aCount[i]=SearchBuf.Find(KSeperation8)+posI;
1.395 + posI=aCount[i]+3;
1.396 + if(aCount[i]<aCount[i-1])
1.397 + {
1.398 + delete pBuf1;
1.399 + return; // wrong format string from client
1.400 + }
1.401 + }
1.402 + // seperating common log message and extra log fields will be easy for future maintaince.
1.403 + TLogField8* alogField = new TLogField8[6]; // the common part of log message for both
1.404 + // with and without extra log fields
1.405 + if(!alogField)
1.406 + {
1.407 + delete pBuf1;
1.408 + return; // no memory
1.409 + }
1.410 +
1.411 + TLogField8* extralogField=NULL; // only applied to extra log fields
1.412 +
1.413 + TInt alength=0; // a length of array of extra log fields
1.414 +
1.415 + alogField[0].iLogTag8.Copy(_L8("TIME"));
1.416 + alogField[1].iLogTag8.Copy(_L8("SEVERITY"));
1.417 + alogField[2].iLogTag8.Copy(_L8("THREAD"));
1.418 + alogField[3].iLogTag8.Copy(_L8("FILENAME"));
1.419 + alogField[4].iLogTag8.Copy(_L8("LINENUMBER"));
1.420 + alogField[5].iLogTag8.Copy(_L8("TEXT"));
1.421 +
1.422 + alogField[0].iLogValue8.Copy(aPtr.Mid(aCount[0],aCount[1]-aCount[0]));
1.423 + for(i=1; i<5; i++)
1.424 + {
1.425 + alogField[i].iLogValue8.Copy(aPtr.Mid(aCount[i]+3,aCount[i+1]-aCount[i]-3));
1.426 + }
1.427 +
1.428 + SearchBuf.Set(aPtr.Mid(posI));
1.429 + aCount[6]=SearchBuf.Find(_L8("LogFieldsRequiredBeingAddedToAboveLogMessage"))+posI;
1.430 + if(aCount[6]<posI) // no addtional fields. Find return value is KErrNotFound or >0
1.431 + {
1.432 + alogField[5].iLogValue8.Copy(aPtr.Mid(aCount[5]+3,aDes.Length()-aCount[5]-5));
1.433 + }
1.434 + else
1.435 + {
1.436 + alogField[5].iLogValue8.Copy(aPtr.Mid(aCount[5]+3,aCount[6]-aCount[5]-5));
1.437 + posI=aCount[6]+45; //45 is from the length of long string and a tab
1.438 + SearchBuf.Set(aPtr.Mid(posI));
1.439 + aCount[7]=SearchBuf.Find(_L8("\r\n"))+posI;
1.440 + TLex8 lex(aPtr.Mid(posI,aCount[7]-posI)); // get the length
1.441 + TInt err=lex.Val(alength);
1.442 + if (err)
1.443 + alength=0; // ignor the extra log fields. Let the log go
1.444 +
1.445 + // Retrive the extra log fields
1.446 + extralogField = new TLogField8[alength];
1.447 + if(!extralogField)
1.448 + {
1.449 + delete pBuf1;
1.450 + return; // no memory
1.451 + }
1.452 + for(TInt i=0; i<alength; i++)
1.453 + {
1.454 + aCount[6]=aCount[7]+2;
1.455 + SearchBuf.Set(aPtr.Mid(aCount[6]));
1.456 + aCount[7]=SearchBuf.Find(_L8("\t"))+aCount[6];
1.457 + extralogField[i].iLogTag8.Copy(aPtr.Mid(aCount[6],aCount[7]-aCount[6]));
1.458 + aCount[6]=aCount[7]+1;
1.459 + SearchBuf.Set(aPtr.Mid(aCount[6]));
1.460 + aCount[7]=SearchBuf.Find(_L8("\r\n"))+aCount[6];
1.461 + extralogField[i].iLogValue8.Copy(aPtr.Mid(aCount[6],aCount[7]-aCount[6]));
1.462 + }
1.463 + }
1.464 + // Start to organize an XML format:
1.465 + TInt afileSize;
1.466 + _LIT(KLogMutex, "LoggingServerMutex");
1.467 + RMutex mutex;
1.468 + TInt r = mutex.CreateGlobal(KLogMutex);
1.469 + if(r==KErrAlreadyExists)
1.470 + r = mutex.OpenGlobal(KLogMutex);
1.471 +
1.472 + if(!r)
1.473 + mutex.Wait(); // If still failed, let logging go without bother the mutex.
1.474 + iLogFile.Size(afileSize);
1.475 + if(afileSize<12) // 12 is from charters of "\r\n</LOGFILE>"
1.476 + //It shoud happened once at the beginning of the file
1.477 + // such as overwrite mode
1.478 + {
1.479 + afileSize=12; // used for lock position
1.480 + alogbuf.Copy(KxmlHeader);
1.481 + }
1.482 + alogbuf.Append(_L8("\r\n<MESSAGE>\r\n"));
1.483 + for(TInt i=0; i<6; i++)
1.484 + {
1.485 + alogbuf.Append(_L8(" <"));
1.486 + alogbuf.Append(alogField[i].iLogTag8);
1.487 + alogbuf.Append(_L8(">"));
1.488 + alogbuf.Append(alogField[i].iLogValue8);
1.489 + alogbuf.Append(_L8("</"));
1.490 + alogbuf.Append(alogField[i].iLogTag8);
1.491 + alogbuf.Append(_L8(">\r\n"));
1.492 + }
1.493 + for(TInt i=0; i<alength; i++)
1.494 + {
1.495 + alogbuf.Append(_L8(" <"));
1.496 + alogbuf.Append(extralogField[i].iLogTag8);
1.497 + alogbuf.Append(_L8(">"));
1.498 + alogbuf.Append(extralogField[i].iLogValue8);
1.499 + alogbuf.Append(_L8("</"));
1.500 + alogbuf.Append(extralogField[i].iLogTag8);
1.501 + alogbuf.Append(_L8(">\r\n"));
1.502 + }
1.503 +
1.504 + alogbuf.Append(_L8("</MESSAGE>"));
1.505 + alogbuf.Append(_L8("\r\n</LOGFILE>"));
1.506 +
1.507 + iLogFile.Write(afileSize-12, alogbuf,iStatus);
1.508 +
1.509 + if(!r)
1.510 + {
1.511 + mutex.Signal();
1.512 + mutex.Close();
1.513 + }
1.514 +
1.515 + if(extralogField)
1.516 + delete[] extralogField;
1.517 +
1.518 + delete[] alogField;
1.519 + delete pBuf1;
1.520 + }
1.521 +
1.522 +void CLogFileControl::WriteTxt(const TDesC8 &aDes)
1.523 +/**
1.524 + * @param aDes - send a aDes string in xml format to a log file
1.525 + */
1.526 + {
1.527 +/*--------- Maintaince Warning for aLogBuffer -----------------------------------
1.528 +******* the fomat of below is sensible from client original format.
1.529 +******* Any change should match actual string formated from client.
1.530 +******* Double check the code on client side
1.531 +
1.532 +* The current assumtion of format:
1.533 +* First string values are seperated by sign " - " and extra pair of fields are
1.534 +* seperated from main log message by long unusual string "LogFieldsRequiredBeingAddedToAboveLogMessage"
1.535 +* The \t used to seperate field name and field value and \r\n used to seperate
1.536 +* each other from those pairs of field
1.537 +--------------------------------------------------------------------------------*/
1.538 + iLogFile.Write(aDes,iStatus);
1.539 + }
1.540 +///////
1.541 +CLogBuffer::CLogBuffer(HBufC8& aLogBuffer) : iLogBuffer(aLogBuffer)
1.542 +/**
1.543 + * @param aLogBuffer - Heap descriptor. This class takes ownership
1.544 + * Constructor
1.545 + */
1.546 + {
1.547 + }
1.548 +
1.549 +CLogBuffer::~CLogBuffer()
1.550 +/**
1.551 + * Destructor
1.552 + * This class owns a heap buffer so just free it
1.553 + */
1.554 + {
1.555 + delete &iLogBuffer;
1.556 + }