XRootD
Loading...
Searching...
No Matches
XrdSsiServReal.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d S s i S e r v R e a l . c c */
4/* */
5/* (c) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* Produced by Andrew Hanushevsky for Stanford University under contract */
7/* DE-AC02-76-SFO0515 with the Department of Energy */
8/* */
9/* This file is part of the XRootD software suite. */
10/* */
11/* XRootD is free software: you can redistribute it and/or modify it under */
12/* the terms of the GNU Lesser General Public License as published by the */
13/* Free Software Foundation, either version 3 of the License, or (at your */
14/* option) any later version. */
15/* */
16/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19/* License for more details. */
20/* */
21/* You should have received a copy of the GNU Lesser General Public License */
22/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24/* */
25/* The copyright holder's institutional names and contributor's names may not */
26/* be used to endorse or promote products derived from this software without */
27/* specific prior written permission of the institution or contributor. */
28/******************************************************************************/
29
30#include <cerrno>
31#include <cstdio>
32#include <cstring>
33
36#include "XrdSsi/XrdSsiScale.hh"
39#include "XrdSsi/XrdSsiTrace.hh"
40#include "XrdSsi/XrdSsiUtils.hh"
41
42#ifndef ENOSR
43#define ENOSR ENOSPC
44#endif
45
46/******************************************************************************/
47/* S t a t i c s & G l o b a l s */
48/******************************************************************************/
49
50namespace XrdSsi
51{
53}
54
55using namespace XrdSsi;
56
57/******************************************************************************/
58/* D e s t r u c t o r */
59/******************************************************************************/
60
62{
64
65// Free pointer to the manager node
66//
67 if (manNode) {free(manNode); manNode = 0;}
68
69// Delete all free session objects
70//
71 while((sP = freeSes))
72 {freeSes = sP->nextSess;
73 delete sP;
74 }
75}
76
77/******************************************************************************/
78/* Private: A l l o c */
79/******************************************************************************/
80
81XrdSsiSessReal *XrdSsiServReal::Alloc(const char *sName, int uent, bool hold)
82{
84
85// Reuse or allocate a new session object and return it
86//
87 myMutex.Lock();
88 actvSes++;
89 if ((sP = freeSes))
90 {freeCnt--;
91 freeSes = sP->nextSess;
92 myMutex.UnLock();
93 sP->InitSession(this, sName, uent, hold);
94 } else {
95 myMutex.UnLock();
96 if (!(sP = new XrdSsiSessReal(this, sName, uent, hold)))
97 {myMutex.Lock(); actvSes--; myMutex.UnLock();}
98 }
99 return sP;
100}
101
102/******************************************************************************/
103/* Private: G e n U R L */
104/******************************************************************************/
105
106bool XrdSsiServReal::GenURL(XrdSsiResource *rP, char *buff, int blen, int uEnt)
107{
108 static const char affTab[] = "\0\0n\0w\0s\0S";
109 const char *xUsr, *xAt, *iSep, *iVal, *tVar, *tVal, *uVar, *uVal;
110 const char *aVar, *aVal, *qVal = "";
111 char uBuff[8];
112 int n;
113
114// Preprocess avoid list, if any
115//
116 if (rP->hAvoid.length() == 0) tVar = tVal = "";
117 else {tVar = "&tried=";
118 tVal = rP->hAvoid.c_str();
119 qVal = "?";
120 }
121
122// Preprocess affinity
123//
124 if (!(rP->affinity)) aVar = aVal = "";
125 else {aVar = "&cms.aff=";
126 aVal = &affTab[rP->affinity*2];
127 qVal = "?";
128 }
129
130// Check if we need to add a user name
131//
132 if (rP->rUser.length() == 0) uVar = uVal = "";
133 else {uVar = "&ssi.user=";
134 uVal = rP->rUser.c_str();
135 qVal = "?";
136 }
137
138// Preprocess the cgi information
139//
140 if (rP->rInfo.length() == 0) iSep = iVal = "";
141 else {iVal = rP->rInfo.c_str();
142 iSep = "&ssi.cgi=";
143 qVal = "?";
144 }
145
146// Check if we need to qualify the host with a user index
147//
148 if (uEnt == 0) xUsr = xAt = "";
149 else {snprintf(uBuff, sizeof(uBuff), "%d", uEnt);
150 xUsr= uBuff;
151 xAt = "@";
152 }
153
154// Generate appropriate url
155// ? t a u i
156 n = snprintf(buff, blen, "xroot://%s%s%s/%s%s%s%s%s%s%s%s%s%s",
157 xUsr, xAt, manNode, rP->rName.c_str(), qVal,
158 tVar, tVal, aVar, aVal,
159 uVar, uVal, iSep, iVal);
160
161// Return overflow or not
162//
163 return n < blen;
164}
165
166/******************************************************************************/
167/* P r o c e s s R e q u e s t */
168/******************************************************************************/
169
171 XrdSsiResource &resRef)
172{
173 static const uint32_t useCache = XrdSsiResource::Reusable
175 XrdSysMutexHelper mHelp;
176 XrdSsiSessReal *sObj;
177 std::string resKey;
178 int uEnt;
179 bool hold = (resRef.rOpts & XrdSsiResource::Reusable) != 0;
180 char epURL[4096];
181
182// Validate the resource name
183//
184 if (resRef.rName.length() == 0)
185 {XrdSsiUtils::RetErr(reqRef, "Resource name missing.", EINVAL);
186 return;
187 }
188
189// Check if this is a reusable resource. Reusable resources are a bit more
190// complicated to pull off. In any case, we need to hold the cache lock.
191//
192 if (resRef.rOpts & useCache)
193 {mHelp.Lock(&rcMutex);
194 if (ResReuse(reqRef, resRef, resKey)) return;
195 }
196
197// Get a sid entry number
198//
199 if ((uEnt = sidScale.getEnt()) < 0)
200 {XrdSsiUtils::RetErr(reqRef, "Out of stream resources.", ENOSR);
201 return;
202 }
203
204// Construct url
205//
206 if (!GenURL(&resRef, epURL, sizeof(epURL), uEnt))
207 {XrdSsiUtils::RetErr(reqRef, "Resource url is too long.", ENAMETOOLONG);
208 sidScale.retEnt(uEnt);
209 return;
210 }
211
212// Obtain a new session object
213//
214 if (!(sObj = Alloc(resRef.rName.c_str(), uEnt, hold)))
215 {XrdSsiUtils::RetErr(reqRef, "Insufficient memory.", ENOMEM);
216 sidScale.retEnt(uEnt);
217 return;
218 }
219
220// Tag the session object with the resource key if it is being held. We need
221// to do this before doing provision as that may fail at any point.
222//
223 if (hold) sObj->SetKey(resKey.c_str());
224
225// Now just provision this resource which will execute the request should it
226// be successful. If Provision() fails, we need to delete the session object
227// because its file object now is in an usable state (funky client interface).
228//
229 if (!(sObj->Provision(&reqRef, epURL))) Recycle(sObj, false);
230
231// If this was started with a reusable resource, put the session in the cache.
232// The resource key was constructed by the call to ResReuse() and the cache
233// mutex is still held at this point (will be released upon return).
234//
235 if (hold) resCache[resKey] = sObj;
236}
237
238/******************************************************************************/
239/* R e c y c l e */
240/******************************************************************************/
241
243{
244 EPNAME("Recycle");
245 static const char *tident = 0;
246 const char *resKey;
247 bool doDel;
248
249// Clear all pending events (likely not needed)
250//
251 sObj->ClrEvent();
252
253// Remove entry from the reusable cache if present
254//
255 if ((resKey = sObj->GetKey())) StopReuse(resKey);
256
257// Add to queue unless we have too many of these or caller wants a deletion.
258//
259 myMutex.Lock();
260 actvSes--;
261 DEBUG("Sess " <<sObj->GetSID() <<"# reuse=" <<reuse <<" free=" <<freeCnt
262 <<" active=" <<actvSes);
263
264 doDel = ((actvSes == 0 && doStop) || !reuse || freeCnt >= freeMax);
265
266 DEBUG("reuse=" <<reuse <<" del=" <<doDel
267 <<"; sessions: free=" <<freeCnt <<" active=" <<actvSes);
268
269 if (doDel) {myMutex.UnLock(); delete sObj;}
270 else {sObj->nextSess = freeSes;
271 freeSes = sObj;
272 freeCnt++;
273 myMutex.UnLock();
274 }
275}
276
277/******************************************************************************/
278/* Private: R e s R e u s e */
279/******************************************************************************/
280
281// Called with rcMutex held!
282
283bool XrdSsiServReal::ResReuse(XrdSsiRequest &reqRef,
284 XrdSsiResource &resRef,
285 std::string &resKey)
286{
287 std::map<std::string, XrdSsiSessReal *>::iterator it;
288 XrdSsiSessReal *sesP;
289
290// Construct lookup key
291//
292 resKey.reserve(resRef.rUser.size() + resRef.rName.size() + 2);
293 resKey = resRef.rUser;
294 resKey += "@";
295 resKey += resRef.rName;
296
297// Find the cache entry
298//
299 it = resCache.find(resKey);
300 if (it == resCache.end()) return false;
301
302// Entry found, check if this session can actually be reused
303//
304 sesP = it->second;
305 bool doDiscard = (resRef.rOpts & XrdSsiResource::Discard) != 0
306 || XrdSsiRRAgent::isaRetry(&reqRef);
307 if (doDiscard || !sesP->Run(&reqRef))
308 {resCache.erase(it);
309 sesP->UnHold();
310 return false;
311 }
312
313// All done, the request should have been sent off via Reusable() call.
314//
315 return true;
316}
317
318/******************************************************************************/
319/* S t o p */
320/******************************************************************************/
321
322bool XrdSsiServReal::Stop(bool immed)
323{
324// Make sure we are clean
325//
326 myMutex.Lock();
327 if (actvSes)
328 {if (immed) {myMutex.UnLock(); return false;}
329 doStop = true;
330 myMutex.UnLock();
331 return true;
332 }
333 myMutex.UnLock();
334 delete this;
335 return true;
336}
337
338/******************************************************************************/
339/* S t o p R e u s e */
340/******************************************************************************/
341
342void XrdSsiServReal::StopReuse(const char *resKey)
343{
344 EPNAME("StopReuse");
345 static const char *tident = "ServReuse";
346 std::map<std::string, XrdSsiSessReal *>::iterator it;
347
348// Remove this entry from the reuse cache
349//
350 rcMutex.Lock();
351 it = resCache.find(resKey);
352 if (it != resCache.end())
353 {resCache.erase(it);
354 DEBUG("resCache " <<resKey <<" removed.");
355 }
356 rcMutex.UnLock();
357}
#define tident
#define DEBUG(x)
#define EPNAME(x)
#define ENOSR
void ClrEvent()
static bool isaRetry(XrdSsiRequest *rP, bool reset=false)
std::string rUser
-> Name of the resource user (nil if anonymous)
uint32_t rOpts
Resource options. One or more of he following:
Affinity affinity
Resource affinity.
std::string rInfo
-> Additional information in CGI format
static const uint32_t Reusable
std::string rName
-> Name of the resource to be used
std::string hAvoid
-> Comma separated list of hosts to avoid
static const uint32_t Discard
‍Resource context may be cached and reused
void retEnt(int xEnt)
void ProcessRequest(XrdSsiRequest &reqRef, XrdSsiResource &resRef)
Process a request; client-side or server-side.
void StopReuse(const char *resKey)
void Recycle(XrdSsiSessReal *sObj, bool reuse)
bool Stop(bool immed=false)
Stop the client-side service. This is never called server-side.
uint32_t GetSID()
const char * GetKey()
void InitSession(XrdSsiServReal *servP, const char *sName, int uent, bool hold, bool newSID=false)
void SetKey(const char *key)
bool Provision(XrdSsiRequest *reqP, const char *epURL)
bool Run(XrdSsiRequest *reqP)
XrdSsiSessReal * nextSess
void UnHold(bool cleanup=true)
static void RetErr(XrdSsiRequest &reqP, const char *eTxt, int eNum)
void Lock(XrdSysMutex *Mutex)
XrdSsiScale sidScale