January 21, 2008 at 1:34PM Port management tools from my ~/bin directory
Here’s two small tools I have in my ~/bin directory. The first one, check-deps, checks the upwards and downwards dependencies for each package are consistent:
#!/usr/bin/env python
from __future__ import with_statement
import os
PKG_DB = '/var/db/pkg'
def each_dep(lines):
"""Extracts the package dependencies from the iterable."""
return (line[8:-1] for line in lines if line.startswith('@pkgdep '))
# Figure out from the dependencies which packages are required by which.
reqs = {}
for pkg in os.listdir(PKG_DB):
path = os.path.join(PKG_DB, pkg, "+CONTENTS")
if os.path.isfile(path):
with open(path) as f:
for dep in each_dep(f):
if dep not in reqs:
reqs[dep] = set()
reqs[dep].add(pkg)
# Check that the currently recorded ones are valid.
for pkg in reqs.keys():
path = os.path.join(PKG_DB, pkg, "+REQUIRED_BY")
if os.path.isfile(path):
with open(path) as f:
for line in f:
line = line[:-1]
if line in reqs[pkg]:
reqs[pkg].remove(line)
else:
print "===>\tErroneous dep '%s' in '%s'" & (line, pkg)
if len(reqs[pkg]) == 0:
del reqs[pkg]
else:
print "===>\t%s does not record:\n%s" % (pkg, "\n".join(reqs[pkg]))
if len(reqs) == 0:
print "All clean!"
The second one, check-packing, checks that the file manifest for each port is correct and that all the files are intact by comparing them against their recorded MD5 hashes:
#!/usr/bin/env python
from __future__ import with_statement
import os
import hashlib
import sys
PKG_DB = '/var/db/pkg'
def each_file(f):
"""
Generates a list of tuples, the first element being an absolute file
path, and the second being its recorded hash, from a package manifest.
"""
cwd = ''
file = ''
try:
for line in f:
if line[0] != '@':
file = os.path.join(cwd, line.strip())
elif line.startswith('@cwd '):
new_cwd = line.split(' ', 2)[1].strip()
# This check is a hack for dealing with broken manifests.
# mysql-client-5.0, nss, pth, portupgrade and zsh are
# examples.
if new_cwd != '':
cwd = new_cwd
elif file != '' and line.startswith('@comment MD5:'):
digest = line.split(':', 2)[1].strip()
yield (file, digest)
file = ''
finally:
pass
def hash_matches(path, original_hash):
"""Checks if the given file matches a hash."""
with open(path) as f:
new_hash = hashlib.md5(f.read()).hexdigest()
return new_hash == original_hash
for pkg in sorted(os.listdir(PKG_DB)):
path = os.path.join(PKG_DB, pkg, "+CONTENTS")
if os.path.isfile(path):
print "===>\tChecking %s" % (pkg,)
with open(path) as f:
for (file_path, hash) in each_file(f):
try:
if not os.path.exists(file_path):
if os.path.islink(file_path):
print "Missing: %s -> %s" % (file_path, os.path.realpath(file_path))
else:
print "Missing: %s" % (file_path,)
elif not os.path.islink(file_path) and not hash_matches(file_path, hash):
print "Altered: %s" % (file_path,)
except KeyboardInterrupt:
raise
except:
print "Unchecked: %s" % (file_path,)
They might be useful to someone.
Update (Feb 12th): Removed some stupid inefficiencies in how the files are hashed.