GPU72.com

Updated: 2021.07.16 15:24 UTC

Submit Spider for GPU72

#!/usr/bin/perl -w

######################################################
# submit_spider version 0.25
#
# Copyright 2014-2017 Chris Halsall et al.
# All rights reserved.
#
# Much thanks to "Bdot" for the delta to handle the
# new Primenet log-in method.
#
# Released under the GPL license version 2.0 or later.
# http://www.gnu.org/copyleft/gpl.html
#
# This is part of the GPU to 72 Project.
# http://www.gpu72.com/
#
# This software is provided with no warrantees.  Use 
# at your own risk.

# Updates:
#
# 2017.02.23
# Changed $PrimeNetURL to use https.
# Thanks to Sebastian "Brain" (from the Mersenne Forum) 
# for the delta.


use strict;
use LWP::UserAgent;
use HTTP::Cookies;


##################################################
# Set these to be appropriate for your environment
#

# Change this if you use a web proxy.
my $PrimeNetURL = "https://www.mersenne.org/";

# Your PrimeNet username.
my $PNUsername = "YOUR_USERNAME";

# Your PrimeNet password.
my $PNPassword = "YOUR_PASSWORD";

# If you trust this script enough, you can have it delete the results file after successful submissions.
my $DeleteAfterSend = 0;

# The spider's log file.  Use an explicit path to place the file somewhere other than the current directory.
my $SpiderLog = "spider.log";

# 0 for deep debugging; 1 for information; 2 for warnings; 3 for errors only; 4 or higher for silence.
my $LoggingLevel = 1;

# Set to 4 for crontab usage.
my $ConsoleLevel = 1;




#################################################
# You should not need to modify anything below...
#

my $Cookies = HTTP::Cookies->new;
my $LogOpen = 0;


# Change into the appropriate directory, if passed as a parameter.  Intended for crontabs.

if (defined($ARGV[0])) {
   if (! chdir ($ARGV[0])) {
      print STDERR "ERROR: Cannot change to directory \"${ARGV[0]}\n";
      exit;
   }
}

Log(1, "Submission spider starting...");

if (! -e "results.txt" || -z "results.txt") {
   Log(2, "No results.txt file exists or it is empty.  Exiting.");
   LogClose();
   exit;
}

# Attempt to log into Mersenne PrimeNet.

if (LogIntoPrimeNet($PNUsername, $PNPassword)) {
   # Log in successful.  Do the submissions...

   DoSubmissions();
}

Log(1, "Spider has finished.  Exiting.\n\n");
LogClose();

exit;


sub DoSubmissions {
   my ($Now) = UTCString();
   my $Success;

   # Move the results file so it won't get clobbered.
   rename("results.txt","submitting_${Now}.txt");

   # Sleep for two seconds to ensure any other users of results.txt finish up.
   sleep(2);

   if (open (IN, "submitting_${Now}.txt")) {
      while () {
         $Success = DoSubmit($_);

         if (! $Success) {
            close (IN);
            Log(3, "Submission not complete.  Moving results file to \"not_completely_submitted_${Now}.txt\"");
            rename("submitting_${Now}.txt", "not_completely_submitted_${Now}.txt");
            return;
         }
         sleep(1);  # To lessen the load on PrimeNet
      }
   } else {
      Log(3, "Weird.  We couldn't open \"submitting_${Now}.txt\"");
      rename("submitting_${Now}.txt", "not_completely_submitted_${Now}.txt");
      return;
   }

   close (IN);

   if ($DeleteAfterSend) {
      Log(1, "Submission complete.  Deleting results file.");
      unlink("submitting_${Now}.txt");
   } else {
      Log(1, "Submission complete.  Moving results file to \"submitted_${Now}.txt\"");
      rename("submitting_${Now}.txt", "submitted_${Now}.txt");
   }
}


