XRootD
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 
34 #include "XrdSsi/XrdSsiResource.hh"
35 #include "XrdSsi/XrdSsiRRAgent.hh"
36 #include "XrdSsi/XrdSsiScale.hh"
37 #include "XrdSsi/XrdSsiServReal.hh"
38 #include "XrdSsi/XrdSsiSessReal.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 
50 namespace XrdSsi
51 {
53 }
54 
55 using namespace XrdSsi;
56 
57 /******************************************************************************/
58 /* D e s t r u c t o r */
59 /******************************************************************************/
60 
62 {
63  XrdSsiSessReal *sP;
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 
81 XrdSsiSessReal *XrdSsiServReal::Alloc(const char *sName, int uent, bool hold)
82 {
83  XrdSsiSessReal *sP;
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 
106 bool 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 
283 bool 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 
322 bool 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 
342 void 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)
Definition: XrdBwmTrace.hh:54
#define EPNAME(x)
Definition: XrdBwmTrace.hh:56
#define ENOSR
void ClrEvent()
Definition: XrdSsiEvent.hh:42
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
int getEnt()
Definition: XrdSsiScale.cc:50
void retEnt(int xEnt)
Definition: XrdSsiScale.cc:95
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()
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)
const char * GetKey()
XrdSsiSessReal * nextSess
void UnHold(bool cleanup=true)
static void RetErr(XrdSsiRequest &reqP, const char *eTxt, int eNum)
Definition: XrdSsiUtils.cc:220
void Lock(XrdSysMutex *Mutex)
XrdSsiScale sidScale