pastebin - collaborative debugging tool
kpaste.net RSS


md5db script (threaded)
Posted by Anonymous on Mon 15th Oct 2012 06:13
raw | new post
modification of post by Anonymous (view diff)

  1. #!/usr/bin/perl
  2.  
  3. use 5.14.2;
  4. use strict;
  5. use warnings;
  6. use Cwd qw(abs_path cwd);
  7. use Digest::MD5 qw(md5_hex);
  8. use IO::Handle qw(autoflush);
  9. use File::Basename qw(basename dirname);
  10. #use File::Slurp qw(read_file);
  11. use diagnostics;
  12.  
  13. use threads qw(yield);
  14. use threads::shared;
  15. use Thread::Queue;
  16. use Thread::Semaphore;
  17. #use Fcntl qw(:flock);
  18. use POSIX qw(SIGINT);
  19. use POSIX qw(ceil);
  20.  
  21. # Create the thread queue.
  22. my $q = Thread::Queue->new();
  23.  
  24. chomp(my $cores = `grep -c ^processor /proc/cpuinfo`);
  25.  
  26. my (@lib, $mode);
  27.  
  28. # Path to and name of log file to be used for logging.
  29. my $logf = "$ENV{HOME}/md5db.log";
  30.  
  31. # Delimiter used for database
  32. my $delim = "\t\*\t";
  33.  
  34. # Array for storing the actual arguments used by the script internally.
  35. # Might be useful for debugging.
  36. my @cmd = (basename($0));
  37.  
  38. # Name of database file.
  39. my $db = 'md5.db';
  40.  
  41. # Clear screen command.
  42. my $clear = `clear && echo`;
  43.  
  44. # Creating a hash that will store the names of files that are
  45. # too big to fit into RAM. We'll process them last.
  46. my %large :shared;
  47.  
  48. # Creating a few shared variables.
  49. # %err will be used for errors
  50. # $n will be used to count the number of files processed
  51. # %md5h is the database hash
  52. my %err :shared;
  53. my $n :shared = 0;
  54. my %md5h :shared;
  55. my %file_contents :shared;
  56. my $stopping :shared = 0;
  57. my $file_stack :shared = 0;
  58. my $busy :shared = 0;
  59.  
  60. my $disk_size = 1000000000;
  61.  
  62. # This will be used to control access to the logger subroutine.
  63. my $semaphore = Thread::Semaphore->new();
  64.  
  65. POSIX::sigaction(SIGINT, POSIX::SigAction->new(\&handler))
  66. || die "Error setting SIGINT handler: $!\n";
  67.  
  68. # Creating a custom POSIX signal handler.
  69. # First we create a shared variable that will work as a SIGINT switch.
  70. # Then we define the handler subroutine.
  71. # Each subroutine to be used for starting threads will have to
  72. # take notice of the state of the $saw_sigint variable.
  73. my $saw_sigint :shared = 0;
  74. sub handler { $saw_sigint = 1; }
  75.  
  76. # Open file handle for the log file
  77. open(my $LOG, '>>', $logf) or die "Can't open '$logf': $!";
  78.  
  79. # Make the $LOG file handle unbuffered for instant logging.
  80. $LOG->autoflush(1);
  81.  
  82. # Duplicate STDERR as a regular file handle
  83. open(my $SE, ">&STDERR") or die "Can't duplicate STDERR: $!";
  84.  
  85. ### Subroutine for printing usage instructions
  86. sub usage {
  87.  
  88.         my $s = basename($0);
  89.  
  90.         say <<"HELP"
  91. Usage: $s [options] [directory 1] .. [directory N]
  92.  
  93.         -help Print this help message.
  94.  
  95.         -double Check database for files that have identical
  96.         hashes.
  97.  
  98.         -import Import MD5 sums to the database from already existing
  99.         \*.MD5 files in each directory.
  100.  
  101.         -index Index new files in each directory.
  102.  
  103.         -test Test the MD5 sums of the files in the database to see if
  104.         they've changed.
  105.  
  106. HELP
  107. }
  108.  
  109. # This loop goes through the argument list as passed to the script
  110. # by the user when ran.
  111. foreach my $arg (@ARGV) {
  112.  
  113.         # If argument starts with a dash '-', interprete it as an option
  114.         if ($arg =~ /^-/) {
  115.  
  116.                 given ($arg) {
  117.                         # When '-double', set script mode to 'double', and call
  118.                         # the md5double subroutine later.
  119.                         when (/^-double$/) {
  120.                                 if (!$mode) { push(@cmd, $arg); $mode = 'double'; }
  121.                         }
  122.  
  123.                         # When '-import', set script mode to 'import', and call
  124.                         # the md5import subroutine later.
  125.                         when (/^-import$/) {
  126.                                 if (!$mode) { push(@cmd, $arg); $mode = 'import'; }
  127.                         }
  128.  
  129.                         # When '-help', set script mode to 'help', and print
  130.                         # usage instructions later.
  131.                         when (/^-help$/) {
  132.                                 if (!$mode) { push(@cmd, $arg); $mode = 'help'; }
  133.                         }
  134.  
  135.                         # When '-index', set script mode to 'index', and call
  136.                         # the md5index subroutine later.
  137.                         when (/^-index$/) {
  138.                                 if (!$mode) { push(@cmd, $arg); $mode = 'index'; }
  139.                         }
  140.  
  141.                         # When '-test', set the script mode to 'test', and call
  142.                         # the md5test subroutine later.
  143.                         when (/^-test$/) {
  144.                                 if (!$mode) { push(@cmd, $arg); $mode = 'test'; }
  145.                         }
  146.                 }
  147.         # If argument is a directory, include it in the @lib array
  148.         } elsif (-d $arg) {
  149.                 my $dn = abs_path($arg);
  150.                 push(@lib, $dn); push(@cmd, $dn); }
  151. }
  152.  
  153. # If no switches were used, print usage instructions
  154. if (!@lib || !$mode || $mode eq 'help')
  155.         { usage(); exit; }
  156.  
  157. #say "@cmd\n";
  158.  
  159. # This routine is called if something goes wrong and the script needs
  160. # to quit prematurely.
  161. sub iquit {
  162.         my $tid = threads->tid();
  163.         if ($tid == 1) {
  164.  
  165.                 # Set the $stopping variable to let the threads know it's time
  166.                 # to stop, and sleep for 1 second so they'll have time to quit.
  167.                 { lock($stopping);
  168.                 $stopping = 1; }
  169.  
  170.                 sleep(1);
  171.  
  172.                 # Write the hash to the database file and write to the log.
  173.                 hash2file();
  174.                 logger('int', $n);
  175.  
  176.                 # Detaching the threads so Perl will clean up after us.
  177.                 foreach my $t (threads->list()) { $t->detach(); }
  178.                 exit;
  179.  
  180.         # If the thread calling this function isn't thread 0/1, yield until
  181.         # $stopping is set.
  182.         } elsif ($tid > 1) { while (!$stopping) { yield(); } }
  183. }
  184.  
  185. # Subroutine for controlling the log file
  186. # Applying a semaphore so multiple threads won't try to
  187. # access it at once, just in case ;-)
  188. sub logger {
  189.  
  190.         $semaphore->down();
  191.  
  192.         my($arg, $sw, @fn, $n);
  193.  
  194.         # Creating a variable to hold the current time.
  195.         my $now = localtime(time);
  196.  
  197.         # Array of accepted switches to this subroutine
  198.         my @larg = qw{start int gone corr diff end};
  199.  
  200.         # Loop through all the arguments passed to this subroutine
  201.         # Perform checks that decide which variable the arguments are to
  202.         # be assigned to.
  203.         CHECK: while (@_) {
  204.                         $arg = shift(@_);
  205.  
  206.                         # If $arg is a switch, set the $sw variable and start
  207.                         # the next iteration of the CHECK loop.
  208.                         foreach (@larg) {
  209.                                 if ($_ eq $arg) { $sw = $arg; next CHECK; }
  210.                         }
  211.  
  212.                         # If $arg is a number assign it to $n, if it's a file
  213.                         # add it to @fn.
  214.                         if ($arg =~ /^[0-9]+$/) { $n = $arg; }
  215.                         else { push(@fn, $arg); }
  216.         }
  217.         given ($sw) {
  218.                 # Starts writing the log.
  219.                 when ('start') {
  220.                         say $LOG "\n**** Logging started on $now ****\n";
  221.                         say $LOG "Running script in '$mode' mode on:\n";
  222.                         foreach my $dn (@lib) { say $LOG $dn; }
  223.                         say $LOG "";
  224.                 }
  225.                 # When the script is interrupted by user pressing ^C,
  226.                 # say so in STDOUT, close the log.
  227.                 when ('int') {
  228.                         say "\nInterrupted by user!\n";
  229.                         say $LOG $n . " file(s) were tested.";
  230.                         say $LOG "\n**** Logging ended on $now ****\n";
  231.                         close $LOG or die "Can't close '$LOG': $!";
  232.                 }
  233.                 # Called when file has been deleted or moved.
  234.                 when ('gone') {
  235.                         say $LOG $fn[0] . "\n\t" . "has been (re)moved.\n";
  236.                         $err{$fn[0]} = "has been (re)moved.\n";
  237.                 }
  238.                 # Called when file has been corrupted.
  239.                 when ('corr') {
  240.                         say $LOG $fn[0] . "\n\t" .
  241.                         "has been corrupted.\n";
  242.                         $err{$fn[0]} = "has been corrupted.\n";
  243.                 }
  244.                 when ('diff') {
  245.                         say $LOG $fn[0] . "\n\t" .
  246.                                 "doesn't match the hash in database.\n";
  247.                         $err{$fn[0]} = "doesn't match the hash in database.\n";
  248.                 }
  249.                 # Called when done, and to close the log.
  250.                 # If no errors occurred write "Everything is OK!" to the log.
  251.                 # If errors occurred print the %err hash.
  252.                 # Either way, print number of files processed.
  253.                 when ('end') {
  254.                         if (!%err) {
  255.                                 say $LOG "\nEverything is OK!\n";
  256.                         } else {
  257.                                 say "\n**** Errors Occurred ****\n";
  258.                                 foreach my $fn (sort keys %err) {
  259.                                         say $SE $fn . "\n\t" . $err{$fn};
  260.                                 }
  261.                         }
  262.  
  263.                         say $LOG $n . " file(s) were tested.\n" if ($n);
  264.                         say $LOG "\n**** Logging ended on $now ****\n";
  265.                         close $LOG or die "Can't close '$LOG': $!";
  266.                 }
  267.         }
  268.         $semaphore->up();
  269. }
  270.  
  271.  
  272. # Subroutine for reading a database file into the database hash.
  273. # This is the first subroutine that will be executed and all others
  274. # depend upon it, cause without it we don't have a
  275. # database hash to work with.
  276. sub file2hash {
  277.  
  278.         my $db = shift;
  279.         my $dn = shift;
  280.  
  281.         # The format string which is used for parsing the database file
  282.         my $format = qr/^.*\t\*\t[[:alnum:]]{32}$/;
  283.         my (@dbfile, @gone, $md5db_in);
  284.  
  285.         # Open the database file and read it into the @dbfile variable
  286.         open($md5db_in, '<', $db) or die "Can't open '$db': $!";
  287.         chomp (@dbfile = (<$md5db_in>));
  288.         close($md5db_in) or die "Can't close '$db': $!";
  289.  
  290.         # Loop through all the lines in the database file and split
  291.         # them before storing in the database hash.
  292.         # Also, print each line to STDOUT for debug purposes
  293.         foreach my $line (@dbfile) {
  294.  
  295.                 # If current line matches the proper database file format,
  296.                 # continue.
  297.                 if ($line =~ /$format/) {
  298.  
  299.                         # Split the line into relative file name, and MD5 sum.
  300.                         # Also create another variable that contains the absolute
  301.                         # file name.
  302.                         my ($rel_fn, $hash) = (split(/\Q$delim/, $line));
  303.                         my $abs_fn;
  304.                         if ($dn ne '.') { $abs_fn = "$dn/$rel_fn"; }
  305.                         else { $abs_fn = $rel_fn; }
  306.  
  307.                         # If $abs_fn is a real file and not already in the hash,
  308.                         # continue.
  309.                         if (-f $abs_fn && ! $md5h{$abs_fn}) {
  310.                                 $md5h{$abs_fn} = $hash;
  311.                                 say $abs_fn . $delim . $hash;
  312.  
  313.                         # If the file is in the database hash but the MD5 sum
  314.                         # found in the database doesn't match the one in the hash,
  315.                         # print to the log.
  316.                         #
  317.                         # This will most likely only be the case for any extra
  318.                         # databases that are found in the search path given to
  319.                         # the script.
  320.                         } elsif (-f $abs_fn && $md5h{$abs_fn} ne $hash) {
  321.                                 logger('diff', $abs_fn);
  322.                         # Saves the names of deleted or moved files in '@gone'
  323.                         # for printing at the end of this subroutine.
  324.                         } elsif (! -f $abs_fn) { push(@gone, $abs_fn); }
  325.                 }
  326.         }
  327.  
  328.         # Clears the screen, thereby scrolling past the database file print
  329.         print $clear;
  330.  
  331.         # Loops through the @gone array and logs every file name
  332.         # that's been deleted or moved.
  333.         foreach my $fn (@gone) { logger('gone', $fn); }
  334. }
  335.  
  336. # Subroutine for printing the database hash to the database file
  337. sub hash2file {
  338.  
  339.         my $md5db_out;
  340.  
  341.         open($md5db_out, '>', $db) or die "Can't open '$db': $!";
  342.         # Loops through all the keys in the database hash and prints
  343.         # the entries (divided by the $delim variable) to the database file.
  344.         foreach my $k (sort(keys(%md5h))) {
  345.                 say $md5db_out $k . $delim . $md5h{$k};
  346.         }
  347.         close($md5db_out) or die "Can't close '$db': $!";
  348. }
  349.  
  350. sub init_hash {
  351.         my $dn = shift;
  352.  
  353.         # Get all the file names in the path.
  354.         my($files, $md5dbs) = getfiles($dn);
  355.  
  356.         # But first import hashes from any databases found
  357.         # in the search path to avoid re-hashing them.
  358.         if (@{$md5dbs}) {
  359.                 foreach my $db (@{$md5dbs}) {
  360.                         my $dn = dirname($db);
  361.                         file2hash($db, $dn);
  362.                 }
  363.         }
  364.         return($files, $md5dbs);
  365. }
  366.  
  367. # Subroutine for finding files
  368. # Finds all the files inside the directory name passed to it,
  369. # and sorts the output before storing it in the @files array.
  370. sub getfiles {
  371.  
  372.         my $dn = shift;
  373.         my(@files, @md5dbs);
  374.  
  375.         open(FIND, '-|', 'find', $dn, '-type', 'f', '-name', '*', '-nowarn')
  376.         or die "Can't run 'find': $!";
  377.         while (my $fn = (<FIND>)) {
  378.                         chomp($fn);
  379.                         $fn =~ s($dn/)();
  380.                         if (-f $fn && basename($fn) ne $db) {
  381.                                 push(@files, $fn);
  382.                         } elsif (-f $fn && basename($fn) eq $db) {
  383.                                 push(@md5dbs, $fn);
  384.                         }
  385.         }
  386.         close(FIND) or die "Can't close 'find': $!";
  387.         return(\@files, \@md5dbs);
  388. }
  389.  
  390. sub md5double {
  391.  
  392.         if (!keys(%md5h)) {
  393.                 say "No database file. Run the script in 'index' mode first\n" .
  394.                 "to index the files.";
  395.                 exit;
  396.         }
  397.  
  398.         # Loop through the %md5h hash and save the checksums as keys in a
  399.         # new hash called %exists. Each of those keys will hold an
  400.         # anonymous array with the matching file names.
  401.         my %exists;
  402.         foreach my $fn (keys(%md5h)) {
  403.                 my $hash = $md5h{$fn};
  404.                 if (!$exists{${hash}}) {
  405.                         $exists{${hash}}->[0] = $fn;
  406.                 } else {
  407.                         push(@{$exists{${hash}}}, $fn);
  408.                 }
  409.         }
  410.  
  411.         # Loop through the %exists hash and print files that are identical,
  412.         # if any.
  413.         foreach my $hash (keys(%exists)) {
  414.                 if (@{$exists{${hash}}} > 1) {
  415.                         say "These files have the same hash (${hash}):";
  416.                         foreach my $fn (@{$exists{${hash}}}) {
  417.                                 say $fn;
  418.                         }
  419.                         say "";
  420.                 }
  421.         }
  422. }
  423.  
  424. # Subroutine for finding and parsing *.MD5 files, adding the hashes
  425. # to the database hash and thereby also to the file.
  426. # It takes 1 argument:
  427. # (1) file name
  428. sub md5import {
  429.  
  430.         my $md5fn = shift;
  431.  
  432.         my ($fn, $hash, @fields, @lines);
  433.  
  434.         # The format string which is used for parsing the *.MD5 files.
  435.         my $format = qr/^[[:alnum:]]{32}\s\*.*/;
  436.  
  437.         # If the file extension is *.MD5 in either upper- or
  438.         # lowercase, continue.
  439.         if ($md5fn =~ /.md5$/i) {
  440.  
  441.                 # Open the *.MD5 file and read its contents to the
  442.                 # @lines array.
  443.                 open(MD5, '<', $md5fn) or die "Can't open '$md5fn': $!";
  444.                 chomp(@lines = (<MD5>));
  445.                 close(MD5) or die "Can't close '$md5fn': $!";
  446.  
  447.                 # Loop to check that the format of the *.MD5 file really
  448.                 # is correct before proceeding.
  449.                 foreach my $line (@lines) {
  450.  
  451.                         # If format string matches the line(s) in the *.MD5
  452.                         # file, continue.
  453.                         if ($line =~ /$format/) {
  454.  
  455.                                 # Split the line so that the hash and file name go
  456.                                 # into @fields array.
  457.                                 # After that strip the path (if any) of the file
  458.                                 # name, and prepend the path of the *.MD5 file to
  459.                                 # it instead.
  460.                                 # Store hash and file name in the $hash and $fn
  461.                                 # variables for readability.
  462.                                 @fields = split(/\s\Q*/, $line, 2);
  463.                                 my $path = dirname($md5fn);
  464.                                 $hash = $fields[0];
  465.  
  466.                                 if ($path eq '.') { $fn = basename($fields[1]); }
  467.                                 else { $fn = dirname($md5fn)
  468.                                 . '/' . basename($fields[1]); }
  469.  
  470.                                 # Convert CR+LF newlines to proper LF to avoid
  471.                                 # identical file names from being interpreted as
  472.                                 # different.
  473.                                 $fn =~ s/\r//;
  474.  
  475.                                 # Unless file name already is in the database hash,
  476.                                 # print a message, add it to the hash.
  477.                                 if (! $md5h{$fn} && -f $fn) {
  478.  
  479.                                         say "$fn" . "\n\t" .
  480.                                         "Imported MD5 sum from '" .
  481.                                         basename($md5fn) .
  482.                                         "'.\n";
  483.  
  484.                                         $md5h{$fn} = $hash;
  485.  
  486.                                         # If file name is not a real file, write to
  487.                                         # the log.
  488.                                         # If file name is in database hash but the
  489.                                         # MD5 sum from the MD5 file doesn't match,
  490.                                         # print to the log.
  491.                                 } elsif (! -f $fn) { logger('gone', $fn); }
  492.                                 elsif ($md5h{$fn} ne $hash)
  493.                                 { logger('diff', $md5fn); }
  494.                         }
  495.                 }
  496.         }
  497. }
  498.  
  499.  
  500. sub md5sum {
  501.         my $fn = shift;
  502.         my $hash;
  503.  
  504.         while ($busy) { yield(); }
  505.  
  506.         if ($large{$fn}) {
  507.                 lock($busy);
  508.                 $busy = 1;
  509.                 my $read_fn;
  510.                 open($read_fn, '<:raw', $fn) or die "Can't open '$fn': $!";
  511.                 $hash = Digest::MD5->new->addfile($read_fn)->hexdigest;
  512.                 close($read_fn) or die "Can't close '$fn': $!";
  513.                 $busy = 0;
  514.         } else {
  515.                 $hash = md5_hex($file_contents{$fn});
  516.  
  517.                 { lock($file_stack);
  518.                 $file_stack -= length($file_contents{$fn}); }
  519.  
  520.                 { lock(%file_contents);
  521.                 delete($file_contents{$fn}); }
  522.         }
  523.  
  524.         return $hash;
  525. }
  526.  
  527. # Subroutine to index the files
  528. # i.e calculate and store the MD5 sums in the database hash/file.
  529. sub md5index {
  530.         my $tid = threads->tid();
  531.  
  532.         # Loop through the thread que.
  533.         LOOP2: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
  534.                         if (!$fn) { yield(); next; }
  535.  
  536.                         $md5h{$fn} = md5sum($fn);
  537.                         say "$tid $fn: done indexing (${file_stack})";
  538.  
  539.                         { lock($n);
  540.                         $n++; }
  541.  
  542.                         # If the $saw_sigint variable has been tripped.
  543.                         # Quit this 'while' loop, thereby closing the thread.
  544.                         if ($saw_sigint == 1) {
  545.                                 say "Closing thread: " . $tid;
  546.                                 iquit();
  547.                         }
  548.         }
  549.  
  550.         while (!$q->pending() && !$stopping) {
  551.                 yield();
  552.                 goto(LOOP2);
  553.         }
  554. }
  555.  
  556. # Subroutine for testing to see if the MD5 sums
  557. # in the database file are correct (i.e. have changed or not).
  558. sub md5test {
  559.         my $tid = threads->tid();
  560.         my ($oldmd5, $newmd5);
  561.  
  562.         # Loop through the thread queue.
  563.         LOOP: while ((my $fn = $q->dequeue_nb()) || !$stopping) {
  564.                                 if (!$fn) { yield(); next; }
  565.  
  566.                                 $newmd5 = md5sum($fn);
  567.                                 say "$tid $fn: done testing (${file_stack})";
  568.                                 $oldmd5 = $md5h{$fn};
  569.  
  570.                                 # If the new MD5 sum doesn't match the one in the hash,
  571.                                 # and file doesn't already exist in the %err hash,
  572.                                 # log it and replace the old MD5 sum in the hash with
  573.                                 # the new one.
  574.                                 if ($newmd5 ne $oldmd5 && ! $err{$fn}) {
  575.                                         logger('diff', $fn);
  576.                                         $md5h{$fn} = $newmd5;
  577.                                 }
  578.  
  579.                                 { lock($n);
  580.                                 $n++; }
  581.  
  582.                                 # If the $saw_sigint variable has been tripped.
  583.                                 # Quit this 'while' loop, thereby closing the thread.
  584.                                 if ($saw_sigint == 1) {
  585.                                         say "Closing thread: " . $tid;
  586.                                         iquit();
  587.                                 }
  588.         }
  589.  
  590.         while (!$q->pending() && !$stopping) {
  591.                 yield();
  592.                 goto(LOOP);
  593.         }
  594. }
  595.  
  596. sub md5flac {
  597.         my $fn = shift;
  598.         my (@req, $hash);
  599.  
  600.         if ($fn =~ /.flac$/i) {
  601.  
  602.                 if (! @req) {
  603.                         chomp(@req = ( `which flac metaflac 2>&-` ));
  604.  
  605.                         if (! $req[0] || ! $req[1]) {
  606.                                 say "You need both 'flac' and 'metaflac' to test FLAC files!\n" .
  607.                                 "Using normal test method...\n";
  608.                                 @req = '0';
  609.                                 return;
  610.                         }
  611.                 }
  612.  
  613.                 unless ($req[0] = '0') {
  614.                         chomp($hash = `metaflac --show-md5sum "$fn" 2>&-`);
  615.                         if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
  616.  
  617.                         system('flac', '--totally-silent', '--test', "$fn");
  618.                         if ($? != 0 && $? != 2) { logger('corr', $fn); return; }
  619.  
  620.                         return $hash;
  621.                 }
  622.         }
  623. }
  624.  
  625. # Depending on which script mode is active,
  626. # set the @run array to the correct arguments.
  627. # This will be used to start the threads later.
  628. my @run;
  629. given ($mode) {
  630.         when ('index') {
  631.                 @run = (\&md5index);
  632.         }
  633.         when ('test') {
  634.                 @run = (\&md5test);
  635.         }
  636. }
  637.  
  638. # If script mode is either 'import' or 'double' we'll start only
  639. # one thread, else we'll start as many as the available number of CPUs.
  640. my @threads;
  641. if ($mode ne 'import' && $mode ne 'double') {
  642.         foreach (1 .. $cores) {
  643.                 START_THREADS: push(@threads, threads->create(@run));
  644.         }
  645. }
  646.  
  647. # This loop is where the actual action takes place
  648. # (i.e. where all the subroutines get called from)
  649. foreach my $dn (@lib) {
  650.         if (-d $dn) {
  651.  
  652.                 # Change directory to $dn.
  653.                 chdir($dn)
  654.                         or die "Can't change directory to '$dn': $!";
  655.  
  656.                 # Start logging.
  657.                 logger('start');
  658.  
  659.                 # Initialize the database hash, and the files array.
  660.                 # The init_hash function returns references.
  661.                 my($files, $md5dbs) = init_hash($dn);
  662.  
  663.                 given ($mode) {
  664.                         when ('double') {
  665.                                 # Find identical files in database.
  666.                                 md5double();
  667.                         }
  668.                         when ('import') {
  669.  
  670.                                 # For all the files in $dn, run md5import.
  671.                                 foreach my $fn (@{$files}) { md5import($fn); }
  672.                         }
  673.                         when ('index') {
  674.  
  675.                                 foreach my $fn (@{$files}) {
  676.                                         if ($saw_sigint) { iquit(); }
  677.                                         while ($file_stack >= $disk_size) {
  678.                                                 my $active = threads->running();
  679.                                                 say("${active}: $file_stack > $disk_size");
  680.                                                 yield();
  681.                                         }
  682.                                         # Unless file name exists in the database hash,
  683.                                         # continue.
  684.                                         unless ($md5h{$fn}) {
  685.                                                 my $size = (stat($fn))[7];
  686.                                                 if ($size && $size < $disk_size) {
  687.  
  688.                                                         open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
  689.                                                         sysread(FILE, $file_contents{$fn}, $size);
  690.                                                         close(FILE) or die "Can't close '$fn': $!";
  691.  
  692.                                                         { lock($file_stack);
  693.                                                         $file_stack += length($file_contents{$fn}); }
  694.  
  695.                                                         $q->enqueue($fn);
  696.                                                 } elsif ($size) {
  697.                                                         $large{$fn} = 1;
  698.                                                 }
  699.                                         }
  700.                                 }
  701.                         }
  702.                         when ('test') {
  703.  
  704.                                 # Fetch all the keys for the database hash and put
  705.                                 # them in the queue.
  706.                                 foreach my $fn (sort(keys(%md5h))) {
  707.                                         if ($saw_sigint) { iquit(); }
  708.                                         while ($file_stack >= $disk_size) {
  709.                                                 say("$file_stack > $disk_size");
  710.                                                 yield();
  711.                                         }
  712.  
  713.                                         my $size = (stat($fn))[7];
  714.                                         if ($size && $size < $disk_size) {
  715.  
  716.                                                 open(FILE, '<:raw', $fn) or die "Can't open '$fn': $!";
  717.                                                 sysread(FILE, $file_contents{$fn}, $size);
  718.                                                 close(FILE) or die "Can't close '$fn': $!";
  719.  
  720.                                                 { lock($file_stack);
  721.                                                 $file_stack += length($file_contents{$fn}); }
  722.  
  723.                                                 $q->enqueue($fn);
  724.                                         } elsif ($size) {
  725.                                                 $large{$fn} = 1;
  726.                                         }
  727.                                 }
  728.                         }
  729.                 }
  730.  
  731.                 if (%large) {
  732.                         while ($file_stack > 0) {
  733.                                 say("$file_stack > 0");
  734.                                 yield();
  735.                         }
  736.                         foreach my $fn (sort(keys(%large))) {
  737.                                 $q->enqueue($fn);
  738.                         }
  739.                 }
  740.  
  741. #       use Digest::MD5;
  742. #       $md5 = Digest::MD5->new;
  743. #       $md5->add('foo', 'bar');
  744. #       $md5->add('baz');
  745. #       $digest = $md5->hexdigest;
  746. #       print "Digest is $digest\n";
  747.  
  748.                 { lock($stopping);
  749.                 $stopping = 1; }
  750.  
  751.                 foreach my $t (threads->list()) { $t->join(); }
  752.                 #say("All threads joined");
  753.  
  754.                 # Print the hash to the database file and close the log
  755.                 hash2file();
  756.                 logger('end', $n);
  757.         }
  758. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at