home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume43
/
ftpmail
/
part02
/
dq.pl
< prev
next >
Wrap
Perl Script
|
1994-07-07
|
25KB
|
1,082 lines
#!/usr/bin/perl -s
# Very simple ftpmail system
# De-Queue a transfer and do it
# By Lee McLoughlin <lmjm@doc.ic.ac.uk>
# You can do what you like with this except claim that you wrote it or
# give copies with changes not approved by Lee. Neither Lee nor any other
# organisation can be held liable for any problems caused by the use or
# storage of this package.
#
# $Header: /a/swan/home/swan/staff/csg/lmjm/src/perl/mirror/src/ftpmail/RCS/dq.pl,v 1.24 1994/07/06 14:56:35 lmjm Exp lmjm $
# $Log: dq.pl,v $
# Revision 1.24 1994/07/06 14:56:35 lmjm
# Added multiple dq processes.
# Do test work in /usr/tmp as sun's tmpfs doesn't implement locks!
# Added vvencodeing.
#
# Revision 1.23 1994/05/02 18:10:47 lmjm
# Switched to lchat.
#
# Revision 1.22 1993/06/17 18:18:05 lmjm
# Use new internal buffer name for ftp'ftpbuf
#
# Revision 1.21 1993/06/17 09:58:17 lmjm
# Make sure that the target file the a command is trying to generate
# doesn't already exist.
# Allow for spaces in the command to run.
#
# Revision 1.20 1993/06/16 20:45:21 lmjm
# Fixup subject in case they contain 's
#
# Revision 1.19 1993/06/16 20:13:27 lmjm
# Don't use system so that filenames containing single quotes can be
# handled correctly.
#
# Revision 1.18 1993/05/13 21:23:03 lmjm
# Change all atob to be btoa
#
# Revision 1.17 1993/05/11 20:07:56 lmjm
# Init right variable for btoa!
#
# Revision 1.16 1993/04/28 18:19:19 lmjm
# From chris, corrected filename in mime message.
#
# Revision 1.15 1993/04/25 20:27:49 lmjm
# Use own split routine to implement size paramater.
#
# Revision 1.14 1993/04/25 14:38:52 lmjm
# Dont requeue jobs that have been tried too many times.
#
# Revision 1.13 1993/04/25 14:14:59 lmjm
# Conform to mime rules on filenames.
#
# Revision 1.12 1993/04/25 13:18:01 lmjm
# Moved signal handling into ftp'pl.
#
# Revision 1.11 1993/04/23 23:27:04 lmjm
# Massive renaming for sys5.
#
# Revision 1.10 1993/04/23 20:03:16 lmjm
# Don't use STDIN, STDOUT or STDERR.
# Use own verion of library routines before any others.
# Log the pid when sleeping to make it easier to kill.
#
# Revision 1.9 1993/04/23 17:23:37 lmjm
# Renamed ftpmail-local-config.pl to ftpmail-config.pl
# Made pathnames relative to $ftpmail_dir.
# Moved the check_tries handle to the start of the job.
#
# Revision 1.8 1993/04/21 10:58:38 lmjm
# Added jobid to response.
#
# Revision 1.7 1993/04/20 20:15:37 lmjm
# Turned printing job to mail into a library routine.
#
# Revision 1.6 1993/04/15 18:07:14 lmjm
# Scan queue in perl not by calling ls.
# Added more logging.
# Done inplace change the comms variable.
# Dump stdout onto stderr when playing with fd's before mailing.
# Don't send a completed message if job was zapped.
#
# Revision 1.5 1993/04/15 14:17:43 lmjm
# log when quitting.
# Something is adding spaces to the start of job lines - zap them for now.
# Don't requeue overtried jobs.
# Added some patches from Christophe.
#
# Revision 1.4 1993/04/13 10:34:36 lmjm
# Lots of little cleanups in logging and response messages
#
# Revision 1.3 1993/03/30 20:32:19 lmjm
# Must have an ftpmail account whose home directory everything is in.
# New -test option that uses /tmp/ftpmail-test
# Simplified the parsing of the jobs.
# ftpmail-dq keeps running till shutdown
# Changed the mime code, now handles force better.
# Moved the close( STDOUT ) to where it doesn't cause mail to fail!
#
# Revision 1.2 1993/03/23 21:40:10 lmjm
# Fixed all those little internal problems.
# Rewrote the setup routines.
# Added gzip and btoa support
# Added mime, multipart and all sorts of other good things based on work by
# Christophe.Wolfhugel@grasp.insa-lyon.fr
#
$ftpmail = 'ftpmail';
if( $test ){
$ftpmail_dir = '/usr/tmp/ftpmail-test';
}
else {
# The ftpmail_dir is the home directory of ftpmail.
$ftpmail_dir = (getpwnam( $ftpmail ))[ 7 ];
}
if( ! $ftpmail_dir ){
die "No home directory for ftpmail\n";
}
if( ! -d $ftpmail_dir ){
die "no such directory as $ftpmail_dir\n";
}
chdir( $ftpmail_dir ) || die "cannot chdir to $ftpmail_dir\n";
# All the auxillary scripts come from ftpmail's home dir.
unshift( @INC, '.' );
require 'config.pl';
require 'support.pl';
require 'ftp.pl';
require 'lchat.pl';
require 'seize.pl';
# Don't leave files around writable
umask( 077 );
# It is more meaningful for the user to say 1 but in here zero is
# more useful.
$max_dqs--;
sub handler {
local( $sig ) = @_;
local( $msg ) = "Caught a SIG$sig shutting down";
warn $msg;
&log( $msg );
&empty_slot();
exit( 0 );
}
$SIG{ 'PIPE' } = 'handler';
# Only allow jobs to be updated. (In case q.pl has deleted it.)
$updating_only = 1;
# Mime types
$partial = 1;
$octets = 2;
$text = 3;
# Counters for Mime multiparts;
$partno = 0;
$nparts = 0;
# part id
$id = '';
# Use when multi dq's are allowed to show which is which.
if( $slot ){
$slot = shift;
}
else {
$slot = 0;
}
if( $slot ){
$tmpdir .= $slot;
# Only create the additional tmpdirs the base one
# should have been created on installation.
if( ! -d $tmpdir ){
mkdir( $tmpdir, 0700 );
}
# last_started is checked to see if it is time to
# try spawning new dq's. This delays the check in all the
# secondary dq's till after the main dq has started up the
# initial batch.
$last_started = time;
}
$incoming = "$tmpdir/$incoming";
$xferlog = "$tmpdir/$xferlog";
&log( "tmpdir $tmpdir, incoming $incoming, xferlog $xferlog" ) if $verbose;
&fill_slot();
&log( "starting" );
&trap_signals();
&ftp'set_timeout( 120 ); # Use long timeouts
&ftp'set_signals( "main'log" ); # Beware of SIGPIPES
&ftp'debug( 1 );
while( ! -f $ftpmail_scan_end ){
&start_dqs();
&scan_q();
&process_qfiles();
if( $between_runs_pause && ! -f $ftpmail_scan_end ){
&log( "nothing to do - sleeping pid=$$" ) if $verbose;
sleep( $between_runs_pause );
}
}
&log( "found $ftpmail_scan_end so quiting" );
&empty_slot();
exit( 0 );
sub process_qfiles
{
local( $qf );
$processed = 0;
foreach $qf ( @qfiles ){
if( $processed > $max_per_scan || -f $ftpmail_scan_end ){
last;
}
$qfile = "$quedir/$qf";
next if ! &lock_qf();
&process_qfile();
&unlock_qf();
}
}
sub process_qfile
{
# Only give up if a serious error occurs - otherwise retry.
$give_up = 0;
# Force encoding?
$force = 0;
# filters
$compress_it = 0;
$gzip_it = 0;
$uuencode_it = 0;
$btoa_it = 0;
$mime_it = 0;
$vvencode_it = 0;
# Set the max file size from the local config file.
$max_file_size = $def_max_size;
# When running in non-interactive mode this is the
# jobs to do.
@mailback = (); # an elem is true if @comms elem needs to be mailed
@filename = (); # filename to report in messages
@filters = (); # filters to apply to file.
# input lines
# Strip out the informational lines and stick the rest into @comms
@comms = ();
seek( qfile, 0, 0 );
while( <qfile> ){
chop;
# This s/.. is to get around an old bug - shouldn't be needed now
s/^\s*//;
last if /^$/;
if( /^reply-to (.+)$/ ){
$reply_to = $1;
next;
}
elsif( /^tries (\d+)( (\d+))?$/ ){
$tries = $1;
$whenretry = $2;
next;
}
elsif( /^open (\S+)/ ){
# remember the site.
$site = $1;
}
push( @comms, $_ );
}
# TODO
# To avoid flooding a site.
# if( ! &lock_site( $site ) ){
# return;
# }
if( ! &check_tries() ){
# Too many - job has been dequeued
return;
}
if( $whenretry > time() ){
&log( "too early to process $qfile" ) if $verbose;
return;
}
&log( "starting job: $qfile" );
$processed++;
$tries++;
# On failure don't retry the job for progressively
# longer times.
$whenretry = time() + $retry_pause;
&update_entry();
# Send all ftp errors into xferlog
open( out, ">$xferlog" ) || &fatal( "Cannot create $xferlog" );
$ftp'showfd = "main'out";
$mailing_back = $immediate;
&ftp_to_site();
close( out );
if( ! $immediate ){
# mail out all the completed get/dir/ls
$mailing_back = 1;
for( $cmdno = 0; $cmdno <= $#comms; $cmdno++ ){
if( $mailback[ $cmdno ] ){
&mail_back();
}
}
}
&finish_entry();
unlink( $xferlog );
}
sub ftp_to_site
{
local( $mode ) = undef;
local( $open ) = undef;
# All done?
$job_done = 0;
# Make sure connection is shut down.
&chat'close();
&log( "$qfile: tries=$tries [$max_tries] reply_to=$reply_to" );
# process commands
$site = $user = $pass = '';
for( $cmdno = 0; $cmdno <= $#comms; $cmdno++ ){
$_ = $comm = $comms[ $cmdno ];
if( /^DONE|FAILED/ ){
&log( "skipping: $_" );
}
elsif( /^open (.+)$/i ){
$site = $1;
}
elsif( /^user (.+)$/i ){
$user = $1;
}
elsif( /^pass (.+)$/i ){
$pass = $1;
&log( "connecting to $site" );
$res = &ftp'open( $site, $ftp_port, $retry_call, $retry_attempts );
if( $res != 1 ){
&pralog( "Failed to connect" );
last;
}
&log( "logging in as $user $pass" );
if( ! &ftp'login( $user, $pass ) ){
&pralog( "Failed to login" );
&ftp'close();
last;
}
$pwd = &ftp'pwd();
&log( "pwd=$pwd" );
# Default type is binary
if( ! defined( $mode ) ){
$mode = 'I';
}
if( ! &ftp'type( $mode ) ){
&pralog( "Failed to set type to binary" );
}
}
elsif( /^mode (.+)$/i ){
$mode = $1 eq 'binary' ? 'I' : 'A';
if( defined( $open ) ){
if( ! &ftp'type( $mode ) ){
&pralog( "Failed to set type to $1" );
}
}
}
elsif( /^cd (.+)$/i ){
$dir = $1;
&log( "cwd $dir" );
if( ! &ftp'cwd( $dir ) ){
&pralog( "Failed to change to remote directory: $dir" );
$give_up = 1;
last;
}
$pwd = &ftp'pwd();
&log( "pwd=$pwd" );
}
elsif( /^(compress|gzip)( no)?$/i ){
eval "\$$1_it = 1";
&log( "$1_it set" ) if $verbose;
}
elsif( /^(force )?(compress|gzip|uuencode|btoa|mime)( no)?$/i ){
$force = $1 eq 'force ';
&log( "force set" ) if $force && $verbose;
eval "\$$2_it = 1";
&log( "$2_it set" ) if $verbose;
}
elsif( /^size (\d+)/i ){
$max_file_size = $1;
}
elsif( /^(ls|dir) (.*)/i ){
$path = $2;
local( $old_mode );
&log( $comm );
if( $mode ne 'A' ){
if( &ftp'type( 'A' ) ){
$old_mode = $mode;
}
else {
&pralog( "Cannot set type to ascii for dir listing, trying to carry on" );
}
}
if( ! &ftp'dir_open( $path ) ){
&pralog( "Cannot get remote directory listing because: $ftp'response" );
$give_up = 1;
}
local( $in ) = "$incoming.$cmdno";
open( IN, ">$in" ) || &fail( "cannot create $in" );
# Suck back dir listing output into a temp file
while( ($len = &ftp'read()) > 0 ){
$bytes += $len;
if( $mode eq 'A' ){
$ftp'ftpbuf =~ s/\r//g;
}
print IN $ftp'ftpbuf;
}
close( IN );
&ftp'dir_close();
if( defined( $old_mode ) && ! &ftp'type( $old_mode ) ){
&pralog( "Cannot reset type after dir" );
}
if( $len < 0 ){
&pralog( "\nTimed out reading data" );
last;
}
$filename = "directory-listing";
&mail_back();
}
elsif( /^get (.+)/i ){
local( $in ) = "$incoming.$cmdno";
$filename = $1;
&log( $comm );
if( ! &ftp'get( $filename, $in, 0 ) ){
$comms[ $cmdno ] = "FAILED $comms[ $cmdno ]";
&pralog( "failed to get $filename" );
}
else {
&mail_back();
}
}
else {
&log( "Internal error: found command: $_" );
}
if( $cmdno == $#comms ){
$job_done = 1;
}
}
&log( "job done" );
&ftp'quit();
}
# Check out the tries counter. If too many then dequeue job.
# Return 1 if ok.
sub check_tries
{
# Tries counts from 0
if( $tries < $max_tries ){
return 1;
}
unlink( $qfile );
&log( "Job $qfile failed and dequeued" );
&respond( "failed", "Your job failed to be fully processed after too many tries ($tries)" );
$job_done = 1;
return 0;
}
# This should check error status
sub mail_back
{
if( ! $mailing_back ){
# Not mailing stuff back yet, just remember it.
$mailback[ $cmdno ] = 1;
$filename[ $cmdno ] = $filename;
$pwd[ $cmdno ] = $pwd;
local( $f ) = '';
$f .= 'c' if $compress_it;
$f .= 'g' if $gzip_it;
$f .= 'a' if $btoa_it;
$f .= 'u' if $uuencode_it;
$f .= 'm' if $mime_it;
$f .= 'v' if $vvencode_it;
$f .= 'F' if $force;
$filters[ $cmdno ] = $f;
&log( "delayed mail back: $pwd $filename $f" ) if $verbose;
return;
}
local( $note, $suff, $infile, $command );
$infile = "$incoming.$cmdno";
if( ! $immediate ){
$command = $comms[ $cmdno ];
$filename = $filename[ $cmdno ];
$pwd = $pwd[ $cmdno ];
local( $f ) = $filters[ $cmdno ];
$compress_it = ($f =~ /c/);
$gzip_it = ($f =~ /g/);
$btoa_it = ($f =~ /a/);
$uuencode_it = ($f =~ /u/);
$mime_it = ($f =~ /m/);
$vvencode_it = ($f =~ /v/);
$force_it = ($f =~ /F/);
&log( "NOW mailing back: $pwd $filename $f" ) if $verbose;
}
$partno = 0;
$nparts = 0;
$id = '';
$cte = '';
local( $report ) = "$site:$pwd";
if( $command =~ /get/ ){
$report .= "/$filename";
}
if( $compress_it ){
&log( "compressing $infile" );
unlink( "$infile.Z" );
&runcmd( $compress, $infile, "", "" );
if( -r "$infile.Z" ){
$note = ' compressed';
$infile .= '.Z';
$suff = '.Z';
}
}
elsif( $gzip_it ){
&log( "gzip $infile" );
unlink( "$infile.gz" );
&runcmd( $gzip, "", $infile, "$infile.gz" );
if( -r "$infile.gz" ){
$note = ' gzipped';
$infile .= '.gz';
$suff = '.gz';
}
}
$is_text = (-T $infile);
if( $force || $mime_it || ! $is_text ){
if( !$mime_it && !$uuencode_it && !$btoa_it && !$vvencode_it ){
&log( "non text but no method, using uuencode" ) if $verbose;
$uuencode_it = 1;
}
# Convert binary file using given filter
# (Execpt mime, only encode if you have to)
if( $mime_it && ($force || !$is_text) ){
&log( "mmencoding $infile" );
unlink( "$infile.mm" );
&runcmd( $mmencode, "", $infile, "$infile.mm" );
unlink( $infile );
$note .= ' mmencoded';
$infile .= '.mm';
$cte = 'base64';
}
elsif( $uuencode_it ){
&log( "uuencoding $infile" );
unlink( "$infile.uu" );
&runcmd( $uuencode, "$filename$suff", $infile, "$infile.uu" );
unlink( $infile );
$note .= ' uuencoded';
$infile .= '.uu';
}
elsif( $btoa_it ){
&log( "btoa-ing $infile" );
unlink( "$infile.btoa" );
&runcmd( $btoa, "", $infile, "$infile.btoa" );
unlink( $infile );
$note .= ' btoa';
$infile .= '.btoa';
}
elsif( $vvencode_it ){
&log( "vvencoding $infile" );
unlink( "$infile.vv" );
&runcmd( $vvencode, "", $infile, "$infile.vv" );
unlink( $infile );
$note .= ' vvencode';
$infile .= '.vv';
}
}
$report .= $note . " ($command)";
if( $mime_it ){
$nparts = 0;
$partno = 0;
$id = "ftpmail-" . time . "-$$@$hostname";
}
local( $file_size ) = &size( $infile );
if( $file_size > $max_processing_size ){
local( $msg ) = "file size exceeded max. processing size ($max_processing_size), canceling job";
&log( $msg );
&log( $report );
&mailit( 'aborting job: too big', $msg );
}
elsif( $file_size >= $max_file_size ){
# Split the file up and mail back the parts
# Allow for mail headers. If you have to pay
# by size then it is important not to accidentally go over
# limit.
$nparts = &tsplit( $infile, $max_file_size - $mail_overhead );
&log( "tsplit $infile $max_file_size into $nparts" );
for( $partno = 1; $partno <= $nparts; $partno++ ){
local( $file ) = "$tmpdir/part$partno";
local( $reppart ) = sprintf( "[%03d of %03d]",
$partno, $nparts );
&mailit( "$reppart $report", $file, 1 );
unlink( $file );
}
}
else {
&mailit( $report, $infile, 1 );
}
unlink( $infile );
$comms[ $cmdno ] = "DONE $comms[ $cmdno ]";
&update_entry();
}
sub mime_header
{
local( $kind, $file ) = @_;
print MAIL "Mime-Version: $mime_version\n";
if( $kind == $text ){
print MAIL "Content-Type: text/plain; charset=US-ASCII\n";
}
elsif( $kind == $partial ){
print MAIL "Content-Type: message/partial;\n";
print MAIL " id=\"$id\"; number=$partno; total=$nparts\n";
}
elsif( $kind == $octets ){
print MAIL "Content-Type: application/octet-stream;\n";
print MAIL " name=\"$filename$suff\"\n";
}
if( $cte ){
print MAIL "Content-Transfer-Encoding: $cte\n";
}
}
# A Mime message has extra header fields
# and if the message is a (mime) split up message then whole
# mime message is chopped up and sent as a series of message/partial messages
sub mailit
{
local( $subject, $file, $isfile ) = @_;
local( $size );
if( $mail_cmd =~ /sendmail/ ){
open( MAIL, "| $mail_cmd" ) || &fail( "Can't start $mail_cmd" );
print MAIL "To: $reply_to\n";
print MAIL "Subject: $subject\n";
print MAIL "Precedence: bulk\n";
if( $mime_it ){
# cte is set if this file was encoded
local( $kind ) = $cte ? $octets : $text;
if( $nparts != 0 ){
# Don't output the cte except in the
# inner message.
local( $real_cte ) = $cte;
$cte = '';
&mime_header( $partial, $file );
$cte = $real_cte;
if( $partno == 1 ){
# Output the header for the
# inner message.
print MAIL "\n";
&mime_header( $kind, $file );
}
}
else {
&mime_header( $kind, $file );
}
}
print MAIL "\n";
}
else {
local( $subj ) = $subject;
$subj =~ s/'/_/g;
open( MAIL, "| $mail_cmd -s '$subj' '$reply_to' >/dev/null 2>&1" ) ||
&fail( "Can't start $mail_cmd" );
}
if( ! $isfile ){
# $file is the string to send
print MAIL $file;
$size = length( $file );
}
else {
open( IN, $file ) || &fail( "Can't reopen $file" );
while( <IN> ){
print MAIL;
}
close( IN );
$size = -s $file;
}
close( MAIL );
&log( "mailit $size $reply_to $subject" );
sleep( $mail_pause ) if $mail_pause;
}
sub size
{
local( $file ) = @_;
local( $dev,$ino,$fmode,$nlink,$uid,$gid,$rdev,$ssize,
$atime,$mtime,$ctime,$blksize,$blocks ) =
stat( $file );
return( $ssize );
}
# Output a standard lump of messages
sub respond
{
local( $status, $msg ) = @_;
local( $c );
local( $subject ) = "ftpmail job $status";
&log( "respond $reply_to $subject" );
if( $mail_cmd =~ /sendmail/ ){
open( MAIL, "| $mail_cmd" ) || &fail( "Can't start $mail_cmd" );
print MAIL "To: $reply_to\n";
print MAIL "Subject: $subject\n\n";
}
else {
local( $subj ) = $subject;
$subj =~ s/'/_/g;
open( MAIL, "| $mail_cmd -s '$subj' '$reply_to' >/dev/null 2>&1" ) ||
&fail( "Can't start $mail_cmd" );
}
print MAIL "$ftpmail_response\n";
if( -f $xferlog ){
print MAIL "$msg\nYour job was (lines beginning DONE show completed transfers):\n";
&mail_comms();
print MAIL "\nThe ftp log contains:\n";
open( LOG, $xferlog ) || &fail( "cannot reopen $xferlog" );
local( @log ) = <LOG>;
close( LOG );
print MAIL join( "\n", "@log" );
print MAIL "\n";
}
close MAIL;
sleep( $mail_pause ) if $mail_pause;
}
sub finish_entry
{
if( $job_done ){
if( -f $qfile ){
# The job is done and hasn't been deleted due to too many tries
unlink( $qfile );
&log( "deleting $qfile" );
&respond( "completed", "" );
}
}
elsif( $give_up ){
unlink( $qfile );
&log( "Job $qfile failed when a serious error occured" );
&respond( "failed", "An unrecoverable error occured so your job was aborted" );
}
else {
if( ! &check_tries() ){
return;
}
&log( "Requeing job: $qfile" );
&respond( "queueing for retry $qfile", "" );
&update_entry();
}
}
sub start_dqs
{
local( $now ) = time;
local( @empty );
local( $min_restart ) = 5 * 60; # Only recheck once every 5 mins.
return if ($now - $min_restart) <= $last_started;
$last_started = $now;
&slots_lock_read();
local( $i );
for( $i = 0; $i <= $max_dqs; $i++ ){
$empty[ $i ] = ($pid[ $i ] == 0 || kill( 0, $pid[ $i ] ) <= 0);
}
for( $i = 0; $i <= $max_dqs; $i++ ){
next unless $empty[ $i ];
# Hmm an empty slot or one where the process has died.
# Better fill it in!
&log( "proc $$ slot $slot spawning $dq_path -slot $i" );
local( $flags ) = '';
$flags = ' -test' if $test;
$flags .= ' -verbose' if $verbose;
system( "$dq_path $flags -slot $i &" );
}
&slots_write_unlock();
}
sub fill_slot
{
&slots_lock_read();
if( $pid[ $slot ] != 0 ){
# Maybe the proc in that slot has died?
if( kill( 0, $pid[ $slot ] ) == 1 ){
if( $max_dqs == 0 ){
&log( "queue already locked by $pid[ $slot ]" );
}
else {
&log( "slot $slot is already filled by $pid[ $slot ]" );
}
exit( 0 );
}
}
$pid[ $slot ] = $$;
$time[ $slot ] = time;
&slots_write_unlock();
}
sub empty_slot
{
&slots_lock_read();
$pid[ $slot ] = 0;
$time[ $slot ] = time;
$site[ $slot ] = '';
&slots_write_unlock();
}
sub slots_lock_read
{
local( $mode, $msg );
if( ! -r $dq_stats ){
$mode = "+>>";
$msg = "create";
}
else {
$mode = "+<";
$msg = "open";
}
if( ! open( dqs, "$mode$dq_stats" ) ){
&log( "Cannot $msg $dq_stats, aborting" );
exit( 0 );
}
if( &seize( dqs, &LOCK_EX ) !~ /^0/ ){
&log( "Cannot lock $dq_stats, aborting" );
exit( 0 );
}
local( $i ) = 0;
for( $i = 0; $i <= $max_dqs; $i++ ){
$pid[ $i ] = $time[ $i ] = 0;
$site[ $i ] = '';
}
seek( dqs, 0, 0 );
$i = 0;
while( <dqs> ){
last if /^$/;
chop;
($pid[ $i ], $time[ $i ], $site[ $i ]) = split(/:/);
last if $i++ > $max_dqs;
}
}
sub slots_write_unlock
{
local( $i, $len );
seek( dqs, 0, 0 );
for( $i = 0; $i <= $max_dqs; $i++ ){
local( $line ) = "$pid[ $i ]:$time[ $i ]:$site[ $i ]\n";
print dqs $line;
$len += length( $line );
}
print dqs "\n";
eval "truncate( dqs, $len )";
# closing will also unlock.
close( dqs );
}
sub lock_qf
{
if( ! open( qfile, "+<$qfile" ) ){
return 0;
}
# return (&seize( qfile, &LOCK_EX | &LOCK_NB ) =~ /^0/);
$ret = &seize( qfile, &LOCK_EX | &LOCK_NB );
$r = ($ret =~ /^0/);
return $r;
}
sub unlock_qf
{
# TODO: unlock the site
# closing will also unlock.
close( qfile );
}
sub lock_retry
{
local( $fh ) = @_;
local( $max_tries ) = 5;
local( $pause ) = 1;
local( $tries ) = 0;
local( $lk );
while( ($lk = &seize( $fh, &LOCK_EX | &LOCK_NB )) !~ /^0/ ){
if( $tries++ < $max_tries ){
sleep( $pause );
}
else {
$lk = -1;
last;
}
}
return $lk >= 0;
}
sub shutdown
{
&log( "Received HUP so shutting down" );
&empty_slot();
exit( 0 );
}
sub trap_signals
{
$SIG{ 'HUP' } = "main\'shutdown";
}
# print to out and log it.
sub pralog
{
local( $msg ) = @_;
print out "$msg\n";
&log( $msg );
}
# Split the file up into chunks size big, remove the
# original and return the number of parts
sub tsplit
{
local( $file, $size ) = @_;
local( $buffer, $in, $sofar );
local( $index ) = 0;
local( $part );
open( f, $file ) || &fatal( "Cannot open $file to split" );
$sofar = $size;
while( <f> ){
$in = length( $_ );
if( $sofar >= $size ){
if( $part ){
close( part );
}
$index++;
$part = "$tmpdir/part$index";
unlink( $part );
open( part, ">$part" ) || &fatal( "cannot create $part" );
$sofar = 0;
}
print part;
$sofar += $in;
}
close( part );
close( f );
return $index;
}
# Split the file up into chunks size big, remove the
# original and return the number of parts
sub binsplit
{
local( $file, $size ) = @_;
local( $bufsiz ) = 512;
local( $buffer, $in, $sofar );
local( $index ) = 0;
local( $part );
open( f, $file ) || &fatal( "Cannot open $file to split" );
$sofar = $size; # Force a new file
while( ($in = sysread( f, $buffer, $bufsiz )) > 0 ){
if( $sofar >= $size ){
if( $part ){
close( part );
}
$index++;
$part = "$tmpdir/part$index";
unlink( $part );
open( part, ">$part" ) || &fatal( "cannot create $part" );
$sofar = 0;
}
if( ($out = syswrite( part, $buffer, $in )) != $in ){
&fatal( "Failed to write data to $part" );
}
$sofar += $in;
}
close( part );
close( f );
return $index;
}
# Run a command
# like system( "$cmd $cmdargs < $infile > $outfile" ) but without
# shell expansion happening.
# Each space seperated part of $cmd is passed as a seperate arg but
# otherwise spaces are preserved.
sub runcmd
{
local( $cmd, $cmdargs, $infile, $outfile ) = @_;
local( $child_pid, $dying );
local( @cmd);
@cmd = split( /\s+/, $cmd );
if( $cmdargs ){
push( @cmd, $cmdargs );
}
if( ($child_pid = fork()) < 0 ){
&fail( "Couldn't fork: $!" );
}
elsif( $child_pid == 0 ){
if( $infile ){
open( STDIN, $infile ) ||
&fail( "Cannot open $infile: $!" );
}
else {
close( STDIN );
}
if( $outfile ){
open( STDOUT, ">$outfile" ) ||
&fail( "Cannot write to $outfile: $!" );
}
else {
close( STDOUT );
}
exec @cmd;
&fail( "failed to exec @cmd: $!" );
}
# Wait for the child to terminate.
while( ($dying = wait()) != -1 && ($dying != $child_pid) ){
;
}
}