talideon.com

...for when I have better things to be doing

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.

Technorati Search Technorati Search Irish Bloggers

Comments

No comments.

Post a comment

All form information is optional, but it’s a good idea to fill in your name and email address if you want me to take your comment seriously.

Spammers, don’t bother posting crap down here. The site is set up so that legitimate search engines (Google, for instance) won’t index pages with comments on them. Posting crud here only means you’re wasting my time and patience. Shoo!

Real names, please. Please include!
Won’t be displayed. Please include!
Displayed, if present.