#!/bin/bash ############################################################################### # Copyright 2003 Thomas de Grenier de Latour (TGL) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2, as # published by the Free Software Foundation. # TODO: # - Submit to Gentoo forum for comments. # - Skip wellknown useless messages? (like "Applying something.patch", # "Installing GNOME 2 GConf Schemas", etc.) # - Anything useful to do with "emerge.log"? (Would probably be a good idea to # merge some features of this script in some already existing "emerge.log" # parser. Remember to contact Giorgio Mandolfo , the # "genlop" tool author) # - Yes, things could be optimized. I don't really care, tho... # - Add an option to force PORT_LOGDIR. # - Optionaly display emerge steps (unpack, compile, etc.)? ############################################################################### # Seetings, etc. # Meta PROG=`basename ${0}` VERSION="0.3.1" # Uncomment for debugging. #debug=yes # What messages to show if no option? default_show="errors warnings infos \ okaycolor failedcolor \ okaymono failedmono \ unknown portage" # Change this if you want to force PORT_LOGDIR PORT_LOGDIR="$(/usr/lib/portage/bin/portageq envvar PORT_LOGDIR)" ############################################################################### # Checks and warnings # PORT_LOGDIR? if [ ! -d "${PORT_LOGDIR}" ] ; then echo "PORT_LOGDIR not found." exit 1 fi # NOCOLOR? NOCOLOR="$(/usr/lib/portage/bin/portageq envvar NOCOLOR)" if [ "${NOCOLOR}" == "yes" ] ; then echo "Warning: NOCOLOR is set in your /etc/make.conf. \ I won't be able to guess message types in monochrome logs..." >&2 fi ############################################################################### # Parse args parse_args() { local a since # Command line options a="${1}" while [ -n "${a}" ] ; do case "${a}" in -h|--help) usage=y ; break ;; -e|--error|--errors) show="errors failedcolor ${show}" ;; -w|--warning|--warnings) show="warnings ${show}" ;; -i|--info|--infos) show="infos okaycolor ${show}" ;; -u|--unknown) show="unknown okaymono failedmono ${show}" ;; -p|--portage-message|--portage-messages) show="portage ${show}" ;; -m|--monochrome) monochrome=y ;; -s|--since) shift ; since="${1}" ;; --since=*) since="${a#--since=}" ;; -n|--nomatch) showall=y ;; --) shift ; a="${1}" ; break ;; -*) usageerror='${YL}Invalid option ${RD}'"${a}"' 1>&2' usage=y ; break ;; *) break ;; esac shift ; a="${1}" done # Now, get patterns. We disable some regexp special chars. while [ -n "${a}" ] ; do a="${a//./\\.}" a="${a//-/\\-}" a="${a//\\*/.*}" patterns="${a} ${patterns}" shift ; a="${1}" done # Parse the "since" value, produce "cmin" (in minutes) [ -n "${since}" ] && \ if ( echo ${since} | egrep -q "[0-9]+d") ; then cmin=${since%d} let cmin*=60*24 elif ( echo ${since} | egrep -q "[0-9]+h") ; then cmin=${since%h} let cmin*=60 elif ( echo ${since} | egrep -q "[0-9]+m") ; then cmin=${since%m} else usageerror='${YL}Invalid time parameter: ${RD}'"${since}"' 1>&2' usage=y fi # Set default values [ -z "${patterns}" ] && patterns='.*' [ -z "${show}" ] && show="${default_show}" } ############################################################################### # Setup output colors set_colors() { 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" } ############################################################################### # Display help message display_help() { if [ -n "${usageerror}" ]; then eval echo -e '${CY}${PROG}${NO}:${YL}' ${usageerror} else echo -e "${CY}${PROG} v. ${VERSION}${NO} ${BR}${PROG}${NO} is a tool to retrieve informations in per packages emerge logs. You have to set ${BR}PORT_LOGDIR${NO} in ${BR}/etc/make.conf${NO} to use it. By default, all messages are displayed. If you want only some of this informations, specify them in options." fi echo -e " ${BR}Usage: ${CY}${PROG}${NO} [${BR}options${NO}] [${YL}pattern1${NO} [${YL}pattern2${NO}]...]" if [ -z "${usageerror}" ]; then echo -e " ${YL}Patterns${NO} are interpreted as substrings to match either on ${YL}package-name${NO} or on ${YL}package-name-version${NO}. Some ${BR}special chars${NO} are supported: ${YL}^${NO} to match the beginning of the string, ${YL}\$${NO} to match the end of the string, ${YL}*${NO} to any substring (need to be single-quoted or escaped). (Note: categories are not supported since they don't appear in logs)" fi echo -e " ${BR}Available options:${NO} ${BL}-e${NO}, ${BL}--errors${NO} search for eerror messages (\" ${RD}*${NO} \") ${BL}-w${NO}, ${BL}--warnings${NO} search for ewarn messages (\" ${YL}*${NO} \") ${BL}-i${NO}, ${BL}--infos${NO} search for einfos messages (\" ${GR}*${NO} \") ${BL}-u${NO}, ${BL}--unknown${NO} search for unknown messages (\" * \", for monochrome logs) ${BL}-p${NO}, ${BL}--portage${NO} search for portage messages (\"!!!\", aka \"die\" messages) ${BL}-s${NO}, ${BL}--since=${NO} only read logs modified in the last ${BR}X u${NO} (where ${BR}X${NO} is an integer and ${BR}u${NO} a time unit: ${BR}d${NO} for days, ${BR}h${NO} for hours and ${BR}m${NO} for minutes) ${BL}-n${NO}, ${BL}--nomatch${NO} also display packages that contain no matching message ${BL}-m${NO}, ${BL}--monochrome${NO} don't use colors for ${BR}${PROG}${NO} output ${BL}-h${NO}, ${BL}--help${NO} display this message and exit" if [ -n "${usageerror}" ]; then exit 1 fi } ############################################################################### # List matching logs files: # - list log files that are recent enough, # - filter the list with patterns (trying both with and without "-version"), # - prefix results with mtimes, sort, and remove mtime prefix. find_logs() { local cminopt p f [ -n "${cmin}" ] && cminopt="-cmin -${cmin}" for f in $( find ${PORT_LOGDIR} -maxdepth 1 -name "*.log" -type f ${cminopt} ) ; do for p in $1 ; do if ( echo "$f" \ | sed -e's:\(/.*/[0-9]*\-\)\|\(\.log\)::g' \ | grep -q "$p" ) || \ ( echo "$f" \ | sed -e's:\(/.*/[0-9]*\-\)\|\(\-[0-9].*\.log\)::g' \ | grep -q "$p" ); then echo "`stat -c%Y $f` $f" break fi done done | sort -n | sed 's:^[0-9]* ::' } ############################################################################### # Build the search regexp build_sed_regexp() { local INFO='\o033\[32;01m' local WARNING='\o033\[33;01m' local ERROR='\o033\[31;01m' local NORMAL='\o033\[0m' local BRACKET='\o033\[34;01m' local a outexp while [ ${#} -gt 0 ] ; do outexp="${outexp}\\(" a="$1" shift case "${a}" in errors) outexp="${outexp}^ $ERROR\\*$NORMAL " ;; warnings) outexp="${outexp}^ $WARNING\\*$NORMAL " ;; infos) outexp="${outexp}^ $INFO\\*$NORMAL " ;; unknown) outexp="${outexp}^ \\* " ;; portage) outexp="${outexp}^!!! " ;; okaycolor) outexp="${outexp}${BRACKET}\\[ ${INFO}ok${BRACKET} \\]$NORMAL" ;; failedcolor) outexp="${outexp}${BRACKET}\\[ ${ERROR}!!${BRACKET} \\]$NORMAL" ;; okaymono) outexp="${outexp}\\[ ok \\]" ;; failedmono) outexp="${outexp}\\[ !! \\]" ;; *) echo "Unknown message type ${a}" ; exit 1 ;; esac outexp="${outexp}\\)" [ ${#} -gt 0 ] && outexp="${outexp}\\|" done echo "${outexp}" } ############################################################################### # Extract interresting messages. We group them as they are in the file. log_sed() { sed -n -e " /$sed_exp/{ i... p n :block /$sed_exp/{ p n b block } } \$i..." ${1} } ############################################################################### # Display a pretty title for this section of the result. log_title() { # Infos to be displayed local logfile="${1}" local logsize=$(du -h "${logfile}" | awk '{ print $1 }' ) local package="`echo ${logfile} | sed -e 's:\(^/.*/[0-9]*\-\)\|\(\.log$\)::g'`" local mtime="`stat -c%y ${logfile} | sed 's/:[0-9][0-9]\..*$//'`" # Let's have fun with block justification local middle="==" local side="=" local firstline=" === ${mtime} ${middle} ${package} === " local secondline=" ${side} ${logfile} (${logsize}) ${side} " while [ ${#firstline} -gt ${#secondline} ]; do side="=${side}" secondline=" ${side} ${logfile} ${side} " done while [ ${#firstline} -lt ${#secondline} ]; do middle="=${middle}" firstline=" === ${mtime} ${middle} ${package} === " done # Okay, now we can display it, with colors echo -e "${CY} === ${YL}${mtime}${CY} ${middle} ${BL}${package}${CY} === ${NO}" echo -e "${CY} ${side} ${NO}${logfile}${YL} (${logsize})${CY} ${side} ${NO}" } ############################################################################### # Main # First, we parse arguments, display help if needed, etc. parse_args ${*} [ -z "${monochrome}" ] && set_colors [ -n "${usage}" ] && display_help && exit 0 [ -n "${debug}" ] && echo "-> Portage log directory: ${PORT_LOGDIR}" >&2 [ -n "${debug}" ] && echo "-> Packages to search: ${patterns}" >&2 [ -n "${debug}" ] && echo "-> Messages to search: ${show}" >&2 [ -n "${debug}" ] && [ -n "${cmin}" ] \ && echo "-> Only logs modified in the last ${cmin} minutes" >&2 # Next, we search for logfiles that may interest us logs=`find_logs "$patterns"` [ -n "${debug}" ] && echo -e "-> Matching log files: \n${logs}" >&2 # And we prepare a nice piece of regexp depending on the options sed_exp=`build_sed_regexp $show` [ -n "${debug}" ] && echo -e "-> Sed regexp to search: ${sed_exp}" >&2 # Finally, we can parse the logs for logfile in $logs ; do [ -n "${debug}" ] && echo -e "-> Working on ${logfile}" >&2 messages=`log_sed $logfile` if [[ "${messages}" != "..." || -n "${showall}" || -n ${debug} ]] ; then log_title ${logfile} echo -e "${messages}\n" fi done # Arrivederci ! [ -n "${debug}" ] && echo "-> All done." >&2