#!/usr/local/bin/perl # # CdTest - Check CD-ROM database consistency # V1.1 pl 00 - 3 May 1997 # # # Written by # Jouko Valta (jopi@x2ftp.oulu.fi) # # Revision History # V1.0 pl 00 - 8 Apr 1997 # Initial revision. # Checks explicit one-per-file CD-ROM flag files. # # V1.0 pl 01 - 11 Apr 1997 # Options for suppressing output. # Checks one-per-directory CD-ROM flag files. # CD-ROM timestamp compare and generation. # Ignore time change on index files. # # V1.0 pl 02 - 17 Apr 1997 # Takes filename to test as parameter. # Check whether file given is on FTP or CDROM side. # Figure out the full pathname for file or directory given. # Remove backwards references on pathnames. # # V1.1 pl 00 - 3 May 1997 # Check recent files by option. (Files for 5 weeks by default.) # . # . # . # # $Usage = "\ Options:\ -h Show help page and exit\ -l Check recent FTP files only.\ -m Print 'missing FTP' warnings\ -nm -q Suppress printing 'missing FTP' messages\ -R Read ON_ and OFF_ lists for directories (default)\ -on Check ON_ time stamps only\ -t Check time stamps for each file (default)\ -T Specify time difference allowed (default 240 mins (4h))\ -nR Don't read ON_ and OFF_ lists\ -nt Don't check time stamps\n\n"; ## Default Filenames $LOGHOME = "/pc/log"; # $TEXTDIR = "/src/txt"; $ROOTDIR = "/ftp-service"; # Work Dir to chdir to $FTPDIRS = "ftp/pub/msdos"; $CDROMDIRS = "cdrom/pub/msdos"; # $TARGET = $TEXTDIR . "/00index.txt"; $FIND = "/bin/find"; $SORT = "/bin/sort"; ## Declare CDROM Constants $CD_NONE = 0; $CD_YES = 1; $CD_FREE = 2; $CD_NONPROFIT = 3; $CD_GPL = 4; $CD_DEMO = 5; # Demo version, not for CD $CD_PERM = 6; # Prior permission required $CD_NO = 7; $CD_CONFL = 8; # Claimed yes, but contents seem suspicious $CD_ASKED = 16; @CDPrefix = ( "", "ON_", "ON_", "ON_", "", "OFF_", "OFF_", "OFF_", "OFF_", "", "", "", "", "", "", "", "ASKED_" ); @CDString = ( "N/A", "Yes", "Yes", "Yes", "N/A", "No", "No", "No", "No", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A" ); # ----------------------------------------------------------------------------- ## Defaults $ReadCDList = 1; $TimeCheck = 1; $PrintNoMain = 1; $PrintTime = 0; $TimeLimit = 4*3600; # Allow delay (in seconds) $CheckRecent = 0; # Check recent files. $CheckLimit = 5*7; # Find recent files for N days. ## Command line options while ($_ = $ARGV[0], /^-/) { shift; last if /^--$/; if (/^-l/) { $CheckRecent = 1; next; } # Check recent files. if (/^-p/) { $PrintTime = 1; next; } # Print implied CD's. if (/^-m/) { $PrintNoMain = 1; next; } # Print missing FTP's. if (/^-q|^-nm/) { $PrintNoMain = 0; next; } # Suppress missing FTP's. if (/^-R/) { $ReadCDList = 1; next; } if (/^-on/) { $TimeCheck = 2; next; } # Check ON_ times only. if (/^-t/) { $TimeCheck = 1; next; } if (/^-T/) { if ($TimeCheck != 2) { $TimeCheck = 1; } $TimeLimit = $ARGV[1] * 60; shift; next; } if (/^-np/) { $PrintTime = 0; next; } # Suppress implied CD's. if (/^-nR/) { $ReadCDList = 0; next; } if (/^-nt/) { $TimeCheck = 0; next; } if (/^-h/) { print $Usage; exit 2; } if (/^-/) { die "Unsupported argument near $_\n Stopped"; } } ($TimeLimit < 0) && die "Hey, this is not fair! Time given < 0. Stopped"; # ----------------------------------------------------------------------------- # Initialize Counters $FtpFiles = 0; $CdFiles = 0; $CdFilesImplied = 0; $CdFilesOn = 0; $CdFilesOff = 0; $CdFilesAsked = 0; $CdFilesBad = 0; $MatchLess = 0; $MatchOne = 0; $MatchMany = 0; $FilesNewer = 0; # The CDROM flag has not been updated. $CategoryNewer = 0; # Same for implied files. # ----------------------------------------------------------------------------- # # Filename(s) given ? # Figure out the full pathname for file or directory given, and transform # it relative to $ROOTDIR. # if ($ARGV[0]) { $FTPDIRS = ""; $CDROMDIRS = ""; # Path $cwd = `/bin/pwd`; $cwd =~ s/$ROOTDIR\///so; chop $cwd; # printf "CWD: >>%s<<\n", $cwd; while ($ARGV[0]) { $path = $ARGV[0]; if ($path =~ /^\//) { # Absolute path $path =~ s/$ROOTDIR\///so; } elsif ($path eq ".") { # Current work dir $path = $cwd; } else { # Relative path $path = "$cwd/$ARGV[0]"; } # Certain files do not exist on CDROM. Remove backwards references. # This fails if trying to 'cd /..', which is impossible, anyway. $path =~ s/\/[A-Za-z0-9_\~\-\+\.]+\/\.\.//g; # Parent dir reference $path =~ s/\/$//; # Remove trailing slash # Check which file was given. if ($path =~ /cdrom/) { $cdpath = $path; $path =~ s/^cdrom\//ftp\//; } else { $cdpath = $path; $cdpath =~ s/^ftp\//cdrom\//; } printf ">>%s<<\n", $cdpath; # Prepare wildcards to prefix any CDROM directory. # We can use wildcards even in the path list. # If given filename refers to file in ON/OFF directory, cut it. $cdpath =~ s/ON_|OFF_|ASKED_/\*/; # Assumes path is at least 3 units long, e.g. "cdrom/pub/msdos". if ($cdpath =~ /(\S+)\/([A-Za-z0-9_\~\-\+\.]+)\/(\S+)/) { $cdpath = "$1/*$2"; # Is ($3) file or directory ? #printf " >%s<\n", $cdpath; } $FTPDIRS .= "$path "; $CDROMDIRS .= "$cdpath "; shift @ARGV; } } # argv # ----------------------------------------------------------------------------- # Find recent files on FTP. $REC = ""; if ($CheckRecent) { $REC = "-mtime -$CheckLimit"; } # CheckRecent # ----------------------------------------------------------------------------- # # Scan the CDROM Database # # ----------------------------------------------------------------------------- chdir $ROOTDIR || die "Cannot access $ROOTDIR. Stopped"; $FIND_FTP = "$FIND $FTPDIRS -type f $REC -print"; $FIND_CDROM = "$FIND $CDROMDIRS -type f -print"; scan_cdrom(); # ----------------------------------------------------------------------------- # # Scan the FTP archive # # ----------------------------------------------------------------------------- open (foo, "$FIND_FTP |") || die "Can't excute '$FIND_FTP'"; printf "\n\nScanning the FTP Archive.\n"; print "Checking for matches.\n\n"; # Check matches while () { chop; if (/^\s*$/) { next; } # Empty line elsif (/incoming\//) { next; } # Incoming directory # Takes the last slash by default. elsif (/ftp\/(\S+)\/(\S+)/) { # File in any FTP dir $dir = $1; $file = $2; $index= $dir ."/" . $file; if ($dir ne $prev) { $prev = $dir; (!$CheckRecent) && printf "\n$dir/\n"; # Print dirs. } ++$FtpFiles; # Count all FTP files. # Perfect match. File is explicitly declared, i.e # the file has CD flag file or it is listed on ON_dir. if ($MatchCnt{$index} == 1) { if ($FileStatus{$index} == $CD_YES) { ++$CdFilesOn; } elsif ($FileStatus{$index} == $CD_NO) { ++$CdFilesOff; } elsif ($FileStatus{$index} == $CD_ASKED) { printf "ASKED for file: $_.\n"; ++$CdFilesAsked; } else { printf "*** ERROR: Invalid CDROM entry for $_.\n"; # Note: $CdFilesBad already counted, } ++$MatchOne; next; # Time check done. } # exact match # File is not explicitly declared on CD with either CD_file or CD_dir. # Try the parent value, i.e. either ON_dir or OFF_dir. elsif (!$MatchCnt{$index}) { # First, find parent directory with Status value set. if (! $FileStatus{$dir}) { $orig = $dir; while ($dir =~ /(\S+)\/(\S+)/ && !$FileStatus{$dir}) { $dir = $1; # Drop one level # print "cd $dir\t$_\n"; } $FileStatus{$orig} = $FileStatus{$dir}; # Copy parent Status $DirTime{$orig} = $DirTime{$dir}; # Copy parent Time } # no status if ($FileStatus{$dir} == $CD_YES) { ++$CdFilesImplied; ++$CdFilesOn; } elsif ($FileStatus{$dir} == $CD_NO) { ++$CdFilesImplied; ++$CdFilesOff; if ($TimeCheck == 2) { next; } } elsif ($FileStatus{$dir} == $CD_ASKED) { ++$CdFilesImplied; ++$CdFilesAsked; } else { printf "No CDROM entry for $_.\n"; # No DIR status avail ++$MatchLess; next; # Well, no time check } } # no direct match else { # Many CD files ? printf "*** ERROR: Multiple matches for $_.\n"; ++$MatchMany; next; # Cannot time check } # Check if there are files newer than the directory flag. This # checks files not listed anywhere against the directory timestamp. if ($TimeCheck && $DirTime{$dir} > 0) { @statftp = stat ($_); $ftptime = $statftp[9]; # Last modify time if ($ftptime > ($DirTime{$dir} + $TimeLimit)) { # Delay allowed $tdiff = ($ftptime - $DirTime{$dir}) / 60; # minutes $days = $tdiff / 1440; $tdiff %= 1440; # Skip timestamp checking on index files (/00\_*index\.|00\S\S\S\d\d\.new|week\d\d\.log/) && next; printf "File Changed: %3d %2d:%02d\t%s\n", $days, $tdiff/60, $tdiff%60, $_; # prints FTP ++$CategoryNewer; } else { # Implied file OK ($PrintTime) && printf "+%d\t%s\n", $DirTime{$dir}, $file; # CD entry format } } # time check } # FTP file } # while close T; printf "\n"; printf "\t%4d files on FTP archive (Total).\n", $FtpFiles; printf "\t%4d files on CDROM Database (Explicit).\n", $CdFiles; printf "\n"; printf "\t%4d CDROM files don't have a FTP file.\n", $MatchCnt{"-"}; printf "\t%4d FTP files explicitly have a CDROM entry.\n", $MatchOne; printf "\t%4d FTP files implied by their Category.\n", $CdFilesImplied; printf "\t%4d FTP files don't have a CDROM entry.\n", $MatchLess; $MatchMany && printf "\t%4d NAME CONFLICTS! Many CD Status files found.\n", $MatchMany; printf "\n"; printf "\t%4d FTP files ON on CDROM Database.\n", $CdFilesOn; printf "\t%4d FTP files OFF on CDROM Database.\n", $CdFilesOff; printf "\t%4d FTP files ASKED on CDROM Database.\n", $CdFilesAsked; printf "\t%4d FTP files failed on CDROM Database.\n", $CdFilesBad; if ($TimeCheck) { printf "\n\t%4d FTP files newer than their CDROM flag. (Limit %3.1f hours)\n", $FilesNewer, $TimeLimit / 3600; printf "\t%4d FTP files newer than their CDROM Category. (Implied)\n", $CategoryNewer; } printf "\n"; #printf OF "\n"; #close OF; #chmod 0664, $TARGET; print "Done.\n"; # ----------------------------------------------------------------------------- # # Scan the CDROM Database # # ----------------------------------------------------------------------------- # Get the directory descriptions for ON_dir and OFF_dir ... sub scan_cdrom { open (foo, "$FIND_CDROM |") || die "Can't excute '$FIND_CDROM'"; #if (! $CntOnly) { # open (OF, ">$TARGET"); # print "\nCreating $TARGET\n"; #} printf "\nScanning the CDROM Database.\n"; ($TimeCheck) && printf "Testing the timestamps for CDROM flags.\n"; printf "\n"; line: while () { chop; # print $_; if (/^\s*$/) { next line; } # Empty line # Takes the last slash by default. if (/cdrom\/(\S+)\/(\S+)/) { # File in any CD dir $dir = $1; $file = $2; $main = $dir . "/" . $file; $main =~ s/ON_|OFF_|ASKED_//os; # Set flags for FTP file # Both the parent dir dir and the file must default to 0. if ($FileStatus{$dir} || $FileStatus{$main}) { printf "*** Conflicting CDROM values for $_.\n"; } if (/ON_/) { $FileStatus{$main} = $CD_YES; } elsif (/OFF_/) { $FileStatus{$main} = $CD_NO; } elsif (/ASKED_/) { printf "ASKED for: $_.\n"; $FileStatus{$main} = $CD_ASKED; } else { # Non-prefixed CD file printf "*** Invalid CDROM entry $_.\n"; $FileStatus{$main} = $CD_NONE; ++$CdFilesBad; } # Get the object type from respective FTP directory. # Note: If stat is passed the special filehandle '_', no stat is done, # but the current contents of the stat structure from the last stat # or filetest are returned. if (! -e "ftp/$main") { $PrintNoMain && printf "No mainfile for $_.\n"; ++$MatchCnt{"-"}; # '-' for no main ++$CdFiles; next; } # File on FTP: Count one file if (-f _) { ++$MatchCnt{$main}; ++$CdFiles; # Check if the file is newer than its flag. if ($TimeCheck) { if ($TimeCheck ==1 || $CDPrefix[$FileStatus{$main}] eq "ON_") { check_file_time ($main, $_, 0); } } } # File status # Directory on FTP: It is ON_dir or OFF_dir flag. elsif (-d _) { # Check if directory conflicts it's parent. # unimplemented # Read list of files actually checked. if ($ReadCDList) { # print "\nDIR: $_\n"; read_ON_OFF_index($FileStatus{$main}, $main, $_); } } # Directory status else { printf "*** Fatal error: Unknown file type $_\n"; } } # line } # while } # scan_cdrom # ----------------------------------------------------------------------------- # # ON_dir and OFF_dir are lists of files checked, supplied with contact address # if known. # # Note: This routine only handles files specified on the list given. # sub read_ON_OFF_index { local ($status, $maindir, $dir) = @_; # First, set Category flags. if (! $CDPrefix[$status]) { printf "*** Invalid CDROM value for $dir.\n"; return; } $tcheck = 0; if ($TimeCheck == 1 || ($TimeCheck == 2 && $CDPrefix[$status] eq "ON_")) { @cdstat = stat ($_); $DirTime{$maindir} = $cdstat[9]; # Last CD modify time ++$tcheck; } (!ReadCDList) && return; # Read files explicitly listed $FileCnt = 0; open (M, "<$_"); # Open given file. while () { chop; if (/^\s*$/) { next; } # Empty line if (/ON_|OFF_|ASKED_/) { # Prefix not alloved. printf "*** Prefix not alloved within list $_.\n"; next; } if (/^\s*\+*\s*(\d+)\s+(\S+)/) { # File listed (ignore the rest) $cdtime = $1; $cdname = $dir . "/" . $2; $index = $maindir . "/" . $2; # Set flags for a FTP file if ($FileStatus{$main}) { printf "*** Conflicting CDROM values for $_.\n"; } $FileStatus{$index} = $status; # Get the object type from respective FTP directory. # Note: If stat is passed the special filehandle '_', no stat is done, # but the current contents of the stat structure from the last stat # or filetest are returned. if (! -e "ftp/$index") { $PrintNoMain && printf "No mainfile for $_. [ $dir ]\n"; ++$MatchCnt{"-"}; # '-' for no main ++$CdFiles; ++$FileCnt; next; } # File on FTP: Count one file if (-f _) { ++$MatchCnt{$index}; ++$CdFiles; ++$FileCnt; if ($tcheck) { check_file_time ($index, $cdname, $cdtime); } } # File status # Not file or directory on FTP: flag error. elsif (! -d _) { printf "*** Fatal error: Unknown file type $_\n"; next; } } # line } close (M); printf "Read %2d files for '$dir'.\n", $FileCnt; } # ----------------------------------------------------------------------------- # Check timestamp on the FTP file against the CD-rom flag time. # sub check_file_time { local ($main, $cdname, $cdtime) = @_; local $cdsize = 0; # Check if the file is newer than its flag. if ($cdtime <= 0) { @cdstat = stat ($cdname); $cdsize = $cdstat[7]; $cdtime = $cdstat[9]; # Last modify time } #($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$cdsize, # $atime,$cdtime,$ctime,$blksize,$blocks) = stat ($cdname); @ftpstat = stat ("ftp/$main"); $ftpsize = $ftpstat[7]; $ftptime = $ftpstat[9]; # Last modify time #($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$ftpsize, # $atime,$ftptime,$ctime,$blksize,$blocks) = stat ("ftp/$main"); # DEBUG: Print Directory Listing if (0) { ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($cdtime); printf ("\n %02d-%02d-%d %02d:%02d %6d %s\n", $mday, $mon, $year, $hour, $min, $cdsize, $cdname); ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($ftptime); printf (" %02d-%02d-%d %02d:%02d %6d %s\n", $mday, $mon, $year, $hour, $min, $ftpsize, $main); } # Debug if ($ftptime > $cdtime + $TimeLimit) { # Allow small delay $tdiff = ($ftptime - $cdtime) / 60; # minutes $days = $tdiff / 1440; $tdiff %= 1440; printf "Changed: %3d %2d:%02d\t%s\n", $days, $tdiff/60, $tdiff%60, $cdname; # printd CD ++$FilesNewer; } } # check_file_time