#!/usr/bin/env python # Copyright (C) 2005 Vittorio Palmisano # # This program 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 2 # of the License, or (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # '''apt-history 0.1 Usage: apt-history [command] [options] Commands: log - log the current packages status show - shows the history for installed, removed, and upgraded packages. show install|remove|upgrade - shows only installed, removed or upgraded packages clean - clean log informations Options: --help, -h - shows this help ''' import os, sys, cPickle, time, getopt import apt_pkg class Status: '''dpkg status file''' def __init__(self, tagfile='/var/lib/dpkg/status'): '''Init of the status informations''' apt_pkg.InitSystem() self._parse = apt_pkg.ParseTagFile(open(tagfile)); self.status = {} while self._parse.Step() == 1: offset = self._parse.Offset() - self._parse.Section.Bytes() - 1 self.status[self._parse.Section.get('Package')] = offset def get(self, name, field): '''Get `field' from status for package `name' ''' try: self._parse.Jump(self.status[name]) return self._parse.Section.get(field, '') except KeyError: return '' def get_version(self, name): '''Get the version of package `name' ''' return self.get(name, 'Version') def get_status(self, name): '''Get the status of package `name' ''' return self.get(name, 'Status') prefix= '/var/log/apt-history' status_file = os.path.join(prefix, 'status') log_file = os.path.join(prefix, 'log') def save_status(s): p = {} for name in s.status.iterkeys(): p[name] = (s.get_version(name), s.get_status(name)) cPickle.dump(p, open(status_file, 'w')) def get_changes(old_status, new_status): changes = {'install': [], 'remove': [], 'upgrade': []} for pkg_name in new_status.status.iterkeys(): pkg_status = new_status.get_status(pkg_name) pkg_version = new_status.get_version(pkg_name) if not pkg_name in old_status: if 'install ok installed' in pkg_status: changes['install'].append((pkg_name, pkg_version)) elif pkg_status != old_status[pkg_name][1]: if 'install ok installed' in pkg_status: changes['install'].append((pkg_name, pkg_version)) elif 'purge ok not-installed' in pkg_status: changes['remove'].append((pkg_name, pkg_version)) elif pkg_version != old_status[pkg_name][0]: changes['upgrade'].append((pkg_name, pkg_version, old_status[pkg_name][0])) return changes def do_log(): try: old_status = cPickle.load(open(status_file)) log = cPickle.load(open(log_file)) except: do_init_log() return new_status = Status() changes = get_changes(old_status, new_status) date = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime()) log.append((date, changes)) cPickle.dump(log, open(log_file, 'wa')) print 'Saving new apt-history status...' save_status(new_status) def do_init_log(): date = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime()) log = [(date, dict(install=[], upgrade=[], remove=[]))] cPickle.dump(log, open(log_file, 'w')) new_status = Status() save_status(new_status) def do_show(args=[]): print_str = '%(date)s: %(action)-8s %(name)-40s ' def print_fields(date, entry, action): info = {'date': date, 'action': action} for fields in entry[action]: if action == 'upgrade': info['name'] = '%-30s' %(fields[0]+'='+fields[2]) info['new_v'] = fields[1] print (print_str+'%(new_v)s') %info else: info['name'] = fields[0] info['new_v'] = fields[1] print (print_str+'%(new_v)s') %info try: log = cPickle.load(open(log_file)) except: log = [] if len(args): actions = [] for action in args: if action in ['install', 'remove', 'upgrade']: actions.append(action) else: actions = ['install', 'remove', 'upgrade'] for date, log_entry in log: for action in actions: print_fields(date, log_entry, action) def usage(): print __doc__ def main(): try: assert sys.argv[1] in ('log', 'show', 'clean') except: usage() sys.exit(-1) try: opts, args = getopt.getopt(sys.argv[1:], 'h', ['help']) except getopt.GetoptError: usage() sys.exit(-1) for o, a in opts: if o in ('-h', '--help'): usage() sys.exit(0) if not os.path.exists(prefix): try: os.mkdir(prefix) do_init_log() except OSError, e: print e sys.exit(-1) if sys.argv[1] == 'log': do_log() elif sys.argv[1] == 'show': do_show(args[1:]) elif sys.argv[1] == 'clean': do_init_log() sys.exit(0) if __name__ == '__main__': main()