1#! @SCRIPT_SH@ 2# $Id$ 3# 4# HylaFAX Facsimile Software 5# 6# Copyright (c) 1993-1996 Sam Leffler 7# Copyright (c) 1993-1996 Silicon Graphics, Inc. 8# HylaFAX is a trademark of Silicon Graphics 9# 10# Permission to use, copy, modify, distribute, and sell this software and 11# its documentation for any purpose is hereby granted without fee, provided 12# that (i) the above copyright notices and this permission notice appear in 13# all copies of the software and related documentation, and (ii) the names of 14# Sam Leffler and Silicon Graphics may not be used in any advertising or 15# publicity relating to the software without the specific, prior written 16# permission of Sam Leffler and Silicon Graphics. 17# 18# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 19# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 20# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 21# 22# IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR 23# ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, 24# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 25# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 26# LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 27# OF THIS SOFTWARE. 28# 29 30# 31# Script to run periodically from cron: 32# 33# 0. Print transmit and receive statistics. 34# 1. Purge info directory of old remote machine capabilities. 35# 2. Purge old session logs from the log directory. 36# 3. Purge old files in the received facsimile queue. 37# 4. Notify about sites that currently have jobs rejected. 38# 39 40AGEINFO=30 # purge remote info after 30 days inactivity 41AGELOG=30 # keep log info for last 30 days 42AGERCV=7 # purge received facsimile after 7 days 43AGETMP=1 # purge orphaned temp files after 1 day 44FAXUSER=@FAXUID@ # owner of log files 45LOGMODE=0644 # mode for log files 46XFERLOG=etc/xferfaxlog # HylaFAX xferfaxlog file location 47LAST=etc/lastrun # file where time+date of last run recorded 48 49cd @SPOOL@ # NB: everything below assumes this 50. bin/common-functions 51 52test -f etc/setup.cache || { 53 SPOOL=`pwd` 54 cat<<EOF 55 56FATAL ERROR: $SPOOL/etc/setup.cache is missing! 57 58The file $SPOOL/etc/setup.cache is not present. This 59probably means the machine has not been setup using the faxsetup(@MANNUM1_8@) 60command. Read the documentation on setting up HylaFAX before you 61startup a server system. 62 63EOF 64 hfExit 1 65} 66. etc/setup.cache 67 68RM="$RM -f" 69TEE=tee 70UPDATE="date +'%D %H:%M' >$LAST" 71 72# security 73SetupPrivateTmp 74 75JUNK=$TMPDIR/faxjunk$$ # temp file used multiple times 76AWKTMP=$TMPDIR/faxawk$$ # temp file for awk program 77 78while [ x"$1" != x"" ] ; do 79 case $1 in 80 -n) RM=":" TEE=":" CP=":" MV=":" CHOWN=":" CHMOD=":" UPDATE=":";; 81 -l) shift; LASTRUN="$1";; 82 -info) shift; AGEINFO="$1";; 83 -log) shift; AGELOG="$1";; 84 -rcv) shift; AGERCV="$1";; 85 -tmp) shift; AGETMP="$1";; 86 -mode) shift; LOGMODE="$1";; 87 -*) echo "Usage: $0 [-n] [-l lastrun] [-info days] [-log days] [-rcv days] [-tmp days] [-mode logmode]"; hfExit 1;; 88 esac 89 shift 90done 91 92test -z "$LASTRUN" && LASTRUN=`$CAT $LAST 2>/dev/null` 93 94echo "Facsimile transmitted since $LASTRUN:" 95echo "" 96$SBIN/xferfaxstats -since "$LASTRUN" 97echo "" 98 99echo "Facsimile transmitted in the last seven days:" 100echo "" 101$SBIN/xferfaxstats -age 7 102echo "" 103 104echo "Facsimile received since $LASTRUN:" 105echo "" 106$SBIN/recvstats -since "$LASTRUN" 107echo "" 108 109echo "Facsimile received in the last seven days:" 110echo "" 111$SBIN/recvstats -age 7 112echo "" 113 114echo "Report failed calls and associated session logs:" 115$CAT>$AWKTMP<<'EOF' 116# 117# Sort array a[l..r] 118# 119function qsort(a, l, r) { 120 i = l; 121 k = r+1; 122 item = a[l]; 123 for (;;) { 124 while (i < r) { 125 i++; 126 if (a[i] >= item) 127 break; 128 } 129 while (k > l) { 130 k--; 131 if (a[k] <= item) 132 break; 133 } 134 if (i >= k) 135 break; 136 t = a[i]; a[i] = a[k]; a[k] = t; 137 } 138 t = a[l]; a[l] = a[k]; a[k] = t; 139 if (k != 0 && l < k-1) 140 qsort(a, l, k-1); 141 if (k+1 < r) 142 qsort(a, k+1, r); 143} 144 145function cleanup(s) 146{ 147 gsub("\"", "", s); 148 gsub("^ +", "", s); 149 gsub(" +$", "", s); 150 return s; 151} 152 153function setupToLower() 154{ 155 upperRE = "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]"; 156 upper["A"] = "a"; upper["B"] = "b"; upper["C"] = "c"; 157 upper["D"] = "d"; upper["E"] = "e"; upper["F"] = "f"; 158 upper["G"] = "g"; upper["H"] = "h"; upper["I"] = "i"; 159 upper["J"] = "j"; upper["K"] = "k"; upper["L"] = "l"; 160 upper["M"] = "m"; upper["N"] = "n"; upper["O"] = "o"; 161 upper["P"] = "p"; upper["Q"] = "q"; upper["R"] = "r"; 162 upper["S"] = "s"; upper["T"] = "t"; upper["U"] = "u"; 163 upper["V"] = "v"; upper["W"] = "w"; upper["X"] = "x"; 164 upper["Y"] = "y"; upper["Z"] = "z"; 165} 166 167function toLower(s) 168{ 169 if (match(s, upperRE) != 0) { 170 do { 171 c = substr(s, RSTART, 1); 172 gsub(c, upper[c], s); 173 } while (match(s, upperRE)); 174 } 175 return s; 176} 177 178# 179# Accumulate a statistics record. 180# 181function acct(dest, status, datetime, commid) 182{ 183 split(datetime, a, " "); 184 split(a[1], b, "/"); 185 t = b[3] b[1] b[2] a[2]; 186 if (t < LASTt) 187 return; 188 status = cleanup(status); 189 if (length(status) > 11) { 190 msg = toLower(substr(status, 1, 11)); 191 if (callFailed[msg]) 192 return; 193 } 194 if (status != "") { 195 dest = cleanup(dest); 196 datetime = cleanup(datetime); 197 for (i = 0; i < nerrmsg; i++) 198 if (errmsg[i] == status) 199 break; 200 if (i == nerrmsg) 201 errmsg[nerrmsg++] = status; 202 if (errinfo[dest] == "") 203 errinfo[dest] = datetime "@" i "/" commid; 204 else 205 errinfo[dest] = errinfo[dest] "|" datetime "@" i "/" commid; 206 } 207} 208 209function printOldTranscript(canon, datetime) 210{ 211 gsub("[^0-9]", "", canon); 212 split(datetime, parts, " "); 213 split(parts[1], p, "/"); 214 cmd = sprintf(TRANSCRIPT, canon, months[p[1]], p[2], parts[2]); 215 system(cmd); 216} 217 218function printTranscript(commid) 219{ 220 printf "\n ---- Transcript of session follows ----\n\n" 221 comFile = "log/c" commid; 222 if ((getline <comFile) > 0) { 223 do { 224 if (index($0, "-- data") == 0) 225 print $0 226 } while ((getline <comFile) > 0); 227 close(comFile); 228 } else 229 print "No transcript available."; 230} 231 232BEGIN { FS="\t"; 233 callFailed["Busy signal"] = 1; 234 callFailed["Unknown pro"] = 1; 235 callFailed["No carrier "] = 1; 236 callFailed["No local di"] = 1; 237 callFailed["No answer f"] = 1; 238 callFailed["Job aborted"] = 1; 239 callFailed["Invalid dia"] = 1; 240 callFailed["Can not loc"] = 1; 241 months["01"] = "Jan"; months["02"] = "Feb"; 242 months["03"] = "Mar"; months["04"] = "Apr"; 243 months["05"] = "May"; months["06"] = "Jun"; 244 months["07"] = "Jul"; months["08"] = "Aug"; 245 months["09"] = "Sep"; months["10"] = "Oct"; 246 months["11"] = "Nov"; months["12"] = "Dec"; 247 248 split(LASTRUN, a, " "); 249 split(a[1], b, "/"); 250 LASTt = b[3] b[1] b[2] a[2]; 251 setupToLower(); 252 } 253$2 == "SEND" && NF == 9 { acct($4, $9, $1, ""); } 254$2 == "SEND" && NF == 11 { acct($5, $11, $1, ""); } 255$2 == "SEND" && NF == 12 { acct($6, $12, $1, ""); } 256$2 == "SEND" && NF == 13 { acct($7, $13, $1, $3); } 257$2 == "SEND" && NF == 14 { acct($7, $14, $1, $3); } 258END { nsorted = 0; 259 for (key in errinfo) 260 sorted[nsorted++] = key; 261 qsort(sorted, 0, nsorted-1); 262 for (k = 0; k < nsorted; k++) { 263 key = sorted[k]; 264 n = split(errinfo[key], a, "|"); 265 for (i = 1; i <= n; i++) { 266 if (split(a[i], b, "@") != 2) 267 continue; 268 if (split(b[2], d, "/") != 2) 269 continue; 270 printf "\n" 271 printf "To: %-16.16s Date: %s\n", key, b[1] 272 printf "Error: %s\n\n", errmsg[d[1]] 273 274 if (d[2] == "") 275 printOldTranscript(key, b[1]); 276 else 277 printTranscript(d[2]); 278 } 279 } 280 } 281EOF 282$AWK -f $AWKTMP -v LASTRUN="$LASTRUN" TRANSCRIPT="\ 283 LOGFILE=log/%s;\ 284 TMP=$TMPDIR/faxlog\$\$;\ 285 if [ -f \$LOGFILE ]; then\ 286 $SED -n -e '/%s %s %s.*SESSION BEGIN/,/SESSION END/p' \$LOGFILE |\ 287 $SED -e '/start.*timer/d'\ 288 -e '/stop.*timer/d'\ 289 -e '/-- data/d'\ 290 -e 's/^/ /' >\$TMP;\ 291 fi;\ 292 if [ -s \$TMP ]; then\ 293 $CAT \$TMP;\ 294 else\ 295 echo ' No transcript available.';\ 296 fi;\ 297 rm -f \$TMP\ 298 " $XFERLOG 299echo "" 300 301# 302# Collect phone numbers that haven't been called 303# in the last $AGEINFO days. We use this to clean 304# up the info files. 305# 306find info -type f -ctime +$AGEINFO -print >$JUNK 307 308if [ -s $JUNK ]; then 309echo "Purge remote device capabilities older than $AGEINFO days:" 310INFOTMP=info/tmp$$ 311for i in `$CAT $JUNK`; do 312 if $GREP '^&' $i >/dev/null 2>&1; then 313 echo " $i (saving locked down values)" 314 $SED '/^[^&]/d' $i > $INFOTMP 315 $MV $INFOTMP $i; $CHOWN ${FAXUSER} $i; $CHMOD ${LOGMODE} $i 316 elif [ -f $i ]; then 317 echo " $i" 318 $RM $i 319 fi 320done 321$RM $INFOTMP # for -n option 322else 323 echo "Nothing to purge in info directory." 324fi 325echo "" 326 327find log -type f -mtime +$AGELOG ! -name seqf -print >$JUNK 328if [ -s $JUNK ]; then 329 echo "Purge session logs older than $AGELOG days:" 330 for i in `$CAT $JUNK`; do 331 echo " Remove $i" 332 $RM $i 333 done 334 echo "" 335fi 336 337echo "Truncate merged session logs older than $AGELOG days:" 338 339$CAT>$AWKTMP<<'EOF' 340# 341# Setup date conversion data structures. 342# 343function setupDateTimeStuff() 344{ 345 Months["Jan"] = 0; Months["Feb"] = 1; Months["Mar"] = 2; 346 Months["Apr"] = 3; Months["May"] = 4; Months["Jun"] = 5; 347 Months["Jul"] = 6; Months["Aug"] = 7; Months["Sep"] = 8; 348 Months["Oct"] = 9; Months["Nov"] = 10; Months["Dec"] = 11; 349 350 daysInMonth[ 0] = 31; daysInMonth[ 1] = 28; daysInMonth[ 2] = 31; 351 daysInMonth[ 3] = 30; daysInMonth[ 4] = 31; daysInMonth[ 5] = 30; 352 daysInMonth[ 6] = 31; daysInMonth[ 7] = 31; daysInMonth[ 8] = 30; 353 daysInMonth[ 9] = 31; daysInMonth[10] = 30; daysInMonth[11] = 31; 354 355 FULLDAY = 24 * 60 * 60; 356} 357 358# 359# Convert MMM DD hh:mm:ss.ms to seconds. 360# NB: this does not deal with leap years. 361# 362function cvtTime(s) 363{ 364 mon = Months[substr(s, 1, 3)]; 365 yday = substr(s, 5, 2) - 1; 366 for (i = 0; i < mon; i++) 367 yday += daysInMonth[i]; 368 s = substr(s, 7); 369 t = i = 0; 370 for (n = split(s, a, ":"); i++ < n; ) 371 t = t*60 + a[i]; 372 return yday*FULLDAY + t; 373} 374 375BEGIN { setupDateTimeStuff(); 376 KEEP = cvtTime(TODAY) - AGE*FULLDAY; 377 lastRecord = "$" 378 } 379 { if (cvtTime($1 ":" $2 ":" $3) >= KEEP) { 380 lastRecord = NR; exit 381 } 382 } 383END { print lastRecord } 384EOF 385TODAY="`date +'%h %d %T'`" 386 387for i in log/[0-9]*; do 388 if [ -f $i ]; then 389 START=`$AWK -F: -f $AWKTMP -v TODAY="$TODAY" -v AGE=$AGELOG $i` 2>/dev/null 390 if [ "$START" != 1 ]; then 391 $SED 1,${START}d $i >$JUNK 392 if [ -s $JUNK ]; then 393 $MV $JUNK $i; $CHOWN ${FAXUSER} $i; $CHMOD ${LOGMODE} $i 394 ls -ls $i 395 else 396 echo " Remove empty $i" 397 $RM $i 398 fi 399 fi 400 fi 401done 402echo "" 403 404# 405# Purge old stuff from the receive queue. 406# 407find recvq -type f -mtime +$AGERCV ! -name seqf -print >$JUNK 408if [ -s $JUNK ]; then 409 echo "Purge received facsimile older than $AGERCV days:" 410 (for i in `$CAT $JUNK`; do 411 $SBIN/faxinfo $i 412 $RM $i >/dev/null 2>&1 413 done) | $AWK -F: ' 414/recvq.*/ { file=$1; } 415/Sender/ { sender = $2; } 416/Pages/ { pages = $2; } 417/Quality/ { quality = $2; } 418/Received/ { date = $2; 419 for (i = 3; i <= NF; i++) 420 date = date ":" $i; 421 printf " %-18.18s %21.21s %2d %8s%s\n", \ 422 file, sender, pages, quality, date; 423 } 424' 425else 426 echo "Nothing to purge in receive queue." 427fi 428echo "" 429 430# 431# Purge old stuff from the temp directory. 432# 433find tmp -type f -mtime +$AGETMP -print >$JUNK 434if [ -s $JUNK ]; then 435 echo "Purge tmp files older than $AGETMP days:" 436 for i in `$CAT $JUNK`; do 437 if [ -f $i ]; then 438 echo " Remove $i" 439 $RM $i 440 fi 441 done 442else 443 echo "Nothing to purge in the tmp directory." 444fi 445echo "" 446 447# 448# Note destinations whose jobs are currently being rejected. 449# 450find info cinfo -type f -newer $LAST -print 2>/dev/null >$JUNK 451if [ -s $JUNK ]; then 452 echo "Destinations being rejected (added since $LASTRUN):" 453 (for i in `$CAT $JUNK`; do $GREP "^rejectNotice:" $i; done) | $AWK -F: ' 454 { reason = $3; 455 for (i = 4; i <= NF; i++) 456 reason = reason ":" $i; 457 sub("^[ ]*", "", reason); 458 if (reason != "") { 459 sub(".*/", "", $1); 460 printf "Rejecting jobs to +%s because \"%s\".\n", \ 461 $1, reason; 462 } 463 } 464' 465fi 466 467$RM $LAST; eval $UPDATE 468 469# cleanup 470hfExit 0 471