/******************************************************************************/ /* */ /* X r d O s s S p a c e . c c */ /* */ /* (c) 2008 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* 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 #include #include #include "XrdOss/XrdOssCache.hh" #include "XrdOss/XrdOssSpace.hh" #include "XrdOuc/XrdOuca2x.hh" #include "XrdOuc/XrdOucEnv.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucUtils.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysPlatform.hh" #include "XrdSys/XrdSysPthread.hh" /******************************************************************************/ /* G l o b a l s a n d S t a t i c s */ /******************************************************************************/ extern XrdSysError OssEroute; const char *XrdOssSpace::qFname = 0; const char *XrdOssSpace::uFname = 0; XrdOssSpace::uEnt XrdOssSpace::uData[XrdOssSpace::maxEnt]; short XrdOssSpace::uDvec[XrdOssSpace::maxEnt] = {0}; int XrdOssSpace::fencEnt = 0; int XrdOssSpace::freeEnt =-1; int XrdOssSpace::aFD =-1; int XrdOssSpace::Solitary = 0; time_t XrdOssSpace::lastMtime = 0; /******************************************************************************/ /* A d j u s t */ /******************************************************************************/ void XrdOssSpace::Adjust(int Gent, off_t Space, sType stNum) { int offset, unlk = 0; int uOff = offsetof(uEnt,Bytes[0]) + (sizeof(long long)*stNum); // Verify the entry number // if (Gent < 0 || Gent >= fencEnt) return; offset = sizeof(uEnt)*Gent + uOff; // For stand-alone processes, we need to convert server adjustments to make // the update inter-process safe. // if (Solitary && stNum == Serv) stNum = (Space > 0 ? Pstg : Purg); // Check if we need a lock and a refresh. For admin stats we need to make the // result idempotent w.r.t. updates by convoluting pstg/purg space numbers. // if (stNum != Serv) {if (!UsageLock()) return; if (pread(aFD, &uData[Gent], sizeof(uEnt), offset-uOff) < 0) {OssEroute.Emsg("Adjust", errno, "read usage file", uFname); UsageLock(0); return; } if (stNum == Admin) {uData[Gent].Bytes[Admin] = 0; Space = Space - uData[Gent].Bytes[Pstg] + uData[Gent].Bytes[Purg]; } unlk = 1; } // Update the space statistic (protected by caller's mutex) // if ((uData[Gent].Bytes[stNum] += Space) < 0 && stNum != Admin) uData[Gent].Bytes[stNum] = 0; // Write out the the changed field. For servers, we can do this without a lock // because we are the only ones allowed to write this field. // if (pwrite(aFD, &uData[Gent].Bytes[stNum], ULen, offset) < 0) OssEroute.Emsg("Adjust", errno, "update usage file", uFname); // Unlock the file if we locked it // if (unlk) UsageLock(0); } /******************************************************************************/ void XrdOssSpace::Adjust(const char *GName, off_t Space, sType stNum) { int i; // Try to find the current entry in the file // if ((i = findEnt(GName)) >= 0) Adjust(i, Space, stNum); } /******************************************************************************/ /* A s s i g n */ /******************************************************************************/ int XrdOssSpace::Assign(const char *GName, long long &Usage) { off_t offset; int i; // Try to find the current entry in the file // if ((i = findEnt(GName)) >= 0) {Usage = uData[i].Bytes[Serv]; return i; } // See if we can create a new entry // Usage = 0; if (freeEnt >= maxEnt || freeEnt < 0) {OssEroute.Emsg("Assign", uFname, "overflowed for", GName); return -1; } // Create the entry // if (!UsageLock()) return -1; memset(&uData[freeEnt], 0, sizeof(uEnt)); strcpy(uData[freeEnt].gName, GName); uData[freeEnt].Bytes[addT] = static_cast(time(0)); offset = sizeof(uEnt) * freeEnt; if (pwrite(aFD, &uData[freeEnt], sizeof(uEnt), offset) < 0) {OssEroute.Emsg("Adjust", errno, "update usage file", uFname); UsageLock(0); return -1; } UsageLock(0); // Add this to the vector table // uDvec[fencEnt++] = i = freeEnt; // Find next free entry // for (freeEnt = freeEnt+1; freeEnt < maxEnt; freeEnt++) if (*uData[freeEnt].gName == '\0') break; // All done here // return i; } /******************************************************************************/ /* f i n d E n t */ /******************************************************************************/ int XrdOssSpace::findEnt(const char *GName) { int i; // Try to find the current entry in the file // for (i = 0; i < fencEnt; i++) if (!strcmp(uData[uDvec[i]].gName, GName)) return i; return -1; } /******************************************************************************/ /* I n i t */ /******************************************************************************/ int XrdOssSpace::Init() {return (uFname ? haveUsage:0) | (qFname ? haveQuota:0);} /******************************************************************************/ int XrdOssSpace::Init(const char *aPath, const char *qPath, int isSOL) { static const mode_t theMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; struct stat buf; const char *iP; char *aP, buff[1048]; int i, opts, updt = 0; // Initialize th usage array now // memset(uData, 0, sizeof(uData)); // Indicate whether we are solitary or not // Solitary = isSOL; // Handle quota file first // if (qPath) {qFname = strdup(qPath); if (!Quotas()) return 0; XrdOucEnv::Export("XRDOSSQUOTAFILE", qFname); } // Construct the file path for the usage file // if (!aPath) return 1; strcpy(buff, aPath); aP = buff + strlen(aPath); if (*(aP-1) != '/') *aP++ = '/'; if ((iP = XrdOucUtils::InstName(-1))) {strcpy(aP, iP); aP += strlen(iP); *aP++ = '/'; *aP = '\0'; mkdir(buff, S_IRWXU | S_IRWXG); } strcpy(aP, ".Usage"); uFname = strdup(buff); XrdOucEnv::Export("XRDOSSUSAGEFILE", uFname); // First check if the file really exists, if not, create it // if (stat(uFname, &buf)) if (errno != ENOENT) {OssEroute.Emsg("Init", errno, "open", uFname); return 0; } else opts = O_CREAT|O_TRUNC; else if ( buf.st_size != DataSz && buf.st_size) {OssEroute.Emsg("Init", uFname, "has invalid size."); return 0;} else opts = 0; // Open the target file // if ((aFD = open(uFname, opts|O_RDWR|O_SYNC, theMode)) < 0) {OssEroute.Emsg("Init", errno, "open", uFname); return 0; } // Lock the file // UsageLock(); // Either read the contents or initialize the contents // if (opts & O_CREAT || buf.st_size == 0) {if (!write(aFD, uData, sizeof(uData))) {OssEroute.Emsg("Init", errno, "create", uFname); UsageLock(0); return 0; } fencEnt = 0; freeEnt = 0; } else { if (!read(aFD, uData, sizeof(uData))) {OssEroute.Emsg("Init", errno, "read", uFname); UsageLock(0); return 0; } for (i = 0; i < maxEnt; i++) {if (*uData[i].gName != '\0') {uDvec[fencEnt++] = i; updt = Readjust(i);} else if (freeEnt < 0) freeEnt = i; } if (freeEnt < 0) OssEroute.Emsg("Init", uFname, "is full."); } // If we need to rewrite the data, do so // if (updt && pwrite(aFD, uData, sizeof(uData), 0) < 0) OssEroute.Emsg("Init", errno, "rewrite", uFname); // All done // UsageLock(0); sprintf(buff, "%d usage log entries in use; %d available.", fencEnt, maxEnt-fencEnt); OssEroute.Emsg("Init", buff); return 1; } /******************************************************************************/ /* Q u o t a s */ /******************************************************************************/ int XrdOssSpace::Quotas() { XrdOucStream Config(&OssEroute); XrdOssCache_Group *fsg; struct stat buf; long long qval; char cgroup[minSNbsz], *val; int qFD, NoGo = 0; // See if the file has changed (note the firs time through it will have) // if (stat(qFname,&buf)) {OssEroute.Emsg("Quotas", errno, "process quota file", qFname); return 0; } if (buf.st_mtime == lastMtime) return 0; lastMtime = buf.st_mtime; // Try to open the quota file. // if ( (qFD = open(qFname, O_RDONLY, 0)) < 0) {OssEroute.Emsg("Quotas", errno, "open quota file", qFname); return 0; } // Attach the file to a stream and tell people what we are doing // OssEroute.Emsg("Quotas", "Processing quota file", qFname); Config.Attach(qFD); // Now start reading records until eof. // while((val = Config.GetMyFirstWord())) {if (strlen(val) >= sizeof(cgroup)) {OssEroute.Emsg("Quotas", "invalid quota group =", val); NoGo = 1; continue; } strcpy(cgroup, val); if (!(val = Config.GetWord())) {OssEroute.Emsg("Quotas", "quota value not specified for", cgroup); NoGo = 1; continue; } if (XrdOuca2x::a2sz(OssEroute, "quota", val, &qval)) {NoGo = 1; continue; } fsg = XrdOssCache_Group::fsgroups; while(fsg && strcmp(cgroup, fsg->group)) fsg = fsg->next; if (fsg) fsg->Quota = qval; if (!strcmp("public", cgroup)) XrdOssCache_Group::PubQuota = qval; else if (!fsg) OssEroute.Emsg("Quotas", cgroup, "cache group not found; quota ignored"); } close(qFD); return (NoGo ? 0 : 1); } /******************************************************************************/ /* R e a d j u s t */ /******************************************************************************/ int XrdOssSpace::Readjust() { static time_t lastUtime = 0; struct stat buf; int k, rwsz, updt = 0; // No readjustment needed if we are not a server or we have nothing // if (fencEnt <= 0) return 0; if (!fstat(aFD, &buf)) {if (buf.st_mtime == lastUtime) return 0; lastUtime = buf.st_mtime; } rwsz = sizeof(uEnt)*(uDvec[fencEnt-1] + 1); // Lock the file // UsageLock(); // Read the file again // if (!pread(aFD, uData, rwsz, 0)) {OssEroute.Emsg("Readjust", errno, "read", uFname); UsageLock(0); return 0; } // Perform necessary readjustments but only for things we know about // for (k = 0; k < fencEnt; k++) updt |= Readjust(uDvec[k]); // If we need to rewrite the data, do so // if (updt && pwrite(aFD, uData, rwsz, 0) < 0) OssEroute.Emsg("Readjust", errno, "rewrite", uFname); // All done // UsageLock(0); return updt; } /******************************************************************************/ int XrdOssSpace::Readjust(int i) { // Check if any readjustment is needed // if (uData[i].Bytes[Pstg] || uData[i].Bytes[Purg] || uData[i].Bytes[Admin]) {uData[i].Bytes[Serv] = uData[i].Bytes[Serv] + uData[i].Bytes[Pstg] - uData[i].Bytes[Purg] + uData[i].Bytes[Admin]; uData[i].Bytes[Pstg] = uData[i].Bytes[Purg] = uData[i].Bytes[Admin] = 0; return 1; } return 0; } /******************************************************************************/ /* U n a s s i g n */ /******************************************************************************/ int XrdOssSpace::Unassign(const char *GName) { off_t offset; int k, i; // Try to find the current entry in the file // for (k = 0; k < fencEnt; k++) if (!strcmp(uData[uDvec[k]].gName, GName)) break; if (k >= fencEnt) return -1; i = uDvec[k]; // Create the entry // if (!UsageLock()) return -1; memset(&uData[i], 0, sizeof(uEnt)); offset = sizeof(uEnt) * i; if (pwrite(aFD, &uData[freeEnt], sizeof(uEnt), offset) < 0) {OssEroute.Emsg("Unassign", errno, "update usage file", uFname); UsageLock(0); return -1; } UsageLock(0); // Squish out the uDvec // if (i < freeEnt) freeEnt = i; for (i = k+1; i < fencEnt; i++) uDvec[k++] = uDvec[i]; fencEnt--; return 0; } /******************************************************************************/ /* U s a g e */ /******************************************************************************/ long long XrdOssSpace::Usage(const char *GName, struct uEnt &uVal, int rrd) { int i, rwsz; // If we need to re-read the file, do so // if (rrd) {if (fencEnt <= 0) return -1; UsageLock(); rwsz = sizeof(uEnt)*(uDvec[fencEnt-1] + 1); if (!pread(aFD, uData, rwsz, 0)) {OssEroute.Emsg("Readjust", errno, "read", uFname); UsageLock(0); return -1; } UsageLock(0); } // Try to find the current entry in the file // if ((i = findEnt(GName)) >= 0) {uVal = uData[i]; return uData[i].Bytes[Serv]; } // Not found // memset(&uVal, 0, sizeof(uEnt)); return -1; } /******************************************************************************/ /* private: U s a g e L o c k */ /******************************************************************************/ int XrdOssSpace::UsageLock(int Dolock) { static XrdSysMutex uMutex; FLOCK_t lock_args; const char *What; int rc; // Establish locking options // bzero(&lock_args, sizeof(lock_args)); if (Dolock) {lock_args.l_type = F_WRLCK; What = "lock";} else {lock_args.l_type = F_UNLCK; What = "unlock";} // First obtain the usage mutex or unlock it // if (Dolock) uMutex.Lock(); else uMutex.UnLock(); // Perform action. // do {rc = fcntl(aFD,F_SETLKW,&lock_args);} while(rc < 0 && errno == EINTR); if (rc < 0) {OssEroute.Emsg("UpdateLock", errno, What, uFname); return 0;} // All done // return 1; }