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