CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
refresh.cc
Go to the documentation of this file.
7 
10 
11 #include <boost/bind.hpp>
12 #include <boost/filesystem/operations.hpp>
13 #include <boost/mem_fn.hpp>
14 #include <boost/program_options.hpp>
15 
16 #include <algorithm>
17 #include <cstdlib>
18 #include <fstream>
19 #include <iostream>
20 #include <set>
21 #include <string>
22 #include <utility>
23 
24 #include <sys/wait.h>
25 
26 using namespace edmplugin;
27 
28 // We process DSOs by forking and processing them in the child, in bunches of
29 // PER_PROCESS_DSO. Once a bunch has been processed we exit the child and
30 // spawn a new one.
31 // Due to the different behavior of the linker on different platforms, we process
32 // a different number of DSOs per process.
33 // For macosx the cost is to actually resolve weak symbols and it grows with
34 // the number of libraries as well, so we try to process not too many libraries.
35 // In linux the main cost is to load the library itself, so we try to load as
36 // many as reasonable in a single process to avoid having to reload them in
37 // the subsequent process. Obviuously keeping the PER_PROCESS_DSO low keeps the
38 // memory usage low as well.
39 #ifdef __APPLE__
40 #define PER_PROCESS_DSO 20
41 #else
42 #define PER_PROCESS_DSO 200
43 #endif
44 
45 namespace std {
46  ostream& operator<<(std::ostream& o, vector<std::string> const& iValue) {
47  std::string sep("");
48  std::string commaSep(",");
49  for(std::vector<std::string>::const_iterator it = iValue.begin(), itEnd = iValue.end();
50  it != itEnd;
51  ++it) {
52  o << sep << *it;
53  sep = commaSep;
54  }
55  return o;
56  }
57 }
58 namespace {
59  struct Listener {
60  typedef edmplugin::CacheParser::NameAndType NameAndType;
61  typedef edmplugin::CacheParser::NameAndTypes NameAndTypes;
62 
63  void newFactory(edmplugin::PluginFactoryBase const* iBase) {
64  iBase->newPluginAdded_.connect(boost::bind(boost::mem_fn(&Listener::newPlugin), this, _1, _2));
65  }
66  void newPlugin(std::string const& iCategory, edmplugin::PluginInfo const& iInfo) {
67  nameAndTypes_.push_back(NameAndType(iInfo.name_, iCategory));
68  }
69 
70  NameAndTypes nameAndTypes_;
71  };
72 }
73 int main (int argc, char **argv) try {
74  using namespace boost::program_options;
75 
76  static char const* const kPathsOpt = "paths";
77  static char const* const kPathsCommandOpt = "paths,p";
78  //static char const* const kAllOpt = "all";
79  //static char const* const kAllCommandOpt = "all,a";
80  static char const* const kHelpOpt = "help";
81  static char const* const kHelpCommandOpt = "help,h";
82 
83  std::string descString(argv[0]);
84  descString += " [options] [[--";
85  descString += kPathsOpt;
86  descString += "] path [path]] \nAllowed options";
87  options_description desc(descString);
88  std::string defaultDir(".");
89  std::vector<std::string> defaultDirList = edmplugin::standard::config().searchPath();
90  if(!defaultDirList.empty()) {
91  defaultDir = defaultDirList[0];
92  }
93  desc.add_options()
94  (kHelpCommandOpt, "produce help message")
95  (kPathsCommandOpt, value<std::vector<std::string> >()->default_value(
96  std::vector<std::string>(1, defaultDir))
97  , "a directory or a list of files to scan")
98  //(kAllCommandOpt, "when no paths given, try to update caches for all known directories [default is to only scan the first directory]")
99  ;
100 
101  positional_options_description p;
102  p.add(kPathsOpt, -1);
103 
104  variables_map vm;
105  try {
106  store(command_line_parser(argc, argv).options(desc).positional(p).run(), vm);
107  notify(vm);
108  } catch(error const& iException) {
109  std::cerr << iException.what();
110  return 1;
111  }
112 
113  if(vm.count(kHelpOpt)) {
114  std::cout << desc << std::endl;
115  return 0;
116  }
117 
119 
120  /*if(argc == 1) {
121  std::cerr << "Requires at least one argument. Please pass either one directory or a list of files (all in the same directory)." << std::endl;
122  return 1;
123  } */
124 
125  int returnValue = EXIT_SUCCESS;
126 
127  try {
128  std::vector<std::string> requestedPaths(vm[kPathsOpt].as<std::vector<std::string> >());
129 
130  //first find the directory and create a list of files to look at in that directory
131  path directory(requestedPaths[0]);
132  std::vector<std::string> files;
133  bool removeMissingFiles = false;
134  if(boost::filesystem::is_directory(directory)) {
135  if (requestedPaths.size() > 1) {
136  std::cerr << "if a directory is given then only one argument is allowed" << std::endl;
137  return 1;
138  }
139 
140  //if asked to look at whole directory, then we can also remove missing files
141  removeMissingFiles = true;
142 
143  boost::filesystem::directory_iterator file (directory);
144  boost::filesystem::directory_iterator end;
145 
146  path cacheFile(directory);
147  cacheFile /= standard::cachefileName();
148 
149  std::time_t cacheLastChange(0);
150  if(exists(cacheFile)) {
151  cacheLastChange = last_write_time(cacheFile);
152  }
153  for (; file != end; ++file)
154  {
155 
156  path filename (*file);
157  path shortName(file->path().filename());
158  std::string stringName = shortName.string();
159 
160  static std::string const kPluginPrefix(standard::pluginPrefix());
161  if (stringName.size() < kPluginPrefix.size()) {
162  continue;
163  }
164  if(stringName.substr(0, kPluginPrefix.size()) != kPluginPrefix) {
165  continue;
166  }
167 
168  if(last_write_time(filename) > cacheLastChange) {
169  files.push_back(stringName);
170  }
171  }
172  } else {
173  //we have files
174  directory = directory.branch_path();
175  for(std::vector<std::string>::iterator it = requestedPaths.begin(), itEnd = requestedPaths.end();
176  it != itEnd; ++it) {
178  if (!exists(f)) {
179  std::cerr << "the file '" << f.string() << "' does not exist" << std::endl;
180  return 1;
181  }
182  if (is_directory(f)) {
183  std::cerr << "either one directory or a list of files are allowed as arguments" << std::endl;
184  return 1;
185  }
186  if(directory != f.branch_path()) {
187  std::cerr << "all files must have be in the same directory (" << directory.string() << ")\n"
188  " the file " << f.string() << " does not." << std::endl;
189  }
190 #if (BOOST_VERSION / 100000) >= 1 && ((BOOST_VERSION / 100) % 1000) >= 47
191  files.push_back(f.filename().string());
192 #else
193  files.push_back(f.leaf());
194 #endif
195  }
196  }
197 
198  path cacheFile(directory);
199  cacheFile /= edmplugin::standard::cachefileName();//path(s_cacheFile);
200 
202  if(exists(cacheFile)) {
203  std::ifstream cf(cacheFile.string().c_str());
204  if(!cf) {
205  cms::Exception("FailedToOpen") << "unable to open file '" << cacheFile.string() << "' for reading even though it is present.\n"
206  "Please check permissions on the file.";
207  }
208  CacheParser::read(cf, old);
209  }
210 
211 
212  //load each file and 'listen' to which plugins are loaded
213  Listener listener;
215  pfm->newFactory_.connect(boost::bind(boost::mem_fn(&Listener::newFactory), &listener, _1));
216  edm::for_all(*pfm, boost::bind(boost::mem_fn(&Listener::newFactory), &listener, _1));
217 
218  // We open the cache file before forking so that all the children will
219  // use it.
220  std::string temporaryFilename = (cacheFile.string() + ".tmp");
221  std::ofstream cf(temporaryFilename.c_str());
222  if(!cf) {
223  cms::Exception("FailedToOpen") << "unable to open file '"
224  << temporaryFilename << "' for writing.\n"
225  "Please check permissions on the file.";
226  }
227  // Sort the files so that they are loaded "by subsystem", hopefully meaning
228  // they share more dependencies.
229  std::sort(files.begin(), files.end());
230 
231  for(size_t fi = 0, fe = files.size(); fi < fe; fi += PER_PROCESS_DSO)
232  {
234  pid_t worker = fork();
235  if (worker == 0)
236  {
237  // This the child process.
238  // We load the DSO and find out its plugins, write to the cache
239  // stream and exit, leaving the parent to spawn a new proces.
240  size_t ci = PER_PROCESS_DSO;
241  while (ci && fi != fe)
242  {
243  path loadableFile(directory);
244  loadableFile /= (files[fi]);
245  listener.nameAndTypes_.clear();
246 
247  try {
248  try {
249  edmplugin::SharedLibrary lib(loadableFile);
250  //PluginCapabilities is special, the plugins do not call it. Instead, for each shared library load
251  // we need to ask it to try to find plugins
253  ltp[files[fi]] = listener.nameAndTypes_;
254 
255  } catch(cms::Exception const& iException) {
256  if(iException.category() == "PluginLibraryLoadError") {
257  std::cerr << "Caught exception " << iException.what() << " will ignore " << files[fi] << " and continue." << std::endl;
258  } else {
259  throw;
260  }
261  }
262  }catch(std::exception& iException) {
263  std::cerr << "Caught exception " << iException.what() << std::endl;
264  exit(1);
265  }
266  ++fi;
267  --ci;
268  }
269  CacheParser::write(ltp, cf);
270  cf << std::flush;
271  _exit(0);
272  }
273  else
274  {
275  // Throw if any of the child died with non 0 status.
276  int status = 0;
277  waitpid(worker, &status, 0);
278  if (WIFEXITED(status) == true && status != 0)
279  {
280  std::cerr << "Error while processing." << std::endl;
281  exit(status);
282  }
283  }
284  }
285 
286  cf << std::flush;
287 
288  // We read the new cache and we merge it with the old one.
290  std::ifstream icf(temporaryFilename.c_str());
291  if(!icf) {
292  cms::Exception("FailedToOpen") << "unable to open file '" << temporaryFilename.c_str() << "' for reading even though it is present.\n"
293  "Please check permissions on the file.";
294  }
295  CacheParser::read(icf, ltp);
296 
297  for (CacheParser::LoadableToPlugins::iterator itFile = ltp.begin() ;
298  itFile != ltp.end() ;
299  ++itFile)
300  {
301  old[itFile->first] = itFile->second;
302  }
303 
304  // If required, we remove the plugins which are missing. Notice that old is
305  // now the most updated copy of the cache.
306  if(removeMissingFiles) {
307  for(CacheParser::LoadableToPlugins::iterator itFile = old.begin();
308  itFile != old.end();
309  /*don't advance the iterator here because it may have become invalid */) {
310  path loadableFile(directory);
311  loadableFile /= (itFile->first);
312  if(not exists(loadableFile)) {
313  std::cout << "removing file '" << temporaryFilename.c_str() << "'" << std::endl;
314  CacheParser::LoadableToPlugins::iterator itToItemBeingRemoved = itFile;
315  //advance the iterator while it is still valid
316  ++itFile;
317  old.erase(itToItemBeingRemoved);
318  } else {
319  //since we are not advancing the iterator in the for loop, do it here
320  ++itFile;
321  }
322  }
323  }
324 
325  // We finally write the final cache.
326  std::ofstream fcf(temporaryFilename.c_str());
327  if(!fcf) {
328  cms::Exception("FailedToOpen") << "unable to open file '" << temporaryFilename.c_str() << "' for writing.\n"
329  "Please check permissions on the file.";
330  }
331  CacheParser::write(old, fcf);
332  rename(temporaryFilename.c_str(), cacheFile.string().c_str());
333  } catch(std::exception& iException) {
334  std::cerr << "Caught exception " << iException.what() << std::endl;
335  returnValue = EXIT_FAILURE;
336  }
337 
338  return returnValue;
339 } catch(std::exception const& iException) {
340  std::cerr << iException.what() << std::endl;
341  return 1;
342 }
#define PER_PROCESS_DSO
Definition: refresh.cc:42
virtual char const * what() const
Definition: Exception.cc:141
const boost::filesystem::path & cachefileName()
Definition: standard.cc:47
std::pair< std::string, std::string > NameAndType
Definition: CacheParser.h:43
bool tryToFind(const SharedLibrary &iLoadable)
Check to see if any capabilities are in the file, returns &#39;true&#39; if found.
std::string const & category() const
Definition: Exception.cc:183
const std::string & pluginPrefix()
Definition: standard.cc:53
int main(int argc, char **argv)
Func for_all(ForwardSequence &s, Func f)
wrapper for std::for_each
Definition: Algorithms.h:16
std::map< boost::filesystem::path, NameAndTypes > LoadableToPlugins
Definition: CacheParser.h:45
tuple path
else: Piece not in the list, fine.
PluginManager::Config config()
Definition: standard.cc:21
edm::signalslot::Signal< void(const PluginFactoryBase *)> newFactory_
std::vector< NameAndType > NameAndTypes
Definition: CacheParser.h:44
edm::signalslot::Signal< void(const std::string &, const PluginInfo &)> newPluginAdded_
signal containing plugin category, and plugin info for newly added plugin
static PluginCapabilities * get()
double f[11][100]
#define end
Definition: vmac.h:37
static void write(const CategoryToInfos &, std::ostream &)
Definition: CacheParser.cc:151
static const char *const kHelpCommandOpt
Definition: CmsShowMain.cc:94
tuple argc
Definition: dir2webdir.py:38
std::string name_
Definition: PluginInfo.h:29
tuple filename
Definition: lut2db_cfg.py:20
Config & searchPath(const SearchPath &iPath)
Definition: PluginManager.h:59
tuple cout
Definition: gather_cfg.py:121
tuple status
Definition: ntuplemaker.py:245
static void read(std::istream &, const boost::filesystem::path &iDirectory, CategoryToInfos &oOut)
Definition: CacheParser.cc:125
static const char *const kHelpOpt
Definition: CmsShowMain.cc:93
static PluginFactoryManager * get()