1#!/usr/bin/perl 2eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' 3 if $running_under_some_shell; 4 # this emulates #! processing on NIH machines. 5 # (remove #! line above if indigestible) 6 7 8# accounting.pl.in,v 5.5 2000/06/09 21:55:12 papowell Exp 9# LPRng based accounting script. 10# Version Thu Apr 13 21:00:20 PDT 2000 11# LPRng 3.6.14 12# 13# stdin = /dev/null 14# stdout = accounting file 15# stderr = log file 16# 17# command line format: 18# accounting [start|end] [-options] [accounting file] 19# -Tdebug will turn on debugging 20# start - at start of job; scan accounting file, fix up, 21# put in START entry 22# end - at end of job; scan accounting file, fix up, 23# put in END entry 24# 25# Accounting File has format: 26# start '-ppagecounter' '-Athis' -P'that' <- startpage 27# ... 28# end '-ppagecounter' <- lastpage 29# END 't=$elapsed' 'p=$pages' 'q=$lastpage' 's=$startpage' 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time' 'S=$starttime' 30# --- end of a job 31# START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time' 32# start '-ppagecounter' '-Athis' -P'that' <- startpage 33# ... 34# end '-ppagecounter' <- lastpage 35# END 't=$elapsed' 'p=$pages' 'q=$lastpage' 's=$startpage' 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time' 'S=$starttime' 36# 37# We implement a stack based FSM to do the following: 38# Stack = NULL, state = FIND_START 39# 40# FIND_START - find the first START 41# START -> push START line 42# -> FIND_start 43# 44# FIND_start - find the first 'start' entry with page 45# start -> push start 46# -> FIND_end 47# 48# FIND_end - find the first 'end' entry for this job 49# end -> push end line 50# -> FOUND_end 51# START -> push START line 52# -> FIND_start 53# 54# FOUND_end - keep looking for 'end' entries 55# end -> pop stack 56# -> push end line 57# START -> push START line 58# -> FIND_start 59# 60# All states: 61# 62# END -> clear stack 63# -> FIND_START 64# START -> push START line 65# -> FIND_start 66# 67# 68# At end, if you do not have state FOUND_end you do not have a 69# valid accounting file so you have to wait. 70# Stack will have contents: 71# ((START* start )* end) * 72# ^ start page count 73# ^ end page count 74# we start from the bottom of the stack; 75# - end entry sets end page count for job 76# - start entry sets start page count for job 77# - START assigns pagecount as difference between start and end 78# end page count = start page count 79# 80# seq START START START start end 81# pages 0 0 end - start 82# 83# seq START START start1 START START start2 end2 84# pages 0 start2-start1 0 start2-end2 85# 86 87use strict; 88use Getopt::Std; 89use FileHandle; 90 91my($JFAIL, $JABORT, $JREMOVE, $JHOLD) = ( 32, 33, 34, 37); 92my(%opt, $action, $acct_h, $debug,$state,@stack); 93my($FIND_START,$FIND_start,$FIND_end,$FOUND_end)=(0,1,2,3,4,5); 94 95 96$debug = 0; 97 98# print STDERR "$0: '" . join("' '",@ARGV) . "'\n" if $debug; 99$action = ""; 100if( @ARGV ){ 101 if( $ARGV[0] !~ /^-/ ){ 102 $action = shift @ARGV; 103 } 104 print STDERR "action $action\n" if $debug; 105} 106if( $action ne "start" and $action ne "end" ){ 107 print STDERR "$0: invalid action '$action'\n"; 108 exit $JABORT; 109} 110 111# pull out the options 112 113getopts( 'A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:T:S:U:V:W:X:Y:Z:' 114. 'a:b:cd:e:f:g:h:i:j:k:l:m:n:o:p:q:r:t:s:u:v:w:x:y:z:', \%opt ); 115 116my($acct_file) = ""; 117if( @ARGV ){ 118 $acct_file = shift @ARGV; 119} else { 120 $acct_file = $opt{'a'}; 121} 122if( exists( $opt{T} ) && $opt{T} =~ m/debug/ ){ 123 $debug = 1; 124} 125if( !$acct_file ){ 126 print STDERR "$0: no accounting file\n"; 127 exit( $JABORT ); 128} 129 130my($time) = time; 131print STDERR "$0: $action 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n" if $debug; 132 133print STDERR "accounting file '$acct_file'\n" if $debug; 134 135if( $action eq "start" ){ 136 $acct_h = new FileHandle ">>$acct_file" ; 137 print $acct_h "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n"; 138 print STDERR "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n" if $debug; 139 $acct_h->close(); 140 exit 0; 141} 142 143$acct_h = new FileHandle "+<$acct_file" ; 144if( ! defined($acct_h) ){ 145 print STDERR "$0: cannot open $acct_file r/w - $!\n"; 146 exit( $JABORT ); 147} 148 149# now we read the last line from the accounting file 150my($size) = -s $acct_file; 151my($max_seek) = 2048; 152my($read_all) = 0; 153 154if( $size > $max_seek && not $acct_h->seek( -$max_seek, 2 ) 155){ 156 print STDERR "$0: lseek $acct_file failed - $!\n"; 157 exit( $JABORT ); 158} 159 160again: 161 162@stack = (); 163$state = $FIND_START; 164 165while( <$acct_h> ){ 166 chomp; 167 print STDERR "STATE '$state' LINE '$_'\n" if $debug; 168 print STDERR "STACK\n " . join("\n ",@stack) . "\n" if $debug; 169 if( /^END / ){ 170 $state = $FIND_START; 171 @stack = (); 172 next; 173 } 174 if( /^START / ){ 175 push @stack, $_; 176 $state = $FIND_start; 177 next; 178 } 179 if( $state == $FIND_start ){ 180 if( /^(file)?start .*-p\d+/ ){ 181 push @stack, $_; 182 $state = $FIND_end; 183 } 184 } elsif( $state == $FIND_end ){ 185 if( /^(file)?end .*-p\d+/ ){ 186 push @stack, $_; 187 $state = $FOUND_end; 188 } 189 } elsif( $state == $FOUND_end ){ 190 if( /^(file)?end .*-p\d+/ ){ 191 pop @stack; 192 push @stack, $_; 193 $state = $FOUND_end; 194 } 195 } 196} 197 198if( $state != $FOUND_end ){ 199 if( $read_all ){ 200 print STDERR "$0: did not find end record in file"; 201 exit 0; 202 } 203 $acct_h->close(); 204 $acct_h = new FileHandle "+<$acct_file" ; 205 if( ! defined($acct_h) ){ 206 print STDERR "$0: cannot open $acct_file r/w - $!\n"; 207 exit( $JABORT ); 208 } 209 $read_all = 1; 210} 211 212print STDERR "FINAL STATE '$state'\n" if $debug; 213print STDERR "FINAL STACK\n " . join("\n ",@stack) . "\n" if $debug; 214my($end_counter,$start_counter,$start_time,$count,$out,$elapsed); 215 216$end_counter = $start_counter = 0; 217$out = ""; 218 219for( my $i = @stack -1; $i >= 0 ; --$i ){ 220 $_ = $stack[$i]; 221 print STDERR "stack [$i] '$_'\n" if $debug; 222 if( /^[a-z]*end .*-p(\d+)/ ){ 223 $end_counter = $1; 224 } elsif( /^[a-z]*start .*-p(\d+)/ ){ 225 $start_counter = $1; 226 } elsif( /^START/ ){ 227 # we now update the accounting information 228 ($start_time) = /D=(\d+)/; 229 s/D=(\d+)/S=$1/; 230 $count = $end_counter - $start_counter; 231 $elapsed = $time - $start_time; 232 # you should put your make update record stuff here 233 s/^START/END 't=$elapsed' 'p=$count' 's=$start_counter' 'q=$end_counter' 'D=$time'/; 234 $out = $_ . "\n" . $out; 235 $end_counter = $start_counter; 236 $time = $start_time; 237 } 238} 239 240# update the file and the database at this point 241print STDERR "APPEND $out" if $debug; 242print $acct_h $out; 243