CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_5_3_9_patch3/src/FWCore/PluginManager/bin/refresh.cc

Go to the documentation of this file.
00001 #include "FWCore/PluginManager/interface/CacheParser.h"
00002 #include "FWCore/PluginManager/interface/PluginCapabilities.h"
00003 #include "FWCore/PluginManager/interface/PluginFactoryBase.h"
00004 #include "FWCore/PluginManager/interface/PluginFactoryManager.h"
00005 #include "FWCore/PluginManager/interface/SharedLibrary.h"
00006 #include "FWCore/PluginManager/interface/standard.h"
00007 
00008 #include "FWCore/Utilities/interface/Exception.h"
00009 #include "FWCore/Utilities/interface/Algorithms.h"
00010 
00011 #include <boost/bind.hpp>
00012 #include <boost/filesystem/operations.hpp>
00013 #include <boost/mem_fn.hpp>
00014 #include <boost/program_options.hpp>
00015 
00016 #include <algorithm>
00017 #include <cstdlib>
00018 #include <fstream>
00019 #include <iostream>
00020 #include <set>
00021 #include <string>
00022 #include <utility>
00023 
00024 #include <sys/wait.h>
00025 
00026 using namespace edmplugin;
00027 
00028 // We process DSOs by forking and processing them in the child, in bunches of 
00029 // PER_PROCESS_DSO. Once a bunch has been processed we exit the child and
00030 // spawn a new one.
00031 // Due to the different behavior of the linker on different platforms, we process
00032 // a different number of DSOs per process.
00033 // For macosx the cost is to actually resolve weak symbols and it grows with
00034 // the number of libraries as well, so we try to process not too many libraries.
00035 // In linux the main cost is to load the library itself, so we try to load as 
00036 // many as reasonable in a single process to avoid having to reload them in 
00037 // the subsequent process. Obviuously keeping the PER_PROCESS_DSO low keeps the
00038 // memory usage low as well.
00039 #ifdef __APPLE__
00040 #define PER_PROCESS_DSO 20
00041 #else
00042 #define PER_PROCESS_DSO 200
00043 #endif
00044 
00045 namespace std {
00046   ostream& operator<<(std::ostream& o, vector<std::string> const& iValue) {
00047     std::string sep("");
00048     std::string commaSep(",");
00049     for(std::vector<std::string>::const_iterator it = iValue.begin(), itEnd = iValue.end();
00050         it != itEnd;
00051         ++it) {
00052       o << sep << *it;
00053       sep = commaSep;
00054     }
00055     return o;
00056   }
00057 }
00058 namespace {
00059   struct Listener {
00060     typedef edmplugin::CacheParser::NameAndType NameAndType;
00061     typedef edmplugin::CacheParser::NameAndTypes NameAndTypes;
00062 
00063     void newFactory(edmplugin::PluginFactoryBase const* iBase) {
00064       iBase->newPluginAdded_.connect(boost::bind(boost::mem_fn(&Listener::newPlugin), this, _1, _2));
00065     }
00066     void newPlugin(std::string const& iCategory, edmplugin::PluginInfo const& iInfo) {
00067       nameAndTypes_.push_back(NameAndType(iInfo.name_, iCategory));
00068     }
00069 
00070     NameAndTypes nameAndTypes_;
00071   };
00072 }
00073 int main (int argc, char **argv) try {
00074   using namespace boost::program_options;
00075 
00076   static char const* const kPathsOpt = "paths";
00077   static char const* const kPathsCommandOpt = "paths,p";
00078   //static char const* const kAllOpt = "all";
00079   //static char const* const kAllCommandOpt = "all,a";
00080   static char const* const kHelpOpt = "help";
00081   static char const* const kHelpCommandOpt = "help,h";
00082 
00083   std::string descString(argv[0]);
00084   descString += " [options] [[--";
00085   descString += kPathsOpt;
00086   descString += "] path [path]] \nAllowed options";
00087   options_description desc(descString);
00088   std::string defaultDir(".");
00089   std::vector<std::string> defaultDirList = edmplugin::standard::config().searchPath();
00090   if(!defaultDirList.empty()) {
00091     defaultDir = defaultDirList[0];
00092   }
00093   desc.add_options()
00094     (kHelpCommandOpt, "produce help message")
00095     (kPathsCommandOpt, value<std::vector<std::string> >()->default_value(
00096                           std::vector<std::string>(1, defaultDir))
00097      , "a directory or a list of files to scan")
00098     //(kAllCommandOpt, "when no paths given, try to update caches for all known directories [default is to only scan the first directory]")
00099     ;
00100 
00101   positional_options_description p;
00102   p.add(kPathsOpt, -1);
00103 
00104   variables_map vm;
00105   try {
00106     store(command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
00107     notify(vm);
00108   } catch(error const& iException) {
00109     std::cerr << iException.what();
00110     return 1;
00111   }
00112 
00113   if(vm.count(kHelpOpt)) {
00114     std::cout << desc << std::endl;
00115     return 0;
00116   }
00117 
00118   using boost::filesystem::path;
00119 
00120   /*if(argc == 1) {
00121     std::cerr << "Requires at least one argument.  Please pass either one directory or a list of files (all in the same directory)." << std::endl;
00122     return 1;
00123   } */
00124 
00125   int returnValue = EXIT_SUCCESS;
00126 
00127   try {
00128     std::vector<std::string> requestedPaths(vm[kPathsOpt].as<std::vector<std::string> >());
00129 
00130     //first find the directory and create a list of files to look at in that directory
00131     path directory(requestedPaths[0]);
00132     std::vector<std::string> files;
00133     bool removeMissingFiles = false;
00134     if(boost::filesystem::is_directory(directory)) {
00135       if (requestedPaths.size() > 1) {
00136         std::cerr << "if a directory is given then only one argument is allowed" << std::endl;
00137         return 1;
00138       }
00139 
00140       //if asked to look at whole directory, then we can also remove missing files
00141       removeMissingFiles = true;
00142 
00143       boost::filesystem::directory_iterator       file (directory);
00144       boost::filesystem::directory_iterator       end;
00145 
00146       path cacheFile(directory);
00147       cacheFile /= standard::cachefileName();
00148 
00149       std::time_t cacheLastChange(0);
00150       if(exists(cacheFile)) {
00151         cacheLastChange = last_write_time(cacheFile);
00152       }
00153       for (; file != end; ++file)
00154       {
00155 
00156         path  filename (*file);
00157         path shortName(file->path().filename());
00158         std::string stringName = shortName.string();
00159 
00160         static std::string kPluginPrefix(standard::pluginPrefix());
00161         if (stringName.size() < kPluginPrefix.size()) {
00162           continue;
00163         }
00164         if(stringName.substr(0, kPluginPrefix.size()) != kPluginPrefix) {
00165           continue;
00166         }
00167 
00168         if(last_write_time(filename) > cacheLastChange) {
00169           files.push_back(stringName);
00170         }
00171       }
00172     } else {
00173       //we have files
00174       directory = directory.branch_path();
00175       for(std::vector<std::string>::iterator it = requestedPaths.begin(), itEnd = requestedPaths.end();
00176           it != itEnd; ++it) {
00177         boost::filesystem::path f(*it);
00178         if (!exists(f)) {
00179           std::cerr << "the file '" << f.string() << "' does not exist" << std::endl;
00180           return 1;
00181         }
00182         if (is_directory(f)) {
00183           std::cerr << "either one directory or a list of files are allowed as arguments" << std::endl;
00184           return 1;
00185         }
00186         if(directory != f.branch_path()) {
00187           std::cerr << "all files must have be in the same directory (" << directory.string() << ")\n"
00188           " the file " << f.string() << " does not." << std::endl;
00189         }
00190 #if (BOOST_VERSION / 100000) >= 1 && ((BOOST_VERSION / 100) % 1000) >= 47
00191         files.push_back(f.filename().string());
00192 #else
00193         files.push_back(f.leaf());
00194 #endif
00195       }
00196     }
00197 
00198     path cacheFile(directory);
00199     cacheFile /= edmplugin::standard::cachefileName();//path(s_cacheFile);
00200 
00201     CacheParser::LoadableToPlugins old;
00202     if(exists(cacheFile)) {
00203       std::ifstream cf(cacheFile.string().c_str());
00204       if(!cf) {
00205         cms::Exception("FailedToOpen") << "unable to open file '" << cacheFile.string() << "' for reading even though it is present.\n"
00206         "Please check permissions on the file.";
00207       }
00208       CacheParser::read(cf, old);
00209     }
00210 
00211 
00212     //load each file and 'listen' to which plugins are loaded
00213     Listener listener;
00214     edmplugin::PluginFactoryManager* pfm =  edmplugin::PluginFactoryManager::get();
00215     pfm->newFactory_.connect(boost::bind(boost::mem_fn(&Listener::newFactory), &listener, _1));
00216     edm::for_all(*pfm, boost::bind(boost::mem_fn(&Listener::newFactory), &listener, _1));
00217 
00218     // We open the cache file before forking so that all the children will
00219     // use it.
00220     std::string temporaryFilename = (cacheFile.string() + ".tmp");
00221     std::ofstream cf(temporaryFilename.c_str());
00222     if(!cf) {
00223       cms::Exception("FailedToOpen") << "unable to open file '" 
00224           << temporaryFilename << "' for writing.\n"
00225       "Please check permissions on the file.";
00226     }
00227     // Sort the files so that they are loaded "by subsystem", hopefully meaning
00228     // they share more dependencies.
00229     std::sort(files.begin(), files.end());
00230     
00231     for(size_t fi = 0, fe = files.size(); fi < fe; fi += PER_PROCESS_DSO)
00232     {
00233       CacheParser::LoadableToPlugins ltp;
00234       pid_t worker = fork();
00235       if (worker == 0)
00236       {
00237         // This the child process.
00238         // We load the DSO and find out its plugins, write to the cache 
00239         // stream and exit, leaving the parent to spawn a new proces.
00240         size_t ci = PER_PROCESS_DSO;
00241         while (ci && fi != fe)
00242         {
00243           path loadableFile(directory);                                                                                                                                                                                                                                          
00244           loadableFile /= (files[fi]);
00245           listener.nameAndTypes_.clear();
00246 
00247           returnValue = 0;
00248 
00249           try {
00250            try {
00251                edmplugin::SharedLibrary lib(loadableFile);
00252                //PluginCapabilities is special, the plugins do not call it.  Instead, for each shared library load
00253                // we need to ask it to try to find plugins
00254                PluginCapabilities::get()->tryToFind(lib);
00255                ltp[files[fi]] = listener.nameAndTypes_;
00256             
00257             } catch(cms::Exception const& iException) {
00258                if(iException.category() == "PluginLibraryLoadError") {
00259                   std::cerr << "Caught exception " << iException.what() << " will ignore " << files[fi] << " and continue." << std::endl;
00260                } else {
00261                   throw;
00262                }
00263             }
00264           }catch(std::exception& iException) {
00265             std::cerr << "Caught exception " << iException.what() << std::endl;
00266             exit(1);
00267           }
00268           ++fi;
00269           --ci;
00270         }
00271         CacheParser::write(ltp, cf);
00272         cf << std::flush;
00273         _exit(0);
00274       }
00275       else
00276       {
00277         // Throw if any of the child died with non 0 status.
00278         int status = 0;
00279         waitpid(worker, &status, 0);
00280         if (WIFEXITED(status) == true && status != 0)
00281         {
00282           std::cerr << "Error while processing." << std::endl;
00283           exit(status);
00284         }
00285       }
00286     }
00287     
00288     cf << std::flush;
00289     
00290     // We read the new cache and we merge it with the old one.
00291     CacheParser::LoadableToPlugins ltp;
00292     std::ifstream icf(temporaryFilename.c_str());
00293     if(!icf) {
00294       cms::Exception("FailedToOpen") << "unable to open file '" << temporaryFilename.c_str() << "' for reading even though it is present.\n"
00295       "Please check permissions on the file.";
00296     }
00297     CacheParser::read(icf, ltp);
00298     
00299     for (CacheParser::LoadableToPlugins::iterator itFile = ltp.begin() ;
00300          itFile != ltp.end() ;
00301          ++itFile)
00302     {
00303       old[itFile->first] = itFile->second; 
00304     }
00305     
00306     // If required, we remove the plugins which are missing. Notice that old is
00307     // now the most updated copy of the cache.
00308     if(removeMissingFiles) {
00309       for(CacheParser::LoadableToPlugins::iterator itFile = old.begin();
00310           itFile != old.end();
00311           /*don't advance the iterator here because it may have become invalid */) {
00312         path loadableFile(directory);
00313         loadableFile /= (itFile->first);
00314         if(not exists(loadableFile)) {
00315           std::cout << "removing file '" << temporaryFilename.c_str() << "'" << std::endl;
00316           CacheParser::LoadableToPlugins::iterator itToItemBeingRemoved = itFile;
00317           //advance the iterator while it is still valid
00318           ++itFile;
00319           old.erase(itToItemBeingRemoved);
00320         } else {
00321           //since we are not advancing the iterator in the for loop, do it here
00322           ++itFile;
00323         }
00324       }
00325     }
00326   
00327     // We finally write the final cache.
00328     std::ofstream fcf(temporaryFilename.c_str());
00329     if(!fcf) {
00330       cms::Exception("FailedToOpen") << "unable to open file '" << temporaryFilename.c_str() << "' for writing.\n"
00331       "Please check permissions on the file.";
00332     }
00333     CacheParser::write(old, fcf);
00334     rename(temporaryFilename.c_str(), cacheFile.string().c_str());  
00335   } catch(std::exception& iException) {
00336     std::cerr << "Caught exception " << iException.what() << std::endl;
00337     returnValue = 1;
00338   }
00339 
00340   return returnValue;
00341 } catch(std::exception const& iException) {
00342   std::cerr << iException.what() << std::endl;
00343   return 1;
00344 }