/******************************************************************************/ /* */ /* 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 "<