This is nxfaxsummary.sh in view mode; [Download] [Up]
#!/bin/sh - # # NXFax accounting 1.0 - a gawk script to summarize NXFax usage # by Andreas Mueller [afm@mathi.uni-heidelberg.de] and # Adrian Steinmann [ast@lia.di.epfl.ch] # # PATH=/bin:/usr/bin:/usr/ucb:/etc:/usr/etc IFS=' ' export PATH IFS # TRIMDAYS=30 # how many days to wait between trimming KEEPLINES=200 # how many lines to inherit when trimming # # define LOGFILE here, possibly override later # # NXFax 1.03 uses /usr/adm/nxfax/nxfaxlog NXFAXDIR=/usr/adm/nxfax if [ ! -d ${NXFAXDIR} ] then # NXFax 1.02 uses /etc/nxfax/nxfaxlog NXFAXDIR=/etc/nxfax if [ ! -d ${NXFAXDIR} ] then echo "Fatal error: Cannot find NXFax log file directory" >&2 usage fi fi # LOGFILE=${NXFAXDIR}/nxfaxlog # # OLOGFILE is where we put the log file when we trim # OLOGFILE="${LOGFILE}.old" # # COSTFILE, usually in same directory as logfile, possibly override below # : ${COSTFILE:=${NXFAXDIR}/nxfax.cost} # # we use GNU awk for builtin and custom functions # GAWK=/usr/local/bin/gawk # # this is what we say as a heading and subject line in case of mail (-m option) # WELCOME="NXFax Accounting @ `hostname`" # # CMD may be changed to "Mail -s '${WELCOME}' $recipients" below (-m option) # CMD=cat # export CMD WELCOME GAWK COSTFILE LOGFILE OLOGFILE NXFAXDIR KEEPLINES TRIMDAYS # # temporary files are cleanup up on exit or trap # TMPFHED=/tmp/faxhed$$ TMPFSND=/tmp/faxsnd$$ TMPFCST=/tmp/faxcst$$ TMPFPAR=/tmp/faxcst$$ TMPFRCV=/tmp/faxrcv$$ TMPFSUM=/tmp/faxsum$$ # export TMPFHED TMPFSND TMPFCST TMPFPAR TMPFRCV TMPFSUM # umask 077 # help () { echo cat<<!! This shell script uses GNU-awk '$GAWK' to parse log files generated by NXFax versions 1.02 and 1.03. By default, the script will search for a NXFax log file in the directories "/etc/nxfax/" and "/usr/adm/nxfax/". It will also look for a file called "nxfax.cost" in that directory describing the calling costs for various times and phone prefixes. For an example cost description file, invoke this script with the option "-sample". If the cost description file is missing, a simple grand total summary is calculated. Alternate cost description or NXFax log files can be specified via the -c and -l options respectively. With the option -m <address_list>, the summary is sent to the recipients specified in address_list. !! echo usage } # usage () { echo "Usage: $0 [ -help | -sample ]" >&2 echo " $0 [ [-l] <log> ] [ -c <costs> ] [ -m {address_list} ]" >&2 exit 1 } # cleanup () { rm -f ${TMPFHED} ${TMPFSND} ${TMPFPAR} ${TMPFCST} ${TMPFRCV} ${TMPFSUM} } # # note that NXFax 1.02 and 1.03 trims itself to 1000 lines at startup time # trimlog () { if [ -f ${OLOGFILE} ] then (find ${OLOGFILE} -mtime +${TRIMDAYS} -print | grep ${OLOGFILE} ) > /dev/null 2>&1 if [ $? -eq 0 ] then ( cp -p ${LOGFILE} ${OLOGFILE} && tail -${KEEPLINES} ${LOGFILE}.old > ${LOGFILE} && echo "${LOGFILE} rotated to ${OLOGFILE}, ${KEEPLINES} lines kept" ) 2> /dev/null fi else # if the old file doesn't exist, create it today so that # trimlog() will begin to rotate after ${TRIMDAYS} touch ${OLOGFILE} fi } # sample () { cat<<!! # Sample cost desciption file: '${NXFAXDIR}/nxfax.cost' # # This file is used by the nxfaxsummary shell script to compute the # approximate costs of outgoing fax traffic. Its precision depends # highly on the amount of detail given in this file. # # FORMAT DESCRIPTION - there are three types of lines: # # $<currency-string> # :<prefix>:<unit-time>:<unit-cost>:<connect-cost>:<comments> # +<prefix>:<comments> # # The '$' line defines a currency string to be used in the summary. # Lines beginning with ':' define cost computation parameters. # Lines beginning with '+' are called summary lines and are used # to indicate how costs should be summarized by nxfaxsummary.sh # # The script scans the called phone number and uses the parameters # associated with the first matching <prefix> to compute the costs # for such a call. If t is the connect time in seconds, then the # cost is computed according to the formula # # cost = connect-cost + (t/unit-time)*unit-cost # # connect-cost is the minimum fee per connection (0 in most situations) # this can be used to account for connect time measured # by NXFax which is usually shorter than the effective # duration of a call including initial handshake time; # unit-time is the duration (in seconds) of a time unit; # unit-cost is the price of one such unit-time. # # In the final summary, these computations are cumulatively grouped # according to the prefixes in the summary lines (the '+' lines). # # Empty lines and lines beginning with a '#' are ignored. # # EXAMPLE COST DESCRIPTION (for Germany) \$DM :0041:0:12:0.23:Calls to Switzerland :00:0:12:0.23:Other international calls +00:International calls: :0:0:60:0.23:Long distance calls within Germany (Regionalzone) ::0:360:0.23:Local calls +:Calls within Germany: # # The cost description file should always include a line beginning # with an empty prefix string, this is used as the default for phone # numbers that don't match any other prefix. The same is true for # summary lines. Note that the comments of the summary lines are # used as line item titles, hence they are often ended with a ':'. # # In other words, a minimum cost file may consist of just three lines: # # \$\$ # ::0:3600:1.20:Detailed cost description # +:Detailed summary description: # # If no cost description file is found nor specified, then a simple total # transmit time and average per page is output. # # Most phone companies charge differently during and after business hours # - use the parameters that are likely to be valid when *you* send faxes. # # To be done: - differentiate between rates during different time periods # !! exit 0 } # trap 'cleanup; exit 9' 2 3 15 # # parse options - default is output to stdout (use -m option to mail) # dorecipients=0 docost=0 dolog=0 while [ $# -gt 0 ] do while [ $dorecipients -ge 1 -a $# -gt 0 ] do dorecipients=2 case x$1 in x-*) break ;; *) recipients="$recipients $1" CMD="Mail -s '${WELCOME}' $recipients" shift ;; esac done if [ $dorecipients -eq 2 -a `echo $recipients | wc -w` -eq 0 ] then echo "Fatal error: -m option lacks at least one addressee" >&2 usage else dorecipients=0 fi if [ $docost -eq 1 -o $dolog -eq 1 ] then if [ $# -gt 0 ] then if [ ! -r $1 ] then echo "Fatal error: file '$1' unreadable" >&2 usage else if [ $dolog -eq 1 ] then LOGFILE=$1 dolog=2 else COSTFILE=$1 docost=2 fi fi shift else echo "Fatal error: option lacks an argument" >&2 usage fi fi case x$1 in x-m) shift if [ $dorecipients -ne 0 -o $# -lt 1 ]; then usage; fi dorecipients=1 ;; x-c) if [ $docost -ne 0 ]; then usage; fi shift; docost=1 ;; x-l) if [ $dolog -ne 0 ]; then usage; fi shift; dolog=1 ;; x-h*) help ;; x-s*) sample ;; x-*) usage ;; *) dolog=1 ;; esac done # if [ ! -r $LOGFILE ] then echo "Fatal error: Log file '${LOGFILE}' unreadable" >&2 usage fi OLOGFILE="${LOGFILE}.old" # # Finally do some real work: find first and last date in logfile # ( echo -n "\n${WELCOME} " ${GAWK} ' BEGIN { found=1; } $3 ~ /Sun|Mon|Tue|Wed|Thu|Fri|Sat/ \ { date=sprintf("%2d %s %d %s",$5,$4,$7,$6); if (found) { printf("from %s ",date) found=0 } } END { printf("to %s\n",date); }' ${LOGFILE} ) > ${TMPFHED} 2>&1 # # now search logfile for faxes sent, collect information in ${TMPFSND} # ${GAWK} \ 'function resetvars() { connected=false; call=false speed="?"; res="?"; errors="?"; number="?" ctime="?"; ttime="?"; pages=0 date=" ? ? ? "; calldate=date } function printsummary() { printf("%16s %-20s %3s %2s %4s %5s %5s %s\n", calldate,number,pages,substr(res,1,2), speed, ttime,ctime,errors) } BEGIN { true=1; false=0; resetvars() } $3 ~ /(Sun)|(Mon)|(Tue)|(Wed)|(Thu)|(Fri)|(Sat)/ \ { date=sprintf("%2d %s %s",$5,$4,$6) } # Note that NXFax 1.03 frequently contains the line # "%> NXFax: caller id or silent answer forces rings to 3" $3 ~ /call/ { if ($4 !~ /complete/ && $8 !~ /forces/ ) { call=true; number=substr($0,15,20) } } $3 ~ /line/ { if ($4 ~ /busy/) { calldate=date } } $3 ~ /Connected/ { if (call==true) { connected=true; calldate=date } } $4 ~ /resolution/ { res=$3 } $4 ~ /bps/ { speed=sprintf("%4.1f",$3/1000) } $3 ~ /page/ { pages=$4 } $3 ~ /hangup/ { if ( substr($3, 8, 1) == "0" ) { errors=$4" "$5 } else { errors="Hangup ERROR" } } /connect time/ { ctime=$5 } /transmit time/ { ttime=$5 if (connected) { printsummary() } resetvars() } $4 ~ /halt|busy/ { if (call) { printsummary() resetvars() } } /no carrier/ { if (call) { calldate=date; errors="no carrier" printsummary() resetvars() } } ' ${LOGFILE} > ${TMPFSND} 2>&1 # # # use the information collected in the previous step to get a # more accurate cost computation: # if [ ! -r $COSTFILE ] then awk '{ print substr($0,53,5)" "substr($0,39,3)" "substr($0,19,20) }' \ ${TMPFSND} | ${GAWK} ' BEGIN { transmit=0; pages=0; } $1 !~ /\?/ { pages+=$2; split($1,ttime,":") transmit+=60*ttime[1]+ttime[2] } END { time_h=int(transmit/3600); time_s=transmit-3600*time_h; printf("\nTotal transmit time: %3d:%02d:%02d for %d pages",\ time_h,time_s/60,time_s%60,pages) if (pages>0) printf("\t~ %8.2f sec/page",transmit/pages) printf("\n") } ' > ${TMPFCST} 2>&1 else awk '{ print substr($0,53,5)" "substr($0,39,3)" "substr($0,60,5)\ " "substr($0,19,20) }' ${TMPFSND} \ | ${GAWK} -v costparamfile=${COSTFILE} ' BEGIN { # initialize parameter arrays "sed -n -e s/\\^\\\\$//p "costparamfile | getline currency close("sed -n -e s/\\^\\\\$//p "costparamfile) FS=":"; counter=0; while ("sed -n -e s/\\^://p "costparamfile | getline ) { counter++ costp[counter,"prefix"]=$1 costp[counter,"connect"]=$2 costp[counter,"tunit"]=$3 costp[counter,"unitc"]=$4 } close("sed -n -e s/\\^://p "costparamfile) max=counter s=0 while ("sed -n -e s/\\^\\+//p "costparamfile | getline) { s++ summa[s,"prefix"]=$1 summa[s,"comment"]=$2 for (i=3; i<=NF; i++) summa[s,"comment"]=summa[s,"comment"]":"$i } summax=s; FS=" " transmit=0; pages=0; } $2 !~ /\?/ { pages+=$2; split($1,ttime,":") transmit+=60*ttime[1]+ttime[2] } $4 !~ /\?/ {# on every line where you have number and connect time, # do the following if ( $3 !~ /\?/ ) { split($3,timing,":"); time=60*timing[1]+timing[2]; counter=1; found=0; while (counter<=max && !found) { if ( index($4,costp[counter,"prefix"])==1 ) { found=1 cost[counter,"time"]+=time cost[counter,"cost"]+=(costp[counter,"connect"]+ \ time*costp[counter,"unitc"]/costp[counter,"tunit"]) } else if ( length(costp[counter,"prefix"])==0 ) { cost[counter,"time"]+=time cost[counter,"cost"]+=(costp[counter,"connect"]+ \ time*costp[counter,"unitc"]/costp[counter,"tunit"]) } counter++ } } } END { for (counter=1; counter<=max; counter++) { s=1; found=0; while (s<=summax && !found) { if ( index(costp[counter,"prefix"],summa[s,"prefix"])==1 ) { found=1 summa[s,"time"]+=cost[counter,"time"] summa[s,"cost"]+=cost[counter,"cost"] } else if ( length(summa[s,"prefix"])==0 ) { found=1 summa[s,"time"]+=cost[counter,"time"] summa[s,"cost"]+=cost[counter,"cost"] } s++ } } # output collected information for (s=1; s<=summax; s++) { time_h=int(summa[s,"time"]/3600); time_s=summa[s,"time"]-3600*time_h; printf("%-45s\t%2d:%02d:%02d ~%7.2f %s\n", \ summa[s,"comment"],time_h,time_s/60,time_s%60, \ summa[s,"cost"],currency) totalcost+=summa[s,"cost"] } printf("%-45s\t\t %8.2f %s\n","Total cost:", totalcost, \ currency) time_h=int(transmit/3600); time_s=transmit-3600*time_h; printf("\nTotal transmit time: %3d:%02d:%02d for %d pages",\ time_h,time_s/60,time_s%60,pages) if (pages>0) printf("\t~ %8.2f sec/page",transmit/pages) printf("\n") } ' > ${TMPFCST} 2>&1 fi # # now search logfile for faxes received, collect information in ${TMPFRCV} # ${GAWK} ' function resetvars () { connected=false; call=false; speed="?"; res="?"; errors="?"; number="?" ctime="?"; ttime="?"; pages=0; calldate=" ? ? ? " } BEGIN { true=1; false=0; resetvars(); t_ttime=0 } $3 ~ /Sun|Mon|Tue|Wed|Thu|Fri|Sat/ \ { date=sprintf("%2d %s %s",$5,$4,$6) } # this might be special to NXFax 1.02 /%> NXFax: incoming call/ { call=true; calldate=date } /%> NXFax: fax call/ { if (call) connected=true } # this might be special to NXFax 1.03 /%> NXFax: from/ { if (connected) { calldate=date number=substr($0,15,20) } } $4 ~ /halt/ { connected=false; call=false } $4 ~ /resolution/ { res=$3 } $4 ~ /bps/ { speed=sprintf("%4.1f",$3/1000) } $3 ~ /page/ { pages=$4 } $3 ~ /hangup/ { if ( substr($3, 8, 1) == 0 ) { errors=$4" "$5 } else { errors="Hangup ERROR" } } /transmit time/ { ttime=$5 if (connected) { if (number=="call") { number="?" } printf("%16s %-20s %3s %2s %s %5s %s\n", calldate,number,pages,substr(res,1,2), speed,ttime,errors) split(ttime,ttimecomp,":") t_ttime+=ttimecomp[1]*60+ttimecomp[2] t_pages+=pages } resetvars() } # this can happen on an error like # %> NXFax: hangup[70]: Unspecified receive Phase B error. /call complete/ { if (connected) { printf("%16s %-20s %3s %2s %s %5s %s\n", calldate,number,pages,substr(res,1,2), speed,ttime,errors) split(ttime,ttimecomp,":") t_ttime+=ttimecomp[1]*60+ttimecomp[2] t_pages+=pages } resetvars() } END { t_th=int(t_ttime/3600); t_ttime-=3600*t_th; printf("\nTotal transmit time: %3d:%02d:%02d for %d pages",\ t_th,t_ttime/60,t_ttime%60,t_pages); if ( t_pages != 0) { printf("\t~ %8.2f sec/page\n",t_ttime/t_pages) } printf("\n") }' ${LOGFILE} > ${TMPFRCV} 2>&1 # # Concatenate collected data # ( cat ${TMPFHED} if [ `wc -l ${TMPFSND} | awk -e '{print $1}'` -gt 0 ] then echo "\n SEND FAX\n" echo "Date Time Number pgs res kbps xmt conn errors\n" cat ${TMPFSND} if [ -r $COSTFILE ] then if [ `wc -l ${TMPFCST} | awk -e '{print $1}'` -gt 0 ] then echo "\nCost summary\t\t\t\t\t connect cost" cat ${TMPFCST} fi else cat ${TMPFCST} fi fi if [ `wc -l ${TMPFRCV} | awk -e '{print $1}'` -gt 2 ] then echo "\n\n RECEIVE FAX\n" echo "Date Time Number pgs res kbps xmt errors\n" cat ${TMPFRCV} fi echo ) > ${TMPFSUM} 2>&1 # if [ `wc -l ${TMPFSUM} | awk -e '{print $1}'` -gt 3 ] then cat ${TMPFSUM} | eval ${CMD} fi # cleanup # exit 0
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.