XRootD
XrdOssSpace.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O s s S p a c e . c c */
4 /* */
5 /* (c) 2008 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <fcntl.h>
35 #include <cerrno>
36 #include <stddef.h>
37 #include <cstdio>
38 
39 #include "XrdOss/XrdOssCache.hh"
40 #include "XrdOss/XrdOssSpace.hh"
41 #include "XrdOuc/XrdOuca2x.hh"
42 #include "XrdOuc/XrdOucEnv.hh"
43 #include "XrdOuc/XrdOucStream.hh"
44 #include "XrdOuc/XrdOucUtils.hh"
45 #include "XrdSys/XrdSysError.hh"
46 #include "XrdSys/XrdSysFD.hh"
47 #include "XrdSys/XrdSysPlatform.hh"
48 #include "XrdSys/XrdSysPthread.hh"
49 
50 class XrdOucString;
51 
52 /******************************************************************************/
53 /* G l o b a l s a n d S t a t i c s */
54 /******************************************************************************/
55 
56 extern XrdSysError OssEroute;
57 
58  const char *XrdOssSpace::qFname = 0;
59  const char *XrdOssSpace::uFname = 0;
60  const char *XrdOssSpace::uUname = 0;
61  XrdOssSpace::uEnt XrdOssSpace::uData[XrdOssSpace::maxEnt];
62  short XrdOssSpace::uDvec[XrdOssSpace::maxEnt] = {0};
63  int XrdOssSpace::fencEnt = 0;
64  int XrdOssSpace::freeEnt =-1;
65  int XrdOssSpace::aFD =-1;
66  int XrdOssSpace::uAdj = 0;
67  int XrdOssSpace::uSync = 0;
68  int XrdOssSpace::Solitary = 0;
69  time_t XrdOssSpace::lastMtime = 0;
70  time_t XrdOssSpace::lastUtime = 0;
71 
72 namespace
73 {
74 XrdSysMutex uMutex;
75 }
76 
77 /******************************************************************************/
78 /* A d j u s t */
79 /******************************************************************************/
80 
81 void XrdOssSpace::Adjust(int Gent, off_t Space, sType stNum)
82 {
83  XrdSysMutexHelper uHelp(uMutex);
84  int offset, unlk = 0;
85  int uOff = offsetof(uEnt,Bytes[0]) + (sizeof(long long)*stNum);
86 
87 // Verify the entry number
88 //
89  if (Gent < 0 || Gent >= fencEnt) return;
90  offset = sizeof(uEnt)*Gent + uOff;
91 
92 // For stand-alone processes, we need to convert server adjustments to make
93 // the update inter-process safe.
94 //
95  if (Solitary && stNum == Serv) stNum = (Space > 0 ? Pstg : Purg);
96 
97 // Check if we need a lock and a refresh. For admin stats we need to make the
98 // result idempotent w.r.t. updates by convoluting pstg/purg space numbers.
99 //
100  if (stNum != Serv)
101  {if (!UsageLock()) return;
102  if (pread(aFD, &uData[Gent], sizeof(uEnt), offset-uOff) < 0)
103  {OssEroute.Emsg("Adjust", errno, "read usage file", uFname);
104  UsageLock(0); return;
105  }
106  if (stNum == Admin)
107  {uData[Gent].Bytes[Admin] = 0;
108  Space = Space - uData[Gent].Bytes[Pstg] + uData[Gent].Bytes[Purg];
109  }
110  unlk = 1;
111  }
112 
113 // Update the space statistic (protected by caller's mutex)
114 //
115  if ((uData[Gent].Bytes[stNum] += Space) < 0 && stNum != Admin)
116  uData[Gent].Bytes[stNum] = 0;
117 
118 // Write out the the changed field. For servers, we can do this without a lock
119 // because we are the only ones allowed to write this field.
120 //
121  if (pwrite(aFD, &uData[Gent].Bytes[stNum], ULen, offset) < 0)
122  OssEroute.Emsg("Adjust", errno, "update usage file", uFname);
123 
124 // Update the time this occurred if we are not a server
125 //
126  if (stNum != Serv) utimes(uUname, 0);
127 
128 // Check if we need to sync the file
129 //
130  if (uSync)
131  {uAdj++;
132  if (uAdj >= uSync) {fsync(aFD); uAdj = 0;}
133  }
134 
135 // Unlock the file if we locked it
136 //
137  if (unlk) UsageLock(0);
138 }
139 
140 /******************************************************************************/
141 
142 void XrdOssSpace::Adjust(const char *GName, off_t Space, sType stNum)
143 {
144  int i;
145 
146 // Try to find the current entry in the file
147 //
148  if ((i = findEnt(GName)) >= 0) Adjust(i, Space, stNum);
149 }
150 
151 /******************************************************************************/
152 /* A s s i g n */
153 /******************************************************************************/
154 
155 // This is called during initialization and only needs a file lock if the
156 // file is going to be updated. No local mutex is needed.
157 //
158 int XrdOssSpace::Assign(const char *GName, long long &Usage)
159 {
160  off_t offset;
161  int i;
162 
163 // Try to find the current entry in the file
164 //
165  if ((i = findEnt(GName)) >= 0)
166  {Usage = uData[i].Bytes[Serv];
167  return i;
168  }
169 
170 // See if we can create a new entry
171 //
172  Usage = 0;
173  if (freeEnt >= maxEnt || freeEnt < 0)
174  {OssEroute.Emsg("Assign", uFname, "overflowed for", GName);
175  return -1;
176  }
177 
178 // Create the entry
179 //
180  if (!UsageLock()) return -1;
181  memset(&uData[freeEnt], 0, sizeof(uEnt));
182  strcpy(uData[freeEnt].gName, GName);
183  uData[freeEnt].Bytes[addT] = static_cast<long long>(time(0));
184  offset = sizeof(uEnt) * freeEnt;
185  if (pwrite(aFD, &uData[freeEnt], sizeof(uEnt), offset) < 0)
186  {OssEroute.Emsg("Adjust", errno, "update usage file", uFname);
187  UsageLock(0); return -1;
188  }
189  UsageLock(0);
190 
191 // Add this to the vector table
192 //
193  uDvec[fencEnt++] = i = freeEnt;
194 
195 // Find next free entry
196 //
197  for (freeEnt = freeEnt+1; freeEnt < maxEnt; freeEnt++)
198  if (*uData[freeEnt].gName == '\0') break;
199 
200 // All done here
201 //
202  return i;
203 }
204 
205 /******************************************************************************/
206 /* f i n d E n t */
207 /******************************************************************************/
208 
209 int XrdOssSpace::findEnt(const char *GName)
210 {
211  int i;
212 
213 // Try to find the current entry in the file
214 //
215  for (i = 0; i < fencEnt; i++)
216  if (!strcmp(uData[uDvec[i]].gName, GName)) return i;
217  return -1;
218 }
219 
220 /******************************************************************************/
221 /* I n i t */
222 /******************************************************************************/
223 
224 int XrdOssSpace::Init() {return (uFname ? haveUsage:0) | (qFname ? haveQuota:0);}
225 
226 /******************************************************************************/
227 
228 int XrdOssSpace::Init(const char *aPath, const char *qPath, int isSOL, int us)
229 {
230  static const mode_t theMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
231  struct stat buf;
232  const char *iP;
233  char *aP, buff[1048];
234  int i, opts, updt = 0;
235 
236 // Initialize th usage array now
237 //
238  memset(uData, 0, sizeof(uData));
239 
240 // Indicate whether we are solitary or not
241 //
242  Solitary = isSOL;
243 
244 // Handle quota file first
245 //
246  if (qPath)
247  {qFname = strdup(qPath);
248  if (!Quotas()) return 0;
249  XrdOucEnv::Export("XRDOSSQUOTAFILE", qFname);
250  }
251 
252 // Construct the file path for the usage file
253 //
254  if (!aPath) return 1;
255  strcpy(buff, aPath);
256  aP = buff + strlen(aPath);
257  if (*(aP-1) != '/') *aP++ = '/';
258  if ((iP = XrdOucUtils::InstName(-1)))
259  {strcpy(aP, iP); aP += strlen(iP); *aP++ = '/'; *aP = '\0';
260  mkdir(buff, S_IRWXU | S_IRWXG);
261  }
262  strcpy(aP, ".Usage");
263  uFname = strdup(buff);
264  strcpy(aP, ".Usage.upd");
265  uUname = strdup(buff);
266  XrdOucEnv::Export("XRDOSSUSAGEFILE", uFname);
267 
268 // Create the usage update file if it does not exist
269 //
270  if ((i = open(uUname, O_CREAT|O_TRUNC|O_RDWR, theMode)) < 0)
271  {OssEroute.Emsg("Init", errno, "create", uUname);
272  return 0;
273  } else {
274  if (!fstat(i, &buf)) lastUtime = buf.st_mtime;
275  close(i);
276  utimes(uUname, 0);
277  }
278 
279 // First check if the file really exists, if not, create it
280 //
281  if (stat(uFname, &buf))
282  if (errno != ENOENT)
283  {OssEroute.Emsg("Init", errno, "open", uFname);
284  return 0;
285  } else opts = O_CREAT|O_TRUNC;
286  else if ( buf.st_size != DataSz && buf.st_size)
287  {OssEroute.Emsg("Init", uFname, "has invalid size."); return 0;}
288  else opts = 0;
289 
290 // Handle synchornization
291 //
292  if (us > 1) uSync = us;
293  else opts |= O_DSYNC;
294 
295 // Open the target file
296 //
297  if ((aFD = XrdSysFD_Open(uFname, opts|O_RDWR, theMode)) < 0)
298  {OssEroute.Emsg("Init", errno, "open", uFname);
299  return 0;
300  }
301 
302 // Lock the file
303 //
304  UsageLock();
305 
306 // Either read the contents or initialize the contents
307 //
308  if (opts & O_CREAT || buf.st_size == 0)
309  {if (!write(aFD, uData, sizeof(uData)))
310  {OssEroute.Emsg("Init", errno, "create", uFname);
311  UsageLock(0); return 0;
312  }
313  fencEnt = 0; freeEnt = 0;
314  } else {
315  if (!read(aFD, uData, sizeof(uData)))
316  {OssEroute.Emsg("Init", errno, "read", uFname);
317  UsageLock(0); return 0;
318  }
319  for (i = 0; i < maxEnt; i++)
320  {if (*uData[i].gName != '\0')
321  {uDvec[fencEnt++] = i; updt |= Readjust(i);}
322  else if (freeEnt < 0) freeEnt = i;
323  }
324  if (freeEnt < 0) OssEroute.Emsg("Init", uFname, "is full.");
325  }
326 
327 // If we need to rewrite the data, do so
328 //
329  if (updt && pwrite(aFD, uData, sizeof(uData), 0) < 0)
330  OssEroute.Emsg("Init", errno, "rewrite", uFname);
331 
332 // All done
333 //
334  UsageLock(0);
335  sprintf(buff, "%d usage log entries in use; %d available.",
336  fencEnt, maxEnt-fencEnt);
337  OssEroute.Emsg("Init", buff);
338  return 1;
339 }
340 
341 /******************************************************************************/
342 /* Q u o t a s */
343 /******************************************************************************/
344 
346 {
348  XrdOssCache_Group *fsg;
349  struct stat buf;
350  long long qval;
351  char cgroup[minSNbsz], *val;
352  int qFD, NoGo = 0;
353 
354 // See if the file has changed (note the firs time through it will have)
355 //
356  if (stat(qFname,&buf))
357  {OssEroute.Emsg("Quotas", errno, "process quota file", qFname);
358  return 0;
359  }
360  if (buf.st_mtime == lastMtime) return 0;
361  lastMtime = buf.st_mtime;
362 
363 // Try to open the quota file.
364 //
365  if ( (qFD = open(qFname, O_RDONLY, 0)) < 0)
366  {OssEroute.Emsg("Quotas", errno, "open quota file", qFname);
367  return 0;
368  }
369 
370 // Attach the file to a stream and tell people what we are doing
371 //
372  OssEroute.Emsg("Quotas", "Processing quota file", qFname);
373  Config.Attach(qFD);
374  XrdOucString *capstr = Config.Capture((XrdOucString *)0);
375 
376 // Now start reading records until eof.
377 //
378  while((val = Config.GetMyFirstWord()))
379  {if (strlen(val) >= sizeof(cgroup))
380  {OssEroute.Emsg("Quotas", "invalid quota group =", val);
381  NoGo = 1; continue;
382  }
383  strcpy(cgroup, val);
384 
385  if (!(val = Config.GetWord()))
386  {OssEroute.Emsg("Quotas", "quota value not specified for", cgroup);
387  NoGo = 1; continue;
388  }
389  if (XrdOuca2x::a2sz(OssEroute, "quota", val, &qval))
390  {NoGo = 1; continue;
391  }
393  while(fsg && strcmp(cgroup, fsg->group)) fsg = fsg->next;
394  if (fsg) fsg->Quota = qval;
395  if (!strcmp("public", cgroup)) XrdOssCache_Group::PubQuota = qval;
396  else if (!fsg) OssEroute.Emsg("Quotas", cgroup,
397  "cache group not found; quota ignored");
398  }
399  close(qFD);
400  Config.Capture(capstr);
401  return (NoGo ? 0 : 1);
402 }
403 
404 /******************************************************************************/
405 /* R e a d j u s t */
406 /******************************************************************************/
407 
408 int XrdOssSpace::Readjust()
409 {
410  XrdSysMutexHelper uHelp(uMutex);
411  struct stat buf;
412  int k, rwsz, updt = 0;
413 
414 // Sync the usage file if need be
415 //
416  if (uSync && uAdj)
417  {uAdj = 0;
418  if (fsync(aFD))
419  OssEroute.Emsg("Readjust", errno, "sync usage file", uFname);
420  }
421 
422 // No readjustment needed if we are not a server or we have nothing
423 //
424  if (fencEnt <= 0) return 0;
425  if (!stat(uUname, &buf))
426  {if (buf.st_mtime == lastUtime) return 0;
427  lastUtime = buf.st_mtime;
428  }
429  rwsz = sizeof(uEnt)*(uDvec[fencEnt-1] + 1);
430 
431 // Lock the file
432 //
433  if (!UsageLock()) return 0;
434 
435 // Read the file again
436 //
437  if (!pread(aFD, uData, rwsz, 0))
438  {OssEroute.Emsg("Readjust", errno, "read", uFname);
439  UsageLock(0); return 0;
440  }
441 
442 // Perform necessary readjustments but only for things we know about
443 //
444  for (k = 0; k < fencEnt; k++) updt |= Readjust(uDvec[k]);
445 
446 // If we need to rewrite the data, do so
447 //
448  if (updt)
449  {if (pwrite(aFD, uData, rwsz, 0) < 0)
450  OssEroute.Emsg("Readjust", errno, "rewrite", uFname);
451  else if (uSync && fsync(aFD))
452  OssEroute.Emsg("Readjust", errno, "sync usage file", uFname);
453  }
454 
455 // All done
456 //
457  UsageLock(0);
458  return updt;
459 }
460 
461 /******************************************************************************/
462 
463 int XrdOssSpace::Readjust(int i)
464 {
465 
466 // Check if any readjustment is needed
467 //
468  if (uData[i].Bytes[Pstg] || uData[i].Bytes[Purg] || uData[i].Bytes[Admin])
469  {long long oldVal = uData[i].Bytes[Serv];
470  char buff[256];
471  uData[i].Bytes[Serv] = uData[i].Bytes[Serv] + uData[i].Bytes[Pstg]
472  - uData[i].Bytes[Purg] + uData[i].Bytes[Admin];
473  uData[i].Bytes[Pstg] = uData[i].Bytes[Purg] = uData[i].Bytes[Admin] = 0;
474  snprintf(buff, sizeof(buff), "%lld to %lld bytes",
475  oldVal, uData[i].Bytes[Serv]);
476  OssEroute.Emsg("Readjust",uData[i].gName,"space usage adjusted from",buff);
477  return 1;
478  }
479  return 0;
480 }
481 
482 /******************************************************************************/
483 /* U n a s s i g n */
484 /******************************************************************************/
485 
486 int XrdOssSpace::Unassign(const char *GName)
487 {
488  off_t offset;
489  int k, i;
490 
491 // Try to find the current entry in the file
492 //
493  for (k = 0; k < fencEnt; k++)
494  if (!strcmp(uData[uDvec[k]].gName, GName)) break;
495  if (k >= fencEnt) return -1;
496  i = uDvec[k];
497 
498 // Create the entry
499 //
500  if (!UsageLock()) return -1;
501  memset(&uData[i], 0, sizeof(uEnt));
502  offset = sizeof(uEnt) * i;
503  if (pwrite(aFD, &uData[freeEnt], sizeof(uEnt), offset) < 0)
504  {OssEroute.Emsg("Unassign", errno, "update usage file", uFname);
505  UsageLock(0); return -1;
506  }
507  UsageLock(0);
508 
509 // Squish out the uDvec
510 //
511  if (i < freeEnt) freeEnt = i;
512  for (i = k+1; i < fencEnt; i++) uDvec[k++] = uDvec[i];
513  fencEnt--;
514  return 0;
515 }
516 
517 /******************************************************************************/
518 /* U s a g e */
519 /******************************************************************************/
520 
521 long long XrdOssSpace::Usage(int gent)
522 {
523  long long retVal;
524 
525 // Safelu get the value and return it
526 //
527  uMutex.Lock();
528  retVal = (gent < 0 || gent >= maxEnt ? 0 : uData[gent].Bytes[Serv]);
529  uMutex.UnLock();
530  return retVal;
531 }
532 
533 /******************************************************************************/
534 
535 long long XrdOssSpace::Usage(const char *GName, struct uEnt &uVal, int rrd)
536 {
537  XrdSysMutexHelper uHelp(uMutex);
538  int i, rwsz;
539 
540 // If we need to re-read the file, do so
541 //
542  if (rrd)
543  {if (fencEnt <= 0) return -1;
544  UsageLock();
545  rwsz = sizeof(uEnt)*(uDvec[fencEnt-1] + 1);
546  if (!pread(aFD, uData, rwsz, 0))
547  {OssEroute.Emsg("Readjust", errno, "read", uFname);
548  UsageLock(0); return -1;
549  }
550  UsageLock(0);
551  }
552 
553 // Try to find the current entry in the file
554 //
555  if ((i = findEnt(GName)) >= 0)
556  {uVal = uData[i];
557  return uData[i].Bytes[Serv];
558  }
559 
560 // Not found
561 //
562  memset(&uVal, 0, sizeof(uEnt));
563  return -1;
564 }
565 
566 /******************************************************************************/
567 /* private: U s a g e L o c k */
568 /******************************************************************************/
569 
570 // Warning: The uMutex must be held when calling this method as it is the
571 // only thing that allows file locking to be effective in an MT environment!
572 // There is no need to hold the mutex when MT execution has not yet started
573 // such as during initialization sequencing.
574 
575 int XrdOssSpace::UsageLock(int Dolock)
576 {
577  static XrdSysMutex uMutex;
578  FLOCK_t lock_args;
579  const char *What;
580  int rc;
581 
582 // Establish locking options
583 //
584  bzero(&lock_args, sizeof(lock_args));
585  if (Dolock) {lock_args.l_type = F_WRLCK; What = "lock";}
586  else {lock_args.l_type = F_UNLCK; What = "unlock";}
587 
588 // Perform action.
589 //
590  do {rc = fcntl(aFD,F_SETLKW,&lock_args);} while(rc < 0 && errno == EINTR);
591  if (rc < 0) {OssEroute.Emsg("UsageLock", errno, What, uFname); return 0;}
592 
593 // All done
594 //
595  return 1;
596 }
void Usage(const char *msg)
Definition: XrdAccTest.cc:105
XrdSysError OssEroute
ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset)
int stat(const char *path, struct stat *buf)
int open(const char *path, int oflag,...)
ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset)
int fstat(int fildes, struct stat *buf)
int fcntl(int fd, int cmd,...)
int mkdir(const char *path, mode_t mode)
int fsync(int fildes)
ssize_t write(int fildes, const void *buf, size_t nbyte)
ssize_t read(int fildes, void *buf, size_t nbyte)
#define close(a)
Definition: XrdPosix.hh:43
struct myOpts opts
#define FLOCK_t
static long long PubQuota
Definition: XrdOssCache.hh:197
static XrdOssCache_Group * fsgroups
Definition: XrdOssCache.hh:199
XrdOssCache_Group * next
Definition: XrdOssCache.hh:186
static long long Usage(int gent)
Definition: XrdOssSpace.cc:521
static int Init()
Definition: XrdOssSpace.cc:224
static const int minSNbsz
Definition: XrdOssSpace.hh:45
long long Bytes[Totn]
Definition: XrdOssSpace.hh:65
static const int haveQuota
Definition: XrdOssSpace.hh:52
static const int haveUsage
Definition: XrdOssSpace.hh:51
static void Adjust(int Gent, off_t Space, sType=Serv)
Definition: XrdOssSpace.cc:81
static int Quotas()
Definition: XrdOssSpace.cc:345
static int Unassign(const char *GName)
Definition: XrdOssSpace.cc:486
static int Export(const char *Var, const char *Val)
Definition: XrdOucEnv.cc:188
static const char * InstName(int TranOpt=0)
Definition: XrdOucUtils.cc:732
static int a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1)
Definition: XrdOuca2x.cc:257
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
XrdCmsConfig Config