//----------------------------------------------------------------------------- /// GetPendingRequests /// /// This is a non-blocking function that will iterate through the pending /// requests and call ProcessRequest (See IProcessRequests.h) for each request. /// Frame-based plugins should call this function at the start of each frame. /// Global plugins do not need to call this function. //----------------------------------------------------------------------------- void GetPendingRequests() { if (smLockGet(g_strSharedMemoryName) == false) { return; } HTTPHeaderData requestHeader; DWORD dwSize = sizeof(HTTPHeaderData); int nCount = 0; while (smGet(g_strSharedMemoryName, NULL, 0) > 0) { smGet(g_strSharedMemoryName, (void*)&requestHeader, dwSize); HTTPRequestHeader* pRequest = new HTTPRequestHeader(requestHeader); // Check to see if POST data is present. if (pRequest->GetPostDataSize() > 0) { string strError; bool bRes = pRequest->ReadPostData(strError, true, g_strSharedMemoryName); if (bRes == false) { Log(logERROR, "Failed to read POST data during smGet().\n"); } // Debug code - leave in for now. //printf ( "%s\n", pRequest->GetPostData() ); } CommunicationID requestID = CreateRequest(pRequest, false); nCount++; if (g_processRequest(requestID) == false) { SendHTTPErrorResponse(requestID, 404); break; } } smUnlockGet(g_strSharedMemoryName); if (nCount > 0) { Log(logMESSAGE, "Server loading: %d\n", nCount); } }
gtASCIIString PeekPendingRequests() { if (smLockGet(g_strSharedMemoryName) == false) { return ""; } // Check to see if there is a command if (smGet(g_strSharedMemoryName, NULL, 0) == 0) { // There are no commands return ""; } HTTPHeaderData requestHeaderData; DWORD dwSize = sizeof(HTTPHeaderData); // Get the header smPeek(g_strSharedMemoryName, (void*)&requestHeaderData, dwSize); HTTPRequestHeader* pRequest = new HTTPRequestHeader(requestHeaderData); gtASCIIString str = pRequest->GetUrl(); smUnlockGet(g_strSharedMemoryName); // delete the header data delete pRequest; return str; }
//////////////////////////////////////////////////////////////////////////////////////////// /// Read the POST data section of a web request from shared memory /// Both input streams read pointers must be set at the beginning of the POST data. /// \param strError Output error string /// \param pSharedMemoryName name of the shared memory to read /// \return True if success, False if fail. //////////////////////////////////////////////////////////////////////////////////////////// bool HTTPRequestHeader::ReadPostData(string& strError, const char* pSharedMemoryName) { unsigned int nContentLength = StartReadPostData(strError); if (nContentLength == 0) { return false; } gtSize_t nRead = 0; // Read data from shared memory nRead = smGet(pSharedMemoryName, (void*)m_pPostData, nContentLength); // Terminate the buffer. m_pPostData[nRead] = '\0'; //// Keep in for Debugging //StreamLog::Ref() << "READ IN SHADER:" << pPostDataBuffer << "\n"; //printf ( "Read shader from web request: \n%s\n", m_pPostData); return true; }
//-------------------------------------------------------------- /// Injects the application specified by the Inject command and /// sends the results of the injection to the requester /// \param requestID the ID of the incoming request /// \param sCmd pointer to the string containing the request /// \param pClientSocket the socket used to send the response //-------------------------------------------------------------- void ProcessTracker::DoInjectCommand(CommunicationID requestID, char** sCmd, NetSocket* pClientSocket) { // clear the previous data since it is about to get reset g_allowedWrappersMap.clear(); m_injectedAppName = ""; m_injectedAppArgs = ""; // parse the command gtASCIIString strCmd(*sCmd); std::list< gtASCIIString > paramList; strCmd.Split("&", true, paramList); for (std::list< gtASCIIString >::const_iterator iter = paramList.begin(); iter != paramList.end(); ++iter) { char* pParam = (char*) iter->asCharArray(); char** ppParam = &pParam; if (IsToken(ppParam, "plugins=")) { // plugins= is followed by a list of comma separated short descriptions of wrappers gtASCIIString strPlugins(*ppParam); std::list< gtASCIIString > pluginList; strPlugins.Split(",", true, pluginList); for (std::list< gtASCIIString >::const_iterator iter1 = pluginList.begin(); iter1 != pluginList.end(); ++iter1) { if (*iter1 == "") { Log(logERROR, "Failed to parse plugin name\n"); SendTextResponse(requestID, "Error: Failed to parse plugin name.", pClientSocket); return; } WrapperMap::iterator iWrapper = GetWrapperMap().find(iter1->asCharArray()); if (iWrapper == GetWrapperMap().end()) { Log(logERROR, "Invalid plugin requested, you requested plugin %s\n", (*iter1).asCharArray()); SendFormattedTextResponse(requestID, pClientSocket, "Error: Invalid plugin requested, you requested plugin %s\n", (*iter1).asCharArray()); return; } // we have a valid index g_allowedWrappersMap.insert(*iWrapper); } } else if (IsToken(ppParam, "app=")) { m_injectedAppName = *ppParam; } else if (IsToken(ppParam, "args=")) { m_injectedAppArgs = *ppParam; } } // make sure an app name was specified if (m_injectedAppName.empty() == true) { Log(logERROR, "Application name not specified\n"); SendTextResponse(requestID, "Error: Application name not specified", pClientSocket); return; } // if no wrappers were specified, then use them all if (g_allowedWrappersMap.empty() == true) { g_allowedWrappersMap = GetWrapperMap(); } // fix the path gtASCIIString appName = m_injectedAppName.c_str(); appName.replace("%20", " "); appName.replace("%22", "\""); appName.replace("%5C", "\\"); appName.replace("%E2%80%93", "-"); appName.replace("%26", "&"); appName.replace("%27", "'"); appName.replace("%60", "`"); appName.replace("%E2%80%98", "`"); m_injectedAppName = appName.asCharArray(); gtASCIIString appArgs = m_injectedAppArgs.c_str(); appArgs.replace("%22", "\""); appArgs.replace("%20", " "); appArgs.replace("%5C", "\\"); appArgs.replace("%E2%80%93", "-"); appArgs.replace("%26", "&"); appArgs.replace("%27", "'"); appArgs.replace("%60", "`"); appArgs.replace("%E2%80%98", "`"); m_injectedAppArgs = appArgs.asCharArray(); if (WritePluginsToSharedMemoryAndLaunchApp() == true) { // loop up to 500 times while checking to see if a // wrapper name has been written into the shared memory from MicroDLL unsigned int uTimeout = 500; while (smGet("ActivePlugins", NULL, 0) == 0 && uTimeout > 0) { uTimeout--; osSleep(10); } if (uTimeout > 0) { SendHTMLResponse(requestID, "<html>OK</html>", pClientSocket); } else { Log(logWARNING, "The server has timed out while trying to receive the current active plugins\n"); gtASCIIString strErrorResponse; strErrorResponse = "<html>The server is taking a while to respond. <p/>Either the application does not use one of the selected APIs or it takes a while to load.<p/><br/>"; strErrorResponse.appendFormattedString("Please <a href='/page/index.html'>try to reconnect</a> if you expect it to continue or<br/> <a href='/%d/Kill'>click here to kill the process</a></html>", m_injectedAppID); SendHTMLResponse(requestID, strErrorResponse.asCharArray(), pClientSocket); } } else { Log(logERROR, "The plugins could not be written to shared memory or the application could not be launched\n"); SendTextResponse(requestID, "Error, see log file for more details", pClientSocket); } return; }
//-------------------------------------------------------------- /// Create the shared memory needed to communicate between the /// web server and the plugin /// \return true if successful, false if error //-------------------------------------------------------------- bool ProcessTracker::CreateSharedMemory() { #ifdef _WIN32 if (smExists("GPS_TO_MDLL")) { if (smOpen("GPS_TO_MDLL")) { if (smLockGet("GPS_TO_MDLL")) { //the shared memory already exists, so we need to read everything from it, to make sure it's empty // closing and recreated the shared memory does not work because the MicroDLL has it open // so that it can inject into any spawned processes (via CreateProcess). If MicroDLL doesn't keep // a handle to the shared memory, it could be closed by everyone, get destroyed and then it wouldn't // be able to follow CreateProcess. char tmp[ PS_MAX_PATH ]; while (smGet("GPS_TO_MDLL", NULL, 0) != 0) { // the smGet call will clear the shared memory smGet("GPS_TO_MDLL", tmp, PS_MAX_PATH); } smUnlockGet("GPS_TO_MDLL"); } } } // now write the plugin info into the shared memory // Put wrapper info into a shared memory for micro DLL // make the shared memory big enough for all the wrappers that are known about // even though we're only going to write in the wrappers that are allowed if (smCreate("GPS_TO_MDLL", (unsigned long)GetWrapperMap().size() * 3, PS_MAX_PATH)) { // lock the buffer if it has enough space for 3 strings for each of the allowed wrappers unsigned long ulNumBuffers = (unsigned long) g_allowedWrappersMap.size() * 3; if (smLockPut("GPS_TO_MDLL", ulNumBuffers * PS_MAX_PATH, ulNumBuffers)) { char strPath[PS_MAX_PATH]; char strName[PS_MAX_PATH]; char strDlls[PS_MAX_PATH]; for (WrapperMap::const_iterator iter = g_allowedWrappersMap.begin(); iter != g_allowedWrappersMap.end(); ++iter) { strcpy_s(strPath, PS_MAX_PATH, iter->second.strPluginPath.asCharArray()); strcpy_s(strName, PS_MAX_PATH, iter->second.strPluginName.asCharArray()); strcpy_s(strDlls, PS_MAX_PATH, iter->second.strWrappedDll.asCharArray()); if (smPut("GPS_TO_MDLL", (void*) strPath, PS_MAX_PATH) == false || smPut("GPS_TO_MDLL", (void*) strName, PS_MAX_PATH) == false || smPut("GPS_TO_MDLL", (void*) strDlls, PS_MAX_PATH) == false) { Log(logERROR, "Couldn't put wrapper info into shared memory.\n"); smUnlockPut("GPS_TO_MDLL"); return false; } } smUnlockPut("GPS_TO_MDLL"); } else { Log(logERROR, "There is not enough space in the shared memory for the desired content.\n"); return false; } } else { Log(logERROR, "Couldn't create shared memory to pass wrapper registration\n"); return false; } #else for (WrapperMap::const_iterator iter = g_allowedWrappersMap.begin(); iter != g_allowedWrappersMap.end(); ++iter) { // On Linux, Create the shared memory from the PerfStudio server so that // it is cleaned up properly. if (smCreate(iter->second.strPluginShortDesc.asCharArray(), 100, sizeof(HTTPRequestHeader)) == false) { Log(logERROR, "Couldn't create shared memory for plugin.\n"); return false; } } #endif // def _WIN32 return true; }
//-------------------------------------------------------------- /// Creates and then waits for the PLUGINS_TO_GPS_SEMAPHORE to be /// signaled, then reads the responses in PLUGINS_TO_GPS shared /// memory and sends them to the requester. /// \param pData should be NULL (it is ignored though) //-------------------------------------------------------------- void PluginResponseThread::WaitForPluginResponses(void* pData) { PS_UNREFERENCED_PARAMETER(pData); //create a semaphore which allows putting at most MAX_SEM_COUNT response into SM; NamedSemaphore semaphore; semaphore.Create("PLUGINS_TO_GPS_SEMAPHORE"); bool bEvent; for (;;) // loop forever { bEvent = semaphore.Wait(); if (bEvent == false) { Log(logERROR, "Failed to wait on an event (Error %d). Closing response thread.\n", osGetLastSystemError()); smClose("PLUGINS_TO_GPS"); semaphore.Close(); return; } // the PLUGINS_TO_GPS_EVENT was signaled // retrieve and send the response if (smLockGet("PLUGINS_TO_GPS")) { // read data from shared memory CommunicationID requestID = 0; char pcMimeType[PS_MAX_PATH]; // the response was signaled, read out one response if (bEvent == true) { requestID = 0; memset(pcMimeType, 0, PS_MAX_PATH); char* pResponse = NULL; unsigned long uResponseSize = 0; LARGE_INTEGER nPreSharedMemoryGetTime; OSWrappers::QueryPerformanceCounter(&nPreSharedMemoryGetTime); if (smGet("PLUGINS_TO_GPS", &requestID, sizeof(CommunicationID)) == sizeof(CommunicationID)) { // successfully got the requestID // Get the socket associated with this requestID and remove the socket since we're now donw with it. NetSocket* client_socket = ProcessTracker::Instance()->GetSocketFromHandle(requestID); ProcessTracker::Instance()->RemoveSocketFromMap(requestID); #ifdef CODEXL_GRAPHICS #ifdef USE_GRAPHICS_SERVER_STATUS_RETURN_CODES // We can remove this message from the DB as we no longer need to monitor it anymore RequestsInFlightDatabase::Instance()->Remove(client_socket); #endif #endif // now try to get the mime type if (smGet("PLUGINS_TO_GPS", &pcMimeType, PS_MAX_PATH) > 0) { // successfully got the mime type // get response size while (uResponseSize == 0) { uResponseSize = smGet("PLUGINS_TO_GPS", NULL, 0); } try { pResponse = new char[uResponseSize]; memset(pResponse, 0, uResponseSize * sizeof(char)); } catch (std::bad_alloc) { Log(logERROR, "Failed to allocate memory for response of size: %lu\n", uResponseSize); pResponse = NULL; } if (pResponse != NULL) { // Read from shared memory if (smGet("PLUGINS_TO_GPS", pResponse, uResponseSize) == 0) { Log(logERROR, "Failed to get response from sharedMemory.\n"); smReset("PLUGINS_TO_GPS"); } #ifdef DEBUG_COMMS_PERFORMANCE // Record the time now CommandTiming* pTiming = CommandTimingManager::Instance()->HandleResponse((NetSocket*)requestID); if (pTiming != NULL) { LARGE_INTEGER nPerformanceCount; QueryPerformanceCounter(&nPerformanceCount); pTiming->SetWebServerRoundTripEnd(nPerformanceCount); // Must set this one before SetPreSharedMemoryGet pTiming->SetPreSharedMemoryGet(nPreSharedMemoryGetTime); pTiming->SetResponseSize(uResponseSize); } #endif // Send the data back to the client SendMimeResponse(requestID, pcMimeType, pResponse, uResponseSize, client_socket); #ifdef DEBUG_COMMS_PERFORMANCE CommandTimingManager::Instance()->IncrementServerLoadingCount(-1); #endif SAFE_DELETE_ARRAY(pResponse); } else { smReset("PLUGINS_TO_GPS"); SendMimeResponse(requestID, "plain/text", "Error: Failed to get response from shared memory\n", 49 * sizeof(char), client_socket); } } else { smReset("PLUGINS_TO_GPS"); SendMimeResponse(requestID, "plain/text", "Error: Could not read mime type\n", 32 * sizeof(char), client_socket); } } else { // in this case, we don't know socket the communication was on, so we can't send any errors or even close the socket // just have to let the client time out. smReset("PLUGINS_TO_GPS"); Log(logERROR, "Failed to get requestID from sharedMemory.\n"); } } // end while sm has data or no responses have been read smUnlockGet("PLUGINS_TO_GPS"); } else { Log(logERROR, "LockGet Failed\n"); } } }