/******************************************************************************/ /* */ /* X r d S s i S e r v R e a l . c c */ /* */ /* (c) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ #include #include #include #include "XrdSsi/XrdSsiResource.hh" #include "XrdSsi/XrdSsiRRAgent.hh" #include "XrdSsi/XrdSsiScale.hh" #include "XrdSsi/XrdSsiServReal.hh" #include "XrdSsi/XrdSsiSessReal.hh" #include "XrdSsi/XrdSsiTrace.hh" #include "XrdSsi/XrdSsiUtils.hh" /******************************************************************************/ /* S t a t i c s & G l o b a l s */ /******************************************************************************/ namespace XrdSsi { XrdSsiScale sidScale; } using namespace XrdSsi; /******************************************************************************/ /* D e s t r u c t o r */ /******************************************************************************/ XrdSsiServReal::~XrdSsiServReal() { XrdSsiSessReal *sP; // Free pointer to the manager node // if (manNode) {free(manNode); manNode = 0;} // Delete all free session objects // while((sP = freeSes)) {freeSes = sP->nextSess; delete sP; } } /******************************************************************************/ /* Private: A l l o c */ /******************************************************************************/ XrdSsiSessReal *XrdSsiServReal::Alloc(const char *sName, int uent, bool hold) { XrdSsiSessReal *sP; // Reuse or allocate a new session object and return it // myMutex.Lock(); actvSes++; if ((sP = freeSes)) {freeCnt--; freeSes = sP->nextSess; myMutex.UnLock(); sP->InitSession(this, sName, uent, hold); } else { myMutex.UnLock(); if (!(sP = new XrdSsiSessReal(this, sName, uent, hold))) {myMutex.Lock(); actvSes--; myMutex.UnLock();} } return sP; } /******************************************************************************/ /* Private: G e n U R L */ /******************************************************************************/ bool XrdSsiServReal::GenURL(XrdSsiResource *rP, char *buff, int blen, int uEnt) { static const char affTab[] = "\0\0n\0w\0s\0S"; const char *xUsr, *xAt, *iSep, *iVal, *tVar, *tVal, *uVar, *uVal; const char *aVar, *aVal, *qVal = ""; char uBuff[8]; int n; // Preprocess avoid list, if any // if (rP->hAvoid.length() == 0) tVar = tVal = ""; else {tVar = "&tried="; tVal = rP->hAvoid.c_str(); qVal = "?"; } // Preprocess affinity // if (!(rP->affinity)) aVar = aVal = ""; else {aVar = "&cms.aff="; aVal = &affTab[rP->affinity*2]; qVal = "?"; } // Check if we need to add a user name // if (rP->rUser.length() == 0) uVar = uVal = ""; else {uVar = "&ssi.user="; uVal = rP->rUser.c_str(); qVal = "?"; } // Preprocess the cgi information // if (rP->rInfo.length() == 0) iSep = iVal = ""; else {iVal = rP->rInfo.c_str(); iSep = "&ssi.cgi="; qVal = "?"; } // Check if we need to qualify the host with a user index // if (uEnt == 0) xUsr = xAt = ""; else {snprintf(uBuff, sizeof(uBuff), "%d", uEnt); xUsr= uBuff; xAt = "@"; } // Generate appropriate url // ? t a u i n = snprintf(buff, blen, "xroot://%s%s%s/%s%s%s%s%s%s%s%s%s%s", xUsr, xAt, manNode, rP->rName.c_str(), qVal, tVar, tVal, aVar, aVal, uVar, uVal, iSep, iVal); // Return overflow or not // return n < blen; } /******************************************************************************/ /* P r o c e s s R e q u e s t */ /******************************************************************************/ void XrdSsiServReal::ProcessRequest(XrdSsiRequest &reqRef, XrdSsiResource &resRef) { static const uint32_t useCache = XrdSsiResource::Reusable | XrdSsiResource::Discard; XrdSysMutexHelper mHelp; XrdSsiSessReal *sObj; std::string resKey; int uEnt; bool hold = (resRef.rOpts & XrdSsiResource::Reusable) != 0; char epURL[4096]; // Validate the resource name // if (resRef.rName.length() == 0) {XrdSsiUtils::RetErr(reqRef, "Resource name missing.", EINVAL); return; } // Check if this is a reusable resource. Reusable resources are a bit more // complicated to pull off. In any case, we need to hold the cache lock. // if (resRef.rOpts & useCache) {mHelp.Lock(&rcMutex); if (ResReuse(reqRef, resRef, resKey)) return; } // Get a sid entry number // if ((uEnt = sidScale.getEnt()) < 0) {XrdSsiUtils::RetErr(reqRef, "Out of stream resources.", ENOSR); return; } // Construct url // if (!GenURL(&resRef, epURL, sizeof(epURL), uEnt)) {XrdSsiUtils::RetErr(reqRef, "Resource url is too long.", ENAMETOOLONG); sidScale.retEnt(uEnt); return; } // Obtain a new session object // if (!(sObj = Alloc(resRef.rName.c_str(), uEnt, hold))) {XrdSsiUtils::RetErr(reqRef, "Insufficient memory.", ENOMEM); sidScale.retEnt(uEnt); return; } // Tag the session object with the resource key if it is being held. We need // to do this before doing provision as that may fail at any point. // if (hold) sObj->SetKey(resKey.c_str()); // Now just provision this resource which will execute the request should it // be successful. If Provision() fails, we need to delete the session object // because its file object now is in an usable state (funky client interface). // if (!(sObj->Provision(&reqRef, epURL))) Recycle(sObj, false); // If this was started with a reusable resource, put the session in the cache. // The resource key was constructed by the call to ResReuse() and the cache // mutex is still held at this point (will be released upon return). // if (hold) resCache[resKey] = sObj; } /******************************************************************************/ /* R e c y c l e */ /******************************************************************************/ void XrdSsiServReal::Recycle(XrdSsiSessReal *sObj, bool reuse) { EPNAME("Recycle"); static const char *tident = "ServRecycle"; const char *resKey; // Clear all pending events (likely not needed) // sObj->ClrEvent(); // Remove entry from the reusable cache if present // if ((resKey = sObj->GetKey())) StopReuse(resKey); // Add to queue unless we have too many of these or caller wants a deletion. // myMutex.Lock(); actvSes--; DEBUG("reuse=" <second; if (resRef.rOpts & XrdSsiResource::Discard || !sesP->Run(&reqRef)) {resCache.erase(it); sesP->UnHold(); return false; } // All done, the request should have been sent off via Reusable() call. // return true; } /******************************************************************************/ /* S t o p */ /******************************************************************************/ bool XrdSsiServReal::Stop() { // Make sure we are clean // myMutex.Lock(); if (actvSes) {myMutex.UnLock(); return false;} myMutex.UnLock(); delete this; return true; } /******************************************************************************/ /* S t o p R e u s e */ /******************************************************************************/ void XrdSsiServReal::StopReuse(const char *resKey) { EPNAME("StopReuse"); static const char *tident = "ServReuse"; std::map::iterator it; // Remove this entry from the reuse cache // rcMutex.Lock(); it = resCache.find(resKey); if (it != resCache.end()) {resCache.erase(it); DEBUG("resCache " <