#!/usr/bin/python # Copyright 1999-2005 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ # TODO: # * add a --root=/path/to/some/dir option: # - check /$ROOT/var/db/pkg exists # - set the env var before importing portage module # - get the right vardbapi # - takes only file paths with ${ROOT} prefix into account __author__ = "Thomas de Grenier de Latour (TGL)" __email__ = "degrenier@easyconnect.fr" __version__ = "0.1" __date__ = "2005/04/27" __productname__ = "fix_contents" __description__ = "\ This script can update some files mtime and/or md5 in the CONTENTS list of\n\ their owning package. Handle with care, this is about messing with your\n\ portage installed packages database." import sys import os, os.path import getopt sys.path.insert(0, "/usr/lib/portage/pym") from portage import settings, db, dblink import portage_checksum ############################################################################### ## Globals: vdbapi = db["/"]["vartree"].dbapi options = { \ 'stdin' : False, \ 'pretend' : False, \ 'quiet' : False, \ 'verbose' : False } files = {} filenames = {} owners = {} ############################################################################### ## CLI functions: def print_version(): print "This is \"%s\" %s (%s)" % (__productname__, __version__, __date__) print "Author: %s <%s>" % (__author__, __email__) def doing(message, stream=sys.stdout): if not options['quiet']: print >>stream,">>>",message,"... " def warn(message, stream=sys.stderr): print >>stream,"!!!",message def info(message, stream=sys.stdout): if options['verbose']: print >>stream,"---",message def print_help(): print_version() print print "Description:\n%s" % __description__ print print \ "\ Usage: \n\ %(name)s [-q|-v] [-p] ...\n\ %(name)s [-q|-v] [-p] -s\n\ %(name)s -h|-V\n\ \n\ Otions:\n\ -s, --stdin : read files list from standard input instead of\n\ command line arguments. It's one file per line.\n\ -p, --pretend : fake mode. Don't write the CONTENTS files.\n\ -q, --quiet : minimal noise level\n\ -v, --verbose : maximal noise level\n\ -V, --version : get version informations\n\ -h, --help : display this help screen\n\ " % { 'name': __productname__ } ############################################################################### ## Parse command line options and collect the files list: def parse_cmdline(): global options, files, filenames try: opts, args = getopt.getopt(sys.argv[1:], "hpqsvV", \ ["help","pretend","quiet","stdin","verbose","version"]) except: warn("Wrong command line options. Exiting") sys.exit(2) for o, a in opts: if o in ("-s", "--stdin"): options['stdin'] = True elif o in ("-p", "--pretend"): options['pretend'] = True elif o in ("-q", "--quiet"): options['quiet'] = True elif o in ("-v", "--verbose"): options['verbose'] = True elif o in ("-h", "--help"): print_help() sys.exit(0) elif o in ("-V", "--version"): print_version() sys.exit(0) if options['verbose'] and options['quiet']: warn("Error: Using both --quiet and --verbose doesn't make sense.") sys.exit(2) if options['stdin']: if args: warn("Error: Arguments found on both the command line and stdin.") sys.exit(2) args = sys.stdin.readlines() for my_file in args: my_file = os.path.normpath(my_file.strip()) my_real_file = os.path.realpath(my_file) if not os.path.isfile(my_real_file): warn("%s: not a regular file. Ignoring." % my_file) else: files[my_real_file] = { 'owners' : [] } filenames[os.path.basename(my_real_file)] = None ############################################################################### ## Find the packages owning the files we want to update: def get_files_owners(): global files, owners for my_cpv in vdbapi.cpv_all(): my_split = my_cpv.split('/') my_contents = \ dblink(my_split[0], my_split[1], '/', settings).getcontents() for my_file in my_contents: if my_contents[my_file][0] != 'obj': continue my_filename = os.path.basename(my_file) if my_filename not in filenames: continue my_real_file = os.path.realpath(my_file) if my_real_file not in files: continue files[my_real_file]['owners'].append(my_cpv) owners[my_cpv] = None info("%s: %s" % (my_real_file, my_cpv)) for my_file in files: if not files[my_file]['owners']: warn("No owner package was found for %s" % my_file) elif len(files[my_file]['owners']) >= 2: warn("Several owner packages were found for %s: %s" \ % (my_file, ', '.join(files[my_file]['owners']))) ############################################################################### ## Get the files new md5sums and mtimes: def get_files_info(): global files for my_file in files: if not files[my_file]['owners']: continue files[my_file]['md5'] = \ portage_checksum.perform_md5(my_file, calc_prelink=1) files[my_file]['mtime'] = os.path.getmtime(my_file) info("%s: md5 = %s -- mtime = %s" \ % (my_file, files[my_file]['md5'], files[my_file]['mtime'])) ############################################################################### ## Update CONTENTS files of the owner packages with the new files md5/mtimes: def write_files_info(): for my_cpv in owners: my_split = my_cpv.split('/') my_dblink = dblink(my_split[0], my_split[1], '/', settings) my_contents = open(os.path.join(my_dblink.dbdir,'CONTENTS'),'r') my_contents_lines = my_contents.readlines() my_contents.close() my_new_contents_lines = [] for my_line in my_contents_lines: my_new_contents_lines.append(my_line) my_line_split = my_line.split() if my_line_split[0] != 'obj': continue my_file = my_line_split[1] my_filename = os.path.basename(my_file) if my_filename not in filenames: continue my_real_file = os.path.realpath(my_file) if my_real_file not in files: continue my_new_line = "obj %s %s %s\n" \ % (my_file, files[my_real_file]['md5'], files[my_real_file]['mtime']) info("%s: %s" % (my_cpv, my_new_line.strip())) my_new_contents_lines[-1] = my_new_line my_new_contents_data = ''.join(my_new_contents_lines) if not options['pretend']: my_dblink.setfile('CONTENTS', my_new_contents_data) ############################################################################### ## Do stuffs: parse_cmdline() if os.geteuid() and not options['pretend']: warn("Error: Only root can run this script for real.") sys.exit(1) if not files: info("Nothing to do.") sys.exit(0) doing("Looking for packages owning this files") get_files_owners() doing("Reading files new mtime and md5") get_files_info() doing("Fixing packages CONTENTS files") write_files_info() sys.exit(0)