#!/bin/bash # # qpkg - query portage package system for various information # # Copyright (c) Vitaly Kushneriuk # This program is distributed under the terms of GPL version 2. # # Maintainer: Brandon Low # Additional code thanks to: # Josh Goebel # # $Header$ ID='$Id$' VERSION=0.`echo ${ID} | cut -d\ -f3` PROG=`basename ${0}` # Parse args verb=0 group="*" params=${#} while [ ${#} -gt 0 ] do a=${1} shift case "${a}" in -h|--help) usage=y break ;; -i|--info) info=y ;; -d|--dups) dups=y inst=y ;; -q|--query-deps) query=y ;; -s|--slot) slot=y ;; -f|--find-file) ffind=y inst=y ;; -fp|--find-pattern) ffind=y fpat=y inst=y ;; -I|--installed) inst=y ;; -m|--masked) grepmask="-L" ;; -n|--non-masked) grepmask="-l" ;; -U|--uninstalled) uninst=y ;; -g|--group) group=$1 shift ;; -l|--list) list=y inst=y ;; -ct|--check-time|-tc|--time-check) tcheck=y inst=y ;; -cm|--check-md5|-mc|--md5-check) mcheck=y inst=y ;; -c|--check) mcheck=y tcheck=y inst=y ;; -v|--verbose) let $((verb++)) ;; -vv) let $((verb++)) let $((verb++)) ;; -nc|--no-color|--nocolor|--no-colors|--nocolors) nocolor=y ;; -*) echo -e ${CY}${PROG}${NO}:${YL} Invalid option ${RD}$a 1>&2 usage=y break ;; *) if [ -n "${arg}" ]; then echo -e ${CY}${PROG}: ${YL}Only one argument supported usage=y break fi arg=$a ;; esac done #This is a dumb way to handle things, take it out next time T="\t" #Set up colors if [ ! "${nocolor}" ]; then NO="\x1b[0;0m" BR="\x1b[0;01m" CY="\x1b[36;01m" RD="\x1b[31;01m" GR="\x1b[32;01m" YL="\x1b[33;01m" BL="\x1b[34;01m" STAR=" *" elif [ ! "${inst}" ] && [ ! "${uninst}" ]; then STAR=" *" fi # check for option conflicts if [ "${inst}" -a "${uninst}" \ -o \( "${ffind}" -o "${list}" -o "${tcheck}" -o "${mcheck}" \) \ -a "${uninst}" ]; then echo -e ${CY}${PROG}${NO}:${YL} conflicting options/modes${NO} usage=y fi if [ "${usage}" ]; then echo -e "${CY}${PROG} v. ${VERSION}${NO} ${CY}${PROG}${NO} is GenToolKit's \"query package\" tool, using it, you can find packages owning files on your filesystem, check the integrity of installed packages, and do other queries against installed or uninstalled packages. ${BR}Usage: ${T}${CY}${PROG}${NO} [${BR}options${NO}] [${YL}pkgname${NO}] [${BL}-g${YL} group${NO}] [${BL}-f${YL} ${NO}|${BL}-fp${YL} ${NO}] ${T}${CY}${PROG}${NO} ${BL}--dups${NO} [${BL}--slot${NO}] ${T}${CY}${PROG}${NO} ${BL}--help${NO} ${BR}Duplicate Locating: ${BL}-d, --dups${NO}${T}${T}print packages that have multiple versions installed ${BL}-s, --slot${NO}${T}${T}make ${BL}-d${NO} SLOT only print dups of the same SLOT ${BR}Package Selection: ${BL}-f, --find-file${NO}${T}finds package that owns file ${BL}-fp, --find-pattern${NO}${T}finds to package that owns file matching ** ${BL}-m, --masked${NO}${T}Include${YL} only${NO} masked packages ${BL}-n, --non-masked${NO}${T}Include${YL} only${NO} non-masked packages ${BL}-I, --installed${NO}${T}Include${YL} only${NO} installed packages ${BL}-U, --uninstalled${NO}${T}Include${YL} only${NO} uninstalled packages ${BL}-g, --group${NO}${T}${T}Find by goup (can be combined with other searches) ${BR}Information Selection: ${BL}-l, --list${NO}${T}${T}List package content ${BL}-i, --info${NO}${T}${T}Get package description and home page. ${BL}-ct, --check-time${NO} ${BL}-tc, --time-check${NO}${T}Verify package files timestamps ${BL}-cm, --check-md5${NO} ${BL}-mc, --md5-check${NO}${T}Verify package files md5 ${BL}-c, --check${NO}${T}${T}Verify mtimes${YL} and${NO} md5. ${BL}-q, --query-deps${NO}${T}display all installed packages ${T}${T}${T}depending on selected packages ${BR}Operation Modifiers: ${BL}-nc, --no-color${NO}${T}don't use colors ${BL}-v, --verbose${NO}${T}Be more verbose [ can be repeated twise ] ${BL}-vv${NO}${T}${T}${T}Same as ${BL}-v -v${NO} ${YL}Notes${NO}: ${YL}*${NO} ${BL}-f${NO}, ${BL}-fp, ${BL}-d${NO}, ${BL}-l${NO}, ${BL}-ct${NO}, ${BL}-cm${NO}, and ${BL}-c${NO} apply only to installed packages. ${YL}*${NO} Short options may not be combined on the command-line, yet. ${YL}*${NO} The operation of some flags has been changed by the stripping of version numbers from some output to see the version numbers play with ${BL}-v${NO} and ${BL}-vv${NO}. ${YL}*${NO} When using${BL} -f${NO} with ${BL}-l${NO} or ${BL}--check.. -v${NO} options, only matching files will be displayed, unless ${BL}-v${NO} is doubled, (yet more verbose) or ${BL}-vv${NO} is used. ${YL}Examples${NO}: ${PROG} --dups print duplicates oldest first ${PROG} --dups -v .. with versions ${PROG} print list of installed packages ${PROG} porta -I print versions of installed portage ${PROG} porta -i .. + versions in portage tree + descriptions and homepages ${PROG} gawk -c -v check integrity all installed versions of gawk the older will have \"damaged\" files. ${PROG} -f /bin/ls print package(s) that own /bin/ls " exit fi #For the --dups switch only if [ "${dups}" ]; then if [ "${grepmask}" ]; then mask=`python -c 'import portage; print portage.settings["ACCEPT_KEYWORDS"];' 2> /dev/null` echo -e "Currently accepted keywords: ${BL}${mask}${NO}" echo -e mask=`echo ${mask} | perl -pe 's/\s+/|/'` fi #First dig out the list of packages with duplicates find /var/db/pkg -iname "*${arg}*.ebuild" 2> /dev/null > /tmp/qpkg.lst dups=`cat /tmp/qpkg.lst | cut -f7 -d/ | sed -e 's:\.ebuild$::; s:-r[0-9]*$::; s:-[^-]*$::; /^$/d' | sort | uniq -d` #Next get all the exact versions duppak=`cat /tmp/qpkg.lst | fgrep "${dups}"` #Now cut that down to the directory name so we can be smart dirs=`sed -e 's:/[^/]*$::' /tmp/qpkg.lst` #Go through each package's DB and create a sortable file #to play with declare -i defcount=`cat /var/cache/edb/counter` for DIR in ${dirs} do #Package COUNTER NUM=`cat "${DIR}/COUNTER" 2> /dev/null` [ -z "${NUM}" ] && NUM=defcount #Package slot if requested [ ${slot} ] && SLOT=`cat "${DIR}/SLOT"` #Package fullname PKG=`ls --color=no -1 ${DIR}/*.ebuild|cut -f5,7 -d"/"` #Package basename NAME=`echo "${PKG}"|sed -e 's:\.ebuild$::; s:-r[0-9]\+$::; s:-[0-9].*$::'` echo "${NUM} ${PKG} ${NAME}${SLOT}" #Finish loop, and sort that nice sortable file based on #installation order, and then based on package basename #bash hates me so I decided to use a temp file done |sort -t" " -k3 -k1g,2|uniq -D -f2 > /tmp/qpkg.lst duppak=`cat /tmp/qpkg.lst` rm /tmp/qpkg.lst #If max verbosity is set output with full path to each ebuild if [ "${verb}" -gt 1 ]; then echo -n "${duppak}"|cut -f2 -d" "| \ sed -e "s:^:${BL}/var/db/pkg/${BR}:" \ -e "s:\(/\)\([^/]*\)\(.ebuild\):\1${CY}\2${NO}\1\2\3:" #If normal verbosity output package group, package name and package version elif [ "${verb}" -gt 0 ]; then echo -n "${duppak}"|cut -f2 -d" "| \ sed -e "s:\(^[^/]*/\)\(.*\)\(\.ebuild\):${BR}\1${CY}\2${NO}:" #Otherwise just output package group and package name else echo -n "${duppak}"|cut -f2 -d" "| \ sed -e "s:-r[0-9]\+$::" \ -e "s:-[0-9].*$::" \ -e "s:\(^[^/]*/\)\(.*\):${BR}\1${CY}\2${NO}:"|uniq fi exit fi # get list of ebuilds to work on if [ "${ffind}" ]; then # file find mode - list all ebuilds for # package/CONTENTS containing if [ "${fpat}" ]; then dirs=`ls /var/db/pkg/${group}/*/CONTENTS \ | xargs grep -l "${arg}" \ | xargs --no-run-if-empty -n 1 dirname` else # if the user didnt specify a full path assume they # want to check in the working dir #17331 [ "${arg:0:1}" != "/" ] && arg="${PWD}/${arg}" dirs=`ls /var/db/pkg/${group}/*/CONTENTS \ | xargs grep -l " ${arg}\( .*\)*$" \ | xargs --no-run-if-empty -n 1 dirname` fi ipak=`( for d in ${dirs} -;do [ "-" = "$d" ] && break ls ${d}/*.ebuild done)` else # normal mode - list ebuilds for ebuild name containing # installed packages if [ ! "${uninst}" ]; then ipak=`find /var/db/pkg/ -iname "*.ebuild" 2>/dev/null` if [[ ${group} != "*" ]]; then ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep ${group}` fi if [ ${arg} ]; then # avoid ${arg}="db" from pulling in every installed package temp="/var/db/pkg/.*${arg}" ipak=`echo ${ipak}|sed -e "s: :\n:g"|grep ${temp}` fi if [ -n "${mask}" ]; then ipak=`echo ${ipak}|xargs -r egrep ${grepmask} "^KEYWORDS=.*[[:space:]\"\'](${mask})[[:space:]\"\']"` fi fi # not installed packages (yet:-) if [ ! "${inst}" ]; then upak=`find /usr/portage/ -iname "*.ebuild" 2>/dev/null|grep -v --regex="/usr/portage/[^/]*\.ebuild"` if [[ ${group} != "*" ]]; then upak=`echo ${upak}|sed -e "s: :\n:g"|grep ${group}` fi if [ ${arg} ]; then upak=`echo ${upak}|sed -e "s: :\n:g"|grep ${arg}` fi if [ -n "${mask}" ]; then upak=`echo ${upak}|xargs -r egrep ${grepmask} "^KEYWORDS=.*[[:space:]\"\'](${mask})[[:space:]\"\']"` fi fi fi X="\([^/]*\)" for p in ${ipak} ${upak} -;do [ "${p}" = "-" ] && break # cut common prefix from ebuild name and mark installed/uninstalled packages # Note: iii/uuu will be replaced by the pipe at the end n=${p%.ebuild} var_db_pkg="/var/db/pkg/" n=${n/${var_db_pkg}/iii } usr_portage="/usr/portage/" n=${n/${usr_portage}/uuu } n=${n/\/*\//\/} d=${p%\/*.ebuild} # faster d=`dirname ${p}` echo ${n} # if we have no passed parameters then # we can skip the extra conditional checks [[ ${params} == 0 ]] && continue; if [ "${mask}" ]; then keywords=`grep KEYWORDS ${p}| cut -d\" -f2` echo -e "${T}Keywords: ${BL}${keywords}${NO}" fi if [ ${verb} -gt 1 ];then echo "vvv ${p}" fi if [ "${info}" ]; then home=`grep HOMEPAGE ${p}| cut -d\" -f2` desc=`grep DESCRIPTION ${p}| cut -d\" -f2` echo -e "${T}${BL}${desc}${NO} [ ${YL}${home}${NO} ]" fi if [ "${query}" ]; then echo -e "${BL}DEPENDED ON BY:${NO}" package="`echo ${n}|sed -e 's:-r[0-9]\+$::' \ -e 's:-[0-9].*$::' \ -e 's:^iii ::' \ -e 's:^uuu ::'`" place="`echo ${n}|cut -f1 -d' '`" [[ "${place}" == "iii" ]] && color="${GR}" || color="${RD}" grep -R "${package}" /var/db/pkg/*/*/RDEPEND | \ cut -f5,6 -d"/" | sed -e "s:^:\t${color}:;s:$:${NO}:" | sort | uniq # gawk -F "/" '{printf("${place}\n\t%s/%s${NO}",$5,$6)}' | sort | uniq fi # cat package content, remove obj/sym/dir, md5 and mtime when not verbose # display only match in file-find mode unless extra verbose if [ "${list}" ]; then echo -e ${BL}CONTENTS:${NO} if [ ${verb} -gt 1 ]; then cat ${d}/CONTENTS else if [ "${ffind}" ]; then if [ "${fpat}" ]; then grep "${arg}" $d/CONTENTS else grep " ${arg}\( .*\)*$" $d/CONTENTS fi else cat $d/CONTENTS fi | if [ ${verb} -gt 0 ]; then cat else sed -e "s:\(^obj \)\([^ ]*\)\(.*$\):\1${BR}\2${NO}:; s:\(^sym \)\([^ ]*\)\( -> \)\([^ ]*\)\(.*$\):\1${CY}\2${NO}\3\4:; s:\(^dir \)\([^ ]*\)\(.*$\):\1${YL}\2${NO}:" fi fi echo # check files mtime and md5, display summary at the end elif [ "${tcheck}" -o "${mcheck}" ]; then # counters fe=0 fs=0 # read the CONTENTS file and check md5 and mtime if needed # process only matching files in find-file mode unless extra verbose cat ${d}/CONTENTS | if [ "${ffind}" -a ${verb} -lt 2 ];then if [ "${fpat}" ]; then grep "${arg}" else grep " ${arg} " fi else cat fi | ( while read -a line do fs=$((fs + 1)) unset md5 unset _md5 unset mtime unset _mtime unset err name=${line[1]} missing= [ ! -e ${name} ] && missing=1 # colorize name and compute mtime/md5 if [ "obj" = ${line[0]} ]; then [ -e ${name} ] && { [ "${tcheck}" ] && mtime=${line[3]} [ "${tcheck}" ] && _mtime=`date -r ${name} +%s` [ "${mcheck}" ] && md5=${line[2]} [ "${mcheck}" ] && _md5=`md5sum ${name}|cut -f1 -d" "` } name=${BR}${name}${NO} elif [ "sym" = ${line[0]} ]; then name=${CY}${name}${NO} elif [ "dir" = ${line[0]} ]; then name=${YL}${name}${NO} fi # compare if [ "$missing" ]; then err=1 name="${name} ${RD}!not exist!${NO}" fi if [ "${md5}" != "${_md5}" ]; then #If the md5 fails the first time check it with #everything changed to lowercase :-D md5=`echo "${md5}"|tr A-Z a-z` if [ "${md5}" != "${_md5}" ]; then err=1 name="${name} ${RD}!md5!${NO}" fi fi if [ "${mtime}" != "${_mtime}" ]; then err=1 name="${name} ${RD}!mtime!${NO}" fi [ ${verb} -gt 1 ] && echo -e ${name} [[ ${verb} -eq 1 ]] && [[ $err -eq 1 ]] && echo -e ${name} fe=$((fe + err)) done if [ "$fe" = "0" ]; then echo -e ${YL}$fe${CY}/$fs${NO} else echo -e ${RD}$fe${CY}/$fs${NO} fi echo ) fi done | ( if [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o "${mask}" -o ${verb} -gt 0 \) ]; then sed -e "s:-r[0-9]\+$::" -e "s:-[0-9][^-]*$::"|sort -k2|uniq -f1 elif [ ! \( "${tcheck}" -o "${mcheck}" -o "${info}" -o "${list}" -o "${query}" -o "${mask}" -o ${verb} -lt 2 \) ]; then sort -k2|uniq -f1 else cat fi | sed \ -e "s:^iii ${X}/${X}:${BR}\1/${CY}\2${STAR}${NO}:" \ -e "s:^uuu ${X}/${X}:${BR}\1/${YL}\2${NO}:" \ -e "s:^vvv \(.*\)$:${BL}\1${NO}:" \ -e "s:^obj ::;s:^sym ::;s:^dir ::" )