diff options
author | James Buren <ryuo@frugalware.org> | 2016-02-24 11:29:44 +0100 |
---|---|---|
committer | Juergen Daubert <jue@jue.li> | 2016-02-24 11:29:44 +0100 |
commit | 0ff99dc6663a9d2694f8647eeba708ddbba6c114 (patch) | |
tree | 13b401a9a683fb73c882b5d6e06a7bf7665ec539 | |
parent | 0ea6c95a5b53f749c0814e295ee277404e3db272 (diff) | |
download | prt-utils-0ff99dc6663a9d2694f8647eeba708ddbba6c114.tar.gz prt-utils-0ff99dc6663a9d2694f8647eeba708ddbba6c114.tar.xz |
revdep: new version 2.0
- rewrite in C++
- much improved speed
- new feature to add non-standard library locations, see the
man-page for details
-rw-r--r-- | revdep/.gitignore | 2 | ||||
l--------- | revdep/LICENSE | 1 | ||||
-rw-r--r-- | revdep/Makefile | 50 | ||||
-rw-r--r-- | revdep/TODO | 3 | ||||
-rw-r--r-- | revdep/elf-cache.cpp | 187 | ||||
-rw-r--r-- | revdep/elf-cache.h (renamed from revdep/global.h) | 27 | ||||
-rw-r--r-- | revdep/elf.cpp | 177 | ||||
-rw-r--r-- | revdep/elf.h | 42 | ||||
-rw-r--r-- | revdep/global.c | 116 | ||||
-rw-r--r-- | revdep/main.c | 293 | ||||
-rw-r--r-- | revdep/main.cpp | 260 | ||||
-rw-r--r-- | revdep/main.h | 4 | ||||
-rw-r--r-- | revdep/package.c | 303 | ||||
-rw-r--r-- | revdep/package.h | 62 | ||||
-rw-r--r-- | revdep/pkg.cpp | 111 | ||||
-rw-r--r-- | revdep/pkg.h | 47 | ||||
-rw-r--r-- | revdep/revdep.1 | 57 | ||||
-rw-r--r-- | revdep/revdep.man | 42 | ||||
-rw-r--r-- | revdep/utility.c | 416 | ||||
-rw-r--r-- | revdep/utility.cpp | 108 | ||||
-rw-r--r-- | revdep/utility.h | 31 |
21 files changed, 1061 insertions, 1278 deletions
diff --git a/revdep/.gitignore b/revdep/.gitignore new file mode 100644 index 0000000..a5638af --- /dev/null +++ b/revdep/.gitignore @@ -0,0 +1,2 @@ +*.o +revdep diff --git a/revdep/LICENSE b/revdep/LICENSE new file mode 120000 index 0000000..d24842f --- /dev/null +++ b/revdep/LICENSE @@ -0,0 +1 @@ +COPYING
\ No newline at end of file diff --git a/revdep/Makefile b/revdep/Makefile index f7a1050..ecae89f 100644 --- a/revdep/Makefile +++ b/revdep/Makefile @@ -1,34 +1,30 @@ -VERSION := 1.1 -BINDIR := /usr/bin -MANDIR := /usr/man/man1 -CFLAGS ?= -O2 -CFLAGS += -Wall -Wextra -Wno-unused-parameter -DVERSION='"$(VERSION)"' -LDFLAGS += -static -lz -lelf -CC ?= cc - -%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - -all: program man - -program: revdep - -man: revdep.1 - -revdep: main.o global.o utility.o package.o - $(CC) $^ $(LDFLAGS) -o $@ - -revdep.1: revdep.man - txt2man -t revdep $< > $@ - -install: revdep revdep.1 +VERSION := 2.0 +BINDIR := /usr/bin +MANDIR := /usr/man/man1 +ETCDIR := /etc/revdep.d +CXXFLAGS ?= -O2 +CXXFLAGS += -std=gnu++0x -Wall -Wextra -g -DVERSION='"$(VERSION)"' +LDFLAGS += -static -lelf +SOURCES := elf.cpp utility.cpp main.cpp pkg.cpp elf-cache.cpp +OBJECTS := $(patsubst %.cpp,%.o,$(SOURCES)) + +all: revdep revdep.1 + +install: all install -d $(DESTDIR)$(BINDIR) install -d $(DESTDIR)$(MANDIR) + install -d $(DESTDIR)$(ETCDIR) install -m 0755 revdep $(DESTDIR)$(BINDIR)/ install -m 0644 revdep.1 $(DESTDIR)$(MANDIR)/ -dist: man - tar --transform s:^:revdep-$(VERSION)/: -c *.c *.h *.man *.1 COPYING Makefile | xz > revdep-$(VERSION).tar.xz +dist: revdep.1 + git archive --format tar --prefix revdep-$(VERSION)/ HEAD | xz > revdep-$(VERSION).tar.xz + +revdep: $(OBJECTS) + $(CXX) $(OBJECTS) $(LDFLAGS) -o revdep + +revdep.1: revdep.man + txt2man revdep.man > revdep.1 clean: - rm -f *.o revdep #revdep.1 + $(RM) $(OBJECTS) revdep diff --git a/revdep/TODO b/revdep/TODO new file mode 100644 index 0000000..bd1cc25 --- /dev/null +++ b/revdep/TODO @@ -0,0 +1,3 @@ +Add support for NODEFLIB? +Add support for LD_LIBRARY_PATH? +Add support for hardware capabilility directories? diff --git a/revdep/elf-cache.cpp b/revdep/elf-cache.cpp new file mode 100644 index 0000000..420f41d --- /dev/null +++ b/revdep/elf-cache.cpp @@ -0,0 +1,187 @@ +// Copyright (C) 2016 James Buren +// +// This file is part of revdep. +// +// revdep is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. + +#include "elf-cache.h" +#include <algorithm> +#include <limits.h> +#include <sys/auxv.h> +#include <libgen.h> + +typedef std::pair <const std::string &, Elf *> ElfPair; +typedef std::unordered_map <std::string, Elf *>::iterator ElfIter; + +static void deleteElement(ElfPair pair) { + delete pair.second; +} + +static std::string resolveDirVars(const Elf *elf, const std::string &path) { + static const char *lib = "lib"; + static const char *platform = (((char *) getauxval(AT_PLATFORM)) ? : ""); + char dir[PATH_MAX]; + + snprintf(dir, sizeof(dir), "%s", elf->Path().c_str()); + dirname(dir); + + struct { + const char *name; + size_t length; + const char *s; + } vars[] = { + { "$LIB", 4, lib }, + { "${LIB}", 6, lib }, + { "$PLATFORM", 9, platform }, + { "${PLATFORM}", 11, platform }, + { "$ORIGIN", 7, dir }, + { "${ORIGIN}", 9, dir }, + { NULL, 0, NULL } + }; + size_t replaces; + std::string out = path; + + do { + replaces = 0; + + for( size_t i = 0 ; vars[i].name != NULL ; ++i ) { + size_t j = out.find(vars[i].name); + + if(j != std::string::npos) { + out.replace(j, vars[i].length, vars[i].s); + ++replaces; + } + } + } while(replaces > 0); + + return out; +} + +static StringVector resolveRunPaths(const Elf *elf, const StringVector &paths) { + StringVector out; + + for( size_t i = 0 ; i < paths.size() ; ++i ) { + out.push_back(resolveDirVars(elf, paths[i])); + } + + return out; +} + +bool ElfCache::findLibraryByDirs(const Elf *elf, const std::string &lib, const StringVector &dirs) { + for( size_t i = 0 ; i < dirs.size() ; ++i ) { + std::string path = dirs[i] + "/" + lib; + char realPath[PATH_MAX]; + + if(realpath(path.c_str(), realPath) == NULL) { + continue; + } + + const Elf *elf2 = LookUp(realPath); + + if(elf2 == NULL) { + continue; + } + + if(!elf->Compatible(elf2[0])) { + continue; + } + + return true; + } + + return false; +} + +bool ElfCache::findLibraryByPath(const Elf *elf, const std::string &lib) { + std::string path; + + if(lib[0] == '/') { + path = lib; + } else { + char dir[PATH_MAX]; + + snprintf(dir, sizeof(dir), "%s", elf->Path().c_str()); + dirname(dir); + + path = dir + ("/" + lib); + } + + char realPath[PATH_MAX]; + + if(realpath(path.c_str(), realPath) == NULL) { + return false; + } + + const Elf *elf2 = LookUp(realPath); + + if(elf2 == NULL) { + return false; + } + + return elf->Compatible(elf2[0]); +} + +ElfCache::~ElfCache() { + std::for_each(_data.begin(), _data.end(), deleteElement); +} + +const Elf *ElfCache::LookUp(const std::string &path) { + ElfIter value = _data.find(path); + + if(value != _data.end()) { + return value->second; + } + + Elf *elf = new Elf(path); + + if(!elf->Valid()) { + delete elf; + return NULL; + } + + ElfPair pair = std::make_pair <const std::string &, Elf *&> (path, elf); + _data.insert(pair); + + return elf; +} + +bool ElfCache::FindLibrary(const Elf *elf, const Package &pkg, const std::string &lib, const StringVector &dirs) { + if(lib.find('/') != std::string::npos) { + return findLibraryByPath(elf, lib); + } + + StringVector paths; + + if(elf->RunPath().size() > 0) { + paths = resolveRunPaths(elf, elf->RunPath()); + if(findLibraryByDirs(elf, lib, paths)) { + return true; + } + } else if(elf->RPath().size() > 0) { + paths = resolveRunPaths(elf, elf->RPath()); + if(findLibraryByDirs(elf, lib, paths)) { + return true; + } + } + + if(findLibraryByDirs(elf, lib, dirs)) { + return true; + } + + if(pkg.Dirs().size() > 0 && findLibraryByDirs(elf, lib, pkg.Dirs())) { + return true; + } + + return false; +} diff --git a/revdep/global.h b/revdep/elf-cache.h index bfeaeb9..829d96a 100644 --- a/revdep/global.h +++ b/revdep/elf-cache.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015 James Buren +// Copyright (C) 2016 James Buren // // This file is part of revdep. // @@ -17,17 +17,20 @@ #pragma once -#include "package.h" +#include "elf.h" +#include "pkg.h" +#include <unordered_map> -typedef struct -{ - int verbose; -} Global_Data; +typedef std::unordered_map <std::string, Elf *> ElfMap; -#ifdef _IN_GLOBAL_ -extern Global_Data rd; -#else -extern const Global_Data rd; -#endif +class ElfCache { +private: + ElfMap _data; + bool findLibraryByDirs(const Elf *elf, const std::string &lib, const StringVector &dirs); + bool findLibraryByPath(const Elf *elf, const std::string &lib); -extern int global_setup(int argc, char **argv, int *arg_start, PackageList *pkgs); +public: + ~ElfCache(); + const Elf *LookUp(const std::string &path); + bool FindLibrary(const Elf *elf, const Package &pkg, const std::string &lib, const StringVector &dirs); +}; diff --git a/revdep/elf.cpp b/revdep/elf.cpp new file mode 100644 index 0000000..6335714 --- /dev/null +++ b/revdep/elf.cpp @@ -0,0 +1,177 @@ +// Copyright (C) 2016 James Buren +// +// This file is part of revdep. +// +// revdep is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. + +#include "elf.h" +#include <stdexcept> +#include <gelf.h> +#include <fcntl.h> +#include <unistd.h> + +__attribute__((constructor)) static void initialize() { + if(elf_version(EV_CURRENT) == EV_NONE) { + throw std::runtime_error("libelf initialization failure"); + } +} + +static bool isValidElf(Elf *elf, int &machine) { + GElf_Ehdr ehdr; + + if(elf_kind(elf) != ELF_K_ELF) { + return false; + } + + if(gelf_getehdr(elf, &ehdr) == NULL) { + return false; + } + + switch(ehdr.e_type) { + case ET_EXEC: break; + case ET_DYN: break; + default: return false; + } + + switch(ehdr.e_ident[EI_OSABI]) { + case ELFOSABI_NONE: break; + case ELFOSABI_LINUX: break; + default: return false; + } + + switch(ehdr.e_machine) { +#if defined(__i386__) + case EM_386: break; +#elif defined(__x86_64__) + case EM_386: break; + case EM_X86_64: break; +#elif defined(__arm__) + case EM_ARM: break; +#else +#error "unsupported architecture" +#endif + default: return false; + } + + machine = ehdr.e_machine; + + return true; +} + +static bool getDynamicSection(Elf *elf, GElf_Shdr &shdr, Elf_Scn *&scn) { + size_t phdrnum; + size_t i; + GElf_Phdr phdr; + + if(elf_getphdrnum(elf, &phdrnum) == -1) { + return false; + } + + for( i = 0 ; i < phdrnum ; ++i ) { + if(gelf_getphdr(elf, i, &phdr) == NULL) { + continue; + } + + if(phdr.p_type != PT_DYNAMIC) { + continue; + } + + scn = gelf_offscn(elf, phdr.p_offset); + + if(scn == NULL) { + continue; + } + + if(gelf_getshdr(scn, &shdr) == NULL) { + continue; + } + + if(shdr.sh_type == SHT_DYNAMIC) { + break; + } + } + + return (i != phdrnum); +} + +static bool readDynamicSection(Elf *elf, StringVector &needed, StringVector &rpath, StringVector &runpath) { + GElf_Shdr shdr; + Elf_Scn *scn = NULL; + Elf_Data *data; + size_t size; + GElf_Dyn dyn; + + if(!getDynamicSection(elf, shdr, scn)) { + return false; + } + + data = elf_getdata(scn, NULL); + + if(data == NULL) { + return false; + } + + size = shdr.sh_size / gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT); + + for( size_t i = 0 ; i < size ; ++i ) { + if(gelf_getdyn(data, i, &dyn) == NULL) { + break; + } + + switch(dyn.d_tag) { + case DT_NEEDED: + needed.push_back(elf_strptr(elf, shdr.sh_link, dyn.d_un.d_val)); + break; + + case DT_RPATH: + split(elf_strptr(elf, shdr.sh_link, dyn.d_un.d_val), rpath, ':'); + break; + + case DT_RUNPATH: + split(elf_strptr(elf, shdr.sh_link, dyn.d_un.d_val), runpath, ':'); + break; + } + } + + return true; +} + +Elf::Elf(const std::string &path) { + _initialized = false; + + int fd = open(path.c_str(), O_RDONLY); + + if(fd == -1) { + return; + } + + Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + + if(elf == NULL) { + close(fd); + return; + } + + if(!isValidElf(elf, _machine) || !readDynamicSection(elf, _needed, _rpath, _runpath)) { + elf_end(elf); + close(fd); + return; + } + + _path = path; + _initialized = true; + + elf_end(elf); + close(fd); +} diff --git a/revdep/elf.h b/revdep/elf.h new file mode 100644 index 0000000..f7b1860 --- /dev/null +++ b/revdep/elf.h @@ -0,0 +1,42 @@ +// Copyright (C) 2016 James Buren +// +// This file is part of revdep. +// +// revdep is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include "utility.h" + +class Elf { +private: + int _machine; + StringVector _needed; + StringVector _rpath; + StringVector _runpath; + std::string _path; + bool _initialized; + +public: + Elf(const std::string &path); + + int Machine() const { return _machine; } + const StringVector& Needed() const { return _needed; } + const StringVector& RPath() const { return _rpath; } + const StringVector& RunPath() const { return _runpath; } + const std::string& Path() const { return _path; } + bool Valid() const { return _initialized; } + + bool Compatible(const Elf &elf) const { return _machine == elf._machine; } +}; diff --git a/revdep/global.c b/revdep/global.c deleted file mode 100644 index 4730d3d..0000000 --- a/revdep/global.c +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 2015 James Buren -// -// This file is part of revdep. -// -// revdep is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. - -#define _IN_GLOBAL_ -#include "global.h" -#include "utility.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <gelf.h> - -Global_Data rd = -{ - .verbose = 0 -}; - -static void ignore_packages(char *list, PackageList *pkgs) -{ - char *s; - char *e; - Package *pkg; - - for( s = list ; (e = strchr(s, ',')) != NULL ; s = e + 1 ) - { - e[0] = '\0'; - - if((pkg = packages_search(pkgs, s)) != NULL) - { - pkg->ignore = 1; - } - - e[0] = ','; - } - - if((pkg = packages_search(pkgs, s)) != NULL) - { - pkg->ignore = 1; - } -} - -extern int global_setup(int argc, char **argv, int *arg_start, PackageList *pkgs) -{ - static const char options[] = ":vi:h"; - int opt; - - if(argc < 1 || argv == NULL || arg_start == NULL) - { - errno = EINVAL; - return -1; - } - - if(elf_version(EV_CURRENT) == EV_NONE) - { - logger(LOG_BRIEF, "libelf: could not initialize version\n"); - return -1; - } - - while((opt = getopt(argc, argv, options)) != -1) - { - switch(opt) - { - case 'v': - ++rd.verbose; - break; - - case 'i': - ignore_packages(optarg, pkgs); - break; - - case 'h': - logger(LOG_BRIEF, - "%s version %s\n" - "Usage: %s [-h] [-v|-vv|-vvv|-vvvv] [package...]\n" - "Where commands are:\n" - "\t-h The option -h displays help.\n" - "\t-i Comma-separated list of ports to ignore.\n" - "\t-v Formatted listing.\n" - "\t-vv Include erroneous files.\n" - "\t-vvv Include precise file errors.\n" - "\t-vvvv Show debug/trace.\n", - argv[0], - VERSION, - argv[0] - ); - return -1; - - case '?': - logger(LOG_BRIEF, "%c: unknown option\n", optopt); - return -1; - - case ':': - logger(LOG_BRIEF, "%c: missing argument\n", optopt); - return -1; - } - } - - arg_start[0] = optind; - - return 0; -} diff --git a/revdep/main.c b/revdep/main.c deleted file mode 100644 index 5678b0c..0000000 --- a/revdep/main.c +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (C) 2015 James Buren -// -// This file is part of revdep. -// -// revdep is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. - -#define _IN_MAIN_ -#include "main.h" -#include "global.h" -#include "package.h" -#include "utility.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <libgen.h> - -static int run_ld_on_file(Package *pkg, File *file) -{ - const char *ld; - char command[4096]; - FILE *in; - char line[4096]; - char libs[4096]; - char *s; - char *e; - File *file2; - char dir[4096]; - char dirs[4096]; - - inline int run_ld(void) - { - if((in = popen(command, "r")) == NULL) - { - logger(LOG_ERROR, "%s:%s: could not execute LD\n", pkg->name, file->path); - return -1; - } - - libs[0] = '\0'; - - while(fgets(line, sizeof(line), in) != NULL) - { - if(strstr(line, "not found") != NULL) - { - if((s = strstr(line, "=>")) != NULL) - { - s[0] = '\0'; - } - - snprintf(libs + strlen(libs), sizeof(libs) - strlen(libs), "%s%s", - (libs[0] == '\0') ? "" : ":", strtrim(line)); - } - } - - pclose(in); - - if(libs[0] == '\0') - { - return 0; - } - - return 1; - } - - logger(LOG_DEBUG, "%s:%s: checking file\n", pkg->name, file->path); - - if(get_ld_for_file(pkg->name, file->path, &ld) == -1) - { - return 0; - } - - logger(LOG_DEBUG, "%s:%s: is ELF\n", pkg->name, file->path); - - snprintf(command, sizeof(command), "LD_TRACE_LOADED_OBJECTS=1 '%s' '%s'", ld, file->path); - - switch(run_ld()) - { - case 0: - return 0; - case -1: - return -1; - case 1: - break; - } - - for( s = libs, dirs[0] = '\0' ; (e = strchr(s, ':')) != NULL ; s = e + 1 ) - { - e[0] = '\0'; - - if((file2 = files_search(pkg->files, s, FILE_COMPARE_WITHIN)) != NULL) - { - snprintf(dir, sizeof(dir), "%s", file2->path); - snprintf(dirs + strlen(dirs), sizeof(dirs) - strlen(dirs), "%s%s", - (dirs[0] == '\0') ? "" : ":", dirname(dir)); - } - else - { - logger(LOG_ERROR, "%s:%s: missing library\n", pkg->name, file->path); - return -1; - } - - e[0] = ':'; - } - - if((file2 = files_search(pkg->files, s, FILE_COMPARE_WITHIN)) != NULL) - { - snprintf(dir, sizeof(dir), "%s", file2->path); - snprintf(dirs + strlen(dirs), sizeof(dirs) - strlen(dirs), "%s%s", - (dirs[0] == '\0') ? "" : ":", dirname(dir)); - } - else - { - logger(LOG_ERROR, "%s:%s: missing library\n", pkg->name, file->path); - return -1; - } - - snprintf(command, sizeof(command), "LD_TRACE_LOADED_OBJECTS=1 LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH:%s\" '%s' '%s'", dirs, ld, file->path); - - switch(run_ld()) - { - case 0: - return 0; - case -1: - return -1; - case 1: - break; - } - - logger(LOG_ERROR, "%s:%s: missing library\n", pkg->name, file->path); - - return -1; -} - -static int work_package(Package *pkg) -{ - unsigned int i; - FileList *files; - File *file; - int rv; - - if(pkg->ignore) - { - return 0; - } - - for( i = rv = 0, files = pkg->files ; i < files->length ; ++i ) - { - file = &files->list[i]; - - if(run_ld_on_file(pkg, file) == -1) - { - logger(LOG_INFO_2, "%s:%s: error\n", pkg->name, file->path); - - rv = -1; - } - } - - return rv; -} - -static void work_all_packages(PackageList *pkgs) -{ - unsigned int i; - Package *pkg; - - logger(LOG_INFO_1, - "** checking %u ports\n" - "** check linking\n", - pkgs->length - ); - - for( i = 0 ; i < pkgs->length ; ++i ) - { - pkg = &pkgs->list[i]; - - if(work_package(pkg) == -1) - { - const char *fmt; - int level; - - if(rd.verbose == 0) - { - fmt = "%s\n"; - level = LOG_BRIEF; - } - else - { - fmt = "%s: error\n"; - level = LOG_INFO_1; - } - - logger(level, fmt, pkg->name); - } - else - { - logger(LOG_INFO_1, "%s: ok\n", pkg->name); - } - } -} - -static void work_specific_packages(PackageList *pkgs, int i, int argc, char **argv) -{ - char *name; - Package *pkg; - - logger(LOG_INFO_1, - "** checking %d ports\n" - "** check linking\n", - argc - i - ); - - for( ; i < argc ; ++i ) - { - name = argv[i]; - - if((pkg = packages_search(pkgs, name)) == NULL) - { - logger(LOG_INFO_1, "%s: cannot find package information\n", name); - continue; - } - - if(work_package(pkg) == -1) - { - const char *fmt; - int level; - - if(rd.verbose == 0) - { - fmt = "%s\n"; - level = LOG_BRIEF; - } - else - { - fmt = "%s: error\n"; - level = LOG_INFO_1; - } - - logger(level, fmt, pkg->name); - } - else - { - logger(LOG_INFO_1, "%s: ok\n", pkg->name); - } - } -} - -extern int main(int argc, char **argv) -{ - static const char DB[] = "/var/lib/pkg/db"; - int start; - PackageList *pkgs; - - pkgs = packages_load_from_database(DB); - - if(pkgs == NULL) - { - logger(LOG_BRIEF, "%s: could not open the database\n", DB); - return EXIT_FAILURE; - } - - packages_sort(pkgs); - packages_sort_all_files(pkgs); - - if(global_setup(argc, argv, &start, pkgs) == -1) - { - return EXIT_FAILURE; - } - - logger(LOG_INFO_1, "** calculating deps\n"); - - if(start == argc) - { - work_all_packages(pkgs); - } - else - { - work_specific_packages(pkgs, start, argc, argv); - } - - packages_free(pkgs); - - return EXIT_SUCCESS; -} diff --git a/revdep/main.cpp b/revdep/main.cpp new file mode 100644 index 0000000..0b000b5 --- /dev/null +++ b/revdep/main.cpp @@ -0,0 +1,260 @@ +// Copyright (C) 2016 James Buren +// +// This file is part of revdep. +// +// revdep is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. + +#include "main.h" +#include "pkg.h" +#include "elf-cache.h" +#include <algorithm> +#include <unistd.h> +#include <stdarg.h> + +enum { + BRIEF, + INFO1, + INFO2, + ERROR, + DEBUG +}; + +static std::string PKG_DB_PATH = "/var/lib/pkg/db"; +static std::string LD_CONF_PATH = "/etc/ld.so.conf"; +static std::string RD_CONF_PATH = "/etc/revdep.d"; +static StringVector ignores; +static int level = BRIEF; +static PackageVector pkgs; +static StringVector dirs; +static ElfCache ec; + +static void logger(int lvl, const char *fmt, ...) __attribute__((format(printf,2,3))); + +static void logger(int lvl, const char *fmt, ...) { + if(lvl > level) { + return; + } + + va_list args; + + va_start(args, fmt); + + vfprintf(stdout, fmt, args); + + va_end(args); +} + +static bool parseArgs(int argc, char **argv) { + static const char OPTIONS[] = ":hd:c:r:i:v"; + int opt; + + while((opt = getopt(argc, argv, OPTIONS)) != -1) { + switch(opt) { + case 'h': + logger(BRIEF, "%s version %s\n", argv[0], VERSION); + logger(BRIEF, "Usage: %s [-h] [-v|-vv|-vvv|-vvvv] [package...]\n", argv[0]); + logger(BRIEF, "Where commands are:\n"); + logger(BRIEF, "\t-h The option -h displays help\n"); + logger(BRIEF, "\t-d Specify an alternate location for the package database\n"); + logger(BRIEF, "\t-c Specify an alternate location for ld.so.conf\n"); + logger(BRIEF, "\t-r Specify an alternate directory for revdep package config\n"); + logger(BRIEF, "\t-i Comma-separated list of ports to ignore\n"); + logger(BRIEF, "\t-v Formatted listing\n"); + logger(BRIEF, "\t-vv Include erroneous files\n"); + logger(BRIEF, "\t-vvv Include precise file errors\n"); + logger(BRIEF, "\t-vvvv Show debug/trace\n"); + return false; + + case 'd': + PKG_DB_PATH = optarg; + break; + + case 'c': + LD_CONF_PATH = optarg; + break; + + case 'i': + split(optarg, ignores, ','); + break; + + case 'r': + RD_CONF_PATH = optarg; + break; + + case 'v': + ++level; + break; + + case ':': + logger(BRIEF, "%c: missing argument\n", optopt); + return false; + + case '?': + logger(BRIEF, "%c: invalid option\n", optopt); + return false; + } + } + + return true; +} + +static void ignorePackages(PackageVector &pkgs, const StringVector &ignores) { + for( size_t i = 0 ; i < ignores.size() ; ++i ) { + PackageVector::iterator pkg = std::find(pkgs.begin(), pkgs.end(), ignores[i]); + + if(pkg == pkgs.end()) { + continue; + } + + pkg->Ignore(); + } +} + +static bool workFile(const Package &pkg, const std::string &file) { + bool rv = true; + + logger(DEBUG, "%s:%s: checking file\n", pkg.Name().c_str(), file.c_str()); + + if(!IsFile(file)) { + return rv; + } + + const Elf *elf = ec.LookUp(file); + + if(elf == NULL) { + return rv; + } + + logger(DEBUG, "%s:%s: is ELF\n", pkg.Name().c_str(), file.c_str()); + + for( size_t i = 0 ; i < elf->Needed().size() ; ++i ) { + const std::string &lib = elf->Needed()[i]; + + if(!ec.FindLibrary(elf, pkg, lib, dirs)) { + logger(ERROR, "%s:%s:%s: missing library\n", pkg.Name().c_str(), file.c_str(), lib.c_str()); + rv = false; + } + } + + return rv; +} + +static bool workPackage(const Package &pkg) { + bool rv = true; + + if(pkg.Ignore()) { + return rv; + } + + for( size_t i = 0 ; i < pkg.Files().size() ; ++i ) { + const std::string &file = pkg.Files()[i]; + + if(!workFile(pkg, file)) { + logger(INFO2, "%s:%s: error\n", pkg.Name().c_str(), file.c_str()); + rv = false; + } + } + + return rv; +} + +static void workAllPackages(const PackageVector &pkgs) { + logger(INFO1, "** checking %zu ports\n", pkgs.size()); + logger(INFO1, "** checking linking\n"); + + for( size_t i = 0 ; i < pkgs.size() ; ++i ) { + const Package &pkg = pkgs[i]; + + if(!workPackage(pkg)) { + int lvl; + const char *fmt; + + if(level > BRIEF) { + lvl = INFO1; + fmt = "%s: error\n"; + } else { + lvl = BRIEF; + fmt = "%s\n"; + } + + logger(lvl, fmt, pkg.Name().c_str()); + } else { + logger(INFO1, "%s: ok\n", pkg.Name().c_str()); + } + } +} + +static void workSpecificPackages(const PackageVector &pkgs, int i, int argc, char **argv) { + logger(INFO1, "** checking %d ports\n", argc - i); + logger(INFO1, "** checking linking\n"); + + for( ; i < argc ; ++i ) { + const std::string name = argv[i]; + PackageVector::const_iterator pkg = std::find(pkgs.begin(), pkgs.end(), name); + + if(pkg == pkgs.end()) { + logger(INFO1, "%s: cannot find package information\n", name.c_str()); + continue; + } + + if(!workPackage(pkg[0])) { + int lvl; + const char *fmt; + + if(level > BRIEF) { + lvl = INFO1; + fmt = "%s: error\n"; + } else { + lvl = BRIEF; + fmt = "%s\n"; + } + + logger(lvl, fmt, pkg->Name().c_str()); + } else { + logger(INFO1, "%s: ok\n", pkg->Name().c_str()); + } + } +} + +int main(int argc, char **argv) { + if(!parseArgs(argc, argv)) { + return 1; + } + + if(!ReadPackages(PKG_DB_PATH, pkgs)) { + logger(BRIEF, "%s:%s: failed to read package database\n", argv[0], PKG_DB_PATH.c_str()); + return 1; + } + + if(!ReadLdConf(LD_CONF_PATH, dirs, 10)) { + logger(BRIEF, "%s:%s: failed to read ld conf\n", argv[0], LD_CONF_PATH.c_str()); + return 1; + } + + dirs.push_back("/lib"); + dirs.push_back("/usr/lib"); + + ReadPackageDirs(RD_CONF_PATH, pkgs); + ignorePackages(pkgs, ignores); + + logger(INFO1, "** calculating deps\n"); + + if(optind == argc) { + workAllPackages(pkgs); + } else { + workSpecificPackages(pkgs, optind, argc, argv); + } + + return 0; +} diff --git a/revdep/main.h b/revdep/main.h index 6c4aabf..fa5b964 100644 --- a/revdep/main.h +++ b/revdep/main.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015 James Buren +// Copyright (C) 2016 James Buren // // This file is part of revdep. // @@ -17,4 +17,4 @@ #pragma once -extern int main(int argc, char **argv); +int main(int argc, char **argv); diff --git a/revdep/package.c b/revdep/package.c deleted file mode 100644 index 4acb723..0000000 --- a/revdep/package.c +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (C) 2015 James Buren -// -// This file is part of revdep. -// -// revdep is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. - -#define _IN_PACKAGE_ -#include "package.h" -#include "utility.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <search.h> - -extern PackageList *packages_load_from_database(const char *path) -{ - char *text; - unsigned int length; - char name[1024]; - char version[1024]; - char pathname[1024]; - PackageList *pkgs; - Package *pkg; - FileList *files; - File *file; - - if(path == NULL || path[0] == '\0') - { - errno = EINVAL; - return NULL; - } - - void parse_callback(int record, int field, char *start, char *end) - { - if(field == 0) - { - snprintf(name, sizeof(name), "%.*s", (int) (end - start), start); - } - else if(field == 1) - { - snprintf(version, sizeof(version), "%.*s", (int) (end - start), start); - } - else if(field == 2) - { - snprintf(pathname, sizeof(pathname), "/%.*s", (int) (end - start), start); - - files = new(FileList, 1); - files->length = 0; - files->size = 256; - files->list = new(File, files->size); - - file = &files->list[files->length]; - file->path = xstrdup(pathname); - file->key = string_crc32(pathname); - ++files->length; - - if(pkgs->length == pkgs->size) - { - pkgs->size *= 2; - pkgs->list = renew(pkgs->list, Package, pkgs->size); - } - - pkg = &pkgs->list[pkgs->length]; - pkg->name = xstrdup(name); - pkg->version = xstrdup(version); - pkg->files = files; - pkg->key = string_crc32(name); - pkg->ignore = 0; - ++pkgs->length; - } - else - { - snprintf(pathname, sizeof(pathname), "/%.*s", (int) (end - start), start); - - if(files->length == files->size) - { - files->size *= 2; - files->list = renew(files->list, File, files->size); - } - - file = &files->list[files->length]; - file->path = xstrdup(pathname); - file->key = string_crc32(pathname); - ++files->length; - } - } - - if(open_file_in_memory(path, &text, &length) == -1) - { - return NULL; - } - - pkgs = new(PackageList, 1); - pkgs->length = 0; - pkgs->size = 256; - pkgs->list = new(Package, pkgs->size); - - if(parse_file_in_memory(text, parse_callback) == -1) - { - packages_free(pkgs); - close_file_in_memory(text, length); - return NULL; - } - - if(pkgs->length == 0) - { - packages_free(pkgs); - close_file_in_memory(text, length); - return NULL; - } - - close_file_in_memory(text, length); - - return pkgs; -} - -extern void packages_free(PackageList *pkgs) -{ - unsigned int i; - Package *pkg; - - if(pkgs == NULL) - return; - - for( i = 0 ; i < pkgs->length ; ++i ) - { - pkg = &pkgs->list[i]; - free(pkg->name); - free(pkg->version); - files_free(pkg->files); - } - - free(pkgs->list); - - free(pkgs); -} - -extern void packages_sort(PackageList *pkgs) -{ - if(pkgs == NULL) - return; - - int package_compare(const void *A, const void *B) - { - const Package *a = A; - const Package *b = B; - - if(a->key < b->key) - return -1; - - if(a->key > b->key) - return 1; - - return 0; - } - - qsort(pkgs->list, pkgs->length, sizeof(Package), package_compare); -} - -extern void packages_sort_all_files(PackageList *pkgs) -{ - unsigned int i; - FileList *files; - - if(pkgs == NULL) - return; - - for( i = 0 ; i < pkgs->length ; ++i ) - { - files = pkgs->list[i].files; - files_sort(files); - } -} - -extern Package *packages_search(PackageList *pkgs, const char *name) -{ - unsigned int key; - - if(pkgs == NULL || name == NULL || name[0] == '\0') - { - errno = EINVAL; - return NULL; - } - - int package_compare(const void *A, const void *B) - { - const unsigned int *a = A; - const Package *b = B; - - if(a[0] < b->key) - return -1; - - if(a[0] > b->key) - return 1; - - return 0; - } - - key = string_crc32(name); - - return bsearch(&key, pkgs->list, pkgs->length, sizeof(Package), package_compare); -} - -extern void files_sort(FileList *files) -{ - if(files == NULL) - return; - - int file_compare(const void *A, const void *B) - { - const File *a = A; - const File *b = B; - - if(a->key < b->key) - return -1; - - if(a->key > b->key) - return 1; - - return 0; - } - - qsort(files->list, files->length, sizeof(File), file_compare); -} - -extern void files_free(FileList *files) -{ - unsigned int i; - File *file; - - if(files == NULL) - return; - - for( i = 0 ; i < files->length ; ++i ) - { - file = &files->list[i]; - free(file->path); - } - - free(files->list); - - free(files); -} - -extern File *files_search(FileList *files, const char *key, int compare_type) -{ - unsigned int hash; - size_t n; - - if(files == NULL || key == NULL || key[0] == '\0') - { - errno = EINVAL; - return NULL; - } - - int file_compare_exact(const void *A, const void *B) - { - const unsigned int *a = A; - const File *b = B; - - if(a[0] < b->key) - return -1; - - if(a[0] > b->key) - return 1; - - return 0; - } - - int file_compare_within(const void *A, const void *B) - { - const char *a = A; - const File *b = B; - - return (strstr(b->path, a) == NULL); - } - - switch(compare_type) - { - case FILE_COMPARE_EXACT: - hash = string_crc32(key); - return bsearch(&hash, files->list, files->length, sizeof(File), file_compare_exact); - - case FILE_COMPARE_WITHIN: - n = files->length; - return lfind(key, files->list, &n, sizeof(File), file_compare_within); - - default: - return NULL; - } -} diff --git a/revdep/package.h b/revdep/package.h deleted file mode 100644 index 1254fb3..0000000 --- a/revdep/package.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2015 James Buren -// -// This file is part of revdep. -// -// revdep is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. - -#pragma once - -typedef struct -{ - char *path; - unsigned int key; -} File; - -typedef struct -{ - File *list; - unsigned int length; - unsigned int size; -} FileList; - -typedef struct -{ - char *name; - char *version; - FileList *files; - unsigned int key; - unsigned int ignore; -} Package; - -typedef struct -{ - Package *list; - unsigned int length; - unsigned int size; -} PackageList; - -enum -{ - FILE_COMPARE_EXACT, - FILE_COMPARE_WITHIN -}; - -extern PackageList *packages_load_from_database(const char *path); -extern void packages_free(PackageList *pkgs); -extern void packages_sort(PackageList *pkgs); -extern void packages_sort_all_files(PackageList *pkgs); -extern Package *packages_search(PackageList *pkgs, const char *name); -extern void files_sort(FileList *files); -extern void files_free(FileList *files); -extern File *files_search(FileList *files, const char *key, int compare_type); diff --git a/revdep/pkg.cpp b/revdep/pkg.cpp new file mode 100644 index 0000000..f79a872 --- /dev/null +++ b/revdep/pkg.cpp @@ -0,0 +1,111 @@ +// Copyright (C) 2016 James Buren +// +// This file is part of revdep. +// +// revdep is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. + +#include "pkg.h" +#include <fstream> +#include <algorithm> +#include <dirent.h> + +Package::Package(const std::string &name, const std::string &version, const StringVector &files) { + _name = name; + _version = version; + _files = files; + _dirs = {}; + _ignore = false; +} + +static Package readPackage(std::istream &in) { + std::string line; + size_t fields = 0; + std::string name; + std::string version; + StringVector files; + + while(getline(in, line) && line != "") { + switch(++fields) { + case 1: name = line; break; + case 2: version = line; break; + default: files.push_back("/" + line); break; + } + } + + if(fields < 2) { + return Package("", "", files); + } + + return Package(name, version, files); +} + +bool ReadPackages(const std::string &path, PackageVector &pkgs) { + std::ifstream fin; + + fin.open(path.c_str()); + + if(!fin.is_open()) { + return false; + } + + while(true) { + Package pkg = readPackage(fin); + + if(pkg.Name() == "" && pkg.Version() == "") { + break; + } + + pkgs.push_back(pkg); + } + + fin.close(); + + return (pkgs.size() > 0); +} + +void ReadPackageDirs(const std::string &path, PackageVector &pkgs) { + DIR *dir; + + dir = opendir(path.c_str()); + + if(dir == NULL) { + return; + } + + struct dirent de, *res; + + while(readdir_r(dir, &de, &res) == 0 && res != NULL) { + if(de.d_type != DT_REG) { + continue; + } + + StringVector dirs; + + ReadRdConf(path + "/" + de.d_name, dirs); + + if(dirs.size() == 0) { + continue; + } + + PackageVector::iterator pkg = std::find(pkgs.begin(), pkgs.end(), de.d_name); + + if(pkg == pkgs.end()) { + continue; + } + + pkg->Dirs(dirs); + } + + closedir(dir); +} diff --git a/revdep/pkg.h b/revdep/pkg.h new file mode 100644 index 0000000..d3e299b --- /dev/null +++ b/revdep/pkg.h @@ -0,0 +1,47 @@ +// Copyright (C) 2016 James Buren +// +// This file is part of revdep. +// +// revdep is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include "utility.h" + +class Package { +private: + std::string _name; + std::string _version; + StringVector _files; + StringVector _dirs; + bool _ignore; + +public: + Package(const std::string &name, const std::string &version, const StringVector &files); + + const std::string& Name() const { return _name; } + const std::string& Version() const { return _version; } + const StringVector& Files() const { return _files; } + const StringVector& Dirs() const { return _dirs; } + void Dirs(const StringVector &dirs) { _dirs = dirs; } + bool Ignore() const { return _ignore; } + void Ignore() { _ignore = true; } + + bool operator == (const std::string &name) const { return _name == name; } +}; + +typedef std::vector <Package> PackageVector; + +bool ReadPackages(const std::string &path, PackageVector &pkgs); +void ReadPackageDirs(const std::string &path, PackageVector &pkgs); diff --git a/revdep/revdep.1 b/revdep/revdep.1 index 2719435..27e1d62 100644 --- a/revdep/revdep.1 +++ b/revdep/revdep.1 @@ -1,38 +1,52 @@ -." Text automatically generated by txt2man -.TH revdep "March 11, 2015" "" "" +.\"Text automatically generated by txt2man +.TH untitled "23 February 2016" "" "" .SH NAME -\fBrevdep \fP- checks for missing / wrong libraries of installed packages using ldd +\fBrevdep \fP- checks for missing libraries of installed packages .SH SYNOPSIS .nf .fam C \fBrevdep\fP [\fB-h\fP] [\fB-v\fP|\fB-vv\fP|\fB-vvv\fP|\fB-vvvv\fP] [package\.\.\.] .fam T .fi +.fam T +.fi .SH DESCRIPTION -Revdep checks for missing / wrong libraries of installed packages using -\fBldd\fP(1). +\fBrevdep\fP checks for missing libraries of installed packages using elfutils' +libelf and the same algorithm that \fBld.so\fP(8) uses. .SH OPTIONS .TP .B \fB-h\fP -The option \fB-h\fP displays help. +The option \fB-h\fP displays help +.TP +.B +\fB-d\fP +Specify an alternate location for the package database +.TP +.B +\fB-c\fP +Specify an alternate location for ld.so.conf +.TP +.B +\fB-r\fP +Specify an alternate directory for \fBrevdep\fP package config .TP .B \fB-i\fP -Comma-separated list of ports to ignore. +Comma-separated list of ports to ignore .TP .B \fB-v\fP -Formatted listing. +Formatted listing .TP .B \fB-vv\fP -Include errorous files. +Include errorous files .TP .B \fB-vvv\fP -Include precise file errors. -\fB-vvvv\fP Show debug/trace. +Include precise file errors +\fB-vvvv\fP Show debug/trace .SH EXAMPLE Try this command to list broken packages: .PP @@ -41,10 +55,27 @@ $ \fBrevdep\fP Use this command to rebuild packages on your system: .PP # prt-get update \fB-fr\fP $(\fBrevdep\fP) +.SH ADDITIONAL LIBRARY DIRECTORIES +Upon startup, \fBrevdep\fP searches the /etc/revdep.d directory for files named +after ports. If any are found, any directories (assumed to be absolute +pathes and delimited by newlines) within the file are added to a list of +port specific directories to search if a library dependency is not found +in one of the many standard locations (as specified by \fBld.so\fP(8)). +.PP +If a port is discovered to have library dependencies that are in a +non-standard location, then a file, as specified above, should be created. +.PP +For example, if the port "firefox" has a "libxul.so" dependency in a +the non-standard directory "/usr/lib/firefox" then a file named "firefox" +should be created under "/etc/revdep.d" with a single line containing +"/usr/lib/firefox". This will tell \fBrevdep\fP where to find firefox's "libxul.so" +when looking for its library dependencies, reducing one source of false +positives. Ideally, this file would be included with the port's own packaging +as well. .SH NOTES -C rewrite inspired by Johannes Winkelmann's <jw@tks6.net> shell \fBrevdep\fP. +C++ rewrite inspired by Johannes Winkelmann's <jw@tks6.net> shell \fBrevdep\fP. Contributed to design Danny Rawlins <contact@romster.me>. .SH SEE ALSO -\fBldd\fP(1), \fBprt-get\fP(8), \fBpkginfo\fP(8) +\fBld.so\fP(8), \fBprt-get\fP(8), \fBpkginfo\fP(1) .SH AUTHOR James Buren, ryuo @ freenode diff --git a/revdep/revdep.man b/revdep/revdep.man index 744277c..63c26c0 100644 --- a/revdep/revdep.man +++ b/revdep/revdep.man @@ -1,17 +1,20 @@ NAME -revdep - checks for missing / wrong libraries of installed packages using ldd +revdep - checks for missing libraries of installed packages SYNOPSIS revdep [-h] [-v|-vv|-vvv|-vvvv] [package...] DESCRIPTION -Revdep checks for missing / wrong libraries of installed packages using -ldd(1). +revdep checks for missing libraries of installed packages using elfutils' +libelf and the same algorithm that ld.so(8) uses. OPTIONS --h The option -h displays help. --i Comma-separated list of ports to ignore. --v Formatted listing. --vv Include errorous files. --vvv Include precise file errors. --vvvv Show debug/trace. +-h The option -h displays help +-d Specify an alternate location for the package database +-c Specify an alternate location for ld.so.conf +-r Specify an alternate directory for revdep package config +-i Comma-separated list of ports to ignore +-v Formatted listing +-vv Include errorous files +-vvv Include precise file errors +-vvvv Show debug/trace EXAMPLE Try this command to list broken packages: @@ -20,10 +23,27 @@ $ revdep Use this command to rebuild packages on your system: # prt-get update -fr $(revdep) +ADDITIONAL LIBRARY DIRECTORIES +Upon startup, revdep searches the /etc/revdep.d directory for files named +after ports. If any are found, any directories (assumed to be absolute +pathes and delimited by newlines) within the file are added to a list of +port specific directories to search if a library dependency is not found +in one of the many standard locations (as specified by ld.so(8)). + +If a port is discovered to have library dependencies that are in a +non-standard location, then a file, as specified above, should be created. + +For example, if the port "firefox" has a "libxul.so" dependency in a +the non-standard directory "/usr/lib/firefox" then a file named "firefox" +should be created under "/etc/revdep.d" with a single line containing +"/usr/lib/firefox". This will tell revdep where to find firefox's "libxul.so" +when looking for its library dependencies, reducing one source of false +positives. Ideally, this file would be included with the port's own packaging +as well. NOTES -C rewrite inspired by Johannes Winkelmann's <jw@tks6.net> shell revdep. +C++ rewrite inspired by Johannes Winkelmann's <jw@tks6.net> shell revdep. Contributed to design Danny Rawlins <contact@romster.me>. SEE ALSO -ldd(1), prt-get(8), pkginfo(8) +ld.so(8), prt-get(8), pkginfo(1) AUTHOR James Buren, ryuo @ freenode diff --git a/revdep/utility.c b/revdep/utility.c deleted file mode 100644 index 67247e1..0000000 --- a/revdep/utility.c +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright (C) 2015 James Buren -// -// This file is part of revdep. -// -// revdep is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. - -#define _IN_UTILITY_ -#include "utility.h" -#include "global.h" -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/mman.h> -#include <errno.h> -#include <gelf.h> -#include <zlib.h> - -extern void *xmalloc(unsigned int bytes) -{ - void *p; - - if(bytes == 0) - { - logger(LOG_BRIEF, "%s: bytes is zero\n", __func__); - abort(); - } - - if((p = malloc(bytes)) == NULL) - { - logger(LOG_BRIEF, "%s: out of memory\n", __func__); - abort(); - } - - return p; -} - -extern void *xrealloc(void *p, unsigned int bytes) -{ - if(p == NULL) - { - logger(LOG_BRIEF, "%s: p is NULL\n", __func__); - abort(); - } - - if(bytes == 0) - { - logger(LOG_BRIEF, "%s: bytes is zero\n", __func__); - abort(); - } - - if((p = realloc(p, bytes)) == NULL) - { - logger(LOG_BRIEF, "%s: out of memory\n", __func__); - abort(); - } - - return p; -} - -extern char *xstrdup(const char *in) -{ - char *out; - - if(in == NULL) - { - logger(LOG_BRIEF, "%s: s is NULL\n", __func__); - abort(); - } - - if((out = strdup(in)) == NULL) - { - logger(LOG_BRIEF, "%s: out of memory\n", __func__); - abort(); - } - - return out; -} - -extern void logger(int level, const char *fmt, ...) -{ - va_list args; - - if(level > rd.verbose) - return; - - va_start(args, fmt); - - vfprintf(stdout, fmt, args); - - va_end(args); -} - -extern char *strtrim(char *s) -{ - static const char SPACE[] = " \t\r\n\v\f"; - unsigned int left; - unsigned int middle; - //unsigned int right; - - left = strspn(s, SPACE); - - middle = strcspn(s + left, SPACE); - - //right = strspn(s + left + middle, SPACE); - - memmove(s, s + left, middle); - - s[middle] = '\0'; - - return s; -} - -extern unsigned int string_crc32(const char *s) -{ - unsigned int hash; - - if(s == NULL || s[0] == '\0') - { - errno = EINVAL; - return -1; - } - - hash = crc32(0, Z_NULL, 0); - - hash = crc32(hash, (Bytef *) s, strlen(s) + 1); - - return hash; -} - -extern int open_file_in_memory(const char *path, char **text, unsigned int *length) -{ - struct stat st; - int fd; - - if(path == NULL || path[0] == '\0' || text == NULL || length == NULL) - { - errno = EINVAL; - return -1; - } - - if((fd = open(path, O_RDONLY)) == -1) - { - return -1; - } - - if(fstat(fd, &st) == -1) - { - close(fd); - return -1; - } - - if((text[0] = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == NULL) - { - close(fd); - return -1; - } - - // We don't need to keep the fd around. - close(fd); - - length[0] = st.st_size; - - return 0; -} - -extern void close_file_in_memory(char *text, unsigned int length) -{ - if(text == NULL || length == 0) - return; - - munmap(text, length); -} - -extern int parse_file_in_memory(char *text, parse_cb_t cb) -{ - static const char RS[] = "\n\n"; - static const char FS[] = "\n"; - char *r_start; - char *r_end; - char *f_start; - char *f_end; - int record; - int field; - - if(text == NULL || text[0] == '\0' || cb == NULL) - { - errno = EINVAL; - return -1; - } - - inline int next_record(void) - { - // Test for EOF. - // Return EOF if so. - if(r_start[0] == '\0') - return -1; - - // Test for RS. - // Skip RS if so. - if(strncmp(r_start, RS, strlen(RS)) == 0) - r_start += strlen(RS); - - // Find the RS. - // If RS is not found, then use the end of the file. - if((r_end = strstr(r_start, RS)) == NULL) - r_end = r_start + strlen(r_start); - - return 0; - } - - inline int next_field(void) - { - // Test for RS and EOF. - // Return EOF if either is found. - if(strncmp(f_start, RS, strlen(RS)) == 0 || strcmp(f_start, FS) == 0 || f_start[0] == '\0') - return -1; - - // Test for FS. - // Skip FS if so. - if(f_start[0] == FS[0]) - f_start += 1; - - // Find the FS. - // If FS is not found, then use the end of the file. - if((f_end = strchr(f_start, FS[0])) == NULL) - f_end = f_start + strlen(f_start); - - return 0; - } - - for( r_start = text, record = 0 ; next_record() == 0 ; r_start = r_end, ++record ) - { - for( f_start = r_start, field = 0 ; next_field() == 0 ; f_start = f_end, ++field ) - { - cb(record, field, f_start, f_end); - } - } - - return 0; -} - -extern int get_ld_for_file(const char *pkg, const char *path, const char **ld) -{ - struct stat st; - int fd = -1; - Elf *elf = NULL; - GElf_Ehdr ehdr; - size_t i; - size_t phdrnum; - GElf_Phdr phdr; - int rv = -1; - - if(pkg == NULL || pkg[0] == '\0' || path == NULL || path[0] == '\0' || ld == NULL) - { - errno = EINVAL; - goto bail; - } - - if(lstat(path, &st) == -1) - { - logger(LOG_ERROR, "%s:%s: could not stat file\n", pkg, path); - goto bail; - } - - if(!S_ISREG(st.st_mode)) - { - goto bail; - } - - if((fd = open(path, O_RDONLY)) == -1) - { - logger(LOG_ERROR, "%s:%s: could not open file\n", pkg, path); - goto bail; - } - - if((elf = elf_begin(fd, ELF_C_READ_MMAP, NULL)) == NULL) - { - goto bail; - } - - if(elf_kind(elf) != ELF_K_ELF) - { - goto bail; - } - - if(gelf_getehdr(elf, &ehdr) == NULL) - { - goto bail; - } - - // Check if file is executable or shared library - switch(ehdr.e_type) - { - case ET_EXEC: - break; - - case ET_DYN: - break; - - default: - goto bail; - } - - // Check if file has SYSTEMV or LINUX ABI - switch(ehdr.e_ident[EI_OSABI]) - { - case ELFOSABI_NONE: - break; - - case ELFOSABI_LINUX: - break; - - default: - goto bail; - } - - // Check if file is supported by this host's architecture - switch(ehdr.e_machine) - { - -#if defined(__i386__) || defined(__x86_64__) - case EM_386: - break; -#endif - -#if defined(__x86_64__) - case EM_X86_64: - break; -#endif - -#if defined(__arm__) - case EM_ARM: - break; -#endif - - default: - goto bail; - - } - - // Check if file has a dynamic section - if(elf_getphdrnum(elf, &phdrnum) == -1) - { - goto bail; - } - - for( i = 0 ; i < phdrnum ; ++i ) - { - if(gelf_getphdr(elf, i, &phdr) != NULL && phdr.p_type == PT_DYNAMIC) - { - break; - } - } - - // If true, then a dynamic section was not found. - if(i == phdrnum) - { - goto bail; - } - - // Check if file has a known LD binary - switch(ehdr.e_ident[EI_CLASS]) - { - -#if defined(__i386__) - case ELFCLASS32: - ld[0] = "/lib/ld-linux.so.2"; - break; -#elif defined(__x86_64__) - case ELFCLASS32: - ld[0] = "/lib32/ld-linux.so.2"; - break; - case ELFCLASS64: - ld[0] = "/lib/ld-linux-x86-64.so.2"; - break; -#elif defined(__arm__) - case ELFCLASS32: - ld[0] = "/lib/ld-linux-armhf.so.3"; - break; -#else -#error "Unsupported Architecture" -#endif - - default: - goto bail; - - } - - rv = 0; - -bail: - - if(elf != NULL) - elf_end(elf); - - if(fd != -1) - close(fd); - - return rv; -} diff --git a/revdep/utility.cpp b/revdep/utility.cpp new file mode 100644 index 0000000..24fd12f --- /dev/null +++ b/revdep/utility.cpp @@ -0,0 +1,108 @@ +// Copyright (C) 2016 James Buren +// +// This file is part of revdep. +// +// revdep is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// revdep 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 revdep. If not, see <http://www.gnu.org/licenses/>. + +#include "utility.h" +#include <fstream> +#include <glob.h> +#include <sys/stat.h> + +void split(const std::string &in, StringVector &out, char delimiter) { + size_t i = 0; + size_t j = in.find(delimiter); + + while(j != std::string::npos) { + out.push_back(in.substr(i, j - i)); + i = ++j; + j = in.find(delimiter, j); + } + + out.push_back(in.substr(i)); +} + +void ReadRdConf(const std::string &path, StringVector &dirs) { + std::ifstream fin; + + fin.open(path.c_str()); + + if(!fin.is_open()) { + return; + } + + std::string line; + + while(getline(fin, line)) { + if(line[0] != '#' && line.length() > 0) { + dirs.push_back(line); + } + } + + fin.close(); +} + +bool ReadLdConf(const std::string &path, StringVector &dirs, int maxDepth) { + if(maxDepth <= 0) { + return false; + } + + std::ifstream fin; + + fin.open(path.c_str()); + + if(!fin.is_open()) { + return false; + } + + std::string line; + + while(getline(fin, line)) { + if(line[0] == '#') { + continue; + } + + if(line.compare(0, 8, "include ") == 0) { + glob_t g; + + if(glob(line.substr(8).c_str(), 0, NULL, &g) == 0) { + for( size_t i = 0 ; i < g.gl_pathc ; ++i ) { + if(!ReadLdConf(g.gl_pathv[i], dirs, maxDepth - 1)) { + globfree(&g); + fin.close(); + return false; + } + } + } + + globfree(&g); + } else if(line.length() > 0) { + dirs.push_back(line); + } + } + + fin.close(); + + return true; +} + +bool IsFile(const std::string &path) { + struct stat st; + + if(lstat(path.c_str(), &st) == -1) { + return false; + } + + return S_ISREG(st.st_mode); +} diff --git a/revdep/utility.h b/revdep/utility.h index 34c02b1..b79484e 100644 --- a/revdep/utility.h +++ b/revdep/utility.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015 James Buren +// Copyright (C) 2016 James Buren // // This file is part of revdep. // @@ -17,27 +17,12 @@ #pragma once -#define new(T,N) ((T*)xmalloc(sizeof(T)*(N))) -#define renew(P,T,N) ((T*)xrealloc((P),sizeof(T)*(N))) +#include <vector> +#include <string> -typedef void (*parse_cb_t) (int record, int field, char *start, char *end); +typedef std::vector <std::string> StringVector; -enum -{ - LOG_BRIEF, - LOG_INFO_1, - LOG_INFO_2, - LOG_ERROR, - LOG_DEBUG -}; - -extern void *xmalloc(unsigned int bytes); -extern void *xrealloc(void *p, unsigned int bytes); -extern char *xstrdup(const char *in); -extern void logger(int level, const char *fmt, ...) __attribute__((format(printf,2,3))); -extern char *strtrim(char *s); -extern unsigned int string_crc32(const char *s); -extern int open_file_in_memory(const char *path, char **text, unsigned int *length); -extern void close_file_in_memory(char *text, unsigned int length); -extern int parse_file_in_memory(char *text, parse_cb_t cb); -extern int get_ld_for_file(const char *pkg, const char *path, const char **ld); +void split(const std::string &in, StringVector &out, char delimiter); +void ReadRdConf(const std::string &path, StringVector &dirs); +bool ReadLdConf(const std::string &path, StringVector &dirs, int maxDepth); +bool IsFile(const std::string &path); |