diff --git a/src/XrdSut/XrdSutPFCache.cc b/src/XrdSut/XrdSutPFCache.cc new file mode 100644 index 00000000000..3084892ce95 --- /dev/null +++ b/src/XrdSut/XrdSutPFCache.cc @@ -0,0 +1,808 @@ +/******************************************************************************/ +/* */ +/* X r d S u t C a c h e . c c */ +/* */ +/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Gerri Ganis for CERN */ +/* */ +/* 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 "XrdSut/XrdSutPFCache.hh" +#include "XrdSut/XrdSutPFile.hh" +#include "XrdSut/XrdSutTrace.hh" +#include "XrdSut/XrdSutAux.hh" +#include "XrdSys/XrdSysTimer.hh" + +/******************************************************************************/ +/* */ +/* For caching temporary information during the authentication handshake */ +/* */ +/******************************************************************************/ + +//__________________________________________________________________ +XrdSutPFCache::~XrdSutPFCache() +{ + // Destructor + + // We are destroying the cache + rwlock.WriteLock(); + + // Cleanup content + while (cachemx > -1) { + if (cachent[cachemx]) { + delete cachent[cachemx]; + cachent[cachemx] = 0; + } + cachemx--; + } + // Cleanup table + if (cachent) + delete[] cachent; + + // Done + rwlock.UnLock(); +} + +//__________________________________________________________________ +int XrdSutPFCache::Init(int capacity, bool lock) +{ + // Initialize the cache to hold up to capacity entries. + // Later on, capacity is double each time more space is needed. + // Return 0 if ok, -1 otherwise + EPNAME("Cache::Init"); + + // Lock for writing + if (lock) rwlock.WriteLock(); + + // Nothing to do if already done + if (isinit) { + if (lock) rwlock.UnLock(); + return 0; + } + + // Make sure capacity makes sense; use a default, if not + capacity = (capacity > 0) ? capacity : 100; + + // Allocate + cachent = new XrdSutPFEntry *[capacity]; + if (cachent) { + for (int i = 0; i < capacity; i++) { cachent[i] = 0; } + cachesz = capacity; + DEBUG("cache allocated for "<pfeMutex.CondLock()) + {urRef.Set(&(pfEnt->pfeMutex)); + return pfEnt; + } + } else return pfEnt; + isg.UnLock(); + XrdSysTimer::Wait(retryMSW); + if (Rehash() != 0) + {DEBUG("problems rehashing"); + return (XrdSutPFEntry *)0 ; + } + isg.Lock(&rwlock, 1); + } + + // Nothing found + return (XrdSutPFEntry *)0 ; +} + +//__________________________________________________________________ +XrdSutPFEntry *XrdSutPFCache::Get(const char *ID, bool *wild) +{ + + // Look in the hash first + kXR_int32 *ie = hashtable.Find(ID); + if (ie && *ie >= 0 && *ie < cachesz) { + // Return the associated entry + return cachent[*ie]; + } + + // If wild cards allowed search sequentially + if (wild) { + XrdOucString sid(ID); + int i = 0, match = 0, nmmax = 0, iref = -1; + for (; i <= cachemx; i++) { + if (cachent[i]) { + match = sid.matches(cachent[i]->name); + if (match > nmmax) { + nmmax = match; + iref = i; + } + } + } + if (iref > -1) { + *wild = 1; + return cachent[iref]; + } + } + + // Nothing found + return (XrdSutPFEntry *)0 ; +} + +//__________________________________________________________________ +XrdSutPFEntry *XrdSutPFCache::Add(XrdSutPFCacheRef &urRef, const char *ID, bool force) +{ + // Add an entry with ID in cache + // Cache buffer is re-allocated with double size, if needed + // Hash is updated + EPNAME("Cache::Add"); + + // + // IF ID is undefined, do nothing + if (!ID || !strlen(ID)) { + DEBUG("empty ID !"); + return (XrdSutPFEntry *)0 ; + } + + // + // If an entry already exists, return it + XrdSutPFEntry *ent = Get(urRef, ID); + if (ent) + return ent; + + // Lock for writing + XrdSysRWLockHelper isg(rwlock, 0); + + // + // Make sure there enough space for a new entry + if (cachemx == cachesz - 1) { + // + // Duplicate buffer + XrdSutPFEntry **newcache = new XrdSutPFEntry *[2*cachesz]; + if (!newcache) { + DEBUG("could not extend cache to size: "<<(2*cachesz)); + return (XrdSutPFEntry *)0 ; + } + // Update info + cachesz *= 2; + // + // Copy existing valid entries, calculating real size + int i = 0, nmx = 0; + for (; i <= cachemx; i++) { + if (cachent[i]) { + newcache[nmx] = cachent[i]; + nmx++; + } + } + // update size + cachemx = nmx - 1; + // + // Reset new entries + for (i = cachemx + 1; i <= cachemx; i++) { + newcache[i] = 0; + } + // + // Cleanup and reassign + delete[] cachent; + cachent = newcache; + // + // Force rehash in this case + force = 1; + } + // + // The next free + int pos = cachemx + 1; + + // + // Add new entry + cachent[pos] = new XrdSutPFEntry(ID); + if (cachent[pos]) { + cachemx = pos; + } else { + DEBUG("could not allocate space for new cache entry"); + return (XrdSutPFEntry *)0 ; + } + // Update time stamp + utime = (kXR_int32)time(0); + + // Rebuild hash table + if (Rehash(force, 0) != 0) { + DEBUG("problems re-hashing"); + return (XrdSutPFEntry *)0 ; + } + + // We are done (we can lock the entry without a wait) + urRef.Lock(&(cachent[pos]->pfeMutex)); + return cachent[pos]; +} + +//__________________________________________________________________ +bool XrdSutPFCache::Remove(const char *ID, int opt) +{ + // If opt==1 remove entry with name matching exactly ID from cache + // If opt==0 all entries with names starting with ID are removed + // Return 1 if ok, 0 otherwise + EPNAME("Cache::Remove"); + + // + // IF ID is undefined, do nothing + if (!ID || !strlen(ID)) { + DEBUG("empty ID !"); + return 0 ; + } + + // Lock for writing + XrdSysRWLockHelper isg(rwlock, 0); + + if (Rehash(0, 0) != 0) { + DEBUG("problems rehashing"); + return 0 ; + } + + bool found = 0; + if (opt == 1) { + int pos = -1; + // Look in the hash first + kXR_int32 *ie = hashtable.Find(ID); + if (*ie >= 0 && *ie < cachesz) { + // Return the associated entry + pos = *ie; + } + + // + // Check if pos makes sense + if (cachent[pos] && !strcmp(cachent[pos]->name,ID)) { + if (!Delete(cachent[pos])) DEBUG("Delete defered for " <= 0; i--) { + if (cachent[i]) { + if (!strncmp(cachent[i]->name,ID,strlen(ID))) { + if (!Delete(cachent[i])) DEBUG("Delete defered for " <next)) + {nTot++; + if (dQ->pfEnt->pfeMutex.CondLock()) + {pQ->next = dQ->next; + dQ->pfEnt->pfeMutex.UnLock(); + delete dQ; + dTot++; + } else pQ = dQ; + } + if (nTot) DEBUG("Defered delete " <pfeMutex.CondLock()) + {pfEnt->pfeMutex.UnLock(); + delete pfEnt; + return true; + } + +// Defer the delete as someone still has a reference to the entry +// + pfDefer.next = new pfQ(pfDefer.next, pfEnt); + return false; +} + +//__________________________________________________________________ +int XrdSutPFCache::Trim(int lifet) +{ + // Remove entries older then lifet seconds. If lifet <=0, compare + // to lifetime, which can be set with SetValidity(). + // Return number of entries removed + + // Lock for writing + EPNAME("Cache::Trim"); + XrdSysRWLockHelper isg(rwlock, 0); + + // + // Make sure lifet makes sense; if not, use internal default + lifet = (lifet > 0) ? lifet : lifetime; + + // + // Reference time + int reftime = time(0) - lifet; + + // Loop over entries + int i = cachemx, nrm = 0; + for (; i >= 0; i--) { + if (cachent[i] && cachent[i]->mtime < reftime) { + if (!Delete(cachent[i])) + DEBUG("Delete defered for " <name); + cachent[i] = 0; + nrm++; + } + if (i == cachemx) { + if (!cachent[i]) + cachemx--; + } + } + + // We are done + return nrm; +} + +//__________________________________________________________________ +int XrdSutPFCache::Reset(int newsz, bool lock) +{ + // Remove all existing entries. + // If newsz > -1, set new capacity to newsz, reallocating if needed + // Return 0 if ok, -1 if problems reallocating. + EPNAME("Cache::Reset"); + + // Lock for writing + if (lock) rwlock.WriteLock(); + + // Loop over entries + int i = cachemx; + for (; i >= 0; i--) { + if (cachent[i]) { + if (!Delete(cachent[i])) + DEBUG("Delete defered for " <name); + cachent[i] = 0; + } + } + + int rc = 0; + // Reallocate, if requested + if (newsz > -1 && newsz != cachesz) { + delete[] cachent; + cachent = 0; + cachesz = 0; + cachemx = -1; + isinit = 0; + rc = Init(newsz, 0); + } + + // Unlock + if (lock) rwlock.UnLock(); + + // We are done + return rc; +} + +//________________________________________________________________ +void XrdSutPFCache::Dump(const char *msg) +{ + // Dump content of the cache + EPNAME("Cache::Dump"); + + PRINT("//-----------------------------------------------------"); + PRINT("//"); + if (msg && strlen(msg) > 0) { + PRINT("// "< 0) { + + XrdSutPFEntry *ent = 0; + int i = 0, nn = 0; + for (; i <= cachemx; i++) { + + // get entry + if ((ent = cachent[i])) { + + char smt[20] = {0}; + XrdSutTimeString(ent->mtime,smt); + + nn++; + PRINT("// #:"<status<<" cn:"<cnt + <<" buf:"<buf1.len<<","<buf2.len<<"," + <buf3.len<<","<buf4.len<<" mod:"<name); + } + + } + PRINT("//"); + } + PRINT("//-----------------------------------------------------"); +} + +//__________________________________________________________________ +int XrdSutPFCache::Load(const char *pfn) +{ + // Initialize the cache from the content of a file of PF entries + // Return 0 if ok, -1 otherwise + EPNAME("Cache::Load"); + + // Make sure file name is defined + if (!pfn) { + DEBUG("invalid input file name"); + return -1; + } + + // Check if file exists and if it has been modified since last load + struct stat st; + if (stat(pfn,&st) == -1) { + DEBUG("cannot stat file (errno: "< -1 && utime > st.st_mtime) { + DEBUG("cached information for file "< 0 && ne < header.entries) { + // + // read index entry + if (ff.ReadInd(nxtofs, ind) < 0) { + DEBUG("problems reading index entry "); + ff.Close(); + return -1; + } + + // If active ... + if (ind.entofs > 0) { + + // Read entry out + XrdSutPFEntry ent; + if (ff.ReadEnt(ind.entofs, ent) < 0) { + ff.Close(); + return -1; + } + + // Copy for the cache + XrdSutPFEntry *cent = new XrdSutPFEntry(ent); + + if (cent) { + // Set the id + cent->SetName(ind.name); + + // Fill the array + cachent[ne] = cent; + + // Count + ne++; + + } else { + DEBUG("problems duplicating entry for cache"); + ff.Close(); + return -1; + } + } + + // Go to next + nxtofs = ind.nxtofs; + } + cachemx = ne-1; + if (nxtofs > 0) + DEBUG("WARNING: inconsistent number of entries: possible file corruption"); + + // Update the time stamp + utime = (kXR_int32)time(0); + + // Save file name + pfile = pfn; + + // Close the file + ff.Close(); + + DEBUG("PF file "<= utime && !force) { + TRACE(Dump, "hash table is up-to-date"); + if (lock) rwlock.UnLock(); + return 0; + } + + // Clean up the hash table + hashtable.Purge(); + + kXR_int32 i = 0, nht = 0; + for (; i <= cachemx; i++) { + if (cachent[i]) { + // Fill the hash table + kXR_int32 *key = new kXR_int32(i); + if (key) { + TRACE(Dump, "Adding ID: "<name<<"; key: "<<*key); + hashtable.Add(cachent[i]->name,key); + nht++; + } + } + } + // Update modification time + htmtime = (kXR_int32)time(0); + + // Unlock + if (lock) rwlock.UnLock(); + + DEBUG("Hash table updated (found "<name, ent)) < 0) { + ff.Close(); + return -1; + } + // + // Write (update) only if older that cache or not found + if (nr == 0 || cachent[i]->mtime > ent.mtime) { + if (ff.WriteEntry(*cachent[i]) < 0) { + ff.Close(); + return -1; + } + nfs++; + } + } + } + + // Close the file + ff.Close(); + + // Update the time stamp (to avoid fake loads later on) + utime = (kXR_int32)time(0); + + // Save file name + if (pfile.length() <= 0) + pfile = pfn; + + DEBUG("Cache flushed to file "< -1 && utime > st.st_mtime) { + DEBUG("cached information for file "<. */ +/* */ +/* 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 "XProtocol/XPtypes.hh" +#include "XrdSut/XrdSutPFEntry.hh" +#include "XrdOuc/XrdOucHash.hh" +#include "XrdOuc/XrdOucString.hh" +#include "XrdSys/XrdSysPthread.hh" + +/******************************************************************************/ +/* */ +/* For caching temporary information during the authentication handshake */ +/* */ +/******************************************************************************/ + +class XrdSutPFCacheRef +{ +public: + +inline void Lock(XrdSysMutex *Mutex) + {if (mtx) {if (mtx != Mutex) mtx->UnLock(); + else return; + } + Mutex->Lock(); + mtx = Mutex; + }; + +inline void Set(XrdSysMutex *Mutex) + {if (mtx) {if (mtx != Mutex) mtx->UnLock(); + else return; + } + mtx = Mutex; + }; + +inline void UnLock() {if (mtx) {mtx->UnLock(); mtx = 0;}} + + XrdSutPFCacheRef() : mtx(0) {} + + ~XrdSutPFCacheRef() {if (mtx) UnLock();} +protected: +XrdSysMutex *mtx; +}; + +class XrdSutPFCache +{ +private: + XrdSysRWLock rwlock; // Access synchronizator + int cachesz; // Number of entries allocated + int cachemx; // Largest Index of allocated entries + XrdSutPFEntry **cachent; // Pointers to filled entries + kXR_int32 utime; // time at which was last updated + int lifetime; // lifetime (in secs) of the cache info + XrdOucHash hashtable; // Reflects the file index structure + kXR_int32 htmtime; // time at which hash table was last rebuild + XrdOucString pfile; // file name (if loaded from file) + bool isinit; // true if already initialized + + XrdSutPFEntry *Get(const char *ID, bool *wild); + bool Delete(XrdSutPFEntry *pfEnt); + + static const int maxTries = 100; // Max time to try getting a lock + static const int retryMSW = 300; // Milliseconds to wait to get lock + +public: + XrdSutPFCache() { cachemx = -1; cachesz = 0; cachent = 0; lifetime = 300; + utime = -1; htmtime = -1; pfile = ""; isinit = 0; } + virtual ~XrdSutPFCache(); + + // Status + int Entries() const { return (cachemx+1); } + bool Empty() const { return (cachemx == -1); } + + // Initialization methods + int Init(int capacity = 100, bool lock = 1); + int Reset(int newsz = -1, bool lock = 1); + int Load(const char *pfname); // build cache of a pwd file + int Flush(const char *pfname = 0); // flush content to pwd file + int Refresh(); // refresh content from source file + int Rehash(bool force = 0, bool lock = 1); // (re)build hash table + void SetLifetime(int lifet = 300) { lifetime = lifet; } + + // Cache management + XrdSutPFEntry *Get(int i) const { return (i<=cachemx) ? cachent[i] : + (XrdSutPFEntry *)0; } + XrdSutPFEntry *Get(XrdSutPFCacheRef &urRef, const char *ID, bool *wild = 0); + XrdSutPFEntry *Add(XrdSutPFCacheRef &urRef, const char *ID, bool force = 0); + bool Remove(const char *ID, int opt = 1); + int Trim(int lifet = 0); + + // For debug purposes + void Dump(const char *msg= 0); +}; + +#endif + diff --git a/src/XrdSut/XrdSutPFile.hh b/src/XrdSut/XrdSutPFile.hh index be60e48f73e..db32c68de36 100644 --- a/src/XrdSut/XrdSutPFile.hh +++ b/src/XrdSut/XrdSutPFile.hh @@ -120,7 +120,7 @@ public: class XrdSutPFile { - friend class XrdSutCache; // for open/close operation; + friend class XrdSutPFCache; // for open/close operation; private: char *name; diff --git a/src/XrdUtils.cmake b/src/XrdUtils.cmake index 6d1f420f0f2..6a5f2199dd1 100644 --- a/src/XrdUtils.cmake +++ b/src/XrdUtils.cmake @@ -137,11 +137,12 @@ add_library( # XrdSut #----------------------------------------------------------------------------- XrdSut/XrdSutAux.cc XrdSut/XrdSutAux.hh - XrdSut/XrdSutCache.cc XrdSut/XrdSutCache.hh + XrdSut/XrdSutPFCache.cc XrdSut/XrdSutPFCache.hh XrdSut/XrdSutBucket.cc XrdSut/XrdSutBucket.hh XrdSut/XrdSutBuckList.cc XrdSut/XrdSutBuckList.hh XrdSut/XrdSutBuffer.cc XrdSut/XrdSutBuffer.hh XrdSut/XrdSutPFile.cc XrdSut/XrdSutPFile.hh + XrdSut/XrdSutCacheEntry.cc XrdSut/XrdSutCacheEntry.hh XrdSut/XrdSutPFEntry.cc XrdSut/XrdSutPFEntry.hh XrdSut/XrdSutRndm.cc XrdSut/XrdSutRndm.hh XrdSut/XrdSutTrace.hh