#! /usr/bin/awk -f # # $Id: pkg_ids,v 1.10 2005/04/24 21:21:21 jschauma Exp $ # # Copyright (c) 2005 Jan Schaumann. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by Jan Schaumann. # 4. The name of the author may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # For each package, calculate the checksum and compare against the one # registered during installation. Print out mismatches, if any. # Obviously, this is expensive. See pkg_ids(1) for details. function init_vars() { PROGNAME="pkg_ids" VERSION="0.1" OLD_ORS=ORS DBDIR=ENVIRON["PKG_DBDIR"] if (DBDIR == "") DBDIR="/var/db/pkg" PKG_IDS_DIR=ENVIRON["HOME"] "/.pkg_ids" DB_CHKSUMS=PKG_IDS_DIR "/dbsums" PKG_IDS_CNFG=PKG_IDS_DIR "/pkg_idsrc" mismatches=0 pkgcount=0 warnings=0 cnfg["dbdir"] = DBDIR cnfg["ignorefrom"] = "" cnfg["pkgs"] = "" cnfg["PKG_IDS_DIR"] = PKG_IDS_DIR cnfg["PKG_IDS_CNFG"] = PKG_IDS_CNFG cnfg["dbchksums"] = DB_CHKSUMS cnfg["verbose"] = 1 } function read_cnfg() { cmd="[ -r " PKG_IDS_CNFG " ]" if (system(cmd)) { info("Unable to read " PKG_IDS_CNFG) close(cmd) return } close(cmd) lineno=0 while (getline ]" print " [ignorefrom=] [pkgs=]" print " [dbchksums=] [cnfgfile=]" } function getopts() { for (arg in ARGV) { if (arg != 0) { if (ARGV[arg] ~ /(-h|-\?|help)/) { usage() exit 0 } else if (ARGV[arg] ~ /version/) { print PROGNAME " Version " VERSION exit 0 } else if (ARGV[arg] ~ /cnfgfile=.*/) { split(ARGV[arg], pair, "=") cnfg["PKG_IDS_CNFG"]=pair[2] } else if (ARGV[arg] ~ /dbchksums=.*/) { split(ARGV[arg], pair, "=") cnfg["dbchksums"]=pair[2] cmd="dirname " cnfg["dbchksums"] cmd | getline cnfg["PKG_IDS_DIR"] close(cmd) } else if (ARGV[arg] ~ /dbdir=.*/) { split(ARGV[arg], pair, "=") cnfg["dbdir"]=pair[2] } else if (ARGV[arg] ~ /ignorefrom=.*/) { split(ARGV[arg], pair, "=") cnfg["ignorefrom"]=pair[2] } else if (ARGV[arg] ~ /pkgs=.*/) { split(ARGV[arg], pair, "=") cnfg["pkgs"]=pair[2] } else if (ARGV[arg] ~ /verbose=[0-9]/) { split(ARGV[arg], pair, "=") cnfg["verbose"] = pair[2] } else { usage() exit 1 } } } } function info(msg) { if (cnfg["verbose"] > 1) print msg } function warn(msg) { if (ORS != OLD_ORS) { setors=1 ORS=OLD_ORS } if (cnfg["verbose"] > 0) print("**Warning** " msg) if (setors == 1) ORS="" } function err(msg) { if (ORS != OLD_ORS) { setors=1 ORS=OLD_ORS } print("**Error** " msg) if (setors == 1) ORS="" } function errx(status, msg) { ORS=OLD_ORS print("**Error** " msg) exit status } function calc_chksum(algo, fullname) { testsum="" cmd="/usr/pkg/bin/digest " algo " \"" fullname "\" 2>/dev/null" if ((cmd| getline testsum) == 0) { warn("Unable to calculate " algo " checksum for " fullname) warnings++ close(cmd) return 0 } close(cmd) sub(".* ([^ ].*) = ", "", testsum) return testsum } function check_file(algo, chksum, fullname, ign_algo, ign_chksum) { retsum="" if (ign_algo != "") { retsum = calc_chksum(ign_algo, fullname) if (retsum == ign_chksum) return } retsum = calc_chksum(algo, fullname) if (retsum == chksum) { if (ign_algo != "") { warn("Package " PKG_NAME ": Checksum matches " \ "original package checksum, but not " \ "the one recorded in " cnfg["ignorefrom"] \ "! Package reinstalled?") warnings++ } else return } else if (retsum != 0) { msg=PKG_NAME if (cnfg["verbose"] > 0) { msg="Package " PKG_NAME ": Checksum mismatch for " \ fullname " (" testsum " != " chksum ")" } warn(msg) mismatches++ } } function do_all_checks(filename) { ORS="" pkgcount++ cmd="[ -r " filename " ]" if (system(cmd)) { warn("Unable to read " filename "; skipping this package.") warnings++ close(cmd) return } close(cmd) while (getline 1) pkg="packages" info(" ===> " PROGNAME " finished <===") info(" ===> " pkgcount " " pkg " checked. <===") if (mismatches == 0 && warnings == 0) info(" ===> No problems found. <===") else { if (mismatches > 0) { msm="mismatch" if (mismatches > 1) msm="mismatches" info(" ===> " mismatches " checksum " msm \ " found. <===") } if (warnings > 0) { prb="problem" if (warnings > 1) prb="problems" info(" ===> " warnings " non-mismatch " prb \ " encountered. <===") } } } function read_dbfile() { lineno=0 cmd="[ -r " cnfg["dbchksums"] " ]" if (system(cmd)) { info("Need to create " cnfg["dbchksums"]) close(cmd) cmd="[ -d " cnfg["PKG_IDS_DIR"] " ]" if (system(cmd)) { info("Creating " cnfg["PKG_IDS_DIR"]) close(cmd) cmd="mkdir -p " cnfg["PKG_IDS_DIR"] if (system(cmd)) { warn("Unable to create " cnfg["PKG_IDS_DIR"]) close(cmd) } close(cmd) } close(cmd) } else { while (getline < cnfg["dbchksums"]) { lineno++ if (/^#/ || /^$/) { } else if (NF != 3) { err("Incorrect format in file " \ cnfg["dbchksums"] " in line " lineno) } else { pkgs[$1, 2] = $2 pkgs[$1, 3] = $3 } } close(cnfg["dbchksums"]) } close(cmd) } # Make sure the file in DBDIR have not been manipulated. # Append new checksums if necessary. function check_dbfile_checksums(filename) { if (pkgs[filename, 2] == "") { retsum = calc_chksum("MD5", filename) cmd="echo " filename " MD5 " retsum " >> " cnfg["dbchksums"] if (system(cmd)) { err("Unable to append checksum for " filename \ " to " cnfg["dbchksums"] "!") close(cmd) return 1 } close(cmd) } else { algo=pkgs[filename, 2] chksum=pkgs[filename, 3] retsum=calc_chksum(algo, filename) if (retsum != chksum) { err("Checksum mismatch for " filename \ "! Skipping this package.") warnings++ return 1 } } return 0 } BEGIN { init_vars() read_cnfg() getopts() build_ignore_hash() read_dbfile() if (cnfg["pkgs"] != "") cnfg["dbdir"]= cnfg["dbdir"] "/" cnfg["pkgs"] findcmd="find " cnfg["dbdir"] " -name \"+CONTENTS\" -print" while (findcmd | getline filename) { if (check_dbfile_checksums(filename) == 0) do_all_checks(filename) } close(findcmd) print_stats() exit 0 }