hello.
i have edited the accounting.pl file comes with LPRng. for the last few lines in the
script, it
says
# you should put your make update record stuff here.
the parts i added does user quota check, error report etc.
my question is - is it the right place to add it ? since i got mysterious errors by
doing that.
different jobs from diff users overlap each other and in term cause the error of the
update user
quota database.
eventually i move the part i added to the end of the accounting script. it seems all
okay now.
but did i do it right ?
i have the sample portion listed below and also the accounting.pl attached. please
take a look.
many thanks!
Qiang.
for( my $i = @stack -1; $i >= 0 ; --$i ){
$_ = $stack[$i];
print STDERR "stack [$i] '$_'\n" if $debug;
if( /^[a-z]*end .*-p(\d+)/ ){
$end_counter = $1;
} elsif( /^[a-z]*start .*-p(\d+)/ ){
$start_counter = $1;
} elsif( /^START/ ){
# we now update the accounting information
($start_time) = /D=(\d+)/;
s/D=(\d+)/S=$1/;
$count = $end_counter - $start_counter;
$elapsed = $time - $start_time;
# you should put your make update record stuff here
s/^START/END 't=$elapsed' 'p=$count' 's=$start_counter' 'q=$end_counter'
'D=$time'/;
$out = $_ . "\n" . $out;
$end_counter = $start_counter;
$time = $start_time;
## everything else go here ??
## such as check user quota, report error base on the checking ??
}
}
# update the file and the database at this point
print STDERR "APPEND $out" if $debug;
print $acct_h $out;
## now i have everything here - the end of the script.
______________________________________________________________________
Post your free ad now! http://personals.yahoo.ca#!/usr/bin/perl -w
#@ new features added: quota control
#@ windows popup error message
#eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
# if $running_under_some_shell;
# this emulates #! processing on NIH machines.
# (remove #! line above if indigestible)
# accounting.pl.in,v 5.5 2000/06/09 21:55:12 papowell Exp
# LPRng based accounting script.
# Version Thu Apr 13 21:00:20 PDT 2000
# LPRng 3.6.14
#
# stdin = /dev/null
# stdout = accounting file
# stderr = log file
#
# command line format:
# accounting [start|end] [-options] [accounting file]
# -Tdebug will turn on debugging
# start - at start of job; scan accounting file, fix up,
# put in START entry
# end - at end of job; scan accounting file, fix up,
# put in END entry
#
# Accounting File has format:
# start '-ppagecounter' '-Athis' -P'that' <- startpage
# ...
# end '-ppagecounter' <- lastpage
# 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'
# --- end of a job
# START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'
# start '-ppagecounter' '-Athis' -P'that' <- startpage
# ...
# end '-ppagecounter' <- lastpage
# 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'
#
# We implement a stack based FSM to do the following:
# Stack = NULL, state = FIND_START
#
# FIND_START - find the first START
# START -> push START line
# -> FIND_start
#
# FIND_start - find the first 'start' entry with page
# start -> push start
# -> FIND_end
#
# FIND_end - find the first 'end' entry for this job
# end -> push end line
# -> FOUND_end
# START -> push START line
# -> FIND_start
#
# FOUND_end - keep looking for 'end' entries
# end -> pop stack
# -> push end line
# START -> push START line
# -> FIND_start
#
# All states:
#
# END -> clear stack
# -> FIND_START
# START -> push START line
# -> FIND_start
#
#
# At end, if you do not have state FOUND_end you do not have a
# valid accounting file so you have to wait.
# Stack will have contents:
# ((START* start )* end) *
# ^ start page count
# ^ end page count
# we start from the bottom of the stack;
# - end entry sets end page count for job
# - start entry sets start page count for job
# - START assigns pagecount as difference between start and end
# end page count = start page count
#
# seq START START START start end
# pages 0 0 end - start
#
# seq START START start1 START START start2 end2
# pages 0 start2-start1 0 start2-end2
#
use strict;
use Getopt::Std;
use DBI;
use FileHandle;
use Socket;
my($JFAIL, $JABORT, $JREMOVE, $JHOLD) = ( 32, 33, 34, 37);
my(%opt, $action, $acct_h, $debug,$state,@stack);
my($FIND_START,$FIND_start,$FIND_end,$FOUND_end)=(0,1,2,3,4,5);
$debug = 0;
# print STDERR "$0: '" . join("' '",@ARGV) . "'\n" if $debug;
$action = "";
if( @ARGV ){
if( $ARGV[0] !~ /^-/ ){
$action = shift @ARGV;
}
print STDERR "action $action\n" if $debug;
}
if( $action ne "start" and $action ne "end" ){
print STDERR "$0: invalid action '$action'\n";
exit $JABORT;
}
# pull out the options
getopts( '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:'
. '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 );
my($acct_file) = "";
if( @ARGV ){
$acct_file = shift @ARGV;
} else {
$acct_file = $opt{'a'};
}
if( exists( $opt{T} ) && $opt{T} =~ m/debug/ ){
$debug = 1;
}
if( !$acct_file ){
print STDERR "$0: no accounting file\n";
exit( $JABORT );
}
my($time) = time;
print STDERR "$0: $action 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n"
if $debug;
print STDERR "accounting file '$acct_file'\n" if $debug;
#################################@@
## define global variable
#################################@@
my ($dbh, $sth, $pages, $uname, $host, $space, $jobhistory, $max_h, $remote_ip
,$remain, $rmssh);
$host=$opt{H};
$uname = $opt{n};
$jobhistory = "/usr/local/quotasrv/jobs.history"; # file to record print history.
$max_h = 20; # max history to keep
# parse ip and convert it to real remote host name.
$host = scalar gethostbyaddr(inet_aton($remote_ip),AF_INET)
if (($remote_ip) = $opt{C}=~ /REMOTEIP=(.*)/);
$rmssh = "/usr/local/quotasrv/rmssh";
$space = ' ';
###################################
if( $action eq "start" ){
##@@ connect and check user paper count.
##@@ abort if with insufficient paper
connect_db();
check_page_count();
$dbh->disconnect() if $dbh;
##@@ user quota check succeed at this point.
$acct_h = new FileHandle ">>$acct_file" ;
print $acct_h "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n";
print STDERR "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n"
if $debug;
$acct_h->close();
exit 0;
}
$acct_h = new FileHandle "+<$acct_file" ;
if( ! defined($acct_h) ){
print STDERR "$0: cannot open $acct_file r/w - $!\n";
exit( $JABORT );
}
# now we read the last line from the accounting file
my($size) = -s $acct_file;
my($max_seek) = 2048;
my($read_all) = 0;
if( $size > $max_seek && not $acct_h->seek( -$max_seek, 2 )
){
print STDERR "$0: lseek $acct_file failed - $!\n";
exit( $JABORT );
}
again:
@stack = ();
$state = $FIND_START;
while( <$acct_h> ){
chomp;
print STDERR "STATE '$state' LINE '$_'\n" if $debug;
print STDERR "STACK\n " . join("\n ",@stack) . "\n" if $debug;
if( /^END / ){
$state = $FIND_START;
@stack = ();
next;
}
if( /^START / ){
push @stack, $_;
$state = $FIND_start;
next;
}
if( $state == $FIND_start ){
if( /^(file)?start .*-p\d+/ ){
push @stack, $_;
$state = $FIND_end;
}
} elsif( $state == $FIND_end ){
if( /^(file)?end .*-p\d+/ ){
push @stack, $_;
$state = $FOUND_end;
}
} elsif( $state == $FOUND_end ){
if( /^(file)?end .*-p\d+/ ){
pop @stack;
push @stack, $_;
$state = $FOUND_end;
}
}
}
if( $state != $FOUND_end ){
if( $read_all ){
print STDERR "$0: did not find end record in file";
exit 0;
}
$acct_h->close();
$acct_h = new FileHandle "+<$acct_file" ;
if( ! defined($acct_h) ){
print STDERR "$0: cannot open $acct_file r/w - $!\n";
exit( $JABORT );
}
$read_all = 1;
}
print STDERR "FINAL STATE '$state'\n" if $debug;
print STDERR "FINAL STACK\n " . join("\n ",@stack) . "\n" if $debug;
my($end_counter,$start_counter,$start_time,$count,$out,$elapsed);
$end_counter = $start_counter = 0;
$out = "";
for( my $i = @stack -1; $i >= 0 ; --$i ){
$_ = $stack[$i];
print STDERR "stack [$i] '$_'\n" if $debug;
if( /^[a-z]*end .*-p(\d+)/ ){
$end_counter = $1;
} elsif( /^[a-z]*start .*-p(\d+)/ ){
$start_counter = $1;
} elsif( /^START/ ){
# we now update the accounting information
($start_time) = /D=(\d+)/;
s/D=(\d+)/S=$1/;
$count = $end_counter - $start_counter;
$elapsed = $time - $start_time;
# you should put your make update record stuff here
s/^START/END 't=$elapsed' 'p=$count' 's=$start_counter' 'q=$end_counter'
'D=$time'/;
$out = $_ . "\n" . $out;
$end_counter = $start_counter;
$time = $start_time;
}
}
# update the file and the database at this point
print STDERR "APPEND $out" if $debug;
print $acct_h $out;
print STDERR "$0: +++++++ $count pages printed ++++++\n";
#@@ count is negative. something fishy.
handle_error("Printing Halted.") if ($count=~/-/);
#######################@@
## update db
#######################@@
connect_db();
$pages = check_page_count();
$remain = $pages-$count;
update_db($remain);
## @@ write job history
write_history('');
$dbh->disconnect() if $dbh;
#------- the rest is the helper function -------#
#################################@@
# write job.history. err msg is passed as first argument
#################################@@
sub write_history {
my $err = shift;
# create history file if it doesn't exist.
unless (-e $jobhistory) {
open F,">$jobhistory";
close F ;
}
# read job history
open F,$jobhistory or warn "$?: read file error\n" and return;
my @history = <F>;
close F;
$count = 0 if $count !~ /\d/;
# set empty filed to html space for pretty printing ;)
# append current job into history
my $current = join '%', map {($_ eq "")?$space:$_}
($uname,$count,$remain,$host,scalar localtime($time),$err);
push @history,$current."\n";
# save history
# keep limited history records.
open F,">$jobhistory" or warn "$?:\n" and return;
if ($#history > $max_h) {
print F splice(@history,$#history-$max_h,$max_h);
} else {
print F @history;
}
close F;
}
#################################@@
# connect db and keep a handle around
#################################@@
sub connect_db {
# abort if user name not present.
handle_error("what is your name?")
unless( defined $uname );
# Perform db connection. abort on connection fail.
$dbh = DBI->connect( "dbi:mysql:dbname", "user", "password" , {
PrintError => 0,
RaiseError => 0
} );
handle_error("database connection failed")
unless ($dbh);
}
#################################@@
## return user quota from database
#################################@@
sub check_page_count {
# Prepare a SQL statement
$sth = $dbh->prepare( "SELECT quota FROM printquota WHERE username=?" );
# Execute the statement
handle_error("User Name Not Found")
unless $sth->execute($uname);
# Retrieve the page record
($pages) = $sth->fetchrow_array();
$sth->finish;
if ( $DBI::err ) {
handle_error("Error getting your quota");
} elsif ( !(defined $pages) ) {
handle_error("Error getting your quota")
} elsif ( $pages <= 0 ) {
handle_error("You have insufficient page quota");
} else {
return $pages;
}
}
##@@ update quota record.
sub update_db {
## page left after print.
my $pages = shift;
$sth = $dbh->prepare("UPDATE printquota set quota=? WHERE username=?");
handle_error("Update quota failed")
unless $sth->execute($pages,$uname);
}
##@@ handle database error and quota error.
##@@ send windows pop up message to user.
sub handle_error {
my ($msg) = @_;
# Disconnect from the database
$dbh->disconnect() if $dbh;
# send windows popup message to win user.
$msg = "'Error - ".$msg." '";
print STDERR "$0: sending popup msg to $uname\n$msg\n";
`$rmssh $uname $msg`;
write_history($msg);
exit( $JABORT );
}