summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Buren <ryuo@frugalware.org>2016-02-24 11:29:44 +0100
committerJuergen Daubert <jue@jue.li>2016-02-24 11:29:44 +0100
commit0ff99dc6663a9d2694f8647eeba708ddbba6c114 (patch)
tree13b401a9a683fb73c882b5d6e06a7bf7665ec539
parent0ea6c95a5b53f749c0814e295ee277404e3db272 (diff)
downloadprt-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/.gitignore2
l---------revdep/LICENSE1
-rw-r--r--revdep/Makefile50
-rw-r--r--revdep/TODO3
-rw-r--r--revdep/elf-cache.cpp187
-rw-r--r--revdep/elf-cache.h (renamed from revdep/global.h)27
-rw-r--r--revdep/elf.cpp177
-rw-r--r--revdep/elf.h42
-rw-r--r--revdep/global.c116
-rw-r--r--revdep/main.c293
-rw-r--r--revdep/main.cpp260
-rw-r--r--revdep/main.h4
-rw-r--r--revdep/package.c303
-rw-r--r--revdep/package.h62
-rw-r--r--revdep/pkg.cpp111
-rw-r--r--revdep/pkg.h47
-rw-r--r--revdep/revdep.157
-rw-r--r--revdep/revdep.man42
-rw-r--r--revdep/utility.c416
-rw-r--r--revdep/utility.cpp108
-rw-r--r--revdep/utility.h31
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);

Generated by cgit