XRootD
Loading...
Searching...
No Matches
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"
44#include "XrdOuc/XrdOucUtils.hh"
45#include "XrdSys/XrdSysError.hh"
46#include "XrdSys/XrdSysFD.hh"
49
50class XrdOucString;
51
52/******************************************************************************/
53/* G l o b a l s a n d S t a t i c s */
54/******************************************************************************/
55
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
72namespace
73{
74XrdSysMutex uMutex;
75}
76
77/******************************************************************************/
78/* A d j u s t */
79/******************************************************************************/
80
81void 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
142void 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//
158int 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
209int 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
224int XrdOssSpace::Init() {return (uFname ? haveUsage:0) | (qFname ? haveQuota:0);}
225
226/******************************************************************************/
227
228int 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{
347 XrdOucStream Config(&OssEroute);
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
408int 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
463int 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
486int 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
521long 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
535long 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
575int 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)
XrdSysError OssEroute
int fcntl(int fd, int cmd,...)
#define close(a)
Definition XrdPosix.hh:43
#define fsync(a)
Definition XrdPosix.hh:59
#define fstat(a, b)
Definition XrdPosix.hh:57
#define write(a, b, c)
Definition XrdPosix.hh:110
#define mkdir(a, b)
Definition XrdPosix.hh:69
#define open
Definition XrdPosix.hh:71
#define stat(a, b)
Definition XrdPosix.hh:96
#define read(a, b, c)
Definition XrdPosix.hh:77
#define pwrite(a, b, c, d)
Definition XrdPosix.hh:102
#define pread(a, b, c, d)
Definition XrdPosix.hh:75
struct myOpts opts
#define FLOCK_t
static long long PubQuota
static XrdOssCache_Group * fsgroups
XrdOssCache_Group * next
static long long Usage(int gent)
static int Init()
static const int minSNbsz
long long Bytes[Totn]
static const int haveQuota
static const int haveUsage
static void Adjust(int Gent, off_t Space, sType=Serv)
static int Quotas()
static int Unassign(const char *GName)
static int Export(const char *Var, const char *Val)
Definition XrdOucEnv.cc:170
static const char * InstName(int TranOpt=0)
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)