-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathdep-checker
executable file
·302 lines (236 loc) · 9.69 KB
/
dep-checker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import pisi
import cPickle
from optparse import OptionParser
# Pisi DB instances
installdb = pisi.db.installdb.InstallDB()
packagedb = pisi.db.packagedb.PackageDB()
componentdb = pisi.db.componentdb.ComponentDB()
### Helper functions
def dump_report_per_packager(results, output_dir):
# Get system.base packages
system_base = componentdb.get_packages("system.base")
def _format(x):
if x in system_base:
x = "%s (*)" % x
return x
packagers = {}
for p in [k for k in results.keys() if len(results[k][2]) > 0]:
# We have a package now
author = packagedb.get_package(p).source.packager.email
if packagers.has_key(author):
packagers[author] += "%s\n %s\n\n" % (p, "\n ".join([_format(m) for m in results[p][2]]))
else:
packagers[author] = "%s\n %s\n\n" % (p, "\n ".join([_format(m) for m in results[p][2]]))
# Create path if it doesn't exist
if not os.path.exists(output_dir):
os.makedirs(output_dir)
for p in packagers.keys():
filename = os.path.join(output_dir, p).replace(".", "_").replace("@", "_")
open(filename, "w").write(packagers[p])
def print_results(results, hide_system_base, colorize):
def _colorize(s):
if colorize:
if "B" in s[:2]:
s = "\x1b[1;33m" + s + "\x1b[0m" # system.base -> yellow
elif s.startswith("-"):
s = "\x1b[1;31m" + s + "\x1b[0m" # missing dep -> red
elif s.startswith("+"):
s = "\x1b[0;32m" + s + "\x1b[0m" # written dep -> green
else:
pass
return s
# Get system.base packages
system_base = componentdb.get_packages("system.base")
for p in results.keys():
# Get the lists
real_deps = results[p][0]
actual_deps = results[p][1]
missing_deps = results[p][2]
deps = []
# Filter system.base if requested
if hide_system_base:
real_deps = [d for d in real_deps if d not in system_base]
for d in real_deps:
marker = ""
if d in actual_deps:
marker += "+"
elif d in missing_deps:
marker += "-"
else:
marker += " "
if d in system_base:
marker += "B"
else:
marker += " "
deps.append("%s %s" % (marker, d))
print "\n".join([_colorize(d) for d in sorted(deps)])
def generate_elf_cache(path):
# Iterate over packages and generate ELF->Package mapping cache
elf_to_package = {}
for p in pisi.api.list_installed():
print "Checking package %s.." % p,
(dyns, execs) = get_elf_list(p, True)
if len(dyns) > 0:
elf_to_package.update(dict((k, p) for k in dyns))
print " %d shared object(s) found and added to the mapping cache." % len(dyns)
else:
print " No shared object(s)."
# Dump elf mapping dictionary for further usage
print "Saving ELF mapping cache..",
f = open(path, "w")
cPickle.Pickler(f, protocol=2)
cPickle.dump(elf_to_package, f, protocol=2)
f.close()
print "Done."
def load_elf_cache(path):
d = {}
if os.path.exists(path):
d = cPickle.Unpickler(open(path, "r")).load()
return d
def get_elf_list(package, dont_trust_packager):
# Eliminate symbolic links and return a list of all the files that needs to be investigated (ELF)
def filter_file(f):
if os.path.exists("/%s" % f.path):
if dont_trust_packager:
return True
else:
return (f.type == 'library' or f.type == 'executable')
else:
return False
files = [("/%s" % p.path) for p in installdb.get_files(package).list if filter_file(p)]
dyns = []
execs = []
for f in files:
elf_type = os.popen("/usr/bin/readelf -h \"%s\" 2>/dev/null | grep \"^ Type:\" | gawk '{ print $2 }'" % f).read().strip()
if elf_type == "DYN":
dyns.append(f)
elif elf_type == "EXEC":
execs.append(f)
else:
continue
return (dyns, execs)
def get_needed_objects(f):
objs = [l.strip().split()[1] for l in os.popen("/usr/bin/objdump -p \"%s\" | grep 'NEEDED'" % f).readlines()]
# Get full path to the objects
filemap = {}
for l in [_l for _l in os.popen("/usr/bin/ldd \"%s\" 2> /dev/null" % f).readlines() if "=>" in _l]:
# Filter these special objects
if "linux-gate" in l or "ld-linux.so" in l:
continue
ls = l.split("=>")
filemap[ls[0].strip()] = ls[1].split(" (")[0].strip()
for i in range(len(objs)):
if filemap.has_key(objs[i]):
objs[i] = filemap[objs[i]]
return objs
def check_single_file(pspec):
import tempfile
# Create pisi object
p = pisi.package.Package(pspec)
dirname = tempfile.mkdtemp(prefix='depchecker-')
p.extract_install(dirname)
# Recurse in dirname to find ELF objects
# Cleanup directory
os.removedirs(dirname)
### Entry point
if __name__ == "__main__":
if len(sys.argv) == 1:
sys.argv.append("--help")
parser = OptionParser()
parser.add_option("-C", "--color",
action="store_true",
dest="colorize",
default=False,
help="Colorize output")
parser.add_option("-s", "--hide-system-base",
action="store_true",
dest="hide_system_base",
default=False,
help="Hide system.base dependencies")
parser.add_option("-c", "--component",
action="store",
dest="component",
help="Check dependencies only for the given component")
parser.add_option("-a", "--all",
action="store_true",
dest="all_packages",
default=False,
help="Check dependencies for all the installed packages")
parser.add_option("-g", "--generate-elf-cache",
action="store_true",
dest="generate_elf_cache",
default=False,
help="Generate elf cache for pisi packages in /var/lib/pisi")
parser.add_option("-D", "--dont-trust-packager",
action="store_true",
dest="dont_trust_packager",
default=False,
help="Checks for all the files regardless of their types in pspec.xml. This will bring a performance penalty.")
parser.add_option("-d", "--output-directory",
action="store",
dest="output_directory",
help="If given, the dependency informations will be written into the output directory")
(options, packages) = parser.parse_args()
if options.generate_elf_cache:
generate_elf_cache("/var/lib/pisi/elfcache.db")
sys.exit(0)
# Load elf cache
elf_to_package = load_elf_cache("/var/lib/pisi/elfcache.db")
if not elf_to_package:
print "You first have to create the elf cache using -g parameter."
sys.exit(1)
# Get packages from the given component
if options.component:
packages = componentdb.get_packages(options.component)
elif options.all_packages:
packages = installdb.list_installed()
print "No package given, processing all %d installed packages.." % len(packages)
elif packages and packages[0].endswith(".pisi") and os.path.exists(packages[0]):
# Single pisi file mode. Will check the dependencies without using a full
# mapping cache but only against the libraries installed on the system.
# Kudos goes to Fatih and Gokcen :)
check_single_file(packages[0])
sys.exit(0)
# Some loop counters here
pindex = 1
total = len(packages)
if total > 1 and not options.output_directory:
# Automatically fallback to directory output if there are multiple packages
options.output_directory = "results"
# Results dictionary: (package->deps)
results = {}
# Iterate over packages and find the dependencies
for p in packages:
needed = set()
actual_deps = set()
missing_deps = set()
real_deps = set()
(dyns, execs) = get_elf_list(p, options.dont_trust_packager)
for elf in dyns+execs:
needed.update(set(get_needed_objects(elf)))
real_deps = set([elf_to_package.get(k, '<Not found>') for k in needed.intersection(elf_to_package.keys())])
try:
actual_deps = set([d.package for d in packagedb.get_package(p).runtimeDependencies()])
missing_deps = real_deps.difference(actual_deps)
except:
print "**** %s cannot be found in package DB, probably the package has been deleted from the repository." % p
continue
# Push the informations to the results dictionary filtering the current package from the sets
results[p] = (real_deps.difference([p]),
actual_deps.difference([p]),
missing_deps.difference([p]))
# Increment the counter
pindex += 1
if options.output_directory:
print "Saving results..",
#save_data_into(options.output_directory, results, options.hide_system_base)
dump_report_per_packager(results, options.output_directory)
print "done."
else:
# The informations will be printed to the screen
print_results(results, options.hide_system_base, options.colorize)
sys.exit(0)