sub pid_file_cleanup { # Call new pid_file_cleanup routine w/7 day expiration (hard coded into # old routine...). Could emulate old return status, too, if I remembered # how it actually returned! No return status was documented - now # returns new status string. &pid_file_cleanup2(shift(@_),7,@_); } sub pid_file_cleanup2 { # An implicit argument is environment variable PID_FILE_CLEANUP_DEBUG. # If set, this program does not delete any files, but prints the ones it # would have deleted # Explicit arguments are: # 1) A "temp indicator"; a string whose presence in a file spec # means that the file is to be treated by this routine. # Often a pid. If a null string, no wildcarding (see below) # is done. Only the files in the array in arg 3 are considered # for cleanup. # 2) The minimum number of days the files should live. Fractions of # days are allowed. # 3) An array of filenames to be considered for cleanup # Returns a string. # The last "word" (blank-separated) of the return string is # 3 numbers separated by 2 slashes. The first number is # the number of files considered for deletion. The second number is # the number of files deleted. The third number is the number of # attempts at deletion which failed. # The return string may begin with 3 consecutive asterisks, indicating # a problem. The string between the asterisks and the count triple # described above defines the problem. In case of failures to # delete files, the reason for the last such failure is returned. ### # Clean up temp files (does not do temp directories) # We could just delete the ones in @temp_files, but that would be # too easy. Idea is that we'd like the files to stay around a while # in case we want to look at them, but delete them if they aren't looked # at. Rather than trying to save a list of exactly what's eligible for # deletion, we apply the following algorithm: # 1) Only look at files in @temp_files # 2) Of those, only look at the ones with this proc's PID in them # Assumption is that presence of PID is indicator that file really # is temporary & that a bunch of them might exist. # Further assumption is that PID only appears once (fails safe- # no such files will be deleted unless PIDs have recycled) # 3) Logically replace PID with * # 4) Look at those files, and delete the ones that haven't been # accessed in the minimum number of days. Use # access date to prolong life of those that have been looked at # after creation... which are probably the ones we REALLY want # to save! # Modification history not clear. There were substantial modifications # 13 May 98, but this version seems better. There was a "parallel" mod # 24 Feb 98 to return if no temp files were passed in. # 23 Nov 08. Version 2.0c WJS # One invokation of proc_dir did not have an arg. Probably been that # way since pid_file_cleanup_2 in Apr 99. Guess most times that code # path wasn't taken! # [Begin v 2.0c] # 16 Nov 07. Version 2.0b WJS # perl warning that defined(@array) is deprecated. Switch to # defined($array[0]), empirically determined. Tests on VAX showed that # @array = (); statement made no difference w/regard to "defined" state # of array. Also, use of @array w/o above statement did not get # "only one use" diagnostic # [Begin v 2.0b] # 16 Mar 05. Version 2.0a WJS # valid_number.pl now in wjs_web_perl_utilities.pl # [Begin v 2.0a] # 30 Jul 99. Version 2.0 WJS # Bug fix - procdir was using $_ instead of $file # 23 Apr 99. Version 2.0 WJS # Remove expiration date part of expiration arg - bad idea. Expiration # date is a delete/nodelete flag that caller can determine. If delete, # call this routine w/ 0 days expiration. If no delete, don't call! # Handle illegal expiration length and null temp file indicator with # more intelligence. The former would have resulted in a "all files # expired" condition. The latter would have caused all files to be be # considered for deletion. # Return some info. # [Changes remove need for DateTime::Precise.pm] # 17 Apr 99. Version 2.0 WJS # Add entry pid_file_cleanup2 to handle exp arg. # 14 Apr 99. Version 2.0 WJS # Add expiration args. # Hardcode DateTime::Precise location w/ use lib # [Needs valid_number.pl] # [Begin v 2.0] # 13 Apr 99. Version 1.5 WJS # Incorporate 24 Feb 98 mod into post-13-May-98 version require "wjs_web_perl_utilities.pl"; local $thisdir = ""; local $debug = $ENV{"PID_FILE_CLEANUP_DEBUG"}; local @del_files; local *DIR; local *DEBUG; local $n_files_deleted = 0; local $n_files_considered = 0; local $n_deletion_problems = 0; local $problem; my ($start_dir_pos); my ($dir,$file_before_ind,$before_ind,$after_ind,$delete_time); my ($ind,$exp,@temp_files) = @_; defined($temp_files[0]) || (return "0/0/0"); # Don't think this is possible (@temp_files == 0) && (return "0/0/0"); if ($debug) { open(DEBUG,">$debug") || die "pid_file_cleanup: cannot open/write $debug: $!\n"; print DEBUG "... indicator string=$ind\n... expiration string=$exp\n"; } # Next line is crude test for valid number. If invalid, $exp++ should # end up as the null string (&valid_number($exp)) || (return "*** Problem: Invalid expiration $exp 0/0/0"); ($exp >= 0) || (return "*** Problem: Invalid expiration $exp 0/0/0"); $n_files_deleted = 0; $n_files_considered = 0; $n_deletion_problems = 0; # If no wildcarding, just examine the files in @temp_files if ($ind eq "") { foreach (@temp_files) { $debug && print DEBUG " .. del_file candidate $_\n"; $n_files_considered++; if ( (-A $_) > $exp) { if ($debug) { print DEBUG " ... would unlink $_\n" } else { if (unlink $_) { $n_files_deleted++ } else { $n_deletion_problems++; $problem = $!; } } } } return &status_string; } # Loop for those files w/PID in them. Sort so all files in a directory # are together. foreach ( sort( grep(/$ind/,@temp_files) ) ) { $debug && print DEBUG "temp file ... $_\n"; # ?=/ means don't include the last / ($dir) = m"(.*(?=/))"; ($before_ind,$after_ind) = /(.*)$ind(.*)/; $debug && print DEBUG "dir = $dir; before = $before_ind; aft = $after_ind\n"; if (defined($dir)) { # Be sure that PID is in file spec, not just directory # (test is that if whole directory does not appear before PID, do # next loop iteration). While we're at it, save the file spec portion # (stuff after directory) ( ($file_before_ind) = ($before_ind =~ m"$dir/(.*)") ) || next; } else { $dir = "."; $file_before_ind = $before_ind; } # Conceptually, following logic is # opendir # find files matching # proc_dir those files # closedir # Actual logic attempts to avoid extra opens and closes since we # expect many files in each directory if ($dir ne $thisdir) { $debug && (print DEBUG " ... New dir $dir\n"); &proc_dir($exp); undef(@del_files); $thisdir = $dir; opendir (DIR,$thisdir) || (die "Bad opendir : $!\n"); $start_dir_pos = telldir(DIR); } # "Rewind" directory file seekdir (DIR,$start_dir_pos); push ( @del_files, grep(/^$file_before_ind.+$after_ind$/,readdir(DIR)) ); } &proc_dir($exp); $debug && close DEBUG; return &status_string; } sub proc_dir { my ($exp) = $_[0]; my $file; if ($thisdir ne "") { closedir (DIR); # Didn't want to unlink files w/directory open. Have no # particular knowledge about this... foreach $file (@del_files) { $file = "$thisdir/$file"; $n_files_considered++; $debug && print DEBUG " .. del_file candidate $file\n"; if ( (-A $file) > $exp) { if ($debug) { print DEBUG " ... would unlink $file\n" } else { if (unlink $file) { $n_files_deleted++ } else { $n_deletion_problems++; $problem = $!; } } } } } } sub status_string { my ($string); $string = ($n_deletion_problems == 0) ? "" : "*** Problem $problem:"; return "$string $n_files_considered/$n_files_deleted/$n_deletion_problems"; } # Next line needed if you intend to require this file 1;