LibraryManager.cc
Go to the documentation of this file.
2 
3 #include "boost/filesystem.hpp"
4 #include "boost/regex.hpp"
8 #include "cetlib/search_path.h"
9 #include "cetlib/shlib_utils.h"
10 #include "cetlib_except/demangle.h"
11 #include "cetlib_except/exception.h"
12 
13 extern "C" {
14 #include <dlfcn.h>
15 }
16 
17 #include <algorithm>
18 #include <iterator>
19 #include <sstream>
20 #include <vector>
21 
22 namespace {
23  std::string const default_pattern_stem{"(?:[A-Za-z0-9\\-]*_)*[A-Za-z0-9]+_"};
24 
25  inline std::string
26  maybe_trim_shlib_prefix(std::string const& spec)
27  {
28  return spec.find(cet::shlib_prefix()) == 0 ?
29  spec.substr(cet::shlib_prefix().size()) :
30  spec;
31  }
32 }
33 
35  std::string lib_type)
36  : LibraryManager{std::move(search_path),
37  std::move(lib_type),
38  default_pattern_stem}
39 {}
40 
42  std::string lib_type,
45  , lib_type_{std::move(lib_type)}
47 {
48  std::vector<std::string> matches;
51 
52  // Note the use of reverse iterators here: files found earlier in
53  // the vector will therefore overwrite those found later, which is
54  // what we want from "search path"-type behavior.
55  std::for_each(matches.rbegin(), matches.rend(), [this](auto const& match) {
56  this->lib_loc_map_inserter(match);
57  });
58 
59  // Build the spec to long library name translation table.
60  for (auto const& p : lib_loc_map_) {
62  }
63 
64  // Build the fast good-translation table.
65  for (auto const& p : spec_trans_map_) {
67  }
68 }
69 
71  : LibraryManager{std::move(lib_type), default_pattern_stem}
72 {}
73 
75  : LibraryManager{search_path{plugin_libpath(), std::nothrow},
76  std::move(lib_type),
77  std::move(pattern)}
78 {}
79 
80 size_t
81 cet::LibraryManager::getLoadableLibraries(std::vector<std::string>& list) const
82 {
83  return getLoadableLibraries(std::back_inserter(list));
84 }
85 
86 size_t
87 cet::LibraryManager::getLoadedLibraries(std::vector<std::string>& list) const
88 {
89  return getLoadedLibraries(std::back_inserter(list));
90 }
91 
92 size_t
93 cet::LibraryManager::getValidLibspecs(std::vector<std::string>& list) const
94 {
95  return getValidLibspecs(std::back_inserter(list));
96 }
97 
98 std::pair<std::string, std::string>
100 {
101  // pair<short_spec,full_spec>
102  std::pair<std::string, std::string> result;
103  for (auto const& entry : spec_trans_map_) {
104  auto const& spec = maybe_trim_shlib_prefix(entry.first);
105  auto const& paths = entry.second;
106 
107  auto const path_iter = paths.find(lib_loc);
108  if (path_iter != paths.end()) {
109  if (spec.find("/") != std::string::npos)
110  result.second = spec;
111  else
112  result.first = spec;
113  }
114  if (!result.first.empty() && !result.second.empty())
115  break;
116  }
117 
118  return result;
119 }
120 
121 void
123 {
124  for (auto const& lib : lib_loc_map_) {
125  if (get_lib_ptr(lib.second) == nullptr) {
126  throw exception("Configuration")
127  << "Unable to load requested library " << lib.second << "\n"
128  << demangle_message(dlerror()) << "\n";
129  }
130  }
131 }
132 
133 bool
135 {
136  return lib_ptr_map_.find(path) != lib_ptr_map_.end();
137 }
138 
139 bool
141 {
142  // TODO: If called with any frequency, this should be made more
143  // efficient.
144  for (auto const& lib : lib_loc_map_) {
145  if (path == lib.second) {
146  return true;
147  }
148  }
149  return false;
150 }
151 
152 void
154 {
155  lib_loc_map_[boost::filesystem::path(path).filename().native()] = path;
156 }
157 
158 void
160  lib_loc_map_t::value_type const& entry)
161 {
162  // First obtain short spec.
163  boost::regex const e{"([^_]+)_" + lib_type_ + dllExtPattern() + '$'};
164  boost::match_results<std::string::const_iterator> match_results;
165  if (boost::regex_search(entry.first, match_results, e)) {
166  spec_trans_map_[match_results[1]].insert(entry.second);
167  } else {
168  throw exception("LogicError")
169  << "Internal error in spec_trans_map_inserter for entry " << entry.first
170  << " with pattern " << e.str();
171  }
172  // Next, convert library filename to full libspec.
173  std::ostringstream lib_name;
174  std::ostream_iterator<char, char> oi{lib_name};
175  boost::regex_replace(oi,
176  entry.first.begin(),
177  entry.first.end(),
178  boost::regex{"(_+)"},
179  std::string{"(?1/)"},
180  boost::match_default | boost::format_all);
181  boost::regex const stripper{"^lib(.*)/" + lib_type_ + "\\..*$"};
182  std::string const lib_name_str{lib_name.str()};
183  if (boost::regex_search(lib_name_str, match_results, stripper)) {
184  spec_trans_map_[match_results[1]].insert(entry.second);
185  } else {
186  throw exception("LogicError")
187  << "Internal error in spec_trans_map_inserter stripping "
188  << lib_name.str();
189  }
190 }
191 
192 void
194  spec_trans_map_t::value_type const& entry)
195 {
196  if (entry.second.size() == 1) {
197  good_spec_trans_map_[entry.first] = *(entry.second.begin());
198  }
199 }
200 
201 void*
203 {
204  lib_ptr_map_t::const_iterator const it{lib_ptr_map_.find(lib_loc)};
205  if (it == lib_ptr_map_.cend() || it->second == nullptr) {
206  dlerror();
207  void* ptr = dlopen(lib_loc.c_str(), RTLD_LAZY | RTLD_GLOBAL);
208  lib_ptr_map_[lib_loc] = ptr;
209  return ptr;
210  }
211  return it->second;
212 }
213 
214 void*
216  std::string const& sym_name,
217  bool should_throw_on_dlsym) const
218 {
219  if (libspec.find("_") != std::string::npos) {
220  // Plugin names (and hence the class name) cannot contain an underscore.
221  throw exception("LogicError", "IllegalUnderscore.")
222  << "Library specification \"" << libspec
223  << "\" contains an illegal underscore.\n"
224  << "The class name and path to it may not contain an underscore. "
225  << "If this is a configuration error, plase correct it; "
226  << "if the module's class name or its location within its "
227  << "enclosing package really do have an underscore this situation "
228  << "must be rectified.\n";
229  }
230  auto const trans = good_spec_trans_map_.find(libspec);
231  if (trans == good_spec_trans_map_.cend()) {
232  // No good translation => zero or too many
233  std::ostringstream error_msg;
234  error_msg << "Library specification \"" << libspec << "\"";
235  auto const bad_trans = spec_trans_map_.find(libspec);
236  if (bad_trans != spec_trans_map_.cend()) {
237  error_msg << " corresponds to multiple libraries:\n";
238  copy_all(bad_trans->second,
239  std::ostream_iterator<std::string>(error_msg, "\n"));
240  } else {
241  auto const& path_name = search_path_.showenv();
242  error_msg << " does not correspond to any library";
243  if (!path_name.empty()) {
244  error_msg << " in " << path_name;
245  }
246  error_msg << " of type \"" << lib_type_ << "\"\n";
247  }
248  throw exception("Configuration") << error_msg.str();
249  }
250  return getSymbolByPath_(trans->second, sym_name, should_throw_on_dlsym);
251 }
252 
253 void*
255  std::string const& sym_name,
256  bool should_throw_on_dlsym) const
257 {
258  void* result = nullptr;
259  void* lib_ptr = get_lib_ptr(lib_loc);
260  if (lib_ptr == nullptr) {
261  throw exception("Configuration")
262  << "Unable to load requested library " << lib_loc << "\n"
263  << demangle_message(dlerror()) << "\n";
264  } else { // Found library
265  dlerror();
266  result = dlsym(lib_ptr, sym_name.c_str());
267  char const* error = dlerror();
268  if (error != nullptr) { // Error message
269  result = nullptr;
270  if (should_throw_on_dlsym) {
271  throw exception("Configuration")
272  << "Unable to load requested symbol " << demangle_symbol(sym_name)
273  << " from library " << lib_loc << "\n"
274  << demangle_message(error) << "\n";
275  }
276  }
277  }
278  return result;
279 }
280 
281 // Local Variables:
282 // mode: c++
283 // End:
QList< Entry > entry
void spec_trans_map_inserter(lib_loc_map_t::value_type const &entry)
size_t getLoadedLibraries(std::vector< std::string > &list) const
size_t getValidLibspecs(std::vector< std::string > &list) const
static QCString result
std::string string
Definition: nybbler.cc:12
void * getSymbolByLibspec_(std::string const &libspec, std::string const &sym_name, bool should_throw_on_dlsym=true) const
bool libraryIsLoadable(std::string const &path) const
std::string const & showenv() const
Definition: search_path.h:50
void * getSymbolByPath_(std::string const &lib_loc, std::string const &sym_name, bool should_throw_on_dlsym=true) const
void lib_loc_map_inserter(std::string const &path)
LibraryManager(cet::search_path search_path, std::string lib_type)
error
Definition: include.cc:26
intermediate_table::const_iterator const_iterator
cet::search_path const search_path_
int find(const type *d) const
Definition: qlist.h:88
std::string const pattern_stem_
std::string shlib_prefix()
QCollection::Item first()
Definition: qglist.cpp:807
constexpr char const * plugin_libpath()
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
static std::string dllExtPattern()
const double e
spec_trans_map_t spec_trans_map_
def move(depos, offset)
Definition: depos.py:107
std::pair< std::string, std::string > getSpecsByPath(std::string const &lib_loc) const
good_spec_trans_map_t good_spec_trans_map_
std::size_t find_files(std::string const &filename_pattern, std::vector< std::string > &result) const
Definition: search_path.cc:126
size_t getLoadableLibraries(std::vector< std::string > &list) const
p
Definition: test.py:223
void loadAllLibraries() const
void good_spec_trans_map_inserter(spec_trans_map_t::value_type const &entry)
bool libraryIsLoaded(std::string const &path) const
lib_ptr_map_t lib_ptr_map_
auto copy_all(FwdCont &, FwdIter)
search_path plugin_search_path()
std::string pattern
Definition: regex_t.cc:35
void * get_lib_ptr(std::string const &lib_loc) const
std::string const lib_type_
lib_loc_map_t lib_loc_map_
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33