[yum-git] 2 commits - docs/Makefile docs/yum-verify.1 plugins/tmprepo plugins/verify yum-utils.spec
James Antill
james at linux.duke.edu
Mon Mar 3 14:48:24 UTC 2008
docs/Makefile | 2
docs/yum-verify.1 | 82 ++++++++
plugins/tmprepo/tmprepo.py | 2
plugins/verify/verify.conf | 27 ++
plugins/verify/verify.py | 436 +++++++++++++++++++++++++++++++++++++++++++++
yum-utils.spec | 22 ++
6 files changed, 568 insertions(+), 3 deletions(-)
New commits:
commit 7c38e78733e8f07ad917a8f5b76b41e493a6c118
Author: James Antill <james at and.org>
Date: Sat Mar 1 17:23:11 2008 -0500
Add verify plugin, and documentation
diff --git a/docs/Makefile b/docs/Makefile
index 9de4a14..24d4e23 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,4 +1,4 @@
-DOCS = repoquery package-cleanup repo-rss yumdownloader yum-builddep yum-changelog reposync yum-complete-transaction yum-list-data yum-filter-data
+DOCS = repoquery package-cleanup repo-rss yumdownloader yum-builddep yum-changelog reposync yum-complete-transaction yum-list-data yum-filter-data yum-verify
DOCS5 = yum-changelog.conf
DOCS8 = yum-security
diff --git a/docs/yum-verify.1 b/docs/yum-verify.1
new file mode 100644
index 0000000..18e75c8
--- /dev/null
+++ b/docs/yum-verify.1
@@ -0,0 +1,82 @@
+.\" yum verify plugin
+.TH "yum-verify" "1" "2008 Mar 1" "James Antill" ""
+.SH "NAME"
+yum verify plugin
+.SH "SYNOPSIS"
+\fByum\fP [options] verify [package ...]
+.SH "DESCRIPTION"
+.PP
+This plugin extends \fByum\fP with some commands that give verification information on the installed system, much like rpm -V. You can change how the verification is done and which files it applies to.
+.PP
+added yum \fIcommand\fPs are:
+.br
+.I \fR * verify
+.br
+.I \fR * verify-rpm
+.br
+.I \fR * verify-all
+.br
+.PP
+all of which take the same arguments as the list yum command, obviously you can
+only verify packages that are installed on the system.
+.PP
+.br
+.br
+.PP
+.IP "\fBverify\fP"
+Is the generic verification command, and is intented to give the most useful
+output. It removes all false matches due to multilib and ignores changes to
+configuration files by default.
+.IP
+.IP "\fBverify-rpm\fP"
+Is meant to be 100% compatible with rpm -V output, and any differences should be
+considered as bugs.
+.IP
+.IP "\fBverify-all\fP"
+Is used to list all the differences, including some that rpm itself will ignore.
+.IP
+.SH "GENERAL OPTIONS"
+These are the options added to yum that are available in the verify commands.
+They are:
+.PP
+.IP "\fB\--verify-filenames\fP"
+This option is used to limit the filenames that the packages will perform
+verification.
+.IP "\fB\--verify-configuration-files\fP"
+This option is only useful in the generic verify command, and will
+enable/disable verification of files that are tagged as configuration files.
+.SH "EXAMPLES"
+.PP
+To do the same as rpm -Va, use:
+.IP
+yum verify-rpm
+.PP
+To verify the packages starting with the name yum, use:
+.IP
+yum verify 'yum*'
+.PP
+To verify the binaries that are in a bin directory, use:
+.IP
+yum verify --verify-filenames='*bin/*'
+.PP
+To verify all include files, Eg. for multilib problems, use:
+.IP
+yum verify-all --verify-filenames='/usr/include/*'
+
+.SH "SEE ALSO"
+.nf
+.I yum (8)
+.I yum.conf (5)
+.I the verify.conf file in /etc/yum/plugins.d
+.fi
+
+.SH "AUTHORS"
+.nf
+James Antill <james.antill at redhat.com>.
+.fi
+
+.SH "BUGS"
+.nf
+Currently yum-verify does not do verify-script checking or dependancy checking,
+only file checking.
+.fi
diff --git a/plugins/verify/verify.conf b/plugins/verify/verify.conf
new file mode 100644
index 0000000..47c13e6
--- /dev/null
+++ b/plugins/verify/verify.conf
@@ -0,0 +1,27 @@
+[main]
+enabled=1
+# Display verify mismatches with config. files, should probably use AIDE etc.
+# instead, if you really want this.
+# configuration-files = True
+
+# # Add more colour to your life (these are the defaults):
+# [highlight]
+#
+# # Don't output these problems in colour:
+# low-priority = mtime, genchecksum, permissions-missing, state,missingok,ghost
+#
+# # Don't highlight the values of these problems
+# filter-old = mtime, checksum
+# filter-new = mtime, checksum
+#
+# # These are the default highlight values
+# new = reverse
+#
+# old-fg = red
+#
+# file = underline
+# file-fg = green
+#
+# tags = bold
+# tags-fg = yellow
+# tags-bg = black
diff --git a/plugins/verify/verify.py b/plugins/verify/verify.py
new file mode 100644
index 0000000..9eb6ef2
--- /dev/null
+++ b/plugins/verify/verify.py
@@ -0,0 +1,436 @@
+#! /usr/bin/python -tt
+# 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 Library 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.
+#
+#
+# Copyright Red Hat Inc. 2008
+#
+# Author: James Antill <james.antill at redhat.com>
+#
+# Examples:
+#
+# yum verify
+# yum verify yum*
+# yum verify all
+# yum verify extras
+
+
+import yum
+import types
+from yum.plugins import TYPE_INTERACTIVE
+import logging # for commands
+from yum import logginglevels
+
+import time
+import stat
+
+requires_api_version = '2.5'
+plugin_type = (TYPE_INTERACTIVE,)
+
+def nevr(pkg):
+ """ Identify a pkg without arch. """
+ return "%s-%s:%s-%s" % (pkg.name, pkg.epoch, pkg.version, pkg.release)
+
+def fmt_rwx(mode, r, w, x):
+ ret = []
+
+ if w & mode:
+ ret.append("w")
+ else:
+ ret.append("-")
+ if r & mode:
+ ret.append("r")
+ else:
+ ret.append("-")
+ if x & mode:
+ ret.append("x")
+ else:
+ ret.append("-")
+
+ return "".join(ret)
+
+def format_mode(mode):
+ ret = []
+ tmp = []
+ if stat.S_ISUID & mode:
+ tmp.append("set user (setuid)")
+ if stat.S_ISGID & mode:
+ tmp.append("set group (setgid)")
+ if stat.S_ISVTX & mode:
+ tmp.append("sticky")
+ if tmp:
+ ret.append("/".join(tmp))
+
+ ret.append("user:" + fmt_rwx(mode, stat.S_IRUSR,stat.S_IWUSR,stat.S_IXUSR))
+ ret.append("group:" + fmt_rwx(mode, stat.S_IRGRP,stat.S_IWGRP,stat.S_IXGRP))
+ ret.append("other:" + fmt_rwx(mode, stat.S_IROTH,stat.S_IWOTH,stat.S_IXOTH))
+ return ", ".join(ret)
+
+import datetime
+def format_time_diff(x, y):
+ frm = datetime.datetime.fromtimestamp
+ if x > y:
+ return str(frm(x) - frm(y)) + " later"
+ else:
+ return str(frm(y) - frm(x)) + " earlier"
+
+def problem_contains(problems, types):
+ for problem in problems:
+ if problem.type in types:
+ return problem
+ return None
+
+def pkg_multilib_file(data, pkg, pkgs, fname):
+
+ problems = data[pkg][fname]
+ ml_csum = problem_contains(problems, ['checksum'])
+ ml_size = problem_contains(problems, ['size'])
+ ml_time = problem_contains(problems, ['mtime'])
+ if not (ml_csum or ml_size or ml_time):
+ return False
+
+ for opkg in pkgs:
+ if opkg == pkg:
+ continue
+
+ if opkg not in data:
+ data[opkg] = opkg.verify()
+ if fname not in data[opkg]:
+ return True
+
+ if problem_contains(problems, ['state', 'missingok', 'ghost']):
+ continue
+
+ problems = data[opkg][fname]
+ srch = []
+ if ml_csum:
+ srch.append('checksum')
+ if ml_size:
+ srch.append('size')
+ if ml_time:
+ srch.append('mtime')
+ if problem_contains(problems, srch):
+ continue
+
+ return True
+
+ return False
+
+# We make decisions based on these
+_verify_multilib = ['mtime', 'size', 'checksum']
+_verify_missingok= ['missingok', 'ghost']
+_verify_none = ['state'] + _verify_missingok
+_verify_missing = ['missing', 'permissions-missing','genchecksum']+_verify_none
+
+# These are user configurable, for output
+_verify_low = ['mtime', 'genchecksum', 'permissions-missing'] +_verify_none
+_verify_onohi = ['mtime', 'checksum']
+_verify_nnohi = ['mtime', 'checksum']
+
+class VerifyCommand:
+
+ def __init__(self, names, conf, multilib=True, verify_configs_override=None,
+ all=False):
+ self.names = names
+ self.conf = conf
+ self.all = all
+ self.multilib = multilib
+ self.verify_configs_override = verify_configs_override
+
+ def getNames(self):
+ return self.names
+
+ def getUsage(self):
+ return "[PACKAGE|all|extras]"
+
+ def getSummary(self):
+ return """\
+Verify packages and display data on bad verifications"""
+
+ def doCheck(self, base, basecmd, extcmds):
+ pass
+
+ def show_pkgs(self, msg, pkgs):
+ pass
+
+ @staticmethod
+ def _filter_results(oresults, verify_none=None):
+ if verify_none is None:
+ verify_none = _verify_none
+ results = {}
+ for fn in oresults:
+ probs = []
+ for problem in oresults[fn]:
+ if problem.type not in verify_none:
+ probs.append(problem)
+ if probs:
+ results[fn] = probs
+ return results
+
+ @staticmethod
+ def _filter_empty(oresults):
+ results = {}
+ for fname in oresults:
+ if oresults[fname]:
+ results[fname] = oresults[fname]
+ return results
+
+ def _filter_multilib(self, data, pkg, results):
+ for fname in results:
+ problems = results[fname]
+ mpkgs = self._multilib[nevr(pkg)]
+ if not pkg_multilib_file(data, pkg, mpkgs, fname):
+ continue
+
+ tmp = []
+ for problem in problems:
+ if problem.type in _verify_multilib:
+ continue
+ tmp.append(problem)
+ results[fname] = tmp
+ return self._filter_empty(results)
+
+ def filter_data(self, msg, pkgs):
+ data = {}
+ for pkg in sorted(pkgs):
+ oresults = pkg.verify(patterns=self._filename_globs, all=self.all)
+
+ if not _verify_configs and not self.verify_configs_override:
+ for fn in oresults.keys():
+ if 'configuration' in oresults[fn][0].file_types:
+ del oresults[fn]
+
+ if self.multilib:
+ data[pkg] = oresults
+ else:
+ if self.all:
+ results = self._filter_results(oresults, _verify_missingok)
+ else:
+ results = self._filter_results(oresults)
+ if results:
+ yield (pkg, results)
+
+ if not self.multilib:
+ return
+
+ ndata = {}
+ for pkg in data:
+ results = self._filter_results(data[pkg])
+ if nevr(pkg) in self._multilib:
+ ndata[pkg] = self._filter_multilib(data, pkg, results)
+ else:
+ ndata[pkg] = results
+
+ for pkg in sorted(pkgs):
+ if pkg in ndata and ndata[pkg]:
+ yield (pkg, ndata[pkg])
+
+ def _mode_except(self, base, line, problem=None, exceptions=None):
+ if exceptions is None:
+ exceptions = _verify_low
+ if problem is not None and problem.type in exceptions:
+ return ("", "")
+
+ hib = ""
+ hie = ""
+
+ name = 'fg_' + line
+ if name in self.conf and self.conf[name] in base.term.FG_COLOR:
+ hib += base.term.FG_COLOR[self.conf[name]]
+ name = 'bg_' + line
+ if name in self.conf and self.conf[name] in base.term.BG_COLOR:
+ hib += base.term.BG_COLOR[self.conf[name]]
+ name = 'hi_' + line
+ if name in self.conf and self.conf[name] in base.term.MODE:
+ hib += base.term.MODE[self.conf[name]]
+
+ hie = base.term.MODE['normal']
+ return (hib, hie)
+
+ def show_problem(self, base, msg, problem, done):
+ if done:
+ msg("%s%s%s" % (' ' * 35, '-' * 8, ' ' * 35))
+ (hib, hie) = self._mode_except(base, 'prob', problem)
+ msg(" Problem: " + hib + problem.message + hie)
+ if problem.type not in _verify_missing:
+ cv = problem.disk_value
+ ov = problem.database_value
+ if problem.type == 'mtime':
+ cv = time.ctime(cv) + " (%s)" % format_time_diff(cv, ov)
+ ov = time.ctime(ov)
+ if problem.type == 'mode':
+ cv = format_mode(cv)
+ ov = format_mode(ov)
+ if problem.type == 'size':
+ cv = "%*s" % (5, base.format_number(cv))
+ ov = "%*s" % (5, base.format_number(ov))
+ (hib, hie) = self._mode_except(base, 'new', problem, _verify_nnohi)
+ msg(" Current: " + hib + cv + hie)
+ (hib, hie) = self._mode_except(base, 'old', problem, _verify_onohi)
+ msg(" Original: " + hib + ov + hie)
+
+ def show_data(self, base, msg, pkgs, name):
+ done = False
+ mcb = lambda x: base.matchcallback(x, [])
+ for (pkg, results) in self.filter_data(msg, pkgs):
+ if not done:
+ msg("%s %s %s" % ('=' * 20, name, '=' * 20))
+ else:
+ msg('')
+ done = True
+
+ mcb(pkg)
+ for fname in sorted(results):
+ hiprobs = len(filter(lambda x: x.type not in _verify_low,
+ results[fname]))
+ if hiprobs:
+ (hib, hie) = self._mode_except(base, 'file')
+ else:
+ (hib, hie) = ("", "")
+ msg(" File: " + hib + fname + hie)
+ if hiprobs:
+ (hib, hie) = self._mode_except(base, 'tags')
+ else:
+ (hib, hie) = ("", "")
+ done_prob = False
+ for problem in sorted(results[fname]):
+ if not done_prob and problem.file_types:
+ tags = ", ".join(problem.file_types)
+ msg(" Tags: " + hib + tags + hie)
+ self.show_problem(base, msg, problem, done_prob)
+ done_prob = True
+
+ def doCommand(self, base, basecmd, extcmds):
+ logger = logging.getLogger("yum.verbose.main")
+ def msg(x):
+ logger.log(logginglevels.INFO_2, x)
+ def msg_warn(x):
+ logger.warn(x)
+
+ opts = base.plugins.cmdline[0]
+ if opts.verify_configuration_files is not None:
+ val = opts.verify_configuration_files
+ if False: pass
+ elif val.lower() in ["0", "no", "false", "off"]:
+ _verify_configs = False
+ elif val.lower() in ["1", "yes", "true", "on"]:
+ _verify_configs = True
+ else:
+ msg_warn("Ignoring bad value \"%s\" for the option %s" %
+ (val, "--verify-configuration-files"))
+ self._filename_globs = None
+ if opts.verify_filenames:
+ self._filename_globs = opts.verify_filenames
+
+ subgroup = ["installed"]
+ if len(extcmds):
+ if extcmds[0] == "all":
+ extcmds = extcmds[1:]
+ elif extcmds[0] == "extras":
+ subgroup = ["extras"]
+ extcmds = extcmds[1:]
+
+ if self.multilib:
+ pkgs = base.returnPkgLists(["installed"]).installed
+ self._multilib = {}
+ for pkg in pkgs:
+ self._multilib.setdefault(nevr(pkg), []).append(pkg)
+ for pkg in pkgs:
+ if len(self._multilib[nevr(pkg)]) == 1:
+ del self._multilib[nevr(pkg)]
+ # self._multilib is now a dict of all pkgs that have more than one
+ # nevr() match
+
+ ypl = base.returnPkgLists(subgroup + extcmds)
+ self.show_data(base, msg, ypl.installed, 'Installed Packages')
+ self.show_data(base, msg, ypl.extras, 'Extra Packages')
+
+ return 0, [basecmd + ' done']
+
+def config_hook(conduit):
+ '''
+ Yum Plugin Config Hook:
+ Add the 'verify' and 'verify-no-multilib' commands.
+ '''
+ global _verify_configs
+ global _verify_low
+ global _verify_onohi
+ global _verify_nnohi
+
+ _verify_configs = conduit.confBool('main', 'configuration-files',
+ default=False)
+
+ low = conduit.confString('highlight', 'low-priority', default=None)
+ if low:
+ _verify_low = filter(len, low.replace(',', ' ').split())
+ fold = conduit.confString('highlight', 'filter-old', default=None)
+ if fold:
+ _verify_onohi = filter(len, fold.replace(',', ' ').split())
+ fnew = conduit.confString('highlight', 'filter-new', default=None)
+ if fnew:
+ _verify_nnohi = filter(len, fnew.replace(',', ' ').split())
+
+ conf = {}
+
+ conf['hi_prob'] = conduit.confString('highlight', 'problem', default='bold')
+ conf['fg_prob'] = conduit.confString('highlight', 'problem-fg',default=None)
+ conf['bg_prob'] = conduit.confString('highlight', 'problem-bg',default=None)
+
+ conf['hi_new'] = conduit.confString('highlight', 'new', default='reverse')
+ conf['fg_new'] = conduit.confString('highlight', 'new-fg', default=None)
+ conf['bg_new'] = conduit.confString('highlight', 'new-bg', default=None)
+
+ conf['hi_old'] = conduit.confString('highlight', 'old', default=None)
+ conf['fg_old'] = conduit.confString('highlight', 'old-fg', default='red')
+ conf['bg_old'] = conduit.confString('highlight', 'old-bg', default=None)
+
+ conf['hi_file'] = conduit.confString('highlight', 'file',
+ default='underline')
+ conf['fg_file'] = conduit.confString('highlight', 'file-fg',
+ default='green')
+ conf['bg_file'] = conduit.confString('highlight', 'file-bg', default=None)
+
+ conf['hi_tags'] = conduit.confString('highlight', 'tags', default='bold')
+ conf['fg_tags'] = conduit.confString('highlight', 'tags-fg',
+ default='yellow')
+ conf['bg_tags'] = conduit.confString('highlight', 'tags-bg',
+ default='black')
+
+ reg = conduit.registerCommand
+ reg(VerifyCommand(['verify-all'], conf, multilib=False,
+ verify_configs_override=True, all=True))
+ reg(VerifyCommand(['verify-rpm'], conf, multilib=False,
+ verify_configs_override=True))
+ reg(VerifyCommand(['verify-multilib','verify'], conf))
+
+ parser = conduit.getOptParser()
+ if not parser:
+ return
+
+ def make_nopt(attrs):
+ attrs = attrs.replace("-", "_")
+ def func(opt, key, val, parser):
+ vals = str(val).replace(",", " ").split()
+ vals = filter(len, vals)
+ getattr(parser.values, 'verify_' + attrs).extend(vals)
+ return func
+
+ parser.values.verify_files = []
+ parser.add_option('--verify-filenames', action="callback",
+ callback=make_nopt('filenames'), default=[],type="string",
+ help='Only verify files matching this')
+
+ parser.add_option('--verify-configuration-files', action="store",
+ type="string",
+ help='Verify files tagged as configuration files')
diff --git a/yum-utils.spec b/yum-utils.spec
index 772aa96..9fe6d8a 100644
--- a/yum-utils.spec
+++ b/yum-utils.spec
@@ -229,7 +229,7 @@ each category, if any were specified.
%package -n yum-tmprepo
Summary: Yum plugin to add temporary repositories
Group: System Environment/Base
-Requires: yum >= 3.0.5
+Requires: yum >= 3.2.11
%description -n yum-tmprepo
This plugin adds the option --tmprepo which takes a url to a .repo file
@@ -237,6 +237,16 @@ downloads it and enables it for a single run. This plugin tries to ensure
that temporary repositories are safe to use, by default, by not allowing
gpg checking to be disabled.
+%package -n yum-verify
+Summary: Yum plugin to add verify command, and options
+Group: System Environment/Base
+Requires: yum >= 3.2.12
+
+%description -n yum-verify
+This plugin adds the commands verify, verify-all and verify-rpm. There are
+also a couple of options. This command works like rpm -V, to verify your
+installation.
+
%prep
%setup -q
@@ -248,7 +258,7 @@ make -C updateonboot DESTDIR=$RPM_BUILD_ROOT install
# Plugins to install
plugins="changelog fastestmirror fedorakmod protectbase versionlock tsflags kernel-module \
downloadonly allowdowngrade skip-broken priorities refresh-updatesd merge-conf \
- security protect-packages basearchonly upgrade-helper aliases list-data filter-data tmprepo"
+ security protect-packages basearchonly upgrade-helper aliases list-data filter-data tmprepo verify"
mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/yum/pluginconf.d/ $RPM_BUILD_ROOT/usr/lib/yum-plugins/
@@ -417,8 +427,16 @@ fi
%config(noreplace) %{_sysconfdir}/yum/pluginconf.d/tmprepo.conf
/usr/lib/yum-plugins/tmprepo.*
+%files -n yum-verify
+%defattr(-, root, root)
+%config(noreplace) %{_sysconfdir}/yum/pluginconf.d/verify.conf
+/usr/lib/yum-plugins/verify.*
+%{_mandir}/man1/yum-verify.1.*
%changelog
+* Sat Mar 1 2008 James Antill <james at fedoraproject.org>
+- Add verify plugin
+
* Wed Feb 20 2008 James Antill <james at fedoraproject.org>
- Add empty versionlock file
commit 5d02f477beda6b35fea7b61a2a81f1321a6ee8f0
Author: James Antill <james at and.org>
Date: Wed Feb 27 01:24:41 2008 -0500
Minor cleanup
diff --git a/plugins/tmprepo/tmprepo.py b/plugins/tmprepo/tmprepo.py
index 83e6054..ad8ca33 100644
--- a/plugins/tmprepo/tmprepo.py
+++ b/plugins/tmprepo/tmprepo.py
@@ -98,6 +98,8 @@ def prereposetup_hook(conduit):
'''
opts, args = conduit.getCmdLine()
+ if not opts.tmp_repos:
+ return
log = logging.getLogger("yum.verbose.main")
add_repos(conduit._base, log, opts.tmp_repos,
make_validate(log, my_gpgcheck))
More information about the Yum-cvs-commits
mailing list