/* vim: set sw=4 sts=4 et foldmethod=syntax : */ /* * Copyright (c) 2007 David Leverton * * This file is part of the Paludis package manager. Paludis is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace paludis; namespace paludis { template <> struct Imp { std::vector ld_library_mask; std::vector search_dirs; std::vector search_dirs_mask; std::vector ld_so_conf; void load_from_environment(); void load_from_etc_revdep_rebuild(const FSPath &); void load_from_etc_profile_env(const FSPath &); void load_from_etc_ld_so_conf(const FSPath &); void add_defaults(); }; template <> struct WrappedForwardIteratorTraits { typedef std::vector::const_iterator UnderlyingIterator; }; } namespace { struct IsGarbageFile : std::unary_function { bool operator() (const FSPath & file) { std::string basename(file.basename()); return '#' == basename[0] || '~' == basename[basename.length() - 1]; } }; template void from_colon_string(const std::function & source, const std::string & varname, std::vector & vec) { std::string str(source.operator() (varname)); /* silly 4.3 ICE */ if (! str.empty()) { Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Got " << varname << "=\"" + str << "\""; tokenise(str, ":", "", create_inserter(std::back_inserter(vec))); } } template void from_string(const std::function & source, const std::string & varname, std::vector & vec) { std::string str(source.operator() (varname)); /* silly 4.3 ICE */ if (! str.empty()) { Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Got " << varname << "=\"" << str << "\""; tokenise_whitespace(str, create_inserter(std::back_inserter(vec))); } } inline void do_wildcards(std::vector &, const FSPath &) { } inline void do_wildcards(std::vector & vec, const FSPath & root) { std::vector scratch; for (std::vector::const_iterator it(vec.begin()), it_end(vec.end()); it_end != it; ++it) std::copy(WildcardExpander(stringify(*it), root), WildcardExpander(), std::back_inserter(scratch)); using std::swap; swap(vec, scratch); } template void cleanup(const std::string & varname, std::vector & vec, const FSPath & root, const C_ & comparator) { vec.erase(std::find(vec.begin(), vec.end(), T_("-*")), vec.end()); do_wildcards(vec, root); std::sort(vec.begin(), vec.end(), comparator); vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Final " << varname << "=\"" << join(vec.begin(), vec.end(), " ") << "\""; } bool char_equals_ci(char a, char b) { return std::tolower(a) == std::tolower(b); } bool equals_ci(const std::string & a, const std::string & b) { return a.length() == b.length() && std::equal(a.begin(), a.end(), b.begin(), char_equals_ci); } void parse_ld_so_conf(const FSPath & root, const FSPath & file, const LineConfigFileOptions & opts, std::vector & res) { FSStat file_stat(file); if (file_stat.is_regular_file_or_symlink_to_regular_file()) { LineConfigFile lines(file, opts); for (auto it(lines.begin()), it_end(lines.end()); it_end != it; ++it) { std::vector tokens; tokenise_whitespace(*it, std::back_inserter(tokens)); if ("include" == tokens.at(0)) { for (auto it2(next(tokens.begin())), it2_end(tokens.end()); it2_end != it2; ++it2) { FSPath rel('/' == it2->at(0) ? root : file.dirname()); for (WildcardExpander it3(*it2, rel), it3_end; it3_end != it3; ++it3) { Context ctx("When reading included file '" + stringify(rel / *it3) + "':"); parse_ld_so_conf(root, rel / *it3, opts, res); } } } else if (equals_ci("hwdep", tokens.at(0))) // XXX Log::get_instance()->message("broken_linkage_finder.etc_ld_so_conf.hwdep", ll_warning, lc_context) << "'hwdep' line in '" << file << "' is not supported"; else if (std::string::npos != it->find('=')) Log::get_instance()->message("broken_linkage_finder.etc_ld_so_conf.equals", ll_warning, lc_context) << "'=' line in '" << file << "' is not supported"; else res.push_back(*it); } } else if (file_stat.exists()) Log::get_instance()->message("broken_linkage_finder.etc_ld_so_conf.not_a_file", ll_warning, lc_context) << "'" << file << "' exists but is not a regular file"; } } BrokenLinkageConfiguration::BrokenLinkageConfiguration(const FSPath & root) : _imp() { Context ctx("When loading broken linkage checker configuration for '" + stringify(root) + "':"); _imp->load_from_environment(); _imp->load_from_etc_revdep_rebuild(root); _imp->load_from_etc_profile_env(root); _imp->load_from_etc_ld_so_conf(root); _imp->add_defaults(); cleanup("LD_LIBRARY_MASK", _imp->ld_library_mask, root, std::less()); cleanup("SEARCH_DIRS", _imp->search_dirs, root, FSPathComparator()); cleanup("SEARCH_DIRS_MASK", _imp->search_dirs_mask, root, FSPathComparator()); // don't need the extra cleanup here std::sort(_imp->ld_so_conf.begin(), _imp->ld_so_conf.end(), FSPathComparator()); _imp->ld_so_conf.erase(std::unique(_imp->ld_so_conf.begin(), _imp->ld_so_conf.end()), _imp->ld_so_conf.end()); Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Final ld.so.conf contents is \"" << join(_imp->ld_so_conf.begin(), _imp->ld_so_conf.end(), " ") << "\""; } BrokenLinkageConfiguration::~BrokenLinkageConfiguration() { } void Imp::load_from_environment() { using namespace std::placeholders; Context ctx("When checking environment variables:"); std::function fromenv( std::bind(getenv_with_default, _1, "")); from_string(fromenv, "LD_LIBRARY_MASK", ld_library_mask); from_string(fromenv, "SEARCH_DIRS", search_dirs); from_string(fromenv, "SEARCH_DIRS_MASK", search_dirs_mask); } void Imp::load_from_etc_revdep_rebuild(const FSPath & root) { using namespace std::placeholders; FSPath etc_revdep_rebuild(root / "etc" / "revdep-rebuild"); FSStat etc_revdep_rebuild_stat(etc_revdep_rebuild); Context ctx("When reading '" + stringify(etc_revdep_rebuild) + "':"); if (etc_revdep_rebuild_stat.is_directory_or_symlink_to_directory()) { std::vector conf_files = std::vector(FSIterator(etc_revdep_rebuild, { }), FSIterator()); conf_files.erase(std::remove_if(conf_files.begin(), conf_files.end(), IsGarbageFile()), conf_files.end()); std::sort(conf_files.begin(), conf_files.end(), FSPathComparator()); KeyValueConfigFileOptions opts; opts += kvcfo_disallow_space_around_equals; opts += kvcfo_disallow_space_inside_unquoted_values; for (std::vector::iterator it(conf_files.begin()), it_end(conf_files.end()); it_end != it; ++it) { Context ctx_file("When reading '" + stringify(*it) + "':"); if (it->stat().is_regular_file_or_symlink_to_regular_file()) { KeyValueConfigFile kvs(*it, opts, &KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation); std::function fromfile( std::bind(&KeyValueConfigFile::get, std::cref(kvs), _1)); from_string(fromfile, "LD_LIBRARY_MASK", ld_library_mask); from_string(fromfile, "SEARCH_DIRS", search_dirs); from_string(fromfile, "SEARCH_DIRS_MASK", search_dirs_mask); } else Log::get_instance()->message("broken_linkage_finder.failure", ll_warning, lc_context) << "'" << *it << "' is not a regular file"; } } else if (etc_revdep_rebuild_stat.exists()) Log::get_instance()->message("broken_linkage_finder.etc_revdep_rebuild.not_a_directory", ll_warning, lc_context) << "'" << etc_revdep_rebuild << "' exists but is not a directory"; } void Imp::load_from_etc_profile_env(const FSPath & root) { using namespace std::placeholders; FSPath etc_profile_env(root / "etc" / "profile.env"); FSStat etc_profile_env_stat(etc_profile_env); Context ctx("When reading '" + stringify(etc_profile_env) + "':"); if (etc_profile_env_stat.is_regular_file_or_symlink_to_regular_file()) { KeyValueConfigFileOptions opts; opts += kvcfo_disallow_space_around_equals; opts += kvcfo_disallow_space_inside_unquoted_values; opts += kvcfo_ignore_export; KeyValueConfigFile kvs(etc_profile_env, opts, &KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation); std::function fromfile( std::bind(&KeyValueConfigFile::get, std::cref(kvs), _1)); from_colon_string(fromfile, "PATH", search_dirs); from_colon_string(fromfile, "ROOTPATH", search_dirs); } else if (etc_profile_env_stat.exists()) Log::get_instance()->message("broken_linkage_finder.etc_profile_env.not_a_file", ll_warning, lc_context) << "'" << etc_profile_env << "' exists but is not a regular file"; } void Imp::load_from_etc_ld_so_conf(const FSPath & root) { FSPath etc_ld_so_conf(root / "etc" / "ld.so.conf"); Context ctx("When reading '" + stringify(etc_ld_so_conf) + "':"); LineConfigFileOptions opts; opts += lcfo_disallow_continuations; opts += lcfo_allow_inline_comments; std::vector res; parse_ld_so_conf(root, etc_ld_so_conf, opts, res); if (res.begin() != res.end()) { Log::get_instance()->message("broken_linkage_finder.got", ll_debug, lc_context) << "Got " << join(res.begin(), res.end(), " "); std::copy(res.begin(), res.end(), create_inserter(std::back_inserter(search_dirs))); std::copy(res.begin(), res.end(), create_inserter(std::back_inserter(ld_so_conf))); } } void Imp::add_defaults() { Context ctx("When adding default settings:"); static const std::string default_ld_library_mask( "libodbcinst.so libodbc.so libjava.so libjvm.so"); static const std::string default_search_dirs( "/bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"); static const std::string default_search_dirs_mask( "/lib*/modules"); static const std::string default_ld_so_conf("/lib /usr/lib"); Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Got LD_LIBRARY_MASK=\"" << default_ld_library_mask << "\""; tokenise_whitespace( default_ld_library_mask, std::back_inserter(ld_library_mask)); Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Got SEARCH_DIRS=\"" << default_search_dirs << "\""; tokenise_whitespace( default_search_dirs, create_inserter(std::back_inserter(search_dirs))); Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Got SEARCH_DIRS_MASK=\"" << default_search_dirs_mask << "\""; tokenise_whitespace( default_search_dirs_mask, create_inserter(std::back_inserter(search_dirs_mask))); Log::get_instance()->message("broken_linkage_finder.config", ll_debug, lc_context) << "Default ld.so.conf contents is \"" << default_ld_so_conf << "\""; tokenise_whitespace( default_ld_so_conf, create_inserter(std::back_inserter(ld_so_conf))); } BrokenLinkageConfiguration::DirsIterator BrokenLinkageConfiguration::begin_search_dirs() const { return DirsIterator(_imp->search_dirs.begin()); } BrokenLinkageConfiguration::DirsIterator BrokenLinkageConfiguration::end_search_dirs() const { return DirsIterator(_imp->search_dirs.end()); } BrokenLinkageConfiguration::DirsIterator BrokenLinkageConfiguration::begin_ld_so_conf() const { return DirsIterator(_imp->ld_so_conf.begin()); } BrokenLinkageConfiguration::DirsIterator BrokenLinkageConfiguration::end_ld_so_conf() const { return DirsIterator(_imp->ld_so_conf.end()); } bool BrokenLinkageConfiguration::dir_is_masked(const FSPath & dir) const { return std::binary_search(_imp->search_dirs_mask.begin(), _imp->search_dirs_mask.end(), dir, FSPathComparator()); } bool BrokenLinkageConfiguration::lib_is_masked(const std::string & lib) const { return std::binary_search(_imp->ld_library_mask.begin(), _imp->ld_library_mask.end(), lib); } namespace paludis { template class WrappedForwardIterator; }