XrdSutCache.cc

Go to the documentation of this file.
00001 // $Id: XrdSutCache.cc 30949 2009-11-02 16:37:58Z ganis $
00002 
00003 const char *XrdSutCacheCVSID = "$Id: XrdSutCache.cc 30949 2009-11-02 16:37:58Z ganis $";
00004 /******************************************************************************/
00005 /*                                                                            */
00006 /*                      X r d S u t C a c h e . c c                           */
00007 /*                                                                            */
00008 /* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University  */
00009 /*       All Rights Reserved. See XrdInfo.cc for complete License Terms       */
00010 /*   Produced by Andrew Hanushevsky for Stanford University under contract    */
00011 /*              DE-AC03-76-SFO0515 with the Department of Energy              */
00012 /******************************************************************************/
00013 #include <stdio.h>
00014 #include <stdlib.h>
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <unistd.h>
00018 #include <time.h>
00019 
00020 #include <XrdSut/XrdSutCache.hh>
00021 #include <XrdSut/XrdSutPFile.hh>
00022 #include <XrdSut/XrdSutTrace.hh>
00023 #include <XrdSut/XrdSutAux.hh>
00024 
00025 /******************************************************************************/
00026 /*                                                                            */
00027 /*  For caching temporary information during the authentication handshake     */
00028 /*                                                                            */
00029 /******************************************************************************/
00030 
00031 
00032 //__________________________________________________________________
00033 XrdSutCache::~XrdSutCache()
00034 {
00035    // Destructor
00036 
00037    // Cleanup content
00038    while (cachemx > -1) {
00039       if (cachent[cachemx]) {
00040          delete cachent[cachemx];
00041          cachent[cachemx] = 0;
00042       }
00043       cachemx--;
00044    }
00045    // Cleanup table
00046    if (cachent)
00047       delete[] cachent;
00048 }
00049 
00050 //__________________________________________________________________
00051 int XrdSutCache::Init(int capacity)
00052 {
00053    // Initialize the cache to hold up to capacity entries.
00054    // Later on, capacity is double each time more space is needed. 
00055    // Return 0 if ok, -1 otherwise
00056    EPNAME("Cache::Init");
00057 
00058    // Make sure capacity makes sense; use a default, if not
00059    capacity = (capacity > 0) ? capacity : 100;
00060 
00061    // Allocate
00062    cachent = new XrdSutPFEntry *[capacity];
00063    if (cachent) {
00064       cachesz = capacity;
00065       DEBUG("cache allocated for "<<cachesz<<" entries");
00066 
00067       // Update time stamp
00068       utime = (kXR_int32)time(0);
00069       
00070       // Init hash table
00071       if (Rehash() != 0) {
00072          DEBUG("problems initialising hash table");
00073          return 0 ;
00074       }
00075       return 0;
00076 
00077    } else
00078       DEBUG("could not allocate cache - out-of-resources ");
00079    return -1;
00080 }
00081 
00082 //__________________________________________________________________
00083 XrdSutPFEntry *XrdSutCache::Get(const char *ID, bool *wild)
00084 {
00085    // Retrieve an entry with ID, if any
00086    // If wild is defined, search also best matching regular expression
00087    // with wildcard '*'; *wild = 0 will indicate exact match, 
00088    // *wild = 1 wild card compatibility match 
00089    EPNAME("Cache::Get");
00090 
00091    TRACE(Dump,"locating entry for ID: "<<ID);
00092 
00093    //
00094    // If ID is undefined, do nothing
00095    if (!ID || !strlen(ID)) {
00096       DEBUG("empty ID !");
00097       return (XrdSutPFEntry *)0 ;
00098    }
00099    if (wild) *wild = 0;
00100 
00101    if (Rehash() != 0) {
00102       DEBUG("problems rehashing");
00103       return (XrdSutPFEntry *)0 ;
00104    }
00105 
00106    // Look in the hash first
00107    kXR_int32 *ie = hashtable.Find(ID);
00108    if (ie && *ie >= 0 && *ie < cachesz) {
00109       // Return the associated entry
00110       return cachent[*ie];
00111    }
00112 
00113    // If wild cards allowed search sequentially
00114    if (wild) {
00115       XrdOucString sid(ID);
00116       int i = 0, match = 0, nmmax = 0, iref = -1;
00117       for (; i <= cachemx; i++) {
00118          if (cachent[i]) {
00119             match = sid.matches(cachent[i]->name);
00120             if (match > nmmax) { 
00121                nmmax = match;
00122                iref = i;
00123             }
00124          }
00125       }
00126       if (iref > -1) {
00127          *wild = 1;
00128          return cachent[iref];
00129       }
00130    }
00131 
00132    // Nothing found
00133    return (XrdSutPFEntry *)0 ;
00134 }
00135 
00136 //__________________________________________________________________
00137 XrdSutPFEntry *XrdSutCache::Add(const char *ID, bool force)
00138 {
00139    // Add an entry with ID in cache
00140    // Cache buffer is re-allocated with double size, if needed
00141    // Hash is updated
00142    EPNAME("Cache::Add");
00143 
00144    //
00145    // IF ID is undefined, do nothing
00146    if (!ID || !strlen(ID)) {
00147       DEBUG("empty ID !");
00148       return (XrdSutPFEntry *)0 ;
00149    }
00150 
00151    //
00152    // If an entry already exists, return it
00153    XrdSutPFEntry *ent = Get(ID);
00154    if (ent)
00155       return ent;
00156 
00157    //
00158    // Make sure there enough space for a new entry
00159    if (cachemx == cachesz - 1) {
00160       //
00161       // Duplicate buffer
00162       XrdSutPFEntry **newcache = new XrdSutPFEntry *[2*cachesz];
00163       if (!newcache) {
00164          DEBUG("could not extend cache to size: "<<(2*cachesz));
00165          return (XrdSutPFEntry *)0 ;
00166       }
00167       // Update info
00168       cachesz *= 2;
00169       //
00170       // Copy existing valid entries, calculating real size
00171       int i = 0, nmx = 0;
00172       for (; i <= cachemx; i++) {
00173          if (cachent[i]) {
00174             newcache[nmx] = cachent[i];
00175             nmx++;
00176          }
00177       }
00178       // update size
00179       cachemx = nmx - 1;
00180       //
00181       // Reset new entries
00182       for (i = cachemx + 1; i <= cachemx; i++) {
00183          newcache[i] = 0;
00184       }
00185       //
00186       // Cleanup and reassign
00187       delete[] cachent;
00188       cachent = newcache;
00189       //
00190       // Force rehash in this case
00191       force = 1;
00192    }
00193    //
00194    // The next free
00195    int pos = cachemx + 1;
00196 
00197    //
00198    // Add new entry
00199    cachent[pos] = new XrdSutPFEntry(ID);   
00200    if (cachent[pos]) {
00201       cachemx = pos;
00202    } else {
00203       DEBUG("could not allocate space for new cache entry");
00204       return (XrdSutPFEntry *)0 ;
00205    }
00206    // Update time stamp
00207    utime = (kXR_int32)time(0);
00208 
00209    // Rebuild hash table
00210    if (Rehash(force) != 0) {
00211       DEBUG("problems re-hashing");
00212       return (XrdSutPFEntry *)0 ;
00213    }
00214 
00215    // We are done      
00216    return cachent[pos];
00217 }
00218 
00219 //__________________________________________________________________
00220 bool XrdSutCache::Remove(const char *ID, int opt)
00221 {
00222    // If opt==1 remove entry with name matching exactly ID from cache
00223    // If opt==0 all entries with names starting with ID are removed
00224    // Return 1 if ok, 0 otherwise
00225    EPNAME("Cache::Remove");
00226 
00227    //
00228    // IF ID is undefined, do nothing
00229    if (!ID || !strlen(ID)) {
00230       DEBUG("empty ID !");
00231       return 0 ;
00232    }
00233 
00234    if (Rehash() != 0) {
00235       DEBUG("problems rehashing");
00236       return 0 ;
00237    }
00238 
00239    bool found = 0;
00240    if (opt == 1) {
00241       int pos = -1;
00242       // Look in the hash first
00243       kXR_int32 *ie = hashtable.Find(ID);
00244       if (*ie >= 0 && *ie < cachesz) {
00245          // Return the associated entry
00246          pos = *ie;
00247       }
00248       
00249       //
00250       // Check if pos makes sense
00251       if (cachent[pos] && !strcmp(cachent[pos]->name,ID)) {
00252          delete cachent[pos];
00253          cachent[pos] = 0;
00254          // We are done, if not the one at highest index
00255          if (pos < cachemx)
00256             return 1;
00257          // We update the highest index
00258          found = 1;
00259       }
00260    } else {
00261       // Loop over entries
00262       int i = cachemx;
00263       for (; i >= 0; i--) {
00264          if (cachent[i]) {
00265             if (!strncmp(cachent[i]->name,ID,strlen(ID))) {
00266                delete cachent[i];
00267                cachent[i] = 0;
00268                found = 1;
00269             }
00270          }
00271       }
00272    }
00273 
00274    if (found) {
00275       // Update time stamp
00276       utime = (kXR_int32)time(0);
00277       
00278       // Rebuild hash table
00279       if (Rehash() != 0) {
00280          DEBUG("problems re-hashing");
00281          return 0 ;
00282       }
00283    }
00284 
00285    // We are done
00286    return found;
00287 }
00288 
00289 //__________________________________________________________________
00290 int XrdSutCache::Trim(int lifet)
00291 {
00292    // Remove entries older then lifet seconds. If lifet <=0, compare
00293    // to lifetime, which can be set with SetValidity().
00294    // Return number of entries removed
00295 
00296    //
00297    // Make sure lifet makes sense; if not, use internal default
00298    lifet = (lifet > 0) ? lifet : lifetime;
00299 
00300    //
00301    // Reference time
00302    int reftime = time(0) - lifet;
00303 
00304    // Loop over entries
00305    int i = cachemx, nrm = 0;
00306    for (; i >= 0; i--) {
00307       if (cachent[i] && cachent[i]->mtime < reftime) {
00308          delete cachent[i];
00309          cachent[i] = 0;
00310          nrm++;
00311       }
00312       if (i == cachemx) {
00313          if (!cachent[i])
00314             cachemx--;
00315       }
00316    }
00317 
00318    // We are done
00319    return nrm;
00320 }
00321 
00322 //__________________________________________________________________
00323 int XrdSutCache::Reset(int newsz)
00324 {
00325    // Remove all existing entries.
00326    // If newsz > -1, set new capacity to newsz, reallocating if needed
00327    // Return 0 if ok, -1 if problems reallocating.
00328 
00329    // Loop over entries
00330    int i = cachemx;
00331    for (; i >= 0; i--) {
00332       if (cachent[i]) {
00333          delete cachent[i];
00334          cachent[i] = 0;
00335       }
00336    }
00337 
00338    // Reallocate, if requested
00339    if (newsz > -1 && newsz != cachesz) {
00340       delete[] cachent;
00341       cachent = 0;
00342       cachesz = 0;      
00343       cachemx = -1;
00344       return Init(newsz);
00345    }
00346 
00347    // We are done
00348    return 0;
00349 }
00350 
00351 //________________________________________________________________
00352 void XrdSutCache::Dump(const char *msg)
00353 {
00354    // Dump content of the cache
00355    EPNAME("Cache::Dump");
00356 
00357    PRINT("//-----------------------------------------------------");
00358    PRINT("//");
00359    if (msg && strlen(msg) > 0) {
00360       PRINT("// "<<msg);
00361       PRINT("//");
00362    }
00363    PRINT("//  Capacity:         "<<cachesz);
00364    PRINT("//  Max index filled: "<<cachemx);
00365    PRINT("//");
00366 
00367    if (cachesz > 0) {
00368 
00369       XrdSutPFEntry *ent = 0;
00370       int i = 0, nn = 0;
00371       for (; i <= cachemx; i++) {
00372 
00373          // get entry
00374          if ((ent = cachent[i])) {
00375 
00376             char smt[20] = {0};
00377             XrdSutTimeString(ent->mtime,smt);
00378                
00379             nn++;
00380             PRINT("// #:"<<nn<<"  st:"<<ent->status<<" cn:"<<ent->cnt
00381                   <<"  buf:"<<ent->buf1.len<<","<<ent->buf2.len<<","
00382                   <<ent->buf3.len<<","<<ent->buf4.len<<" mod:"<<smt
00383                   <<" name:"<<ent->name);
00384          }
00385 
00386       }
00387       PRINT("//");
00388    }
00389    PRINT("//-----------------------------------------------------");
00390 }
00391 
00392 //__________________________________________________________________
00393 int XrdSutCache::Load(const char *pfn)
00394 {
00395    // Initialize the cache from the content of a file of PF entries
00396    // Return 0 if ok, -1 otherwise
00397    EPNAME("Cache::Load");
00398 
00399    // Make sure file name is defined
00400    if (!pfn) {
00401       DEBUG("invalid input file name");
00402       return -1;
00403    }
00404 
00405    // Check if file exists and if it has been modified since last load
00406    struct stat st;
00407    if (stat(pfn,&st) == -1) {
00408       DEBUG("cannot stat file (errno: "<<errno<<")");
00409       return -1;
00410    }
00411    if (utime > -1 && utime > st.st_mtime) {
00412       DEBUG("cached information for file "<<pfn<<" is up-to-date");
00413       return 0;
00414    }
00415 
00416    // Attach to file and open it
00417    XrdSutPFile ff(pfn, kPFEopen);
00418    if (!ff.IsValid()) {
00419       DEBUG("file is not a valid PFEntry file ("<<ff.LastErrStr()<<")");
00420       return -1;
00421    }
00422 
00423    // Read the header
00424    XrdSutPFHeader header;
00425    if (ff.ReadHeader(header) < 0) {
00426       ff.Close();
00427       return -1;
00428    }
00429 
00430    // If the file has no entries there is nothing to do
00431    if (header.entries <= 0) {
00432       DEBUG("PFEntry file is empty - default init and return");
00433       // Save file name
00434       pfile = pfn;
00435       Init();
00436       return 0;
00437    }
00438 
00439    // Allocate cache, if not done already or if too small
00440    if (Reset(header.entries) == -1) {
00441       DEBUG("problems allocating / resizing cache ");
00442       ff.Close();
00443       return -1;
00444    }
00445 
00446    // Read entries
00447    kXR_int32 ne = 0;
00448    XrdSutPFEntInd ind;
00449    kXR_int32 nxtofs = header.indofs;
00450    while (nxtofs > 0 && ne < header.entries) {
00451       //
00452       // read index entry
00453       if (ff.ReadInd(nxtofs, ind) < 0) {
00454          DEBUG("problems reading index entry ");
00455          ff.Close();
00456          return -1;
00457       }
00458 
00459       // If active ...
00460       if (ind.entofs > 0) {
00461 
00462          // Read entry out
00463          XrdSutPFEntry ent;
00464          if (ff.ReadEnt(ind.entofs, ent) < 0) {
00465             ff.Close();
00466             return -1;
00467          }
00468 
00469          // Copy for the cache
00470          XrdSutPFEntry *cent = new XrdSutPFEntry(ent);
00471 
00472          if (cent) {
00473             // Set the id
00474             cent->SetName(ind.name);
00475             
00476             // Fill the array 
00477             cachent[ne] = cent;
00478 
00479             // Count
00480             ne++;
00481 
00482          } else {
00483             DEBUG("problems duplicating entry for cache");
00484             ff.Close();
00485             return -1;
00486          }
00487       }
00488 
00489       // Go to next
00490       nxtofs = ind.nxtofs;
00491    }
00492    cachemx = ne-1;
00493    if (nxtofs > 0)
00494       DEBUG("WARNING: inconsistent number of entries: possible file corruption");
00495 
00496    // Update the time stamp
00497    utime = (kXR_int32)time(0);
00498 
00499    // Save file name
00500    pfile = pfn;
00501 
00502    // Close the file
00503    ff.Close();
00504 
00505    DEBUG("PF file "<<pfn<<" loaded in cache (found "<<ne<<" entries)");
00506 
00507    // Force update hash table
00508    if (Rehash(1) != 0) {
00509       DEBUG("problems creating hash table");
00510       return -1;
00511    }
00512 
00513    return 0;
00514 }
00515 
00516 
00517 //__________________________________________________________________
00518 int XrdSutCache::Rehash(bool force)
00519 {
00520    // Update or create hahs table corresponding to the present content of the
00521    // cache
00522    // Return 0 if ok, -1 otherwise
00523    EPNAME("Cache::Rehash");
00524 
00525    if (htmtime >= utime && !force) {
00526       TRACE(Dump, "hash table is up-to-date");
00527       return 0;
00528    }
00529 
00530    // Clean up the hash table
00531    hashtable.Purge();
00532 
00533    kXR_int32 i = 0, nht = 0;
00534    for (; i <= cachemx; i++) {
00535       if (cachent[i]) {
00536          // Fill the hash table 
00537          kXR_int32 *key = new kXR_int32(i);
00538          if (key) {
00539             TRACE(Dump, "Adding ID: "<<cachent[i]->name<<"; key: "<<*key);
00540             hashtable.Add(cachent[i]->name,key);
00541             nht++;
00542          }
00543       }
00544    }
00545    // Update modification time
00546    htmtime = (kXR_int32)time(0);
00547 
00548    DEBUG("Hash table updated (found "<<nht<<" active entries)");
00549    return 0;
00550 }
00551 
00552 //__________________________________________________________________
00553 int XrdSutCache::Flush(const char *pfn)
00554 {
00555    // Flush cache content to file pfn.
00556    // If pfn == 0 and the cache was initialized from a file, flush
00557    // to initializing file.
00558    // If pfn does not exist, create it.
00559    // Return 0 if ok, -1 otherwise
00560    EPNAME("Cache::Flush");
00561 
00562    // Make sure we have all the info
00563    if (!pfn && pfile.length() <= 0) {
00564       DEBUG("invalid input");
00565       return -1;
00566    }
00567    if (!pfn)
00568       pfn = pfile.c_str();
00569 
00570    // Attach to file and open it; create if not ther
00571    XrdSutPFile ff(pfn, (kPFEopen | kPFEcreate));
00572    if (!ff.IsValid()) {
00573       DEBUG("cannot attach-to or create file "<<pfn<<" ("<<ff.LastErrStr()<<")");
00574       return -1;
00575    }
00576 
00577    //
00578    // Loop over cache entries
00579    int i = 0, nr = 0, nfs = 0;
00580    for (; i <= cachemx; i++ ) {
00581       if (cachent[i]) {
00582          //
00583          // Retrieve related from file, if any
00584          // Read entry out
00585          XrdSutPFEntry ent;
00586          if ((nr = ff.ReadEntry(cachent[i]->name, ent)) < 0) {
00587             ff.Close();
00588             return -1;
00589          }
00590          //
00591          // Write (update) only if older that cache or not found
00592          if (nr == 0 || cachent[i]->mtime > ent.mtime) {         
00593             if (ff.WriteEntry(*cachent[i]) < 0) {
00594                ff.Close();
00595                return -1;
00596             }
00597             nfs++;
00598          }
00599       }
00600    }
00601 
00602    // Close the file
00603    ff.Close();
00604 
00605    // Update the time stamp (to avoid fake loads later on)
00606    utime = (kXR_int32)time(0);
00607 
00608    // Save file name
00609    if (pfile.length() <= 0)
00610       pfile = pfn;
00611 
00612    DEBUG("Cache flushed to file "<<pfn<<" ("<<nfs<<" entries updated / written)");
00613 
00614    return 0;
00615 }
00616 
00617 //__________________________________________________________________
00618 int XrdSutCache::Refresh()
00619 {
00620    // Refresh content of a cache created from file.
00621     // Return 0 if ok, -1 otherwise
00622    EPNAME("Cache::Refresh");
00623 
00624    // Make sure we have all the info
00625    if (pfile.length() <= 0) {
00626       DEBUG("cache was not initialized from file - do nothing");
00627       return -1;
00628    }
00629 
00630    // Check if file exists and if it has been modified since last load
00631    struct stat st;
00632    if (stat(pfile.c_str(),&st) == -1) {
00633       DEBUG("cannot stat file (errno: "<<errno<<")");
00634       return -1;
00635    }
00636    if (utime > -1 && utime > st.st_mtime) {
00637       DEBUG("cached information for file "<<pfile<<" is up-to-date");
00638       return 0;
00639    }
00640 
00641    if (Load(pfile.c_str()) != 0) {
00642       DEBUG("problems loading passwd information from file: "<<pfile);
00643       return -1;
00644    }
00645 
00646    // Update the time stamp (to avoid fake loads or refreshs later on)
00647    utime = (kXR_int32)time(0);
00648 
00649    DEBUG("Cache refreshed from file: "<<pfile);
00650 
00651    return 0;
00652 }
00653 

Generated on Tue Jul 5 14:47:02 2011 for ROOT_528-00b_version by  doxygen 1.5.1