sub DoSubmit() {
   my ($Line) = $_;
   my ($Page, $Succ, $Text);
   my ($Result, $GHzDays, $Candidate);
   my $Success = 1;

   if ($Line =~ /no factor for (M\d*)/ || $Line =~ /(M\d*) has a factor/ 
      || $Line =~ /(M\d*) no factor from/) {

      $Candidate = $1;

      $Page = "manual_result/";

      ($Succ, $Text) = TalkToPrimeNet($Page, $Line);

      if ($Succ) {
         ($Result, $GHzDays) = ParseResponse($Text);
         if ($Result == 0) {
            Log(1, "${Candidate} submitted; ${GHzDays} GHz Days credit.");
         } elsif ($Result == 40) {
            Log(2, "${Candidate} result not needed.");
         } else {
            # Probably want to handle other codes non-fatally in the future, such as "Database not available".
            Log(3, "Error from PrimeNet. Code: ${Result}; Message: \"${Text}\"");
            $Success = 0;
         }
      } else {
         Log(3, "Error from Network. Message: \"${Text}\"");
         $Success = 0;
      }
   }

   return ($Success);
}


sub ParseResponse {
   my ($Result) = @_;

   if ($Result =~ /CPU credit is (.*) GHz-days./) {
      return (0, $1);
   }

   if ($Result =~ /Error code: (\d*), error text:(.*)\n/) {
      return ($1, $2);
   }

   return (99, $Result);
}


sub LogIntoPrimeNet {
   my ($UN, $PW) = @_;
   my ($ua, $Response, $Success, $Text);
   my $Page = '/account/default.php';

   Log(1, "Attempting to log into PrimeNet.  This can take a little while...");

   $ua = LWP::UserAgent->new();
   $ua->agent("GPU_to_72 Submission Spider (v 0.25)");
   $ua->timeout(20);
   $ua->cookie_jar($Cookies);

   $Response = $ua->post("${PrimeNetURL}${Page}", { 'user_login' => $UN,
                                                    'user_password' => $PW } );

   if ($Response->is_success) {
      $Text = $Response->decoded_content();

      $Success = 1;
      Log(0, "Response was:\n${Text}\n\n");
   } else {
      $Text = $Response->status_line;

      Log(3, "Bad response:\n${Text}\n\n");
      $Success = 0;
   }

   return $Success;
}


sub TalkToPrimeNet {
   my ($Page, $Data) = @_;
   my ($Browser, $Response, $Success, $Text);

   $Success = 1;

   $Browser = LWP::UserAgent->new;
   $Browser->agent("GPU_to_72 Submission Spider");
   $Browser->timeout(20);
   $Browser->cookie_jar($Cookies);

   $Response = $Browser->post("${PrimeNetURL}${Page}", { 'data' => $Data } );

   if ($Response->is_success) {
      $Text = $Response->content;

      Log(0, "Response was:\n${Text}\n\n");
   } else {
      $Text = $Response->status_line;

      Log(3, "Bad response:\n${Text}\n\n");
      $Success = 0;
   }

   return ($Success, $Text);
}


sub Log {
   my ($Level, $Msg) = @_;
   my @Levels = ("DEEP", "INFO", "WARN", "ERR ");
   my $Now = UTCString();

   if (! $LogOpen) {
      if (open (LOG, ">>${SpiderLog}")) {
         $LogOpen = 1;
      } else {
         print STDERR "ERROR: Could not open log file: \"${SpiderLog}\".  Exiting\n";
         exit;
      }
   }

   if ($Level >= $LoggingLevel) {
      print LOG "${Now} ${Levels[$Level]}:  ${Msg}\n";
   }

   if ($Level >= $ConsoleLevel) {
      print "${Now} ${Levels[$Level]}:  ${Msg}\n";
   }
}


sub LogClose {
   if ($LogOpen) {
      close(LOG);
   }
}


sub UTCString {
   my ($sec,$min,$hour,$mday,$mon,$year) = gmtime(time);

   return sprintf("%04d%02d%02d_%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
}

Copyright 2009 - 2021 Ideas 4 Lease (Barbados) (I4L). "GPU72" and "GPU to 72" are Service Marks of I4L. All rights reserved. Privacy Policy