epoc-cliapi-server.cpp

Go to the documentation of this file.
00001 // 
00002 // This code is derived from Symbian Ltd provided and copyrighted
00003 // example code from year 2000. Note, however, the boilerplate nature
00004 // of most Symbian client-server code, and therefore the lack of
00005 // originality in such code.
00006 // 
00007 
00008 #include "epoc-cliapi-server.hpp"
00009 
00010 #include "epoc-cl2app-clientserver.hpp"
00011 
00012 #include "lua_cl2.h"
00013 #include "utils_cl2.h" // for string conversions
00014 #include "symbian_auto_ptr.hpp"
00015 
00016 #include "common/gxlowmem.h"
00017 #include "common/logging.h"
00018 #include "common/platform_error.h"
00019 #include "common/sh_utils.h"
00020 
00021 #include <glib.h>
00022 
00023 // --------------------------------------------------
00024 // utilities
00025 // --------------------------------------------------
00026 
00027 _LIT(KServerPanicCat, "cl2cliserv");
00028 
00029 enum TMyPanic
00030   {
00031     EPanicBadDescriptor = 1,
00032     EPanicIllegalFunction,
00033     EPanicAlreadyReceiving
00034   };
00035 
00036 static void PanicClient(const RMessagePtr2& aMessage, TMyPanic aPanicNum)
00037 //
00038 // RMessage::Panic() also completes the message. This is:
00039 // (a) important for efficient cleanup within the kernel
00040 // (b) a problem if the message is completed a second time
00041 //
00042 {
00043   aMessage.Panic(KServerPanicCat, aPanicNum);
00044 }
00045 
00046 // --------------------------------------------------
00047 // session interface
00048 // --------------------------------------------------
00049 
00050 class CCliapiSession : public CSession2
00051 {
00052 public:
00053   CCliapiSession();
00054   void CreateL();
00055   void Send(const TDesC& aMessage);
00056 private:
00057   ~CCliapiSession();
00058   inline CCliapiServer& Server();
00059   void ServiceL(const RMessage2& aMessage);
00060   void ServiceError(const RMessage2& aMessage,TInt aError);
00061   inline TBool ReceivePending() const;
00062 private:
00063   RMessagePtr2 iReceiveMsg;
00064   TInt iReceiveLen;
00065 };
00066 
00067 // --------------------------------------------------
00068 // server implementation
00069 // --------------------------------------------------
00070 
00071 TInt CCliapiServer::Start()
00072 {
00073   return CServer2::Start(KMyServerName);
00074 }
00075 
00076 // This is private, so okay to define as "inline" here.
00077 inline CCliapiServer::CCliapiServer() :
00078   CServer2(CActive::EPriorityStandard, ESharableSessions)
00079 {}
00080 
00081 CCliapiServer* CCliapiServer::NewLC()
00082 {
00083   CCliapiServer* self = new (ELeave) CCliapiServer;
00084   CleanupStack::PushL(self);
00085   self->ConstructL();
00086   return self;
00087 }
00088 
00089 CCliapiServer* CCliapiServer::NewL()
00090 {
00091   CCliapiServer* self = NewLC();
00092   CleanupStack::Pop();
00093   return self;
00094 }
00095 
00096 void CCliapiServer::ConstructL()
00097 //
00098 // 2nd phase construction - ensure the timer and server objects are running
00099 //
00100 {
00101   //User::LeaveIfError(StartL()); // no automatic start for us
00102 }
00103 
00104 CSession2* CCliapiServer::NewSessionL(const TVersion&,const RMessage2&) const
00105 //
00106 // Cretae a new client session. This should really check the version number.
00107 //
00108 {
00109   return new(ELeave) CCliapiSession();
00110 }
00111 
00112 void CCliapiServer::AddSession()
00113 //
00114 // A new session is being created
00115 //
00116 {
00117   ++iSessionCount;
00118 }
00119 
00120 void CCliapiServer::DropSession()
00121 //
00122 // A session is being destroyed
00123 //
00124 {
00125   iSessionCount--;
00126 }
00127 
00128 void CCliapiServer::Send(const TDesC& aMessage)
00129 //
00130 // Pass on the signal to all clients
00131 //
00132 {
00133   iSessionIter.SetToFirst();
00134   CSession2* s;
00135   while ((s=iSessionIter++)!=0)
00136     static_cast<CCliapiSession*>(s)->Send(aMessage);
00137 }
00138 
00139 // --------------------------------------------------
00140 // session implementation
00141 // --------------------------------------------------
00142 
00143 inline CCliapiSession::CCliapiSession()
00144 {}
00145 
00146 inline CCliapiServer& CCliapiSession::Server()
00147 {
00148   return *static_cast<CCliapiServer*>(const_cast<CServer2*>(CSession2::Server()));
00149 }
00150 
00151 inline TBool CCliapiSession::ReceivePending() const
00152 {
00153   return !iReceiveMsg.IsNull();
00154 }
00155 
00156 void CCliapiSession::CreateL()
00157 //
00158 // 2nd phase construct for sessions - called by the CServer framework
00159 //
00160 {
00161   Server().AddSession();
00162 }
00163 
00164 CCliapiSession::~CCliapiSession()
00165 {
00166   Server().DropSession();
00167 }
00168 
00169 void CCliapiSession::Send(const TDesC& aMessage)
00170 //
00171 // Deliver the message to the client, truncating if required
00172 // If the write fails, panic the client, not the sender
00173 //
00174 {
00175   if (ReceivePending())
00176     {
00177       TPtrC m(aMessage);
00178       if (iReceiveLen < aMessage.Length())
00179   m.Set(m.Left(iReceiveLen));
00180       TInt r = iReceiveMsg.Write(0, m);
00181       if (r == KErrNone)
00182   iReceiveMsg.Complete(KErrNone);
00183       else
00184   PanicClient(iReceiveMsg, EPanicBadDescriptor);
00185     }
00186 }
00187 
00188 /*
00189 class MaybeCharPtr
00190 {
00191  public:
00192   MaybeCharPtr() : iPtr(NULL) {}
00193   ~MaybeCharPtr() { if (iPtr) g_free(iPtr); }
00194   char* iPtr;
00195 };
00196 */
00197 
00198 void CCliapiSession::ServiceL(const RMessage2& aMessage)
00199 //
00200 // Handle a client request.
00201 // Leaving is handled by CCliapiServer::ServiceError() which reports
00202 // the error code to the client
00203 //
00204 {
00205   switch (aMessage.Function())
00206     {
00207     case ETickCountFresh:
00208       {
00209   TUint tc = User::TickCount();
00210   TPckg<TUint> result(tc);
00211   aMessage.WriteL(0, result);
00212   aMessage.Complete(KErrNone);
00213   break;
00214       }
00215 
00216     case ETryEvalScript:
00217       {
00218         // It is somewhat of an issue if the client has to allocate a
00219         // "large enough" buffer for us to write the result to, but we
00220         // cannot really go and allocate memory for the client side,
00221         // can we now?
00222 
00223         TInt errCode = KErrNone;
00224 
00225         // No docs or examples about the use of text descriptors to be
00226         // found, but experimentation shows that the length is in
00227         // characters, not bytes, so it is the length of the original
00228         // descriptor, not that of the Pckg.
00229         TInt srcLen = aMessage.GetDesLengthL(0);
00230 
00231         TInt dstLen = aMessage.GetDesMaxLengthL(2);
00232 
00233         // Must allocate a srcLen size buffer into which to read the
00234         // data.
00235         HBufC* sc = HBufC::NewLC(srcLen);
00236         TPtr s(sc->Des());
00237         aMessage.ReadL(0, s);
00238         CleanupStack::PopAndDestroy(sc);
00239 
00240         // This service message is reserved for experiments. Now we
00241         // happen to do this instead of anything useful.
00242         TPckg<TInt> srcLenPk(dstLen);
00243         aMessage.WriteL(1, srcLenPk);
00244 
00245         aMessage.Complete(errCode);
00246         break;
00247       }
00248 
00249     case EEvalGetResult:
00250       {
00251   TInt errCode = KErrNone;
00252 
00253         // No docs or examples about the use of text descriptors to be
00254         // found, but experimentation shows that the length is in
00255         // characters, not bytes, so it is the length of the original
00256         // descriptor, not that of the Pckg.
00257   TInt srcLen = aMessage.GetDesLengthL(0);
00258 
00259         // It is somewhat of an issue if the client has to allocate a
00260         // "large enough" buffer for us to write the result to, but we
00261         // cannot really go and allocate memory for the client side,
00262         // can we now?
00263   TInt dstLen = aMessage.GetDesMaxLengthL(2);
00264 
00265         // Must allocate a srcLen size buffer into which to read the
00266         // data.
00267         HBufC* srcBuf = HBufC::NewLC(srcLen);
00268         TPtr srcDes(srcBuf->Des());
00269         aMessage.ReadL(0, srcDes);
00270   HBufC8* srcBuf8 = ConvToUtf8ZL(srcDes);
00271         CleanupStack::PopAndDestroy(srcBuf);
00272 
00273   e_auto_ptr<HBufC8> cleanupSrcBuf8(srcBuf8); // takes ownership
00274   const TUint8* srcU = srcBuf8->Ptr();
00275   const char* srcC = (const char*)srcU;
00276   logt(srcC);
00277 
00278         // Now must have a Lua VM evaluate the data. The expression
00279         // should evaluate to a string. Lua's strings are 8-bit clean,
00280         // so we can use UTF-8, and Unicode can hence appear in string
00281         // literals, but Unicode in variable names is likely to cause
00282         // a parse error.
00283   lua_State *L = cl_lua_new_libs();
00284   if (!L)
00285     User::Leave(KErrNoMemory);
00286   lua_State_auto_ptr cleanupLuaState(L);
00287 
00288   const char* luaResult = NULL;
00289 #define luaResultBuf_size 100
00290   gchar luaResultBuf[luaResultBuf_size];
00291 
00292         // When we don't get an actual Symbian error, we set the error
00293         // code KErrLuaErr; this way all the errors we write to the
00294         // client are Symbian style.
00295         //
00296         // Note that retrieving the error message when none is
00297         // available can in itself cause an exception, and a USER-EXEC
00298         // 3 panic.
00299   TInt evalErr = 0;
00300   TRAPD(leaveErr, evalErr = luaL_loadstring(L, srcC));
00301   if (leaveErr) {
00302     logg("leave %d in luaL_loadstring!", leaveErr);
00303     evalErr = leaveErr;
00304     luaResult = "<Symbian exception in load>";
00305   } else if (evalErr) {
00306     logg("luaL_loadstring error %d", evalErr);
00307     switch (evalErr) {
00308     case LUA_ERRSYNTAX:
00309       {
00310         luaResult = "<syntax error during precompilation>";
00311         break;
00312       }
00313     case LUA_ERRMEM:
00314       {
00315         luaResult = "<out of memory>";
00316         break;
00317       }
00318     default: 
00319       {
00320         if (!lua_isnone(L, -1)) // if acceptable index
00321     luaResult = lua_tostring(L, -1);
00322         if (!luaResult)
00323     luaResult = "<unknown error in load>";
00324         break;
00325       }
00326     }
00327     evalErr = KErrLuaErr;
00328   } else /* load okay */ {
00329     logt("luaL_loadstring ok");
00330 
00331           // We should get 0 (for success), or one of LUA_ERRRUN,
00332           // LUA_ERRMEM, or LUA_ERRERR (all positive values), or a
00333           // Symbian error code (a negative value). Plus if there was
00334           // an error we should have a string error message as well.
00335     TRAP(leaveErr, evalErr = lua_pcall(L, 0, 1, 0));
00336     if (leaveErr) {
00337       // This should not happen.
00338       logg("leave %d escaped lua_pcall!", leaveErr);
00339       evalErr = leaveErr;
00340       luaResult = "<escaped Symbian exception in eval>";
00341     } else if (evalErr) {
00342       logg("lua_pcall err %d", evalErr);
00343       if (!lua_isnone(L, -1)) // if acceptable index
00344         luaResult = lua_tostring(L, -1);
00345       if (!luaResult) {
00346               // evalErr here may be a Symbian error as well. They are
00347               // negative, while the Lua errors are positive.
00348         switch (evalErr) {
00349         case LUA_ERRRUN:
00350     {
00351       luaResult = "<runtime error>";
00352       break;
00353     }
00354         case LUA_ERRERR:
00355     {
00356       luaResult = "<error handler error>";
00357       break;
00358     }
00359         case LUA_ERRMEM:
00360     {
00361       luaResult = "<out of memory>";
00362       break;
00363     }
00364         default: 
00365     {
00366       TRAP_OOM_FAIL({
00367           g_snprintf(luaResultBuf, luaResultBuf_size,
00368          "Symbian error in eval: %s (%d)",
00369          symbian_error_strerror(evalErr), evalErr);
00370         });
00371       luaResult = luaResultBuf;
00372       break;
00373     fail: 
00374       luaResult = "<out of memory>";
00375       break;
00376     }
00377         }
00378       }
00379       evalErr = KErrLuaErr;
00380     } else /* eval okay */ {
00381       if (lua_isnone(L, -1)) {
00382         luaResult = "<eval to no value>";
00383       } else {
00384         luaResult = lua_tostring(L, -1);
00385         if (!luaResult) {
00386     evalErr = KErrLuaErr;
00387     luaResult = "<eval to non-string value>";
00388         }
00389       }
00390     }
00391   }
00392   logt(luaResult);
00393 
00394   // Convert to Unicode descriptor.
00395   TPtrC8 resultDes8((TUint8*)luaResult);
00396   e_auto_ptr<HBufC> resultBuf16(ConvFromUtf8L(resultDes8)); // takes ownership
00397   TPtrC resultDes(*resultBuf16);
00398 
00399         // I think we shall return an overflow error if the result
00400         // does not fit into the client-side buffer, but must check
00401         // for that of course.
00402   if (resultDes.Length() > dstLen)
00403     User::Leave(KErrOverflow);
00404 
00405         // We do not consider a Lua code evaluation error to be a
00406         // Symbian client-server session error as such, and we should
00407         // complete with KErrNone, and report the error code. An error
00408         // text will have been written if the error code is
00409         // KErrLuaErr.
00410         TPckg<TInt> evalErrPk(evalErr);
00411         aMessage.WriteL(1, evalErrPk);
00412 
00413   // Write the result string to the client side.
00414   aMessage.WriteL(2, resultDes);
00415 
00416   aMessage.Complete(errCode);
00417   break;
00418       }
00419 
00420     default:
00421       {
00422   PanicClient(aMessage, EPanicIllegalFunction);
00423   break;
00424       }
00425     }
00426 }
00427 
00428 void CCliapiSession::ServiceError(const RMessage2& aMessage,TInt aError)
00429 //
00430 // Handle an error from CCliapiSession::ServiceL()
00431 // A bad descriptor error implies a badly programmed client, so panic it;
00432 // otherwise use the default handling (report the error to the client)
00433 //
00434 {
00435   /*
00436   if (aError==KErrBadDescriptor)
00437     PanicClient(aMessage,EPanicBadDescriptor);
00438   */
00439   CSession2::ServiceError(aMessage,aError);
00440 }
00441 
00442 /**
00443 
00444 epoc-cliapi-server.cpp
00445 
00446 Copyright 2009 Helsinki Institute for Information Technology (HIIT)
00447 and the authors. All rights reserved.
00448 
00449 Authors: Tero Hasu <tero.hasu@hut.fi>
00450 
00451 Permission is hereby granted, free of charge, to any person
00452 obtaining a copy of this software and associated documentation files
00453 (the "Software"), to deal in the Software without restriction,
00454 including without limitation the rights to use, copy, modify, merge,
00455 publish, distribute, sublicense, and/or sell copies of the Software,
00456 and to permit persons to whom the Software is furnished to do so,
00457 subject to the following conditions:
00458 
00459 The above copyright notice and this permission notice shall be
00460 included in all copies or substantial portions of the Software.
00461 
00462 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00463 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00464 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00465 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00466 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00467 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00468 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00469 SOFTWARE.
00470 
00471  **/

ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1