CMS 3D CMS Logo

IgModuleCache.cc

Go to the documentation of this file.
00001 //<<<<<< INCLUDES                                                       >>>>>>
00002 
00003 #include "Iguana/Framework/interface/IgModuleCache.h"
00004 #include "Iguana/Framework/interface/IgModuleDescriptor.h"
00005 #include "Iguana/Framework/interface/IgModule.h"
00006 #include "Iguana/Framework/interface/IgPluginManager.h"
00007 #include "Iguana/Framework/interface/IgPluginError.h"
00008 #include "IgPluginParserError.h"
00009 #include "debug.h"
00010 #include "classlib/utils/DebugAids.h"
00011 #include "classlib/utils/Log.h"
00012 #include "classlib/iobase/DirIterator.h"
00013 #include "classlib/utils/SharedLibrary.h"
00014 #include "classlib/utils/StringFormat.h"
00015 #include <algorithm>
00016 #include <cstdio>
00017 #include <cstring>
00018 #include <cstdlib>
00019 #include <cctype>
00020 #include <cerrno>
00021 #include <sys/stat.h>
00022 #include <sstream>
00023 #include <vector>
00024 #include <string>
00025 
00026 //<<<<<< PRIVATE DEFINES                                                >>>>>>
00027 //<<<<<< PRIVATE CONSTANTS                                              >>>>>>
00028 
00029 static const char SAFE [] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00030                               "abcdefghijklmnopqrstuvwxyz"
00031                               "01234567890.,/_-" };
00032 static const char HEX [] = { "0123456789abcdef" };
00033 
00034 //<<<<<< PRIVATE TYPES                                                  >>>>>>
00035 
00036 class IgModuleCache::FileByName
00037 {
00038     lat::Filename m_name;
00039 public:
00040     FileByName (const lat::Filename &name) : m_name (name) {}
00041     bool operator() (const IgModule *info)
00042     { return info->libraryName().nondirectory() == m_name; }
00043 };
00044 
00045 //<<<<<< PRIVATE VARIABLE DEFINITIONS                                   >>>>>>
00046 //<<<<<< PUBLIC VARIABLE DEFINITIONS                                    >>>>>>
00047 //<<<<<< CLASS STRUCTURE INITIALIZATION                                 >>>>>>
00048 
00049 const char              IgModuleCache::s_cacheFile [] = ".iglets";
00050 const char              IgModuleCache::s_cacheTag  [] = "cache";
00051 
00052 //<<<<<< PRIVATE FUNCTION DEFINITIONS                                   >>>>>>
00053 
00056 static std::string
00057 encode (const std::string &input)
00058 {
00059     std::string result;
00060     result.reserve (input.size ());
00061     for (const char *s = input.c_str (); *s; ++s)
00062         if (strchr (SAFE, *s))
00063             result += *s;
00064         else
00065         {
00066             result += '%';
00067             result += HEX [((unsigned char) *s >> 4) & 0xf];
00068             result += HEX [((unsigned char) *s     ) & 0xf];
00069         }
00070     return result;
00071 }
00072 
00076 static std::string
00077 decode (const std::string &input)
00078 {
00079     std::string result;
00080     result.reserve (input.size ());
00081     for (const char *s = input.c_str (); *s; ++s)
00082         if (strchr (SAFE, *s))
00083             result += *s;
00084         else if (*s == '%' && isxdigit (s[1]) && isxdigit (s[2]))
00085         {
00086             result += (char) (((strchr (HEX, tolower (s[1])) - HEX) << 4)
00087                               + ((strchr (HEX, tolower (s[2])) - HEX)));
00088             s += 2;
00089         }
00090         else
00091             throw IgPluginError (new IgPluginParserError ("bad character"));
00092 
00093     return result;
00094 }
00095 
00096 
00097 //<<<<<< PUBLIC FUNCTION DEFINITIONS                                    >>>>>>
00098 //<<<<<< MEMBER FUNCTION DEFINITIONS                                    >>>>>>
00099 
00103 IgModuleCache::IgModuleCache (IgPluginManager *manager, const lat::Filename &directory)
00104     : m_manager (manager),
00105       m_directory (directory.asDirectory ())
00106 {
00107     ASSERT (! m_directory.empty ());
00108 
00109     m_state.status = CACHE_MISSING;
00110     m_state.added  = m_state.removed
00111                    = m_state.changed
00112                    = m_state.skipped
00113                    = false;
00114 
00115     m_parse.module = 0;
00116     refresh ();
00117 }
00118 
00120 IgModuleCache::~IgModuleCache (void)
00121 {
00122     LOG (0, trace, LFplugin_manager,
00123          "destroying module cache for `" << m_directory << "'\n" << lat::indent);
00124     ASSERT (! m_directory.empty ());
00125     for (ModuleIterator m = m_cache.begin (); m != m_cache.end (); ++m)
00126         delete *m;
00127 
00128     LOG (0, trace, LFplugin_manager, lat::undent);
00129 }
00130 
00132 lat::Filename
00133 IgModuleCache::directory (void) const
00134 { return m_directory; }
00135 
00137 IgModuleCache::Iterator
00138 IgModuleCache::begin (void) const
00139 { return m_modules.begin (); }
00140 
00142 IgModuleCache::Iterator
00143 IgModuleCache::end (void) const
00144 { return m_modules.end (); }
00145 
00149 void
00150 IgModuleCache::refresh (void)
00151 {
00152     load ();
00153     rebuild ();
00154     notify ();
00155     update ();
00156 }
00157 
00166 bool
00167 IgModuleCache::parse (const lat::Filename &file, IgModuleDescriptor *root /* = 0 */)
00168 {
00169     // FIXME: Use platform independent facilities for all file stuff (actually, registry)
00170     struct stat         statbuf;
00171     lat::Filename       filename (m_directory, file);
00172     bool                errors = false;
00173     IgModuleDescriptor  *regdata = root;
00174 
00175     if (::stat (filename, &statbuf) == -1)
00176     {
00177         delete regdata;
00178         return true;
00179     }
00180 
00181     LOG (0, trace, LFplugin_manager, "parsing file: " << file << "\n");
00182 
00183     ASSERT (m_parse.result.empty ());
00184     ASSERT (! m_parse.module);
00185 
00186     // Parse the registry file
00187     try {
00188         // FIXME: use an object so we don't need to fclose() it.
00189         FILE *f = fopen (filename, "r");
00190         if (! f)
00191             throw IgPluginError (new IgPluginParserError ("can't read the file"));
00192 
00193         typedef std::vector<IgModuleDescriptor *> ContextStack;
00194         ContextStack    stack;
00195         int             offset = 0;
00196         int             ch;
00197 
00198         // Bias the stack in case we were give a root preamble.  In
00199         // that case we simply descend as far as we can into the first
00200         // children of the preamble, and insert parse results there.
00201         // `regdata' is assinged
00202         while (root)
00203         {
00204             ++offset;
00205             stack.push_back (root);
00206             root = root->children () ? root->child (0) : 0;
00207         }
00208 
00209         // Process the file
00210         do {
00211             std::string token;
00212             int         level = offset;
00213 
00214             while ((ch = fgetc (f)) != EOF && ch == ' ')
00215                 ++level;
00216 
00217             if (ch == EOF)
00218                 break;
00219 
00220             if (level == 0 && regdata)
00221             {
00222                 fclose (f);
00223                 throw IgPluginError (new IgPluginParserError ("duplicate top level line"));
00224             }
00225 
00226             stack.resize (level+1);
00227             IgModuleDescriptor *doc
00228                 = new IgModuleDescriptor (level ? stack [level-1] : 0);
00229             stack [level] = doc;
00230             if (level == 0)
00231                 regdata = doc;
00232 
00233             for ( ; ch != EOF; ch = fgetc (f))
00234                 if (ch == '\n')
00235                 {
00236                     if (! token.empty ())
00237                         doc->addToken (decode (token));
00238 
00239                     if (doc->tokens () == 0)
00240                     {
00241                         fclose (f);
00242                         throw IgPluginError (new IgPluginParserError ("empty line"));
00243                     }
00244 
00245                     break;
00246                 }
00247                 else if (ch == ' ')
00248                 {
00249                     if (! token.empty ())
00250                         doc->addToken (decode (token));
00251                     token = "";
00252                 }
00253                 else
00254                     token += ch;
00255         } while (ch != EOF);
00256 
00257         fclose (f);
00258         reconstruct (regdata);
00259 
00260         ASSERT (! m_parse.module);
00261     } catch (lat::Error &e) {
00262         m_manager->feedback (IgPluginManager::ErrorBadCacheFile, file, &e);
00263 
00264         // FIXME: Notify owner
00265         LOG (0, error, LFplugin_manager,
00266              file << ": error, file ignored (" << e.explain() << ")\n");
00267 
00268         errors = true;
00269     } catch (...) {
00270         m_manager->feedback (IgPluginManager::Other, file);
00271 
00272         // FIXME: Notify owner
00273         LOG (0, error, LFplugin_manager,
00274              file << ": unknown error, file ignored\n");
00275 
00276         errors = true;
00277     }
00278 
00279     delete regdata;
00280 
00281     if (errors)
00282     {
00283         delete m_parse.module;
00284         m_parse.module = 0;
00285 
00286         for (ModuleIterator m = m_parse.result.begin ();
00287              m != m_parse.result.end (); ++m)
00288             delete *m;
00289 
00290         m_parse.result.clear ();
00291     }
00292 
00293     return !errors;
00294 }
00295 
00302 void
00303 IgModuleCache::load (void)
00304 {
00305     LOG (0, trace, LFplugin_manager, "loading cache "
00306          << lat::Filename (m_directory, s_cacheFile) << '\n' << lat::indent);
00307 
00308     m_state.status = CACHE_MISSING;
00309     m_state.added  = m_state.removed
00310                    = m_state.changed
00311                    = m_state.skipped
00312                    = false;
00313 
00314     if (parse (s_cacheFile))
00315     {
00316         m_state.status = CACHE_OLD;
00317 
00318         // Clear the cache before adding to it (in case we already
00319         // have something, i.e. refresh() was already invoked).
00320         for (ModuleIterator m = m_cache.begin (); m != m_cache.end (); ++m)
00321             delete *m;
00322 
00323         m_cache.clear ();
00324         m_cache.splice (m_cache.end (), m_parse.result);
00325     }
00326 
00327     LOG (0, trace, LFplugin_manager, lat::undent << "done\n");
00328 }
00329 
00331 IgModuleDescriptor *
00332 IgModuleCache::makeBad (const std::string &file, const std::string &time)
00333 {
00334     IgPluginManager::get ()->feedback (IgPluginManager::ErrorBadCacheFile, file);
00335     IgModuleDescriptor *top = new IgModuleDescriptor (0, s_cacheTag);
00336     new IgModuleDescriptor (top, IgModule::tag (), file, time, "bad");
00337     return top;
00338 }
00339 
00343 IgModuleCache::CacheStatus
00344 IgModuleCache::scanModules (ModuleList &cache)
00345 {
00346     LOG (0, trace, LFplugin_manager,
00347          "scanning `" << m_directory << "'\n" << lat::indent);
00348 
00349     CacheStatus         result = CACHE_MISSING;
00350     lat::DirIterator    file (m_directory);
00351     lat::DirIterator    end;
00352 
00353     ASSERT (cache.empty ());
00354     for (; file != end; ++file)
00355     {
00356         // Ignore the cache file.
00357         if (*file == s_cacheFile)
00358             continue;
00359 
00360         // Check, read and validate the module definition file.
00361         // FIXME: Use platform-independent facilities!
00362         struct stat     statbuf;
00363         lat::Filename   filename (m_directory, *file);
00364         if (filename.extension () != "iglet")
00365             continue;
00366 
00367         // Ignore files we cannot stat
00368         int status = stat (filename, &statbuf);
00369         if (status == -1)
00370         {
00371             LOG (0, error, LFplugin_manager,
00372                  "stat() failed with error " << errno << ", ignoring `"
00373                  << *file << "'\n");
00374             m_state.skipped = true;
00375             continue;
00376         }
00377 
00378         // Check whether the module reg fragment has changed by
00379         // comparing the file and cached timestamps.
00380         ModuleIterator match
00381             = std::find_if (m_cache.begin(), m_cache.end(),
00382                             FileByName (*file));
00383 
00384         if (match == m_cache.end ())
00385         {
00386             LOG (0, trace, LFplugin_manager,
00387                  "==> new module definition `" << *file << "'\n");
00388             m_state.added = true;
00389         }
00390         else if ((*match)->time () != (unsigned long) statbuf.st_mtime)
00391         {
00392             LOG (0, trace, LFplugin_manager,
00393                  "==> modified module definition `" << *file << "' (file is "
00394                  << statbuf.st_mtime << ", cached was " << (*match)->time ()
00395                  << ")\n");
00396             m_state.changed = true;
00397         }
00398         else
00399         {
00400             // Copy the old cache to the new cache.  This relies on
00401             // the knowledge that the old cache will be thrown away
00402             // and so it is ok to steal the entry from the old one to
00403             // the new one.
00404             LOG (0, trace, LFplugin_manager,
00405                  "==> unchanged definition `" << *file << "'\n");
00406             cache.splice (cache.end (), m_cache, match);
00407             continue;
00408         }
00409 
00410         // Construct a fake registry document root for this file
00411         std::string time = lat::StringFormat ("%1").arg (statbuf.st_mtime);
00412         IgModuleDescriptor *top = new IgModuleDescriptor (0, s_cacheTag);
00413         new IgModuleDescriptor (top, IgModule::tag (), *file, time, "good");
00414         ASSERT (m_parse.result.empty ());
00415         ASSERT (! m_parse.module);
00416         reconstruct (top);
00417         ASSERT (! m_parse.module);
00418 
00419         // Try loading the modules to check that they are well-formed:
00420         // ensure that each module loads and resolves the required
00421         // entry points.  If this fails, mark the whole registration
00422         // file bad so that it will be ignored until the time-stamp
00423         // changes.  This leaves the cache always in valid state,
00424         // remembering anything bad.
00425         ASSERT (m_parse.result.size () == 1);
00426         for (ModuleIterator m = m_parse.result.begin () ; m != m_parse.result.end (); ++m)
00427             try
00428             {
00429                 (*m)->load (); // checks for entry points
00430                 (*m)->query ();
00431                 (*m)->unload ();
00432             }
00433             catch (lat::Error &e)
00434             {
00435                 m_manager->feedback (IgPluginManager::ErrorBadModule,
00436                                      (*m)->libraryName (), &e);
00437 
00438                 // FIXME: Notify to owner
00439                 LOG (0, error, LFplugin_manager,
00440                      *file << ": bad module library `"
00441                      << (*m)->libraryName ()
00442                      << "', module ignored\n (error was: "
00443                      << e.explain () << ")\n");
00444                 m_state.skipped = true;
00445                 (*m)->bad (true);
00446                 break;
00447             }
00448 
00449         // Transfer into cache.
00450         cache.splice (cache.end (), m_parse.result);
00451     }
00452 
00453     // See if cache contains info on removed modules.
00454     for (ModuleIterator m = m_cache.begin (); m != m_cache.end (); ++m)
00455         if (std::find_if (cache.begin (), cache.end (),
00456                           FileByName ((*m)->libraryName ().nondirectory ()))
00457             == cache.end ())
00458         {
00459             LOG (0, trace, LFplugin_manager,
00460                  "==> removed module `" << (*m)->libraryName () << "'\n");
00461             m_state.removed = true;
00462         }
00463 
00464     // Update status codes.  Note that CACHE_VALID means .cache is
00465     // valid, CACHE_OLD means it needs updating.  If directory was
00466     // empty, mark the cache valid so we don't try to create one.
00467     result = (m_state.added
00468               || m_state.removed
00469               || m_state.changed
00470               || m_state.skipped)
00471               ? CACHE_OLD : CACHE_VALID;
00472 
00473     LOG (0, trace, LFplugin_manager,
00474          lat::undent << "done (" << (int) result << ")\n");
00475     return result;
00476 }
00477 
00482 void
00483 IgModuleCache::rebuild (void)
00484 {
00485     LOG (0, trace, LFplugin_manager,
00486          "rebuilding cache in `" << m_directory << "'\n" << lat::indent);
00487 
00488     // Build a new cache and stealing bits from the old one as the new
00489     // one is rebuilt.  Then replace the cache with the new one and
00490     // delete whatever was left over from the old cache (creating the
00491     // new one may steal bits of the old).
00492     ModuleList cache;
00493     m_state.status = scanModules (cache);
00494     m_cache.swap (cache);
00495     for (ModuleIterator m = cache.begin (); m != cache.end (); ++m)
00496         delete *m;
00497 
00498     LOG (0, trace, LFplugin_manager,
00499          lat::undent << "done (" << (int) m_state.status
00500          << ", added = " << m_state.added
00501          << ", removed = " << m_state.removed
00502          << ", changed = " << m_state.changed
00503          << ", skipped = " << m_state.skipped
00504          << ")\n");
00505 }
00506 
00508 void
00509 IgModuleCache::notify (void)
00510 {
00511     LOG (0, trace, LFplugin_manager, "collecting modules\n" << lat::indent);
00512     for (ModuleIterator m = m_cache.begin (); m != m_cache.end (); ++m)
00513         if (! (*m)->bad ()) m_modules.push_back (*m);
00514     LOG (0, trace, LFplugin_manager, lat::undent);
00515 }
00516 
00520 
00524 void
00525 IgModuleCache::write (std::ostream &s, IgModuleDescriptor *doc, int level/*=0*/)
00526 {
00527     s << std::string (level, ' ') << encode (doc->token (0));
00528     for (unsigned i = 1; i < doc->tokens (); ++i)
00529         s << ' ' << encode (doc->token (i));
00530     s << std::endl;
00531 
00532     for (unsigned i = 0; i < doc->children (); ++i)
00533         write (s, doc->child (i), level+1);
00534 }
00535 
00537 void
00538 IgModuleCache::update (void)
00539 {
00540     if (m_state.status == CACHE_VALID)
00541         return;
00542 
00543     // Regenerate cache file from information we have in memory
00544     LOG (0, trace, LFplugin_manager,
00545          "updating cache in `" << m_directory << "'\n" << lat::indent);
00546 
00547     // Build a descriptor tree and dump it out
00548     IgModuleDescriptor *all = new IgModuleDescriptor (0, s_cacheTag);
00549     for (ModuleIterator m = m_cache.begin (); m != m_cache.end (); ++m)
00550         (*m)->cache (all);
00551 
00552     std::ostringstream buf;
00553     write (buf, all);
00554     delete all;
00555 
00556     lat::Filename filename (m_directory, s_cacheFile);
00557     FILE          *file = STDC::fopen (filename, "wb");
00558 
00559     if (! file)
00560     {
00561         LOG (0, error, LFplugin_manager,
00562              filename << ": error " << errno << ", unable to open the cache"
00563              " for writing -- cache not updated\n" << lat::undent);
00564         return;
00565     }
00566 
00567     std::string data (buf.str ());
00568     if (STDC::fwrite (data.c_str (), 1, data.size (), file) != data.size ())
00569     {
00570         LOG (0, error, LFplugin_manager,
00571              filename << ": error " << errno << " while writing cache out"
00572              " -- forcing file truncation\n");
00573 
00574         file = STDC::freopen (filename, "wb", file);
00575     }
00576     else
00577         m_state.status = CACHE_VALID;
00578 
00579     STDC::fclose (file);
00580 
00581     LOG (0, trace, LFplugin_manager, lat::undent << "done\n");
00582 }
00583 
00587 
00588 void
00589 IgModuleCache::reconstruct (IgModuleDescriptor *info)
00590 {
00591     // Protect against random junk, e.g. empty files
00592     if (! info || ! info->tokens ())
00593         throw IgPluginError (new IgPluginParserError ("empty descriptor"));
00594 
00595     // Make sure the root is either a cache...
00596     if (info->token (0) == s_cacheTag)
00597     {
00598         if (info->tokens () != 1)
00599             throw IgPluginError (new IgPluginParserError
00600                                  ("`cache' does not take arguments"));
00601         else if (info->parent ())
00602             throw IgPluginError (new IgPluginParserError
00603                                  ("`cache' must be at the root"));
00604 
00605         for (unsigned i = 0; i < info->children (); ++i)
00606             reconstruct (info->child (i));
00607 
00608         ASSERT (! m_parse.module);
00609     }
00610     // ... or a module itself
00611     else if (info->token (0) == IgModule::tag ())
00612     {
00613         if (info->tokens () != 4)
00614             throw IgPluginError (new IgPluginParserError
00615                                  ("`module' must have three arguments"));
00616         if (! info->parent () || info->parent ()->token (0) != s_cacheTag)
00617             throw IgPluginError (new IgPluginParserError
00618                                  ("`module' must be under `cache'"));
00619         if (info->token (3) != "good" && info->token (3) != "bad")
00620             throw IgPluginError (new IgPluginParserError
00621                                  ("`module' last argument must be"
00622                                   " 'good' or 'bad'"));
00623 
00624         ASSERT (! m_parse.module);
00625         m_parse.module = new IgModule (m_manager,
00626                                        lat::Filename (m_directory, info->token (1)),
00627                                        info->token (2),
00628                                        info->token (3));
00629 
00630         for (unsigned i = 0; i < info->children (); ++i)
00631             reconstruct (info->child (i));
00632 
00633         ASSERT (m_parse.module);
00634         m_parse.result.push_back (m_parse.module);
00635         m_parse.module = 0;
00636     }
00637     // ... or junk we don't understand
00638     else
00639     {
00640         if (! m_parse.module)
00641             throw IgPluginError (new IgPluginParserError
00642                                  (info->token (0) + " unexpected"));
00643 
00644         if (info->tokens () >= 2)
00645             LOG (0, trace, LFplugin_manager, "processing "
00646                  << info->token(0) << ' ' << info->token(1) << '\n');
00647 
00648         m_parse.module->restore (info);
00649     }
00650 }

Generated on Tue Jun 9 17:38:28 2009 for CMSSW by  doxygen 1.5.4